--- id: devops-argocd-gitops title: ArgoCD / GitOps — Git = Source of Truth category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [devops, argocd, gitops, vibe-coding] tech_stack: { language: "YAML / K8s", applicable_to: ["DevOps"] } applied_in: [] aliases: [ArgoCD, Flux, GitOps, declarative deploy, App of Apps, sync wave] --- # ArgoCD / GitOps > "Git = production state". **ArgoCD / Flux 가 Git 의 manifest → cluster 자동 sync**. Push 대신 pull. Kubernetes 의 modern 배포 표준. ## 📖 핵심 개념 - Git = single source of truth. - Pull-based: ArgoCD 가 git poll → apply. - Drift detection: cluster ≠ git → 자동 fix. - App of Apps: meta-app 가 다른 app 관리. ## 💻 코드 패턴 ### ArgoCD 설치 ```bash kubectl create namespace argocd kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml # CLI brew install argocd argocd login # UI kubectl port-forward svc/argocd-server -n argocd 8080:443 ``` ### Application ```yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: my-app namespace: argocd spec: project: default source: repoURL: https://github.com/myorg/k8s-manifests targetRevision: main path: apps/my-app/overlays/prod destination: server: https://kubernetes.default.svc namespace: prod syncPolicy: automated: prune: true # Git 에서 제거된 자원 cluster 에서도 제거 selfHeal: true # Manual 변경 시 자동 revert allowEmpty: false syncOptions: - CreateNamespace=true - PrunePropagationPolicy=foreground - PruneLast=true retry: limit: 5 backoff: duration: 5s factor: 2 maxDuration: 3m ``` → Git push → 1-3 min 안 cluster 자동 update. ### Helm + ArgoCD ```yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: { name: my-app } spec: source: repoURL: https://github.com/myorg/charts targetRevision: HEAD path: charts/my-app helm: valueFiles: - values.yaml - values-prod.yaml parameters: - name: image.tag value: 1.5.0 ``` ### Kustomize + ArgoCD ```yaml spec: source: repoURL: https://github.com/myorg/manifests path: apps/my-app/overlays/prod kustomize: images: - my-app=ghcr.io/myorg/my-app:1.5.0 namePrefix: prod- ``` ### App of Apps (meta) ```yaml # argocd/applications.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: { name: bootstrap, namespace: argocd } spec: source: repoURL: https://github.com/myorg/argocd path: apps/ targetRevision: HEAD destination: server: https://kubernetes.default.svc namespace: argocd syncPolicy: automated: { prune: true, selfHeal: true } ``` ``` apps/ ├── application.yaml ├── api.yaml # Application ├── web.yaml # Application ├── monitoring.yaml # Application └── ... ``` → 한 ArgoCD app 가 다른 app 들 관리. ### ApplicationSet (multi-cluster / multi-tenant) ```yaml apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: { name: my-app, namespace: argocd } spec: generators: - clusters: {} # 모든 등록된 cluster template: metadata: name: my-app-{{name}} spec: project: default source: repoURL: ... path: apps/my-app destination: server: '{{server}}' namespace: prod ``` → N cluster 에 자동 deploy. ### Sync waves (dependency order) ```yaml # Wave 0 (CRDs) metadata: annotations: argocd.argoproj.io/sync-wave: "0" # Wave 1 (DB) metadata: annotations: argocd.argoproj.io/sync-wave: "1" # Wave 2 (App) metadata: annotations: argocd.argoproj.io/sync-wave: "2" ``` → ArgoCD 가 wave 별 순차 deploy. ### Hooks ```yaml # DB migration before app deploy metadata: annotations: argocd.argoproj.io/hook: PreSync argocd.argoproj.io/hook-delete-policy: HookSucceeded ``` ### RBAC ```yaml apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: { name: prod, namespace: argocd } spec: description: Production sourceRepos: - 'https://github.com/myorg/*' destinations: - namespace: 'prod-*' server: '*' roles: - name: deployer policies: - p, proj:prod:deployer, applications, sync, prod/*, allow groups: - acme:platform ``` ### Notifications ```yaml # notifications.yaml service.slack: token: ... template.app-sync-succeeded: | message: 'Application {{.app.metadata.name}} synced successfully' trigger.on-sync-succeeded: | - when: app.status.operationState.phase in ['Succeeded'] send: [app-sync-succeeded] subscriptions: - recipients: [slack:engineering] triggers: [on-sync-succeeded, on-sync-failed] ``` ### CI workflow ```yaml # .github/workflows/deploy.yml - name: Build + push image run: docker build -t $REGISTRY/my-app:$SHA . && docker push $REGISTRY/my-app:$SHA - name: Update manifest run: | cd k8s-manifests yq -i '.image.tag = "${{ github.sha }}"' apps/my-app/values-prod.yaml git add . && git commit -m "Deploy my-app ${{ github.sha }}" && git push ``` → Git push = ArgoCD 자동 deploy. 직접 kubectl 안 함. ### Health check (ArgoCD 가 status 추정) ```yaml # Custom health check (Lua) metadata: name: argocd-cm data: resource.customizations: | networking.k8s.io/Ingress: health.lua: | hs = {} if obj.status ~= nil and obj.status.loadBalancer ~= nil then ... end return hs ``` ### Drift / self-heal ``` 사용자가 cluster 에서 manual 변경: - ArgoCD detect drift - selfHeal: true → 자동 git state 로 revert - selfHeal: false → 알람 만 ``` ### Rollback ```bash # History argocd app history my-app # Rollback to specific revision argocd app rollback my-app 5 # 또는 git revert git revert git push # ArgoCD 자동 sync ``` ### Image automation (Argo Image Updater) ```yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: my-app annotations: argocd-image-updater.argoproj.io/image-list: my-app=ghcr.io/myorg/my-app argocd-image-updater.argoproj.io/my-app.update-strategy: semver argocd-image-updater.argoproj.io/my-app.allow-tags: "regexp:^v[0-9]+\\.[0-9]+\\.[0-9]+$" ``` → 새 image tag 자동 detect → manifest update + commit. ### Flux (alternative) ```yaml apiVersion: source.toolkit.fluxcd.io/v1 kind: GitRepository metadata: { name: my-app, namespace: flux-system } spec: url: https://github.com/myorg/manifests ref: { branch: main } apiVersion: kustomize.toolkit.fluxcd.io/v1 kind: Kustomization metadata: { name: my-app, namespace: flux-system } spec: sourceRef: { kind: GitRepository, name: my-app } path: apps/my-app/overlays/prod prune: true interval: 1m ``` → ArgoCD 와 비슷. Git-friendly UX. ### Flux vs ArgoCD ``` ArgoCD: + UI 강력 + 큰 ecosystem + App-centric Flux: + Cloud-native (CNCF graduated) + More automation features + Lighter weight ``` → ArgoCD 가 더 인기 (2024+). ### Multi-cluster ``` 1. ArgoCD 가 central cluster 2. argocd cluster add — 다른 cluster 3. Application destination 이 어느 cluster 4. ApplicationSet 가 여러 cluster 동시 deploy ``` ### Progressive delivery (Argo Rollouts) ```yaml apiVersion: argoproj.io/v1alpha1 kind: Rollout metadata: { name: my-app } spec: replicas: 10 strategy: canary: steps: - setWeight: 10 - pause: { duration: 5m } - setWeight: 25 - pause: { duration: 5m } - setWeight: 50 - pause: { duration: 10m } - setWeight: 100 analysis: templates: - templateName: success-rate startingStep: 1 ``` → Auto canary + rollback if metric 깨짐. ### Sealed Secrets / External Secrets ```yaml # Secret 가 git 안 안전 — encrypted apiVersion: bitnami.com/v1alpha1 kind: SealedSecret spec: encryptedData: api-key: AgB7... ``` → ArgoCD 가 sealed secret apply → controller decrypt. ## 🤔 의사결정 기준 | 상황 | 추천 | |---|---| | K8s + Git workflow | ArgoCD | | Cloud-native official | Flux | | Multi-cluster | ApplicationSet | | Progressive delivery | Argo Rollouts | | 작은 / 단순 | Manual kubectl + CI | | 매우 큰 organization | + Crossplane | ## ❌ 안티패턴 - **kubectl apply manual prod**: drift / no audit. - **Image tag latest**: hash 안 명시 — 추적 불가. - **Secret in git plain**: leak. - **Sync 너무 자주 (1s)**: API rate limit. - **Self-heal off**: drift 누적. - **App of Apps 없이 100 application**: 관리 불가. - **Sync wave 무시**: dependency 순서 깨짐. ## 🤖 LLM 활용 힌트 - Git push = production update. - App of Apps 패턴 + ApplicationSet. - Argo Rollouts = canary / blue-green 자동. - Sealed / External Secrets. ## 🔗 관련 문서 - [[DevOps_Helm_Deep]] - [[DevOps_Kubernetes_Basics]] - [[Backend_Maintenance_Mode]]