GitOps와 ArgoCD 정리
GitOps란
GitOps란 시스템의 원하는 상태(desired state)를 Git에 선언적으로 저장하고, 에이전트가 이를 pull해 실제 상태와 지속적으로 맞추는 운영 방식
OpenGitOps가 정의한 네 원칙으로 정리됨.
- 선언적(Declarative): 원하는 상태를 절차가 아닌 선언으로 기술 → 무엇을 원하는지만 적고 도달 방법은 시스템에 맡김
- 버전 관리·불변(Versioned and Immutable): 상태를 불변·버전 이력이 남는 저장소에 보관 → Git이 단일 진실 원천(Single Source of Truth)
- 자동 pull(Pulled Automatically): 소프트웨어 에이전트가 저장소에서 desired state를 스스로 가져옴
- 지속 조정(Continuously Reconciled): 에이전트가 실제 상태를 상시 관측하고 desired state로 되돌림
핵심은 "Git 커밋 = 배포"라는 등식. 클러스터 변경은 kubectl이 아니라 Git 커밋으로만 일어나야 함.
push형 CI/CD와의 차이
기존 CI/CD는 파이프라인이 클러스터로 매니페스트를 밀어넣는 push 모델, GitOps는 클러스터 내부 에이전트가 Git을 당겨오는 pull 모델.
| 비교 항목 | push (기존 CI/CD) | pull (GitOps) |
|---|---|---|
| 배포 주체 | CI 러너가 외부에서 kubectl apply | 클러스터 내부 에이전트가 Git pull 후 적용 |
| 크리덴셜 위치 | CI 시스템이 클러스터 자격증명 보관 → 외부 노출 | 자격증명이 클러스터를 벗어나지 않음 |
| drift 처리 | 파이프라인 미실행 구간의 수동 변경 방치 | 에이전트가 desired와 live를 상시 비교 후 자동 조정 |
| 감사(Audit) | 배포 이력이 CI 로그에 산재 | Git 커밋 히스토리가 단일 감사 원장 |
| 롤백 | 이전 파이프라인 재실행 | 이전 커밋으로 revert |
- push는 CI가 클러스터 밖에서 권한을 쥐므로 자격증명이 파이프라인에 상주 → 유출 표면↑
- pull은 에이전트가 방화벽 안에서 저장소를 읽기만 하면 됨 → 클러스터 API를 외부에 열지 않아도 됨
- drift는 pull 모델에서만 자동으로 잡힘. push는 배포 순간 외에는 실제 상태를 감시하지 않음
- 실무에선 하이브리드가 흔함 → CI는 빌드·테스트·이미지 push까지, CD는 GitOps 에이전트가 전담하는 분업
- pull이 항상 우위는 아님. 클러스터 밖 리소스(외부 DNS·SaaS 설정)는 여전히 push형 파이프라인이 필요
ArgoCD 아키텍처
Argo CD는 Git의 desired state와 클러스터의 live state 차이를 지우는 조정 루프(reconcile loop)를 도는 Kubernetes 컨트롤러 집합.
graph LR
Dev["개발자<br>git push"] --> Git["Git 저장소<br>(desired state)"]
Git --> Repo["repo-server<br>매니페스트 생성"]
Repo --> Ctrl["application-controller<br>desired vs live 비교"]
Ctrl -->|apply| K8s["Kubernetes<br>API server"]
K8s -->|live state| Ctrl
UI["Web UI / CLI"] --> API["api-server<br>gRPC/REST"]
API --> Ctrl
Redis["Redis<br>캐시"] -.-> Ctrl
Redis -.-> Repo
| 컴포넌트 | 역할 |
|---|---|
| api-server | gRPC/REST API 노출, Web UI·CLI·CI/CD가 소비. 인증·RBAC·Git webhook 처리 |
| repo-server | Git 저장소 로컬 캐시 유지, revision별로 Kubernetes 매니페스트 생성·반환 |
| application-controller | Application의 desired(Git)와 live(클러스터) 비교, 차이 조정 및 sync hook 실행 |
| Redis | Kube API·Git 요청을 줄이는 캐시 계층. 상태를 영구 저장하지 않음 |
| ApplicationSet controller | 다중 클러스터·다중 환경에 Application을 템플릿으로 대량 생성 |
| Dex(선택) | 외부 IdP 연동용 OIDC 인증 프록시 |
- repo-server는 kustomize·helm·jsonnet 등 템플릿 도구를 렌더링해 순수 매니페스트로 변환하는 지점
- application-controller가 조정 루프의 심장. 여기가 부하 집중점이라 대규모에서 샤딩 대상이 됨
- Redis는 캐시라 유실돼도 재계산으로 복구됨 → HA 구성 시에도 데이터 저장소가 아니라 성능 계층으로 취급
Application CRD
- Argo CD의 배포 단위는 Application 커스텀 리소스(CRD)
- 필드: source(Git repo·path·revision), destination(클러스터·네임스페이스), syncPolicy(동기화 방식)
- Application 자체를 Git으로 관리하는 방식이 App of Apps 패턴 → 상위 Application이 하위 Application들을 선언
application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: dotoryeee-web #Application 이름
namespace: argocd #Argo CD 설치 네임스페이스
spec:
project: default #소속 AppProject
source:
repoURL: https://github.com/dotoryeee/gitops.git #desired state 저장소
targetRevision: main #추적할 브랜치·태그
path: apps/web #매니페스트 경로
destination:
server: https://kubernetes.default.svc #배포 대상 클러스터
namespace: web #배포 대상 네임스페이스
syncPolicy:
automated:
prune: true #Git에서 삭제된 리소스 제거
selfHeal: true #클러스터 drift를 Git 상태로 복원
동기화 정리
sync는 Git 매니페스트를 클러스터에 적용하는 동작, 정책과 순서·상태 판정으로 세분화됨.
| 정책 | 동작 |
|---|---|
| manual | 사용자가 UI·CLI·API로 sync를 명시적으로 트리거 |
| automated | Git 변경을 감지하면 자동으로 sync |
| prune | Git에서 사라진 리소스를 클러스터에서 제거. 기본 비활성 |
| selfHeal | 클러스터에서 직접 수정된 리소스를 Git 상태로 되돌림 |
- automated만 켜고 prune을 끄면 삭제만 반영 안 됨 → 유령 리소스가 남을 수 있음
- selfHeal은 drift 방어의 핵심. 끄면 수동 kubectl 변경이 다음 Git 커밋까지 살아남음
- 긴급 수정 시 selfHeal이 롤백을 방해할 수 있음 → hotfix도 Git 커밋으로 반영하는 습관이 전제
- automated여도 실제 apply 전 diff는 UI·
argocd app diff로 확인 가능
sync wave와 hook
- Phase: PreSync → Sync → PostSync 순으로 매니페스트 적용 단계가 갈림
- hook:
argocd.argoproj.io/hook어노테이션으로 특정 Phase에 Job·Pod를 끼워 넣음 (마이그레이션·스모크 테스트 등) - sync wave:
argocd.argoproj.io/sync-wave정수로 같은 Phase 내 적용 순서를 지정. 낮은 wave 먼저 적용 - prune 시에는 wave 순서가 역전됨 → 높은 wave부터 제거
health 판정
- Argo CD는 리소스별 health를 평가하고, 하위 리소스 중 가장 나쁜 상태를 Application health로 승격
- 우선순위(건강→불량): Healthy → Suspended → Progressing → Missing → Degraded → Unknown
- sync 상태(Synced/OutOfSync)와 health는 별개 축. Synced여도 Pod가 CrashLoop면 Degraded
Flux와의 비교
둘 다 CNCF graduated GitOps 컨트롤러지만 형태가 다름. Argo CD는 올인원 애플리케이션 플랫폼, Flux는 조립형 컨트롤러 집합.
| 비교 항목 | Argo CD | Flux |
|---|---|---|
| UI | 내장 Web 대시보드 제공 | 공식 UI 없음(Flux Operator Web UI 등 별도) |
| 멀티테넌시 | AppProject + RBAC로 논리 격리 | 컨트롤러·CRD가 네임스페이스 단위 분리, 테넌시가 설계 전제 |
| CRD 구조 | Application·ApplicationSet 단일 계열 | source·kustomize·helm·notification 컨트롤러별 CRD 분리 |
| 시크릿 복호화 | 플러그인 연동(KSOPS·vault-plugin 등) | kustomize-controller가 SOPS 복호화 네이티브 지원 |
| 형태 | 통합 플랫폼(UI·SSO·RBAC 내장) | GitOps Toolkit 컨트롤러 조합 |
| 라이선스·거버넌스 | Apache 2.0, CNCF graduated(2022) | Apache 2.0, CNCF graduated(2022) |
- Flux는 source-controller가 Git·Helm·OCI 소스를 당기고, kustomize/helm-controller가 적용하는 역할 분리 구조
- Argo CD는 UI·SSO·RBAC를 한 배포에 묶어 팀 온보딩이 빠름 → 대시보드로 sync·health를 눈으로 확인
- Flux는 컨트롤러를 골라 얹는 방식이라 Kubernetes 네이티브 통합·자동화 파이프라인에 유리
- 두 진영 모두 image 자동 갱신을 지원. Flux는 image-automation·image-reflector 컨트롤러, Argo CD는 Image Updater로 처리
- 우열보다 운영 형태의 차이. UI·중앙 관리가 중요하면 Argo CD, 컨트롤러 조립·GitOps 자동화가 중요하면 Flux
운영 주의
GitOps는 모든 상태를 Git에 두므로 시크릿·규모·권한 세 축에서 사고가 나기 쉬움.
시크릿 관리
- 평문 Secret을 Git에 커밋하면 안 됨 → 저장소 접근자 전원에게 노출됨
- Sealed Secrets: 컨트롤러가 클러스터 안에서만 복호화 가능한 SealedSecret CRD로 암호화 → 암호문만 Git에 저장
- SOPS: age·PGP 키로 값 단위 암호화. Flux는 kustomize-controller가 네이티브 복호화, Argo CD는 KSOPS·argocd-vault-plugin 등으로 연동
- 외부 저장소 연동: External Secrets Operator로 Vault·클라우드 Secret Manager를 참조하는 방식도 병행 가능
대규모 앱 수 성능
- application-controller가 조정 부하 집중점 → Application 수천 개 규모에서 병목
- 대응: 컨트롤러 샤딩(
--replicas, 클러스터별 분산), repo-server 복제, Redis HA - 매니페스트 렌더링(kustomize·helm)이 무거우면 repo-server CPU·메모리가 먼저 포화됨
- webhook을 걸어 폴링 주기 대신 이벤트 기반 sync로 전환하면 지연·부하 감소
RBAC
- Argo CD 자체 RBAC(policy.csv)로 role·group을 매핑 → 프로젝트·액션 단위 권한 제어
- AppProject로 배포 가능한 소스 저장소·대상 클러스터·네임스페이스를 화이트리스트로 제한
- SSO(OIDC·Dex) 연동 시 IdP 그룹을 Argo CD role에 매핑 → 팀별 권한 경계 유지
- admin 계정은 초기 설정 후 비활성화 권장. 상시 admin 사용은 감사 추적을 흐림
appproject.yaml
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: dotoryeee-team
namespace: argocd
spec:
sourceRepos:
- https://github.com/dotoryeee/gitops.git #허용 저장소 화이트리스트
destinations:
- server: https://kubernetes.default.svc
namespace: web #허용 대상 네임스페이스
결론
- GitOps는 배포 상태의 단일 진실 원천을 Git으로 고정하고 에이전트가 상시 조정하는 운영 모델
- Argo CD는 desired(Git)와 live(클러스터)의 차이를 지우는 조정 루프가 전부
- 시크릿·규모·권한은 Git에 모든 걸 두는 대가 → sealed-secrets·샤딩·RBAC로 각각 방어
- push는 "파이프라인이 클러스터에 밀어넣는 방식", pull은 "클러스터가 Git을 당겨와 스스로 맞추는 방식"