Why Argo Split Into Four
한국어 원문으로 표시합니다.
한 줄 요약
Argo 는 배포 도구 하나가 아니라 네 개의 CNCF 프로젝트 묶음이다. Argo CD 는 "클러스터가 Git 과 같은 상태인가", Workflows 는 "이 작업들을 정해진 순서로 끝까지 돌려라", Events 는 "바깥에서 무슨 일이 생기면 그때", Rollouts 는 "새 버전을 얼마나 천천히 내보낼까" 를 각각 푼다. 네 개를 하나로 합치지 않은 이유는 각자가 다루는 시간의 성격이 다르기 때문이다.
왜 이게 필요했나
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 매니페스트를 직접 쓰면서, 방금 말한 "차이를 계속 지우는 루프" 가 어떤 필드들로 조립되는지 손으로 확인한다.