본문으로 건너뛰기

12.5 GitOps 개요

지금까지 배포의 마지막 단계는 항상 사람이 실행하는 kubectl apply -khelm upgrade였다. 이 방식의 약점은 명령이 아니라 기록에 있다. 누가 언제 어떤 매니페스트를 적용했는지는 실행한 사람의 터미널에만 남고, 급한 대응으로 kubectl edit한 수정은 어떤 저장소에도 없으며, 시간이 지나면 클러스터의 실제 상태와 저장소의 매니페스트가 조용히 어긋난다. GitOps는 이 문제를 배포의 방향을 뒤집어서 푼다. 사람이 클러스터에 직접 적용하는 것이 아니라, 클러스터 안의 에이전트가 Git 저장소를 감시하며 스스로 상태를 맞추는 pull 기반 배포다.

Git을 단일 진실 원천으로

GitOps에서 Git 저장소는 클러스터가 도달해야 할 상태의 단일 진실 원천(single source of truth)이다. 배포는 kubectl 명령이 아니라 매니페스트 변경 PR의 머지이고, 롤백은 git revert이고, 배포 이력은 커밋 로그다. 변경 리뷰, 승인, 감사 추적이 전부 이미 쓰고 있는 Git의 기능으로 해결된다.

에이전트의 동작은 이 노트에서 반복해 본 reconciliation 그 자체다. Git의 선언 상태와 클러스터의 현재 상태를 비교하고, 차이가 있으면 apply한다. 12.4까지의 컨트롤러가 클러스터 안 객체를 선언으로 삼았다면, GitOps 에이전트는 그 선언의 원본을 클러스터 밖 Git으로 옮긴 컨트롤러다.

    flowchart TD
  DEV["개발자"] -->|"PR 머지"| GIT["Git 저장소"]
  AGENT["클러스터 안 에이전트"] -->|"주기적 pull"| GIT
  AGENT -->|"비교·apply"| API["apiserver"]
  

pull 방식에는 보안상의 부수 효과도 있다. CI 시스템이 클러스터에 직접 apply하는 push 방식은 CI에 클러스터 관리자 자격 증명을 보관해야 하지만, pull 방식에서는 에이전트가 클러스터 안에서 바깥의 Git을 읽기만 하므로 클러스터 자격 증명이 외부로 나가지 않는다. CI의 역할은 이미지를 빌드해 레지스트리에 올리고 매니페스트 저장소의 태그를 갱신하는 데서 끝나고, 클러스터 반영은 전적으로 에이전트의 몫이 된다. 빌드 파이프라인(CI)과 배포(CD)가 저장소를 경계로 분리되는 구조다.

Argo CD와 Flux

이 패턴의 대표 구현이 Argo CD와 Flux다. 둘 다 CNCF graduated 프로젝트이고, Helm 차트와 Kustomize overlay를 렌더러로 지원한다는 점에서 12.1, 12.2와 자연스럽게 이어진다.

항목Argo CDFlux
동기화 단위 CRDApplication, ApplicationSetGitRepository, Kustomization, HelmRelease
UI내장 웹 UI없음 (CLI, 서드파티 UI)
지원 렌더러Helm, Kustomize, plain YAMLHelm, Kustomize, plain YAML
CNCF 단계graduatedgraduated

Argo CD는 Application이라는 CRD로 “이 Git 경로를 이 클러스터의 이 namespace에 동기화한다"를 선언하고, 내장 웹 UI에서 앱 단위의 동기화 상태와 리소스 트리를 보여준다. 배포 상태를 여러 사람이 눈으로 확인하는 조직에서 채택률이 높다. Flux는 UI 없이 GitRepository, Kustomization, HelmRelease 같은 CRD의 조합으로 구성하는 컨트롤러 묶음이라, Kubernetes 리소스 모델 안에서 전부 선언하는 방식을 선호하는 팀과 어울린다. 개요 수준에서는 어느 쪽이든 이 절의 개념이 그대로 적용된다.

drift 감지와 자동 동기화

Git과 클러스터 실제 상태의 어긋남을 drift라고 부른다. 사람이 kubectl edit로 직접 고친 값, 실험하다 남긴 리소스, 다른 도구가 덮어쓴 필드가 전부 drift다. 에이전트는 주기적인 비교에서 이것을 감지해 OutOfSync로 표시하고, 자동 동기화를 켜 두면 표시에서 끝나지 않고 Git의 상태로 되돌린다. Argo CD 기준으로 selfHeal은 수동 변경을 Git 상태로 원복하는 옵션이고, prune은 Git에서 삭제된 리소스를 클러스터에서도 삭제하는 옵션이다.

자동 동기화가 켜진 클러스터에서는 kubectl edit로 적용한 긴급 수정이 다음 동기화 주기에 사라진다. 도구의 오동작이 아니라 GitOps가 약속한 동작 그대로다. 긴급 대응 절차를 Git 경유로 재설계하거나, 최소한 특정 앱의 동기화를 일시 중지하는 절차를 미리 마련해 두어야 새벽의 수정이 헛수고가 되지 않는다.

자동 동기화를 끄고 감지만 하는 운용도 가능하다. 도입 초기에는 OutOfSync 표시와 diff 확인으로 어긋남의 규모를 파악하고, Git 경유 절차가 팀에 자리 잡은 뒤에 selfHeal과 prune을 켜는 단계적 접근이 흔하다. drift를 되돌리는 힘은 GitOps의 안전판이면서 동시에 규율 요구이기도 하다. 이 방식이 자리 잡으면 클러스터는 손으로 고치는 대상이 아니라 Git의 반영 결과라는 감각이 팀 전체에 공유되어야 한다.

도입 시 따져볼 것

가장 먼저 부딪히는 것은 시크릿이다. Git이 진실 원천이면 Secret도 Git에 있어야 하는데, 평문 커밋은 불가능하다. 암호화한 형태로 커밋하고 클러스터 안에서 복호화하는 Sealed Secrets나 SOPS, Git에는 참조만 두고 실제 값은 외부 비밀 관리 시스템에서 가져오는 External Secrets Operator가 통용되는 답이다. 어느 방식을 고르든 복호화 열쇠나 외부 시스템 접근 권한만큼은 Git 밖에서 별도로 보호해야 한다는 전제는 같다. Secret 자체의 성질은 Part VII. 구성과 시크릿에서 다뤘다.

멀티 클러스터 구조도 방식이 갈린다. Argo CD 인스턴스 하나가 여러 클러스터를 원격으로 관리하는 중앙 집중형과, 클러스터마다 에이전트를 두는 분산형 중에서 장애 반경과 권한 관리 기준으로 선택하게 된다. 중앙 집중형은 전체 배포 상태가 한 화면에 모이는 대신 그 인스턴스가 모든 클러스터의 자격 증명을 보유하는 집중점이 되고, 분산형은 반대의 성질을 갖는다. 승인 흐름은 Git의 것을 그대로 쓰되 환경 구분과 결합해 설계한다. 환경별 브랜치보다 저장소 안 디렉토리 분리가 일반적인 관례이고, 이 지점에서 Kustomize의 overlays 구조가 GitOps 저장소의 표준 골격처럼 재등장한다. prod 디렉토리에만 엄격한 리뷰 규칙을 적용하면 PR 승인이 곧 배포 승인이 된다.

무엇을 갖춰야 하는지가 분명하다는 것 자체가 이 방식의 성숙도를 보여준다. GitOps는 새로운 개념이라기보다, 선언형 상태와 reconciliation이라는 Kubernetes의 원리를 배포 파이프라인의 끝까지 확장한 결과다.