1. 개요# Argo CD 는 Git 에 적힌 쿠버네티스 객체(YAML)를 읽어 클러스터의 실제 상태와 비교하고, 다르면 Git 쪽 내용으로 맞추는 도구다. 사람이 kubectl apply 하던 일을 클러스터 안의 프로그램이 대신 하고, 무엇이 배포돼야 하는지의 기준을 Git 에 둔다. 이 방식을 GitOps 라고 부른다.
kubectl -n argocd get applications # Argo CD 가 관리하는 연결 목록
argocd app diff kukie-dev-apps # Git 과 클러스터의 차이
argocd app sync kukie-dev-apps # Git 쪽으로 맞추기
deployment.yaml service.yaml Argo CD Deployment Service ① 읽기 ③ 다르면 적용 ② 실제 상태 조회 · 비교 Git — 원하는 상태 클러스터 — 실제 상태 ① 정해진 주기(기본 최대 3분)마다 Git 을 읽어 YAML 을 렌더링한다 ② 같은 객체를 클러스터에서 읽어 두 쪽을 비교한다 — 같으면 Synced, 다르면 OutOfSync ③ 동기화하면 Git 쪽 내용으로 클러스터에 적용한다 (수동 또는 자동) Argo CD 는 kubectl apply 를 대신 하는 쪽이다 — 기준은 언제나 Git 이다 2. 구성 요소# Argo CD 자체도 클러스터 안의 Pod 몇 개다. 보통 argocd 네임스페이스에 설치한다.
argocd 네임스페이스 — 보라 상자 하나하나가 Pod (Argo CD 도 같은 클러스터 안에서 돈다) 사람 웹 UI · argocd CLI Git 레포 · Helm 저장소 클러스터 API 서버 argocd-server UI · API 창구 argocd-repo-server 가져와 렌더링 application-controller 비교 · 적용 (주체) argocd-redis 캐시 보기 · Sync clone Sync 요청 렌더링한 YAML 검은 화살표 적용 · 파랑 화살표 읽기·비교 렌더링 결과 · 클러스터 상태 선택 설치 — 켜면 같은 네임스페이스에 Pod 로 더 뜬다 applicationset-controller ApplicationSet → Application 생성 dex SSO 로그인 (argocd-server 앞) notifications 상태 변화 → Slack · 메일 알림 클러스터에 객체를 넣는 건 application-controller 하나다 나머지는 보여 주고(server), 준비하고(repo-server), 기억한다(redis) application-controller 는 넣을 수 있는 권한이 넓다(보통 클러스터 관리자급). 그래서 “무엇을 넣어도 되는가” 를 Argo CD 안에서 다시 좁히는 장치가 5절의 AppProject 다.
3. 추가하는 리소스 종류# 원래 쿠버네티스에는 Application 도 프로젝트도 없다. Argo CD 를 설치하면 CRD 로 아래 세 종류가 API 에 추가되고, Argo CD 를 지우면 함께 사라진다.
Application kind: Application metadata: name: kukie-dev-apps spec: project: kukie-apps ← 이 울타리를 따른다 source: … 어디서 destination: … 어디로 AppProject (별개의 객체) kind: AppProject metadata: name: kukie-apps ← 같은 이름 spec: sourceRepos: […] 울타리 내용 destinations: […] …Whitelist: […] 이름으로 ApplicationSet Application 을 찍어 내는 틀 만든다 (선택) 셋 다 apiVersion argoproj.io/v1alpha1 — 쿠버네티스에는 원래 없고, Argo CD 를 지우면 같이 사라진다 Application 이 AppProject 안에 들어 있는 게 아니다 — 같은 이름 한 줄로 이어진다 kubectl api-resources --api-group= argoproj.io
# applications app,apps argoproj.io/v1alpha1 true Application
# applicationsets appset argoproj.io/v1alpha1 true ApplicationSet
# appprojects appproj argoproj.io/v1alpha1 true AppProject
세 종류의 객체는 Argo CD 가 설치된 네임스페이스(argocd)에 둔다. 다른 네임스페이스에 두려면 따로 켜야 한다.
4. Application# Application 은 Argo CD 가 관리하는 단위다. “이 레포의 이 폴더를 저 클러스터의 이 네임스페이스에” 를 적는다.
apiVersion : argoproj.io/v1alpha1
kind : Application
metadata :
name : kukie-dev-apps
namespace : argocd
spec :
project : kukie-apps # 속한 AppProject 이름
source :
repoURL : https://github.com/durokukie/kukie-manifest.git
targetRevision : main
path : envs/dev
destination :
server : https://kubernetes.default.svc # Argo CD 가 설치된 그 클러스터
namespace : kukie-dev
envs/dev/ Application AppProject source destination 객체들 네임스페이스 source — repoURL(레포) · path(폴더) · targetRevision(브랜치·태그·커밋) destination — server(클러스터 주소) · namespace project — 이 앱이 속한 AppProject 이름 한 줄 (점선: 이름으로만 이어진다) Application 은 '어디서 → 어디로' 를 적은 연결선 하나다 4.1. project 칸# Application 하나는 AppProject 하나 에만 속한다. 한 AppProject 에는 Application 이 여러 개 속할 수 있다 둘은 포함 관계가 아니라 나란히 있는 별개의 객체다. Application 이 project: 에 이름을 적어 가리킬 뿐이다 비워 두면 default 프로젝트에 속한다 (5.2. 참고) 4.2. 상태 표시# Sync — Git 렌더링 결과와 클러스터가 같은가 Synced 같다 OutOfSync 다르다 → 동기화 대상 Health — 넣은 객체가 정상으로 떠 있는가 Healthy 정상 Progressing 뜨는 중 (롤링 등) Degraded 실패 · 반복 재시작 Missing 클러스터에 아직 없음 어디에 보이나 — 원본은 Application 객체의 status 칸 하나, 보는 창구가 셋 status: sync: status: OutOfSync health: status: Missing ① Application 객체 (controller 가 적음) $ kubectl -n argocd get app NAME SYNC STATUS HEALTH kukie-dev-apps OutOfSync Missing reloader Synced Healthy ② kubectl · argocd app get kukie-dev-apps Missing OutOfSync project: kukie-apps ③ Argo CD 웹 UI 앱 카드 두 칸은 따로 움직인다 — 처음 연결한 수동 동기화 앱은 OutOfSync · Missing (읽고 비교만 하고 아직 넣지 않음) 두 값은 application-controller 가 Application 객체의 status 칸에 적고, 웹 UI · kubectl get applications · argocd app get 은 모두 그 칸을 읽어 보여 준다. 수동 동기화인 Application 은 Git 을 읽고 차이를 계산만 한다. 처음 연결했을 때 OutOfSync · Missing 으로 보이는 것은 아직 아무것도 넣지 않았다는 뜻이다.
5. AppProject# AppProject 는 그 프로젝트에 속한 Application 이 어디서 가져와 어디에 무엇을 넣을 수 있는지 의 한계선이다. Application 이 Git 에 뭐라고 적든 이 선 밖이면 Argo CD 가 동기화를 거부한다.
Deployment Service Ingress ExternalSecret kukie-dev 네임스페이스 (destinations) 울타리 = AppProject kukie-apps kukie-manifest sourceRepos ClusterRole ✕ kube-system 행 ✕ 다른 레포 ✕ 초록 벽 — AppProject kukie-apps. 주황 레포에서 읽어 파랑 네임스페이스 안에, 허용한 종류만 넣는다 빨강 점선 — 울타리 밖. 클러스터 전체 단위 종류 · 다른 네임스페이스 · 다른 레포 울타리 밖은 Git 에 적혀 있어도 들어가지 않는다 — Argo CD 가 동기화를 거부한다 5.1. 제한 항목# AppProject — kukie-infra argocd/dev/platform/templates/kukie-apps.yaml kind: AppProject metadata: name: kukie-apps ← Application 의 project: 가 가리키는 이름 namespace: argocd spec: sourceRepos: 어디서 — 읽어도 되는 레포 - https://github.com/durokukie/kukie-manifest.git destinations: 어디로 — kukie-dev 네임스페이스만 - server: https://kubernetes.default.svc namespace: kukie-dev clusterResourceWhitelist: [] 클러스터 단위 종류는 하나도 안 됨 namespaceResourceWhitelist: 무엇을 — 이 여섯 종류만 - {group: apps, kind: Deployment} - {group: "", kind: Service} - {group: "", kind: ConfigMap} - {group: networking.k8s.io, kind: Ingress} - {group: external-secrets.io, kind: ExternalSecret} - {group: external-secrets.io, kind: SecretStore} Secret · Role · Application · AppProject 는 목록에 없다 Secret 은 ESO 가 ExternalSecret 을 보고 만들고, 권한과 Argo CD 객체는 앱 레포에서 넣지 못한다 kukie-dev-apps 가 이 이름을 고르므로, 네 칸(어디서 · 어디로 · 금지 · 허용)이 그 앱의 한계선이다 동기화할 때는 이 항목들을 아래 순서로 검사한다.
Application 동기화 요청 sourceRepos 읽을 레포 destinations 클러스터 · 네임스페이스 clusterResource 클러스터 단위 종류 namespaceResource 네임스페이스 안 종류 적용 ✕ 거부 ✕ 거부 ✕ 거부 ✕ 거부 어디서 → 어디로 → 무엇을 (하나라도 걸리면 동기화 실패) · 뒤의 둘은 …Whitelist / …Blacklist roles 사람 쪽 — 누가 이 프로젝트의 앱을 보고 Sync 하나 ← 객체 검사와 별개로, Argo CD 사용자 권한을 나눈다 앞의 넷은 '무엇이 들어가나' 를, roles 는 '누가 누르나' 를 막는다 5.2. default 프로젝트# Argo CD 를 설치하면 default 프로젝트가 자동으로 생긴다. 모든 레포 · 모든 클러스터 · 모든 종류를 허용하는(*) 사실상 울타리가 없는 프로젝트다. 고칠 수는 있지만 지울 수는 없다. 그래서 운영에서는 용도별 프로젝트를 따로 만들고 Application 이 default 를 쓰지 않게 한다.
5.3. 쿠버네티스 기본 장치와의 차이# 주문서 project: kukie-apps 주문서 project: kukie-platform 앱 팀 · kukie-manifest 인프라 · kukie-infra 규칙표 kukie-apps 레포 kukie-manifest 만 방 kukie-dev 만 가구 Deployment · Service … 규칙표 kukie-platform 레포 kukie-infra · 정해진 차트 방 kube-system · kukie-dev … 가구 컨트롤러 설치용 전부 AppProject — 소속별로 먼저 확인 Argo CD 마스터키 계정 하나 RBAC 같은 계정이라 다 통과 kukie-dev 방 member · agent · web kube-system 방 LBC 같은 클러스터 공용 ① 앱 → kukie-dev ② 컨트롤러 → kube-system ✕ ③ kukie-apps 주문이 kube-system 에 넣으려 하면 규칙표에서 거부 — 마스터키까지 가지 않는다 RBAC 은 '누가 왔나' 만 본다 — Argo CD 가 넣는 건 전부 같은 계정이라 앱마다 구분하지 못한다 AppProject 는 같은 배달원이라도 주문서 소속마다 다른 규칙표로 먼저 거른다 Argo CD 의 application-controller 는 권한이 넓은 하나의 서비스 계정으로 움직인다. RBAC 만으로는 Application 마다 다르게 막을 수 없어서 AppProject 가 그 한 겹을 더한다.
5.4. Application 을 쓸 수 있는 곳# AppProject 는 Application 의 project: 칸으로 정해진다. Application 객체를 새로 쓸 수 있는 사람은 더 넓은 프로젝트를 골라 적을 수도 있다는 뜻이다. 그래서 Application · AppProject 객체는 앱 매니페스트와 다른 레포(관리자만 push 하는 곳)에 두고, 앱 레포 쪽 프로젝트의 허용 종류에는 Application · AppProject 를 넣지 않는다.
6. ApplicationSet# ApplicationSet 은 목록(generator)과 틀(template)을 곱해 Application 을 여러 개 만든다. 환경이나 클러스터가 늘 때마다 Application 을 손으로 복사하지 않기 위해 쓴다.
generator - env: dev - env: stg - env: prod template path: envs/{{env}} ApplicationSet app-dev app-stg app-prod generator — 목록을 만든다 (직접 적은 목록 · 등록된 클러스터 · Git 폴더 등) template — Application 틀. {{env}} 같은 자리에 목록 값이 들어간다 목록이 늘면 Application 이 늘고, 줄면 그 Application 이 지워진다 환경·클러스터가 많을 때 쓴다 — Application 을 손으로 여러 개 쓰는 대신 규칙 하나로 List 직접 적은 값 env: dev, stg, prod Cluster Argo CD 에 등록된 클러스터들 seoul, tokyo … Git Git 의 폴더 · 파일 목록 apps/a, apps/b … Matrix · Merge 위 둘 이상의 조합 클러스터 × 앱 … 목록 → 항목마다 Application 하나 generator 는 '몇 개를 무슨 값으로' 를 정하고, template 이 그 값으로 Application 을 채운다 Application 과 이름이 비슷하지만 별개의 종류이고, argocd-applicationset-controller 가 있어야 동작한다.
7. App of Apps# App of Apps 는 kind: Application 만 들어 있는 Git 폴더를 읽는 Application 하나를 두고, 그것이 나머지 Application 을 만들게 하는 구성이다. 맨 위의 것을 흔히 루트(root) Application 이라고 부른다.
Terraform (처음 한 번) 루트 Application lbc.yaml eso.yaml apps.yaml Git 폴더 — YAML 파일 (내용은 kind: Application) LBC 앱 ESO 앱 kukie-dev-apps Deployment … Deployment … member · agent … 자식 Application — 클러스터에 생긴 객체 실제 객체 회색 — Git 밖. 루트를 Git 안에 두면 그것을 처음 읽어 줄 주체가 없다 주황 문서 = Git 의 파일 · 보라 큐브 = 그 파일대로 클러스터(argocd 네임스페이스)에 생긴 Application 객체 보라는 모두 같은 종류다. '루트' 는 맨 위에 있다는 뜻의 별명 루트에 finalizer(resources-finalizer.argocd.argoproj.io)가 있으면 지울 때 자식과 그 객체까지 함께 지운다 루트가 읽는 폴더에 쓸 수 있으면 Application 을 마음대로 만들 수 있다 — 관리자만 push 하게 둔다 루트는 대장이 아니라 부모다 — 자식을 만들 뿐, 자식보다 큰 권한은 없다 7.1. 루트 Application# 별도 종류가 아니다. 다른 Application 과 똑같이 kind: Application 이고, 자기 프로젝트의 제한을 그대로 받는다 읽는 폴더에 Application 객체가 들어 있어서 결과적으로 다른 Application 을 만들 뿐이다 루트 자체는 그 폴더 밖에서 넣는다(Terraform · Helm · kubectl apply 한 번). 폴더 안에 두면 처음에 그것을 읽어 줄 주체가 없다 이후 폴더에 Application 을 더하거나 빼는 것은 Git 머지만으로 된다 7.2. 삭제 전파# Application 에 resources-finalizer.argocd.argoproj.io finalizer 가 있으면, 그 Application 을 지울 때 그것이 넣은 객체도 함께 지운다. 루트에 붙어 있으면 루트를 지울 때 자식 Application 과 그 객체까지 전부 지워진다. finalizer 가 없으면 Application 만 지워지고 넣었던 객체는 클러스터에 남는다.
8. 관리 범위# Argo CD 가 하는 일은 객체를 클러스터(API 서버)에 넣는 데까지 다. 넣은 객체를 읽고 클러스터 밖 자원을 만들거나 Pod 를 고치는 것은 그 종류를 맡은 컨트롤러다.
Argo CD AWS 클러스터 (API 서버에 저장된 객체) Ingress LBC AWS 로드밸런서 ExternalSecret ESO Secret Secret 변경 Reloader Pod 재시작 Argo CD 가 하는 일은 여기까지 컨트롤러 결과 파랑 문서 — Argo CD 가 넣은 객체. 그 자체는 '이렇게 되어 있어야 한다' 는 기록일 뿐 초록 — 컨트롤러. 객체를 지켜보다가(점선) 실제 일을 한다. 컨트롤러도 Argo CD 가 Deployment 로 띄운 Pod 다 ESO 가 만든 Secret 은 Argo CD 가 넣은 게 아니라서 Git 과 비교 대상이 아니다 컨트롤러가 없으면 객체는 저장만 되고 아무 일도 일어나지 않는다 Ingress 를 넣어도 로드밸런서 컨트롤러가 없으면 로드밸런서는 생기지 않는다컨트롤러 자신도 Deployment 같은 객체로 띄워지므로, 컨트롤러 설치 역시 Argo CD 로 할 수 있다 컨트롤러가 만든 객체(ESO 가 만든 Secret 등)는 Git 에 없으므로 Argo CD 의 비교 대상이 아니다 9. 동기화# Git 이 바뀜 Git 에서 지움 — prune 손으로 바꿈 — selfHeal v2 v1 v2 a.yaml b 삭제 a b ✕ replicas: 1 1 ← 3 kubectl edit Git 클러스터 보라 화살표 — Argo CD 의 동기화 (실선: 기본 동작 · 점선: 켰을 때만) 흐린 점선 — 지워졌거나 덮어써질 것 · 회색 — 사람이 직접 바꾼 것 Git 이 기준이다 — prune 과 selfHeal 은 그 기준을 얼마나 엄격히 지킬지 정한다 9.1. 수동과 자동# 수동 (기본) Git 변경 차이 계산 OutOfSync 사람이 Sync UI · CLI 기다림 클러스터 적용 Synced 자동 — syncPolicy.automated Git 변경 차이 계산 OutOfSync 바로 클러스터 적용 Synced Git 확인 주기는 기본 최대 3분 (웹훅을 걸면 바로) — 두 방식 모두 차이 계산까지는 똑같이 한다 처음 연결 · 선행 준비가 덜 된 앱은 수동, 평소 운영은 자동 9.2. prune# 자동 동기화는 기본적으로 지우지 않는다 . Git 에서 파일을 지워도 클러스터의 객체는 남는다. prune: true 를 켜면 Git 에 더 이상 없는 객체를 동기화 때 함께 지운다.
9.3. selfHeal# 기본적으로 클러스터에서 손으로 바꾼 값은 다음 Git 변경 전까지 그대로 남는다. selfHeal: true 를 켜면 클러스터 쪽 변경도 차이로 보고 Git 내용으로 되돌린다. 되돌리기는 기본 5초 간격으로 다시 시도한다.
9.4. ignoreDifferences# 일부러 Git 과 달라지는 칸은 비교에서 뺀다. 예를 들어 Reloader 는 Secret 이 바뀌면 Deployment 의 Pod 템플릿에 재시작 표시 주석을 붙이는데, 이 칸을 빼지 않으면 selfHeal 이 그 표시를 계속 지운다.
ignoreDifferences :
- group : apps
kind : Deployment
jsonPointers :
- /spec/template/metadata/annotations/reloader.stakater.com~1last-reloaded-from
syncPolicy :
syncOptions : [RespectIgnoreDifferences=true] # 동기화 때도 이 칸을 덮어쓰지 않게
10. 관련 문서# CRD — Application · AppProject · ApplicationSet 이 추가되는 방식kubelet — 객체를 읽고 실제 일을 하는 조정 루프의 예Admission Webhook — API 서버 단계에서 객체를 검증 · 변형하는 다른 장치Controller — 객체를 지켜보다 실제 일을 하는 프로그램 (예정) Helm · Kustomize — repo-server 가 렌더링하는 형식 (예정) 11. 출처# “Declaratively specify one Argo CD app that consists only of other apps.”
“Only admins should have push access to the parent Application’s source repository.”
“automated sync will not delete resources when Argo CD detects the resource is no longer defined in Git.”