LabHub
배우기 러닝패스 코스

CAPA — Argoプロジェクト認定アソシエイト

Argoはなぜ四つに割れたのか

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

한 줄 요약

Argo 는 배포 도구 하나가 아니라 네 개의 CNCF 프로젝트 묶음이다. Argo CD 는 "클러스터가 Git 과 같은 상태인가", Workflows 는 "이 작업들을 정해진 순서로 끝까지 돌려라", Events 는 "바깥에서 무슨 일이 생기면 그때", Rollouts 는 "새 버전을 얼마나 천천히 내보낼까" 를 각각 푼다. 네 개를 하나로 합치지 않은 이유는 각자가 다루는 시간의 성격이 다르기 때문이다.

フロー図: 경계가 흐려지는 자리・CD 와 Rollouts 는 소유권을 두고 부딪힌다.・CD 가 카나리를 되돌리는・Workflows 와 Events 는 "무엇이 방아쇠인가" 로 나뉜다.

왜 이게 필요했나

GitOps 이전의 배포는 대부분 푸시였다. CI 서버가 빌드를 끝내고 kubectl apply 를 때린다. 이 구조에는 세 가지 값이 숨어 있다. 첫째, CI 서버가 프로덕션 클러스터의 자격 증명을 들고 있어야 한다. 젠킨스 한 대가 뚫리면 모든 클러스터가 뚫린다. 둘째, 적용한 뒤에 누가 손으로 replicas 를 바꿔도 아무도 모른다. Git 은 배포 시점의 기록일 뿐 현재 상태의 근거가 아니다. 셋째, 클러스터가 여러 개가 되는 순간 파이프라인이 클러스터 수만큼 늘어난다.

풀 모델은 이 셋을 한 번에 뒤집는다. 클러스터 안에 사는 컨트롤러가 Git 을 읽고 스스로 맞춘다. 자격 증명은 밖으로 나가지 않고, 컨트롤러가 계속 비교하니 드리프트가 상태로 드러나며, 클러스터가 늘어도 Git 저장소는 하나다. Argo CD 가 하는 일이 정확히 이것이고, 나머지 셋은 이 축을 중심으로 붙은 위성이다.

어떻게 동작하나

프로젝트 푸는 문제 시간의 성격
Argo CD 선언한 상태와 실제 상태의 차이를 계속 지운다 끝나지 않는 루프 (기본 180초 주기)
Argo Workflows 여러 컨테이너 작업을 DAG 로 엮어 한 번 완주시킨다 시작과 끝이 있는 유한한 실행
Argo Events 외부 신호를 받아 무언가를 시작시킨다 언제 올지 모르는 이벤트
Argo Rollouts 새 버전을 지표를 보며 조금씩 늘린다 사람의 판단이 끼어드는 몇 분에서 몇 시간

합치지 않은 이유가 여기서 보인다. Argo CD 는 "차이가 0 이 될 때까지" 도는 물건이라 완료 개념이 없다. Workflows 는 반대로 완료가 전부다. 이 둘을 한 컨트롤러에 넣으면 재시도 의미도, 실패 처리도, 메트릭 이름도 전부 충돌한다. Events 가 따로 있는 것도 같은 이유다. Argo CD 에 웹훅 처리를 넣기 시작하면 GitHub, S3, Kafka, 캘린더까지 전부 배포 컨트롤러 안으로 들어온다. Events 는 그 어댑터 지옥을 EventSource → Sensor → Trigger 라는 별도 축으로 빼낸 것이다.

Argo CD 는 CNCF Graduated 프로젝트다. 졸업 등급이 뜻하는 실무적 함의는 "이 API 가 다음 분기에 갈아엎히지 않는다" 이고, 그래서 Application 매니페스트를 조직 표준으로 삼아도 괜찮다는 판단의 근거가 된다.

현장에서 만나는 모습

저자의 홈랩은 컨트롤 플레인 3대와 GPU 워커 4대, 합쳐서 7노드다. MetalLB 가 10.0.0.200부터 215까지를 L2 로 나눠 주고 그 위에 Gitea 가 10.0.0.200, ArgoCD 가 10.0.0.201, Harbor 가 10.0.0.202 로 떠 있다. 소스와 GitOps 컨트롤러와 레지스트리가 같은 주소 대역 안에 나란히 있는 셈인데, 이 배치의 진짜 값어치는 장애가 났을 때 드러났다.

이 클러스터는 controlPlaneEndpoint 가 VIP 가 아니라 첫 컨트롤 플레인의 물리 IP 로 박혀 있었다. etcd 멤버는 3개라 쿼럼은 멀쩡한데, 그 노드가 죽으면 kubectl 도 kubelet 도 전부 접속 불가가 된다. 컨트롤 플레인은 살아 있는데 아무도 문을 못 찾는 상태다. 여기서 배운 것이 두 가지인데, 하나는 "상태가 Ready 인 것과 실제로 동작하는 것은 다른 명제" 라는 것이고, 다른 하나는 클러스터가 통째로 안 보이는 동안에도 Git 저장소에는 원하는 상태가 그대로 남아 있었다는 것이다. 푸시 파이프라인이었다면 복구 절차가 "파이프라인을 다시 짠다" 였겠지만, 풀 모델에서는 클러스터를 되살리는 순간 컨트롤러가 알아서 따라잡는다. GitOps 를 도입하는 이유를 배포 편의로 설명하는 글이 많은데, 실제로 값어치가 드러나는 순간은 이런 복구 시점이다.

네 프로젝트가 실제로 겹치는 자리

Argo 의 네 프로젝트는 따로 쓸 수 있지만, 함께 쓰면 경계가 흐려지는 자리가 생긴다. 시험에서도 현장에서도 여기서 갈린다.

CD 와 Rollouts 는 소유권을 두고 부딪힌다. Rollouts 가 카나리를 진행하는 동안 Deployment 의 replicas 나 이미지가 바뀌는데, CD 는 그것을 git 과의 차이로 본다. ignoreDifferences 로 그 필드를 빼 주지 않으면 CD 가 카나리를 되돌리는 고리가 생긴다.

Workflows 와 Events 는 "무엇이 방아쇠인가" 로 나뉜다. Workflows 는 단계가 있는 작업을 돌리는 엔진이고, Events 는 바깥의 사건(웹훅, 큐 메시지, 일정)을 받아 그 엔진을 부르는 층이다. Events 없이 Workflows 만 쓰면 사람이 시작해야 하고, Workflows 없이 Events 만 쓰면 받은 사건으로 할 일이 단순한 것에 그친다.

동기화 순서가 필요한 자리에는 훅과 물결(wave)을 쓴다. 네임스페이스가 있어야 CRD 를 넣고, CRD 가 있어야 그것을 쓰는 자원을 넣는다. 순서를 안 주면 첫 동기화가 실패하고, 재시도로 우연히 성공한다. argocd.argoproj.io/sync-wave 로 순서를 적고, 마이그레이션처럼 한 번만 돌아야 하는 것은 PreSync 훅에 둔다.

앱을 만드는 앱(App of Apps)은 편하지만 되돌리기가 어렵다. 부모 앱을 지우면 자식이 함께 지워지는지가 finalizer 에 달려 있고, 이것을 모르고 지웠다가 클러스터 전체가 비는 일이 실제로 일어난다. 부모와 자식의 삭제 정책을 명시적으로 정해 둔다.

어느 쪽이든 진짜 어려움은 권한이다. CD 가 여러 클러스터에 배포하려면 그 클러스터의 권한을 들고 있어야 하고, 그것은 CD 를 뚫으면 전부 뚫린다는 뜻이다. 프로젝트별로 대상 네임스페이스와 자원 종류를 제한하는 AppProject 가 그래서 선택이 아니라 기본이다.

다음 퀴즈에서 확인할 것

이 모듈은 개념만 다룬다. 바로 이어지는 모듈에서 Application 매니페스트를 직접 쓰면서, 방금 말한 "차이를 계속 지우는 루프" 가 어떤 필드들로 조립되는지 손으로 확인한다.