운영 클러스터 상태가 Git 저장소와 달라졌지만 누가 언제 수동으로 바꿨는지 찾기 어렵습니다. 배포 이력이 명령어 기록에만 남으면 롤백과 감사가 모두 불안정해집니다. GitOps와 Argo CD는 Git을 기준 상태로 삼아 클러스터 변경을 추적하고 동기화합니다.
GitOps와 ArgoCD
배포 파이프라인이 복잡해질수록 "지금 프로덕션에 떠 있는 버전이 정확히 무엇인가"라는 질문에 답하기 어려워집니다. CI/CD에서 누군가 kubectl 명령을 잘못 실행했거나, 긴급 패치를 적용했거나, 설정을 직접 수정했다면 Git의 코드와 실제 클러스터 상태가 달라집니다. GitOps는 이 문제를 원천 차단하는 운영 패러다임입니다. Git 리포지토리의 선언적 매니페스트가 클러스터의 유일한 진실이 되고, 클러스터를 변경하는 유일한 방법은 Git 커밋입니다. ArgoCD는 이 원칙을 자동화하는 도구로, 60초마다 Git 상태를 폴링해서 클러스터와 차이가 생기면 자동으로 동기화합니다. 롤백은 git revert 한 줄이고, 배포 이력은 곧 커밋 이력입니다. 이 모듈을 마치면 ArgoCD를 설치하고 Application을 정의해서 Git 커밋만으로 배포가 자동화되는 파이프라인을 구성할 수 있습니다.
- 1GitOps 4대 원칙(선언적, 버전 관리, 자동 적용, 지속적 조정)을 설명할 수 있다
- 2ArgoCD를 설치하고 Application(source, destination, syncPolicy)을 정의할 수 있다
- 3자동 동기화, prune, selfHeal 설정으로 드리프트를 자동 교정할 수 있다
- 4ApplicationSet으로 멀티환경·멀티클러스터 배포를 자동화할 수 있다
- 5Sync Waves와 Resource Hooks로 배포 순서를 제어할 수 있다
- 6매니페스트 오류와 RBAC 권한 문제로 인한 Sync 실패를 진단할 수 있다
kubectl create namespace argocd && kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yamlkubectl wait --for=condition=available --timeout=300s deployment/argocd-server -n argocdcurl -sSL -o argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64 && chmod +x argocd && mv argocd /usr/local/bin/kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath='{.data.password}' | base64 -dkubectl port-forward svc/argocd-server -n argocd 8080:443GitOps 4대 원칙
확대
4대 원칙:
- 선언적(Declarative): 시스템 상태를 명령어가 아닌 YAML로 선언
- 버전 관리(Versioned): 모든 변경은 Git 커밋으로 기록 — 누가, 언제, 무엇을 변경했는지 추적 가능
- 자동 적용(Automatically Applied): 승인된 변경(Git 커밋)은 자동으로 클러스터에 적용
- 지속적 조정(Continuously Reconciled): ArgoCD가 주기적으로 Git 상태와 클러스터 상태를 비교하고 차이를 교정
Git 커밋 한 번이 클러스터에 반영되기까지 — reconcile 루프 6단계
매니페스트를 고쳐 git push만 했을 뿐인데 잠시 뒤 클러스터가 알아서 바뀝니다. 아무도 kubectl apply를 치지 않았는데 어떻게 반영될까요? ArgoCD가 Git(desired state)과 클러스터(live state)를 끊임없이 비교하고 차이를 메우는 reconcile 루프를 돌기 때문입니다. 이 루프를 단계로 알면 "커밋했는데 왜 안 떠", "왜 자꾸 OutOfSync야", "수동으로 바꿨더니 되돌아가네"를 어느 단계의 문제인지로 좁힐 수 있습니다. GitOps에서 "배포"는 단발 명령이 아니라 이 루프가 desired와 live의 차이를 0으로 만든 결과입니다.
[개발자] git commit + push → Git 저장소 (desired state = 유일한 진실)
│
① 감지: ArgoCD가 Git을 폴링(기본 3분)하거나 웹훅으로 즉시 당겨옴
│ → 매니페스트를 렌더(Helm/Kustomize)해 desired state 생성
│
② 비교(diff): 렌더된 desired state ↔ 클러스터 live state를 필드 단위로 대조
│
③ 판정: 차이가 있으면 OutOfSync, 없으면 Synced로 표시
│
④ sync(apply): 렌더 매니페스트를 kubectl apply로 클러스터에 반영
│ (automated=true면 자동, 아니면 수동 승인 대기)
│
⑤ health 평가: 적용된 리소스 상태 확인 (Deployment rollout·파드 Ready)
│
⑥ 루프 유지: Synced+Healthy 도달 후에도 계속 폴링하며 drift를 감지·교정
▼
[클러스터] live state = Git desired state (drift가 생기면 다시 ①로)
각 단계에서 무슨 일이 일어나고, 막히면 어떤 증상인가:
| 단계 | 하는 일 | 막히면 증상 |
|---|---|---|
| ① 감지 | repo-server가 저장소를 폴링(기본 3분)하거나 웹훅으로 당겨와 매니페스트를 렌더(Helm/Kustomize) | 웹훅 미설정이면 최대 폴링 주기만큼 반영 지연 · repo 자격증명·path 오류면 ComparisonError로 desired state 생성 실패 |
| ② 비교 | 렌더된 desired state와 live state를 오브젝트·필드 단위로 대조 | K8s가 자동 주입한 필드(caBundle·기본값 등) 때문에 실차이가 없는데도 계속 다르게 보임 → ignoreDifferences 필요 |
| ③ 판정 | 차이 있으면 OutOfSync, 없으면 Synced | image가 latest 같은 가변 태그면 스펙이 그대로라 새 이미지인데도 Synced로 보이는 사각지대 |
| ④ sync | 렌더 매니페스트를 apply로 반영. selfHeal=true면 수동 kubectl edit로 생긴 drift를 Git 상태로 되돌림 | RBAC 부족·매니페스트 invalid → SyncFailed · prune=true면 Git에서 지운 리소스가 클러스터에서도 삭제 |
| ⑤ health 평가 | 적용된 리소스의 상태 판정 (rollout 완료·파드 Ready) | 5분+ Progressing이면 이미지 pull 실패·probe 실패 등 rollout 문제 |
| ⑥ 루프 유지 | Synced+Healthy 이후에도 계속 비교하며 drift 교정 | 폴링·웹훅이 죽으면 커밋해도 감지가 멈춰 반영이 안 됨 |
반영이 안 될 땐 argocd app get·argocd app diff로 지금 어느 단계에 있는지부터 확인합니다 — 아예 안 떠오면 ①(폴링/웹훅·렌더 실패), 계속 OutOfSync면 ②③(무시할 자동 필드), SyncFailed면 ④(RBAC·매니페스트), 오래 Progressing이면 ⑤(rollout)입니다. 수동 변경이 되돌아가는 것은 ④ selfHeal이 정상 동작한 것이고, 유지하려면 클러스터가 아니라 Git을 바꿔야 합니다.
GitOps vs 기존 Push 기반 CD — 보안과 운영 관점 차이
기존 CI/CD는 Jenkins나 GitHub Actions가 kubectl 명령을 직접 실행합니다. 이 방식에서 CI 서버는 클러스터 접근 권한(KUBECONFIG)을 보유해야 합니다. CI 서버가 해킹되면 클러스터 전체가 위험에 노출됩니다.
확대
확대
ArgoCD는 클러스터 내부에서 실행되며 Git을 폴링합니다. 외부 시스템이 클러스터에 접근하는 것이 아니라, 클러스터가 Git에서 상태를 가져오는 Pull 방식입니다. CI/CD 서버에 클러스터 자격증명을 저장할 필요가 없어 공격 표면이 줄어듭니다.
또한 Git 리포지토리 접근 권한만 있으면 배포 이력 확인, 롤백, 특정 버전 재배포가 가능합니다. 별도의 배포 도구 권한 관리가 필요 없습니다.
ArgoCD Application 정의
기본 Application YAML
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: payment-service
namespace: argocd # Application 객체는 argocd 네임스페이스에 생성
labels:
environment: production
team: backend
spec:
project: default
# 소스: Git 리포지토리에서 읽을 매니페스트
source:
repoURL: https://github.com/myorg/k8s-manifests.git
targetRevision: main # 브랜치, 태그, 커밋 SHA 모두 가능
path: apps/payment/production # 리포지토리 내 경로
# 목적지: 어느 클러스터, 어느 네임스페이스에 배포할지
destination:
server: https://kubernetes.default.svc # 현재 클러스터
namespace: production
# 동기화 정책
syncPolicy:
automated:
prune: true # Git에서 삭제된 리소스 자동 제거
selfHeal: true # 수동 변경 감지 시 자동 복원
syncOptions:
- CreateNamespace=true # 네임스페이스 없으면 자동 생성
- PrunePropagationPolicy=foreground # 종속 리소스 먼저 삭제
- RespectIgnoreDifferences=true
retry:
limit: 5 # 실패 시 재시도 횟수
backoff:
duration: 5s
factor: 2
maxDuration: 3m
# 비교 시 무시할 필드 (Kubernetes 자동 추가 필드)
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicas # HPA가 레플리카를 변경해도 OutOfSync로 표시 안 함
Helm Chart Application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: nginx-ingress
namespace: argocd
spec:
project: default
source:
repoURL: https://kubernetes.github.io/ingress-nginx
chart: ingress-nginx
targetRevision: 4.8.3 # Chart 버전 고정
helm:
releaseName: ingress-nginx
values: | # 인라인 values (또는 valuesFiles 사용)
controller:
replicaCount: 2
service:
type: LoadBalancer
metrics:
enabled: true
destination:
server: https://kubernetes.default.svc
namespace: ingress-nginx
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
ArgoCD CLI로 Application 관리
# CLI 로그인
argocd login localhost:8080 \
--username admin \
--password $(kubectl -n argocd get secret argocd-initial-admin-secret \
-o jsonpath='{.data.password}' | base64 -d) \
--insecure
# Application 목록
argocd app list
# 특정 Application 상태 확인
argocd app get payment-service
# 수동 동기화 (즉시 반영)
argocd app sync payment-service
# 특정 커밋으로 동기화 (rollback)
argocd app sync payment-service --revision abc1234
# Git과 클러스터 차이 확인
argocd app diff payment-service
# 롤백 (이전 성공 배포로)
argocd app rollback payment-service
# Application 삭제 (리소스도 함께 삭제)
argocd app delete payment-service --cascade
ApplicationSet으로 멀티환경 배포
# 환경 목록 기반 ApplicationSet
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: payment-service-all-envs
namespace: argocd
spec:
generators:
# List 생성기: 환경 목록을 직접 정의
- list:
elements:
- environment: dev
cluster: https://dev-cluster.example.com
namespace: payment-dev
replicaCount: "1"
imageTag: latest
- environment: staging
cluster: https://staging-cluster.example.com
namespace: payment-staging
replicaCount: "2"
imageTag: "{{ .values.stagingTag }}"
- environment: production
cluster: https://prod-cluster.example.com
namespace: payment-production
replicaCount: "5"
imageTag: "1.2.3"
template:
metadata:
name: "payment-{{ environment }}" # 각 환경별 Application 이름
labels:
environment: "{{ environment }}"
spec:
project: default
source:
repoURL: https://github.com/myorg/k8s-manifests.git
targetRevision: main
path: "apps/payment/{{ environment }}"
helm:
parameters:
- name: replicaCount
value: "{{ replicaCount }}"
- name: image.tag
value: "{{ imageTag }}"
destination:
server: "{{ cluster }}"
namespace: "{{ namespace }}"
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
# ApplicationSet 적용 후 생성된 Application 확인
kubectl apply -f applicationset.yaml -n argocd
argocd app list | grep payment
# NAME CLUSTER NAMESPACE
# payment-dev https://dev-cluster.example.com payment-dev
# payment-staging https://staging-cluster.example.com payment-staging
# payment-production https://prod-cluster.example.com payment-production
- argocd app list에서 SYNC 열 먼저 확인 — Synced이면 Git과 클러스터 상태 일치, OutOfSync이면 Git 변경이 아직 클러스터에 반영 안 된 것으로 argocd app diff <name>으로 차이 확인
- HEALTH 상태 기준: Healthy=정상, Degraded=파드 이상, Progressing=배포 진행 중. 5분 이상 Progressing이면 Deployment rollout 문제로 kubectl get pods -n <ns>에서 파드 상태 확인
- SYNC=OutOfSync이고 HEALTH=Healthy이면 → 클러스터에서 직접 수동 변경이 생긴 것. prune:true 설정 시 다음 sync에서 수동 변경이 삭제됨 — Git이 진실의 원본임을 항상 기억
Git 디렉토리 기반 ApplicationSet
# apps/ 디렉토리의 각 서브디렉토리를 자동으로 Application으로 생성
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: all-apps
namespace: argocd
spec:
generators:
- git:
repoURL: https://github.com/myorg/k8s-manifests.git
revision: main
directories:
- path: "apps/*/production" # apps/payment/production, apps/order/production 등
template:
metadata:
name: "{{ path.basenameNormalized }}"
spec:
project: default
source:
repoURL: https://github.com/myorg/k8s-manifests.git
targetRevision: main
path: "{{ path }}"
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
Sync Waves와 Resource Hooks
배포 순서 제어가 필요할 때 사용합니다. 예를 들어 DB 마이그레이션을 앱 배포 전에 실행해야 하는 경우입니다.
# DB 마이그레이션 Job (wave: -1 → 앱보다 먼저 실행)
apiVersion: batch/v1
kind: Job
metadata:
name: db-migration
annotations:
argocd.argoproj.io/hook: Sync # Sync 단계에서 실행
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
argocd.argoproj.io/sync-wave: "-1" # 음수일수록 먼저 실행
spec:
template:
spec:
containers:
- name: migration
image: myapp:latest
command: ["python", "manage.py", "migrate"]
restartPolicy: Never
---
# 앱 Deployment (wave: 0 — 마이그레이션 성공 후 배포)
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment
annotations:
argocd.argoproj.io/sync-wave: "0"
spec:
# ...
---
# 스모크 테스트 Job (wave: 1 → 앱 배포 후 실행)
apiVersion: batch/v1
kind: Job
metadata:
name: smoke-test
annotations:
argocd.argoproj.io/hook: PostSync # 배포 완료 후 실행
argocd.argoproj.io/hook-delete-policy: HookSucceeded
argocd.argoproj.io/sync-wave: "1"
spec:
template:
spec:
containers:
- name: test
image: curlimages/curl
command:
- sh
- -c
- "curl -f http://payment:8080/health || exit 1"
restartPolicy: Never
트러블슈팅
ArgoCD UI에서 Application이 SyncFailed 상태이고 에러 메시지가 failed to apply resource 또는 invalid spec으로 표시됩니다.
1단계: ArgoCD UI와 CLI에서 에러 상세 확인
# CLI에서 구체적인 에러 메시지 확인
argocd app get payment-service --show-operation
# 출력 예시:
# OPERATION: sync
# PHASE: Failed
# MESSAGE: one or more objects failed to apply, reason:
# Deployment.apps "payment" is invalid:
# spec.template.spec.containers[0].resources.requests:
# Invalid value: "2000m": must be less than or equal to cpu limit
# 전체 동기화 상세
argocd app sync payment-service --dry-run 2>&1
2단계: 매니페스트 로컬 검증
# Git에서 매니페스트 가져와서 로컬 검증
git clone https://github.com/myorg/k8s-manifests.git
cd k8s-manifests/apps/payment/production
# kubectl dry-run으로 유효성 확인
kubectl apply -f . --dry-run=server -n production
# YAML 문법 검증 (kubeval 사용)
kubeval deployment.yaml
# 일반 오류 패턴:
# - resource requests > limits (CPU/메모리)
# - 필수 필드 누락 (selector.matchLabels)
# - API 버전 오류 (deprecated API 사용)
# - 잘못된 imagePullPolicy 값
# deprecated API 확인
kubectl api-resources | grep <resource>
pluto detect-files -d . --output wide # pluto CLI 도구
3단계: RBAC 권한 문제 진단
# ArgoCD 서비스어카운트 확인
kubectl get sa -n argocd
# ArgoCD가 배포 대상 네임스페이스에 접근 권한이 있는지 확인
kubectl auth can-i create deployment \
--as=system:serviceaccount:argocd:argocd-application-controller \
-n production
# kubectl auth can-i list로 필요 권한 전체 확인
kubectl auth can-i '*' '*' \
--as=system:serviceaccount:argocd:argocd-application-controller \
-n production
# 출력: "no" 라면 ClusterRole 또는 RoleBinding 추가 필요
# ClusterRole 확인
kubectl get clusterrolebinding | grep argocd
kubectl describe clusterrolebinding argocd-application-controller
4단계: RBAC 권한 추가
# ArgoCD가 특정 네임스페이스에 배포할 수 있도록 권한 부여
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: argocd-deploy
namespace: production # 배포 대상 네임스페이스
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: admin # 또는 필요한 최소 권한으로 커스텀 Role
subjects:
- kind: ServiceAccount
name: argocd-application-controller
namespace: argocd
5단계: 개인 리포지토리 접근 오류
# Git 리포지토리 연결 상태 확인
argocd repo list
# STATUS TYPE NAME REPO
# Failed git https://github.com/myorg/private-manifests.git
# 리포지토리 자격증명 추가 (SSH 키)
argocd repo add git@github.com:myorg/private-manifests.git \
--ssh-private-key-path ~/.ssh/id_rsa
# 리포지토리 자격증명 추가 (토큰)
argocd repo add https://github.com/myorg/private-manifests.git \
--username myuser \
--password ghp_xxxxxxxxxxxx
# 연결 테스트
argocd repo get https://github.com/myorg/private-manifests.git
흔한 실수: OutOfSync가 되는 자동 추가 필드들
# Kubernetes가 자동으로 추가하는 필드들이 OutOfSync를 유발할 때
# ignoreDifferences로 무시 설정
kubectl apply -f - << 'EOF'
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: payment-service
namespace: argocd
spec:
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicas # HPA가 변경하는 레플리카 수
- group: ""
kind: ConfigMap
jsonPointers:
- /data/last-applied # 자동 추가 어노테이션
- group: admissionregistration.k8s.io
kind: MutatingWebhookConfiguration
jsonPointers:
- /webhooks/0/clientConfig/caBundle # 자동 주입 CA 번들
EOF
심화 — 'Synced'가 '최신'을 뜻하진 않는다
심화: ArgoCD의 diff는 매니페스트를 비교한다 — 이미지 태그와 동기화의 함정
ArgoCD가 Synced라고 표시하면 흔히 프로덕션이 최신 코드라고 안심하지만, Synced의 정확한 뜻을 알아야 이 착각에서 벗어납니다.
- Synced = 선언과 실물의 매니페스트가 같다: ArgoCD는 Git이 선언한 오브젝트 스펙과 클러스터에 존재하는 오브젝트 스펙을 필드 단위로 비교합니다. 둘이 같으면 Synced입니다. 비교 대상은 YAML 스펙이지, 실제로 돌고 있는 컨테이너 이미지의 다이제스트가 아닙니다.
- 가변(mutable) 태그가 만드는 사각지대: Git에 image가 myapp:latest(또는 :prod)로 적혀 있으면, CI가 같은 태그에 새 이미지를 밀어 넣어도 매니페스트 문자열은 그대로 latest입니다. ArgoCD 눈에는 Git도 latest, 클러스터도 latest라 완벽히 Synced인데, 파드는 예전에 pull한 오래된 이미지를 그대로 돌리고 있을 수 있습니다. selfHeal도 이 차이를 못 봅니다 — 되돌릴 스펙 차이가 없기 때문입니다.
- 그래서 GitOps는 불변(immutable) 태그를 씁니다: 커밋 SHA나 시맨틱 버전(myapp:1.4.2, myapp:git-abc1234)처럼 한 번 쓰면 바뀌지 않는 태그로 배포하면, 새 배포는 반드시 매니페스트의 이미지 문자열을 바꾸는 커밋이 됩니다. 그 커밋이 곧 배포 이력이 되고, ArgoCD의 Synced가 비로소 이 정확한 이미지가 돈다와 같은 뜻이 됩니다.
정리하면 ArgoCD의 상태 판정은 선언 대비 실물의 형상 일치를 보장할 뿐, 태그 뒤에 숨은 이미지 내용까지 보장하진 않습니다. 이미지 프로모션은 태그 문자열을 바꾸는 방식으로 설계해야 GitOps가 제값을 합니다.
상황: CI가 이미지를 빌드해 레지스트리에 푸시했고 파이프라인은 초록불입니다. ArgoCD UI도 Synced에 Healthy. 그런데 사용자와 로그에는 여전히 옛 동작이 보입니다. 아무것도 실패하지 않았는데 새 버전이 안 떴습니다.
원인: 매니페스트가 image에 myapp:latest 같은 가변 태그를 씁니다. CI는 같은 latest 태그에 새 이미지를 덮어썼지만 Git의 매니페스트 문자열은 바뀌지 않았습니다. ArgoCD는 Git과 클러스터의 스펙이 동일하니 Synced로 보고, 새 이미지를 당길 이유를 못 느낍니다. 파드는 이미 노드에 캐시된 옛 이미지를 계속 돌립니다(imagePullPolicy가 IfNotPresent면 더 확실히 고착됩니다).
진단: argocd app get이 아니라 실제 파드가 무엇을 돌리는지 봅니다. kubectl get pod -o jsonpath로 각 파드의 image와 imageID(다이제스트)를 확인하고, 레지스트리의 latest 다이제스트와 비교합니다. 매니페스트의 이미지 문자열이 최근 배포에서 한 번도 바뀌지 않았다면 확정입니다. argocd app diff는 아무 차이도 안 보여줍니다 — 그게 바로 이 증상입니다.
해결: 이미지 태그를 커밋 SHA나 버전 번호 같은 불변 태그로 바꾸고, 배포를 매니페스트의 태그 문자열을 갱신하는 커밋으로 만듭니다(이미지 업데이트 자동화 도구나 CI가 매니페스트 리포에 태그를 써 넣는 방식). 급한 롤아웃이 필요하면 임시로 kubectl rollout restart로 파드를 새로 띄워 재pull하게 하되, 이는 임시방편일 뿐 근본 해결은 불변 태그 전환입니다.
시나리오: 프로덕션 롤백 — 잘못된 배포를 3분 안에 이전 버전으로 복구
배포 직후 에러율이 급등했습니다. 기존 CI/CD라면 이전 이미지 태그를 찾아 파이프라인을 다시 실행해야 합니다. GitOps + ArgoCD라면 Git 히스토리에서 이전 커밋을 찾아 revert하거나 ArgoCD 롤백 명령 하나로 복구됩니다.
# 방법 1: ArgoCD CLI 롤백 (가장 빠름 — 이전 성공 배포로 즉시 복원)
argocd app history payment-service
# ID DATE REVISION
# 42 2024-01-15 14:23:45 +0000 UTC main (abc1234) ← 정상
# 43 2024-01-15 15:10:02 +0000 UTC main (def5678) ← 문제 배포
argocd app rollback payment-service 42
# 즉시 이전 배포(커밋 abc1234) 상태로 복원
# 방법 2: Git revert (GitOps 원칙에 부합 — 변경 이력 보존)
git revert def5678 --no-edit
git push origin main
# ArgoCD가 60초 내 자동 감지하여 revert된 상태로 동기화
# 방법 3: 특정 커밋으로 수동 동기화
argocd app sync payment-service --revision abc1234
# 롤백 후 상태 확인
argocd app get payment-service
# 상태: Synced, Health: Healthy 확인
# 파드 교체 완료 확인
kubectl rollout status deployment/payment -n production
kubectl get pods -n production -l app=payment
Git 커밋 로그가 배포 로그가 됩니다. git log --oneline apps/payment/production/ 명령만으로 누가 언제 무엇을 배포했는지 전체 이력을 확인할 수 있습니다. 별도의 배포 로그 시스템이 필요 없습니다.
핵심 요약
| 개념 | 설명 | 실무 적용 |
|---|---|---|
| Application | ArgoCD 배포 단위 (source + destination) | 서비스당 하나 또는 환경당 하나 |
| ApplicationSet | Application 템플릿 (멀티환경/클러스터) | 동일 앱의 dev/staging/prod 자동화 |
| Sync | Git → 클러스터 상태 적용 | automated + selfHeal로 완전 자동화 |
| Prune | Git 삭제 리소스 → 클러스터에서도 삭제 | 활성화 필수 (기본값은 false) |
| Sync Wave | 리소스 배포 순서 제어 | DB 마이그레이션 → 앱 배포 → 스모크 테스트 |
리포지토리 구조 권장 패턴:
k8s-manifests/ 구조입니다.
apps/payment/—base/(공통 매니페스트),dev/·staging/·production/(kustomize 오버레이)order/
infrastructure/—cert-manager/,ingress-nginx/,monitoring/
ArgoCD 설치 및 초기 상태 확인
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
kubectl wait --for=condition=available deployment/argocd-server -n argocd --timeout=120s
kubectl get pods -n argocd예상 출력
NAME READY STATUS argocd-application-controller-0 1/1 Running argocd-argocd-redis-xxx 1/1 Running argocd-repo-server-xxx 1/1 Running argocd-server-xxx 1/1 Running
ArgoCD 초기 비밀번호 확인 및 포트 포워딩
kubectl get secret argocd-initial-admin-secret -n argocd -o jsonpath='{.data.password}' | base64 -d
kubectl port-forward svc/argocd-server -n argocd 8080:443 &
echo 'ArgoCD UI: https://localhost:8080 (admin/위에서 확인한 비밀번호)'예상 출력
abcdefghijklmnop ArgoCD UI: https://localhost:8080 (admin/위에서 확인한 비밀번호)
ArgoCD Application 생성 (선언적)
kubectl apply -f - <<'EOF'
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: guestbook
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/argoproj/argocd-example-apps.git
targetRevision: HEAD
path: guestbook
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
EOF
kubectl get application guestbook -n argocd예상 출력
NAME SYNC STATUS HEALTH STATUS guestbook Synced Healthy
동기화 상태 및 배포된 리소스 확인
kubectl get pods -n default -l app=guestbook-ui
kubectl get svc -n default | grep guestbook예상 출력
NAME READY STATUS AGE guestbook-ui-xxx 1/1 Running 1m guestbook-ui ClusterIP 10.96.x.x <none> 80/TCP
실습 리소스 정리
kubectl delete application guestbook -n argocd
kubectl delete namespace argocd예상 출력
application.argoproj.io "guestbook" deleted namespace "argocd" deleted
명령어·단축키 빠른 참조
이 모듈에서 ArgoCD 설치부터 Application 동기화·롤백, 드리프트/이미지 진단까지 쓴 kubectl·argocd·git 명령을 모았습니다.
| 명령어/단축키 | 용도 | 자주 쓰는 예 |
|---|---|---|
argocd app list | Application 목록·SYNC 상태 | SYNC=OutOfSync면 diff로 원인 확인 |
argocd app get | Application 상세·동기화 상태 | ... --show-operation (sync 실패 사유) |
argocd app diff | Git과 클러스터 매니페스트 차이 | 차이 없는데 미반영이면 가변 태그 함정 의심 |
argocd app sync | 수동 즉시 동기화 | ... --revision abc1234 (특정 커밋) / --dry-run |
argocd app rollback | 이전 성공 배포로 복원 | argocd app history로 ID 확인 후 rollback <id> |
argocd app history | 배포 이력·리비전 확인 | 롤백 대상 ID·커밋 SHA 파악 |
argocd app delete --cascade | 앱+리소스 함께 삭제 | 종속 리소스까지 정리 |
argocd repo add | 프라이빗 Git 자격증명 등록 | --ssh-private-key-path / --username --password |
kubectl apply -f install.yaml -n argocd | ArgoCD 설치 | kubectl create namespace argocd 먼저 |
kubectl wait --for=condition=available | 컨트롤러 배포 대기 | ... deployment/argocd-server -n argocd --timeout=300s |
kubectl get secret ... | base64 -d | 초기 admin 비밀번호 확인 | argocd-initial-admin-secret의 password |
kubectl port-forward | ArgoCD UI 로컬 접근 | kubectl port-forward svc/argocd-server -n argocd 8080:443 |
kubectl auth can-i | ArgoCD SA의 배포 권한 진단 | --as=system:serviceaccount:argocd:argocd-application-controller |
kubectl get pod -o jsonpath | 파드 실제 image·imageID 대조 | Synced인데 옛 이미지일 때 다이제스트 비교 |
git revert / git log | GitOps 롤백·배포 이력 | git revert <sha> --no-edit && git push |
관련 모듈로 더 깊이:
- 복잡한 매니페스트를 차트(Chart) 단위로 원클릭 배포하기 — ArgoCD가 동기화하는 배포 단위로 자주 쓰이는 Helm Chart
- 나만의 커스텀 Helm Chart 작성법과 환경별 Value 튜닝 — Git에 올려 GitOps로 배포할 커스텀 Chart를 직접 작성하는 법
- Deployment를 이용한 안정적인 서비스 배포와 롤백 전략 — ArgoCD가 클러스터에 적용하는 선언적 매니페스트의 기본
- 빌드 자동화와 이미지 태그 배포 파이프라인 구축 — GitOps가 동기화하는 이미지를 빌드·태그·푸시하는 그 앞단 CI/CD 파이프라인 (Docker 트랙)
다음 모듈 helm-basics에서는 복잡한 Kubernetes 매니페스트를 패키지로 관리하는 Helm을 다룹니다. Chart 설치, values.yaml 오버라이드, 롤백으로 멀티 환경 배포를 표준화하는 방법을 실습합니다.