values ファイルは 2 なのに Pod は 3 つ起動した
한국어 원문으로 표시합니다.
목표
진짜 Argo CD 에서 Helm 차트와 Kustomize 오버레이를 소스로 쓰고, 값이 어디서 정해지는지, 동기화 단계·웨이브·훅이 적용 순서를 어떻게 바꾸는지, selfHeal 이 무엇을 되돌리지 않게 할 수 있는지를 클러스터에 생긴 결과로 확인합니다.
왜 중요한가
Argo CD 는 Helm 을 패키지 관리자로 쓰지 않고 템플릿 엔진으로만 씁니다. 그래서 helm list 에 아무것도 없고, 롤백도 Helm 이 아니라 Git 과 Argo CD 가 맡습니다.
대신 값이 네 군데(차트 기본값, 값 파일, Application 의 valuesObject, parameters)에서 올 수 있어 "Git 의 values 파일을 고쳤는데 왜 안 바뀌지" 가 흔한 장애가 됩니다.
Kustomize 덮어쓰기도 같아서 Application 에 적은 이미지는 Git 저장소에 없는 원하는 상태입니다. 동기화는 한 번에 모두 적용하는 것이 아니라
PreSync·Sync·PostSync 단계와 웨이브로 나뉘고, 앞 웨이브가 Healthy 가 될 때까지 기다리며, 훅이 실패하면 뒤 단계는 시작되지 않습니다.
마지막으로 HPA 처럼 다른 조정기가 소유한 필드는 ignoreDifferences 로 비교와 동기화에서 빼야 selfHeal 과 싸우지 않습니다.
단계
- 베어 저장소
/srv/bare/src.git을 만들고/root/capa-src/repo로 복제해charts/web에 Helm 차트를 커밋·push 하세요. Chart.yaml(nameweb, version0.1.0), values.yaml(replicaCount: 1,greeting: chart-default), values-prod.yaml(replicaCount: 2,greeting: from-values-file), 템플릿 두 개({{ .Release.Name }}-greetingConfigMap 의 data.greeting,{{ .Release.Name }}-webDeployment 의 replicas·이미지nginx:1.27-alpine)를 둡니다./root/capa-src/app-src-helm.yaml의 Applicationsrc-helm(저장소git://gitd.gitsrv.svc.cluster.local:9418/src.gitmain·charts/web, 대상 네임스페이스src-helm, 자동 동기화·CreateNamespace)에helm.valueFiles: [values-prod.yaml]를 주고 적용해 Synced·Healthy 를 확인하세요. src-helm의 source.helm 에valuesObject(replicaCount: 3,greeting: from-values-object)와parameters(greeting=from-parameter)를 더해 다시 적용하세요. 동기화가 끝나면/root/capa-src/precedence.json에replicas(src-helm 네임스페이스 Deploymentsrc-helm-web의 spec.replicas, 숫자)와greeting(ConfigMapsrc-helm-greeting의 값)을 적습니다.- Argo CD 는 차트를 렌더링해 적용할 뿐 Helm 릴리스를 만들지 않습니다.
src-helm네임스페이스의 Secret 중 타입이helm.sh/release.v1인 것의 개수와, ConfigMapsrc-helm-greeting에 Argo CD 가 남긴 추적 표식을 확인해/root/capa-src/tracking.json에helm_release_secrets(숫자),tracking_annotation(그 ConfigMap 의argocd.argoproj.io/tracking-id값),instance_label(라벨app.kubernetes.io/instance가 있으면 그 값, 없으면 null)을 적으세요. /root/capa-src/repo/kust에 Kustomize 구조를 커밋·push 하세요.base에 Deploymentapi(replicas 1, 라벨app: api, 이미지nginx:1.27-alpine)와 kustomization,overlays/prod에 base 를 가리키고namePrefix: prod-, replicas 로 api 를 2 로 바꾸는 kustomization 을 둡니다. Applicationsrc-kust(경로kust/overlays/prod, 대상 네임스페이스src-kust, 자동 동기화·CreateNamespace)를/root/capa-src/app-src-kust.yaml에 쓰고, source.kustomize.images 로nginx=nginx:1.28-alpine을 지정해 적용하세요. Deploymentprod-api가 2개 파드·이미지 1.28 로 Healthy 여야 합니다./root/capa-src/repo/waves에 네 파일을 커밋·push 하세요. ConfigMapsettings(sync-wave-1), readinessProbe 가 있는 Deploymentapp(sync-wave0, 이미지 nginx:1.27-alpine), Jobsmoke(sync-wave1, busybox:1.36 이echo smoke ok), PreSync 훅 Job(generateNamemigrate-, hook-delete-policyBeforeHookCreation, busybox:1.36 이echo migrate ok)입니다. Applicationsrc-waves(경로waves, 대상 네임스페이스src-waves, 자동 동기화·CreateNamespace)를/root/capa-src/app-src-waves.yaml로 적용하고 Synced·Healthy·작업 Succeeded 를 확인하세요. 그 시점의 관찰을/root/capa-src/waves.json에hook_job(성공한 PreSync 훅 Job 이름),hook_created(그 Job 의 creationTimestamp),settings_created(ConfigMap settings 의 creationTimestamp)로 남깁니다.- 한 커밋에서
waves/config.yaml의 mode 를green으로,waves/migrate.yaml의 명령을echo migrate failed; exit 1로 바꿔 push 하고src-waves를 hard refresh 하세요. 작업이 실패한 것을 보고/root/capa-src/failed-hook.json에commit(그 커밋 SHA),phase(status.operationState.phase),live_mode(src-waves 의 ConfigMap settings 의 mode)를 적습니다. 그다음 migrate 명령만 원래대로 되돌린 새 커밋을 push 하고(mode 는 green 유지), 필요하면 실패한 작업을 끝낸 뒤src-waves가 새 커밋에 Synced·Healthy·Succeeded 가 되어 mode 가 green 으로 바뀌게 하세요. src-kust에 ignoreDifferences(groupapps, kindDeployment, jsonPointers/spec/replicas)와 syncOptionsRespectIgnoreDifferences=true를 더해 다시 적용하세요(자동 동기화·selfHeal 은 유지). 그다음kubectl scale로prod-api를 4 로 늘리고 40초 이상 기다린 뒤에도 replicas 가 4 이고src-kust가 Synced 인 것을 확인해/root/capa-src/ignore.json에scaled_at(scale 직후 유닉스 초),checked_at,replicas(확인 시점 값, 숫자),sync_status를 적으세요./root/capa-src/report.json에helm_winner(greeting 을 정한 곳:chart·valueFiles·valuesObject·parameters중 하나),replicas_winner(replicaCount 를 정한 곳, 같은 선택지),helm_installed(Helm 릴리스가 만들어졌는지, 불리언),image_source(prod-api 이미지 1.28 이 정의된 곳:git또는application),hook_blocked_sync(6단계 실패 때 mode 가 바뀌지 않았는지, 불리언),replicas_owner(prod-api 의 spec.replicas 필드를 지금 소유한 managedFields 관리자 이름)를 적으세요.
참고
- VM 안에 k3s, Argo CD v3.5.2, git 데몬(
gitd.gitsrv)이 있습니다./srv/bare/<이름>.git은git://gitd.gitsrv.svc.cluster.local:9418/<이름>.git으로 보입니다. - 렌더링 결과 미리 보기:
kubectl -n argocd get app <이름> -o jsonpath='{.status.resources}', 작업 결과:.status.operationState.syncResult.resources. - 즉시 다시 읽게 하기:
kubectl -n argocd annotate app <이름> argocd.argoproj.io/refresh=hard --overwrite. - 흔한 실수: valueFiles 경로를 저장소 루트 기준으로 쓰는 것. 차트 디렉터리 기준입니다.
- 흔한 실수: 6단계에서 훅을 고친 뒤 Synced 만 보고 넘어가는 것. 실패한 revision 을 재시도하는 작업이 남아 있으면 새 커밋이 적용되지 않습니다.
- Helm · Kustomize · Sync Phases and Waves · Diffing · Resource Tracking
차트를 Git 에 두고 Application 으로 가리킨다
베어 저장소 /srv/bare/src.git 을 만들고 /root/capa-src/repo 로 복제해 charts/web 에 Helm 차트를 커밋·push 하세요. Chart.yaml(name web, version 0.1.0), values.yaml(replicaCount: 1, greeting: chart-default), values-prod.yaml(replicaCount: 2, greeting: from-values-file), 템플릿 두 개({{ .Release.Name }}-greeting ConfigMap 의 data.greeting, {{ .Release.Name }}-web Deployment 의 replicas·이미지 nginx:1.27-alpine)를 둡니다. /root/capa-src/app-src-helm.yaml 의 Application src-helm(저장소 git://gitd.gitsrv.svc.cluster.local:9418/src.git main·charts/web, 대상 네임스페이스 src-helm, 자동 동기화·CreateNamespace)에 helm.valueFiles: [values-prod.yaml] 를 주고 적용해 Synced·Healthy 를 확인하세요.
Argo CD 는 경로에 Chart.yaml 이 있으면 Helm 소스로 봅니다. 릴리스 이름을 따로 주지 않으면 Application 이름이 쓰입니다. 값 파일 경로는 차트 디렉터리 기준입니다.
values 파일은 2 인데 파드는 3개였다
src-helm 의 source.helm 에 valuesObject(replicaCount: 3, greeting: from-values-object)와 parameters(greeting = from-parameter)를 더해 다시 적용하세요. 동기화가 끝나면 /root/capa-src/precedence.json 에 replicas(src-helm 네임스페이스 Deployment src-helm-web 의 spec.replicas, 숫자)와 greeting(ConfigMap src-helm-greeting 의 값)을 적습니다.
같은 키가 차트 기본값·valueFiles·valuesObject·parameters 에 모두 있습니다. 어느 것이 이기는지는 추측하지 말고 클러스터에 실제로 만들어진 값을 읽으세요.
helm list 에는 아무것도 없다
Argo CD 는 차트를 렌더링해 적용할 뿐 Helm 릴리스를 만들지 않습니다. src-helm 네임스페이스의 Secret 중 타입이 helm.sh/release.v1 인 것의 개수와, ConfigMap src-helm-greeting 에 Argo CD 가 남긴 추적 표식을 확인해 /root/capa-src/tracking.json 에 helm_release_secrets(숫자), tracking_annotation(그 ConfigMap 의 argocd.argoproj.io/tracking-id 값), instance_label(라벨 app.kubernetes.io/instance 가 있으면 그 값, 없으면 null)을 적으세요.
helm install 은 네임스페이스에 릴리스 기록 Secret 을 남깁니다. Argo CD 가 자기 리소스를 알아보는 방법(tracking method)은 argocd-cm 의 application.resourceTrackingMethod 로 정해지며 이 판의 기본값을 직접 확인하세요.
Git 에는 1.27 인데 클러스터에는 1.28
/root/capa-src/repo/kust 에 Kustomize 구조를 커밋·push 하세요. base 에 Deployment api(replicas 1, 라벨 app: api, 이미지 nginx:1.27-alpine)와 kustomization, overlays/prod 에 base 를 가리키고 namePrefix: prod-, replicas 로 api 를 2 로 바꾸는 kustomization 을 둡니다. Application src-kust(경로 kust/overlays/prod, 대상 네임스페이스 src-kust, 자동 동기화·CreateNamespace)를 /root/capa-src/app-src-kust.yaml 에 쓰고, source.kustomize.images 로 nginx=nginx:1.28-alpine 을 지정해 적용하세요. Deployment prod-api 가 2개 파드·이미지 1.28 로 Healthy 여야 합니다.
Application 의 kustomize 필드는 Argo CD 가 kustomize edit 로 렌더링 직전에 덧씌웁니다. 이 값은 Git 이 아니라 Application 객체에 있다는 점을 기억해 두세요.
스모크 Job 은 앱이 준비된 뒤에야 생겼다
/root/capa-src/repo/waves 에 네 파일을 커밋·push 하세요. ConfigMap settings(sync-wave -1), readinessProbe 가 있는 Deployment app(sync-wave 0, 이미지 nginx:1.27-alpine), Job smoke(sync-wave 1, busybox:1.36 이 echo smoke ok), PreSync 훅 Job(generateName migrate-, hook-delete-policy BeforeHookCreation, busybox:1.36 이 echo migrate ok)입니다. Application src-waves(경로 waves, 대상 네임스페이스 src-waves, 자동 동기화·CreateNamespace)를 /root/capa-src/app-src-waves.yaml 로 적용하고 Synced·Healthy·작업 Succeeded 를 확인하세요. 그 시점의 관찰을 /root/capa-src/waves.json 에 hook_job(성공한 PreSync 훅 Job 이름), hook_created(그 Job 의 creationTimestamp), settings_created(ConfigMap settings 의 creationTimestamp)로 남깁니다.
Argo CD 는 PreSync → Sync → PostSync 단계로 나누고, 한 단계 안에서는 웨이브 번호 순서로 적용하며 다음 웨이브로 가기 전에 앞 웨이브의 리소스가 Healthy 가 되기를 기다립니다. 채점기는 생성 시각과 Deployment 가 Available 이 된 시각을 비교합니다. BeforeHookCreation 훅 Job 은 다음 동기화 때 지워지므로 지금 이름과 시각을 기록해 두어야 나중에도 증거가 남습니다.
훅이 실패하자 바꾼 설정은 적용되지 않았다
한 커밋에서 waves/config.yaml 의 mode 를 green 으로, waves/migrate.yaml 의 명령을 echo migrate failed; exit 1 로 바꿔 push 하고 src-waves 를 hard refresh 하세요. 작업이 실패한 것을 보고 /root/capa-src/failed-hook.json 에 commit(그 커밋 SHA), phase(status.operationState.phase), live_mode(src-waves 의 ConfigMap settings 의 mode)를 적습니다. 그다음 migrate 명령만 원래대로 되돌린 새 커밋을 push 하고(mode 는 green 유지), 필요하면 실패한 작업을 끝낸 뒤 src-waves 가 새 커밋에 Synced·Healthy·Succeeded 가 되어 mode 가 green 으로 바뀌게 하세요.
PreSync 훅이 실패하면 그 동기화 작업은 Sync 단계로 넘어가지 않습니다. 자동 동기화는 실패한 revision 으로 재시도하며 다음 커밋을 막을 수 있으니 status.operationState.operation.sync.revision 을 보세요. core 모드 명령(argocd app terminate-op --core)은 현재 네임스페이스가 argocd 인 kubeconfig 가 필요합니다.
손으로 늘린 레플리카를 selfHeal 이 두고 본다
src-kust 에 ignoreDifferences(group apps, kind Deployment, jsonPointers /spec/replicas)와 syncOptions RespectIgnoreDifferences=true 를 더해 다시 적용하세요(자동 동기화·selfHeal 은 유지). 그다음 kubectl scale 로 prod-api 를 4 로 늘리고 40초 이상 기다린 뒤에도 replicas 가 4 이고 src-kust 가 Synced 인 것을 확인해 /root/capa-src/ignore.json 에 scaled_at(scale 직후 유닉스 초), checked_at, replicas(확인 시점 값, 숫자), sync_status 를 적으세요.
ignoreDifferences 만 두면 비교에서만 빠지고, 다른 이유로 동기화가 일어날 때 Git 의 값으로 덮일 수 있습니다. RespectIgnoreDifferences 는 동기화 때도 그 필드를 건드리지 않게 합니다. HPA 가 레플리카를 관리하는 앱의 흔한 설정입니다.
Git·Application·클러스터 중 누가 값을 정했나
/root/capa-src/report.json 에 helm_winner(greeting 을 정한 곳: chart·valueFiles·valuesObject·parameters 중 하나), replicas_winner(replicaCount 를 정한 곳, 같은 선택지), helm_installed(Helm 릴리스가 만들어졌는지, 불리언), image_source(prod-api 이미지 1.28 이 정의된 곳: git 또는 application), hook_blocked_sync(6단계 실패 때 mode 가 바뀌지 않았는지, 불리언), replicas_owner(prod-api 의 spec.replicas 필드를 지금 소유한 managedFields 관리자 이름)를 적으세요.
앞 단계에서 남긴 JSON 과 클러스터의 managedFields 를 근거로 합니다. kubectl get --show-managed-fields -o json 에서 f:spec 아래에 f:replicas 를 가진 항목을 찾으세요. status 의 replicas 는 컨트롤러가 씁니다.