어느 날 파드가 일시적으로 죽다.
어느 날, 사내 인프라 미들웨어에서 이상 현상이 발생했다. 간헐적으로 일부 특정 파드가 재기동되는 상황이 반복되고 있었고, 이는 단순한 장애라기보다는 반복적으로 나타나는 패턴성 문제처럼 보였다.
재기동되는 파드를 확인해 보니 주로 Harbor와 MinIO가 포함되어 있었다. 특히 Harbor는 내부 CI/CD 파이프라인에서 컨테이너 이미지 레지스트리로 사용되고 있기 때문에, 이 문제가 빌드 프로세스 전반에도 영향을 주고 있었다.

문제 원인을 파악해보자.
Phase 1: 애플리케이션 로그 확인
먼저, 파드 내부 애플리케이션 로그를 확인했다.harbor-core
파드가 SIGTERM 신호를 받아 정상적으로 종료된 로그를 확인할 수 있었지만, 이는 종료 “과정”에 대한 로그일 뿐, 종료 “원인”은 아니었다.

Phase 2: 리소스 문제?
다음으로, 리소스 제약이 원인일 수 있다고 판단해 리소스 Limit 제한을 제거해 보았다.
그러나, 여전히 문제가 반복되었고, OOMKilled와 같은 Out of Memory 에러나 CrashLoopBackOff 이벤트도 발생하지 않았다.
Phase 3: Probe 문제?
파드가 재기동된 원인을 Liveness/Readiness Probe 설정 문제로 의심해 보았다.
보통 probe 설정에 실패하면 파드는 에러 상태로 간주되어 재기동되며, restart count가 증가하게 된다.
그러나 이번 경우는 restart count가 증가하지 않았고, 재시작이 아닌 “롤아웃(Rollout)” 이 발생하고 있었다.
🙋♂️ “혹시 Probe의 timeoutSeconds 값이 너무 짧은 게 아닐까?”라는 의심도 들었지만,
Helm Chart의 values.yaml에는 관련 옵션이 노출되어 있지 않았고,
디폴트 설정이 문제를 유발할 확률은 낮다고 판단했다.
그리고 마침내… ArgoCD를 의심하게 되다..
그렇게 이벤트 로그를 확인하던 중,
ArgoCD의 Sync 이벤트와 파드가 롤아웃되는 시간이 일치하는 것을 발견했다.
처음에는 너무 단순한 문제라고 생각했지만, 결국 ArgoCD에 의해 롤아웃이 발생하고 있었다는 사실이 확인되었다.
하지만 여전히 의문은 남는다.
분명히 Helm Chart에 변경 사항이 없었고, ArgoCD의 자동 동기화(자동 배포)가 트리거 될만한 이유가 없었기 때문이다.
그렇기에 초반에는 ArgoCD를 원인 후보에서 제외하고 있었다.
“애플리케이션 자체에 변경이 없는데, ArgoCD가 배포를 트리거한다면 뭔가 다른 문제가 있는 것이 아닌가?”라고 판단했던 것이다.
왜 ArgoCD가 의도치 않게 애플리케이션을 배포하였을까?
🔍 ArgoCD 구조 이해: App of Apps 패턴
우리 조직의 개발 환경은 Helm 기반의 App of Apps 패턴으로 구성되어 있다.
최상위 애플리케이션은 여러 개의 하위 Helm Chart를 포함하며, 각 하위 애플리케이션은 AutoSync가 활성화되어 있다.
app-of-apps
├── charts
│ ├── mgm
│ │ └── ...
│ └── oss
│ ├── templates
│ │ ├── harbor.yaml
│ │ └── ...
├── dev
│ └── oss-application.yaml
└── ...


🔎 정말 ArgoCD가 문제였을까?
혹시나 하는 마음에, Harbor 애플리케이션의 AutoSync를 비활성화한 후 관찰을 시작했다.
그런데 정말 놀랍게도, 그 이후부터 파드가 더 이상 재기동되지 않았다.
그리고 ArgoCD의 상태는 OutOfSync로 표시되고 있었다.

이쯤 되니 ArgoCD가 재배포를 트리거한 주체라는 점은 분명해졌다.
하지만 왜? 애초에 Helm Chart의 코드나 values 파일에는 변경사항(애플리케이션 변경사항)이 없었는데?
Helm Template 결과가 매번 달라진다면?
Harbor 차트의 템플릿 내용을 확인해 보았고, 다음과 같은 점을 발견했다.
• helm template을 실행할 때마다 결과가 달라지는 필드가 있었다.
• 예: randomAlphaNum, timestamp 기반의 값 등
• MinIO의 경우에는 해당 필드가 고정되도록 템플릿에서 무효화(disable) 옵션을 제공하고 있었지만, Harbor는 그렇지 않았다.

helm template 명령어를 수행할 때마다 값이 달라지는 것을 확인할 수 있다.

주석을 보면 , GitOps 도구를 사용할 때 이러한 동작이 불필요한 재시작을 유발할 수 있다고 설명한다. 이러한 경우 minio 같은 경우는 비활성화를 할 수 있도록 옵션으로 제공하고 있다.

ArgoCD는 Helm Template을 사용해 Diff를 비교한다
그렇다면 ArgoCD는 어떻게 변경을 감지할까?
공식 문서를 보면, Helm 기반 애플리케이션인 경우 ArgoCD는 내부적으로 helm template 명령어를 사용하여 현 상태 vs 선언된 상태의 차이(Diff)를 감지한다.

이를 뒷받침하듯, argocd-repo-server의 로그를 확인해 보면 각 애플리케이션에서 등록된 레포지토리의 차트를 helm template으로 렌더링 하는 과정을 볼 수 있었다..

ArgoCD는 레포지토리 단위로 변경점이 발생했는지 확인한다.
"ArgoCD는 레포지토리에 단위로 변경점이 발생하면, ArgoCD는 해당 레포지토리를 참조하는 모든 애플리케이션에 대해 helm template을 수행한다."
(개인적인 생각)
왜 이렇게 동작할까 고민을 해보았는데, ArgoCD의 Webhook을 사용하면서 문득 생각이 들었다.
예를 들어 Jenkins의 경우, 각 Job(파이프라인) 별로 Webhook을 개별 설정한다.

반면 ArgoCD는 /api/webhook이라는 단일 엔드포인트를 통해 동작하며, 별도로 애플리케이션 단위로 나누어 Webhook을 설정하지 않는다.

왜 그럴까?
ArgoCD는 하나의 Git 저장소를 기반으로 수십~수백 개의 애플리케이션(Application 리소스)을 관리할 수 있다.
만약 Jenkins처럼 각각의 Application에 대해 Webhook을 따로 설정해야 한다면,
Webhook 설정이 지나치게 방대해지고 관리 복잡도도 급격히 증가했을 것이다.
추가적으로.
동일 레포지토리를 여러 환경에서 사용할 경우
우리 환경은 values-dev.yaml
, values-stg.yaml
, values-prd.yaml
파일을 사용하여 동일한 레포지토리를 환경별로 재사용하고 있다
이 말은 곧, 하나의 레포지토리에서 template 결과에 변동이 발생하면, 운영, 스테이징, 개발 환경 모두에 영향을 줄 수 있다는 것이다.
다행히도 운영과 stg 환경은 AutoSync가 비활성화되어 있어 실제로 문제는 피할 수 있었다.
하지만 개발 환경에서만 이슈가 발생했기에 ArgoCD가 원인이라는 판단을 늦게 하게 되었다
😱 Readme 파일만 수정해도 배포가 된다?
ArgoCD가 레포지토리 단위로 동작하다 보니, 예상하지 못했던 상황도 발생했다.
실제로 테스트를 위해 README.md 파일만 수정해서 커밋을 올려봤는데도, 해당 레포지토리를 참조하는 모든 애플리케이션에 대해 ArgoCD는 template → diff → 배포 과정을 실행했다.
이러한 동작은 GitOps 워크플로우의 본질인 “소스 코드 상태 = 실제 클러스터 상태”라는 개념과는 잘 맞지만, (declaritve)
운영 측면에서는 예상치 못한 부작용을 초래할 수 있다.
그러면 bitnami처럼 대형 Helm Chart 레포에선?
만약 bitnami처럼 많은 차트를 담고 있는 Helm Chart 레포지토리에서, randomAlphaNum 등 template 결과가 변동될 수 있는 필드가 존재하고, ArgoCD에 같은 레포를 참조하는 여러 애플리케이션이 등록되어 있다면, 누군가 레포에 푸시만 해도, 하위의 모든 애플리케이션이 “변경됨”으로 감지되는 것은 아닐까?
그렇게 된다면, bitnami 차트를 사용하는 전 세계의 모든 애플리케이션이 변경감지가 되는 것인가? 🤔
증상을 재현해 보자.
앞서 분석한 바와 같이, Helm Chart 내부의 randAlphaNum
과 같은 동적 템플릿 요소가 존재할 경우,
레포지토리 내에 코드와 무관한 변경(예: README.md 수정)만 발생해도 ArgoCD는 Diff를 감지하고 재배포를 수행할 수 있다.
다음과 같은 실습을 통해 증상을 재현해 보자.
1. 테스트용 Helm Chart 준비
먼저, 서로 다른 환경(dev, prd)을 위한 두 개의 Nginx Helm Chart 기반 애플리케이션을 구성했다.
이를 위해 개인 GitHub 레포지토리를 활용해 Helm Chart를 작성하고, 아래와 같이 랜덤 문자열을 생성하는 randAlphaNum 값을 추가했다:
# nginx-deployment.yaml
env:
- name: RANDOM_VALUE
value: {{ randAlphaNum 8 | quote }}
📦 GitHub 레포지토리 클론
# Clone
git clone https://github.com/hackjap/cicd
cd cicd
2. ArgoCD 레포지토리 등록
ArgoCD UI에서 위 레포지토리를 등록하였다.
사내에는 깃 레포지토리로 Bitbucket을 사용하는데. 혹시 내가 모르는 내부의 Webhook 설정이나 정책에 영향을 받을 수도 있다는 생각에, 확실한 테스트를 위해 bitbucket이 아닌 외부의 개인 GitHub 레포지토리를 생성하여 사용하였다.

레포지토리 연결 확인

3. Nginx 애플리케이션 배포 (dev / prd 환경)
각 환경에 대해 동일한 Chart 경로와 소스를 참조하되, values-dev.yaml, values-prd.yaml 파일로 구분된 Application을 생성했다.
📄 Application 리소스
# dev
cat << EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: dev-nginx
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
helm:
valueFiles:
- values-dev.yaml
path: argocd/helm-chartsnginx-chart
repoURL: https://github.com/hackjap/cicd
targetRevision: HEAD
syncPolicy:
automated:
prune: true
syncOptions:
- CreateNamespace=true
destination:
namespace: dev-nginx
server: https://kubernetes.default.svc
EOF
# prd
cat << EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: prd-nginx
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
destination:
namespace: prd-nginx
server: https://kubernetes.default.svc
project: default
source:
helm:
valueFiles:
- values-prd.yaml
path: argocd/helm-chartsnginx-chart
repoURL: https://github.com/hackjap/cicd
targetRevision: HEAD
syncPolicy:
automated:
prune: true
syncOptions:
- CreateNamespace=true
EOF
4. 실습: README.md 파일만 수정해 보기
이제 실질적인 Kubernetes 리소스에는 전혀 영향을 주지 않는
README.md 파일만 수정한 후 커밋 & 푸시를 진행했다
pwd
~/cicd
# README.md 파일 수정 후 커밋&푸시
echo "add" >> README.md && git add -A && git commit -m "update" && git push -u origin main

5. 결과 확인
푸시 직후, ArgoCD에서는 해당 레포지토리를 참조하는 두 애플리케이션 모두에서
OutOfSync 상태가 감지되었고, dev 환경 애플리케이션은 자동으로 동기화(Sync)가 실행되었다.

실습 GIF

정리하자면,
• Helm Chart 내 randAlphaNum과 같은 동적 랜덤 요소가 존재할 경우,
• Git 레포지토리 내 어떤 파일이든 변경되면 helm template 결과가 달라지고,
• ArgoCD는 이를 변경사항으로 감지 → 자동 동기화까지 수행하게 된다.
이러한 동작은 GitOps 철학상 자연스러운 행동일 수 있지만,
실제로는 비즈니스 영향도가 없는 변경에 대해 불필요한 재배포를 발생시킬 수 있다.
해결방법 - Diffing Customization
ArgoCD는 기본적으로 helm template 결과를 바탕으로 쿠버네티스 리소스와 Git 소스 간의 차이를 감지한다.
하지만, 공식문서에서도 명시하듯이 다음과 같이 변경을 무시하는 것이 바람직한 경우도 존재한다.
• randAlphaNum
, now
, timestamp
등 매번 결과가 달라지는 Helm 템플릿 함수
• HPA의 동적 조정 값
• Mutating Admission Webhook이 리소스에 삽입하는 필드
• Secret, TLS 등 자동 생성되는 인증 관련 값
이런 변경을 방지하기 위해 ArgoCD는 ignoreDifferences 옵션을 제공한다.
특정 리소스의 일부 필드를 diff 대상에서 제외시켜 불필요한 배포를 방지할 수 있다.

✍️ Application 설정 예시
다음은 Harbor 애플리케이션의 Diffing Customization 설정 예시이다.
randAlphaNum으로 생성된 secret 값이나, 체크섬 기반의 annotation 등 동적 변경이 잦은 필드들을 제외하는 설정
- 아래 필드를 추가
- group 설정 시, secret의 경우 core group에 해당하지만, 인식하지 못하는 에러 발생, 따라서 *로 일단 설정.(group, kind, name)은 필수 값이 아닌 것으로 보임.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: harbor
spec:
...
ignoreDifferences:
- group: "*"
kind: "*"
namespace: {{ .Values.application.destination.namespace }}
jsonPointers:
- /data/CSRF_KEY
- /data/secret
- /data/tls.crt
- /data/tls.key
- /data/JOBSERVICE_SECRET
- /data/JOBSERVICE_SECRET
- /data/REGISTRY_HTPASSWD
- /data/REGISTRY_HTTP_SECRET
- /spec/template/metadata/annotations/checksum~1secret
- /spec/template/metadata/annotations/checksum~1secret-core
- /spec/template/metadata/annotations/checksum~1secret-jobservice
⚠️ 참고
• group, kind, namespace는 필수 값은 아님.
• Secret의 group은 "core"이나 *로 설정하였다. (인식 이슈 있음).
결론
ArgoCD의 AutoSync는 GitOps 자동화를 구현하는 핵심 기능이지만, 모든 환경에서 무조건 활성화하는 것은 위험할 수 있다. 기본적으로 ArgoCD는 Git 저장소를 주기적으로 Pull 방식으로 감시하며 변경사항을 감지하고 자동으로 배포를 수행한다. 이 방식은 편리하지만, README.md와 같은 비업무 파일의 수정이나 Helm 템플릿 내 randAlphaNum과 같은 동적 요소로 인해 의도하지 않은 재배포가 발생할 수 있다.
특히 관리하는 애플리케이션 수가 많아질수록 ArgoCD의 repo-server는 모든 애플리케이션에 대해 주기적으로 템플릿 렌더링 작업을 수행하게 되며, 이로 인해 CPU 및 메모리 자원을 과도하게 소모하거나 과부하가 발생할 수 있다. 다수의 레포지토리를 동시에 관리하는 구조라면 이러한 부하는 더욱 심각해질 수 있다.
따라서, Push 방식을 적극 고려해 봐야겠다.
Webhook을 활용한 Push 방식은 특정 이벤트(예: 커밋, PR 병합, 태그 푸시) 발생 시에만 동기화를 트리거할 수 있어, 명확한 배포 타이밍 제어와 안전한 운영 환경 구축에 더욱 적합하다.
이번 경험을 통해, GitOps를 운영하는 데 있어 ArgoCD의 동작 원리와 Helm 템플릿의 특성, 특히 helm template 기반의 변경 감지 방식에 대해 보다 깊이 이해할 수 있게 되었다.
'CICD' 카테고리의 다른 글
GoCD 설치 및 기본 사용법 정리 (Feat. Kubernetes) (0) | 2025.04.04 |
---|---|
GitLab CI/CD 기본 사용 방법 및 CI/CD 워크플로우 구성 (0) | 2024.12.21 |
ArgoCD Autopilot: ArgoCD 및 GitOps 환경 자동 구축하기 (0) | 2024.12.19 |
Github Actions 기본 사용 방법 및 CI/CD 워크플로우 구성하기 (2) | 2024.12.14 |
jib를 활용하여 젠킨스 파이프라인 구성하기(GitOps + SpringBoot + Gradle ) (2) | 2024.12.08 |
어느 날 파드가 일시적으로 죽다.
어느 날, 사내 인프라 미들웨어에서 이상 현상이 발생했다. 간헐적으로 일부 특정 파드가 재기동되는 상황이 반복되고 있었고, 이는 단순한 장애라기보다는 반복적으로 나타나는 패턴성 문제처럼 보였다.
재기동되는 파드를 확인해 보니 주로 Harbor와 MinIO가 포함되어 있었다. 특히 Harbor는 내부 CI/CD 파이프라인에서 컨테이너 이미지 레지스트리로 사용되고 있기 때문에, 이 문제가 빌드 프로세스 전반에도 영향을 주고 있었다.

문제 원인을 파악해보자.
Phase 1: 애플리케이션 로그 확인
먼저, 파드 내부 애플리케이션 로그를 확인했다.harbor-core
파드가 SIGTERM 신호를 받아 정상적으로 종료된 로그를 확인할 수 있었지만, 이는 종료 “과정”에 대한 로그일 뿐, 종료 “원인”은 아니었다.

Phase 2: 리소스 문제?
다음으로, 리소스 제약이 원인일 수 있다고 판단해 리소스 Limit 제한을 제거해 보았다.
그러나, 여전히 문제가 반복되었고, OOMKilled와 같은 Out of Memory 에러나 CrashLoopBackOff 이벤트도 발생하지 않았다.
Phase 3: Probe 문제?
파드가 재기동된 원인을 Liveness/Readiness Probe 설정 문제로 의심해 보았다.
보통 probe 설정에 실패하면 파드는 에러 상태로 간주되어 재기동되며, restart count가 증가하게 된다.
그러나 이번 경우는 restart count가 증가하지 않았고, 재시작이 아닌 “롤아웃(Rollout)” 이 발생하고 있었다.
🙋♂️ “혹시 Probe의 timeoutSeconds 값이 너무 짧은 게 아닐까?”라는 의심도 들었지만,
Helm Chart의 values.yaml에는 관련 옵션이 노출되어 있지 않았고,
디폴트 설정이 문제를 유발할 확률은 낮다고 판단했다.
그리고 마침내… ArgoCD를 의심하게 되다..
그렇게 이벤트 로그를 확인하던 중,
ArgoCD의 Sync 이벤트와 파드가 롤아웃되는 시간이 일치하는 것을 발견했다.
처음에는 너무 단순한 문제라고 생각했지만, 결국 ArgoCD에 의해 롤아웃이 발생하고 있었다는 사실이 확인되었다.
하지만 여전히 의문은 남는다.
분명히 Helm Chart에 변경 사항이 없었고, ArgoCD의 자동 동기화(자동 배포)가 트리거 될만한 이유가 없었기 때문이다.
그렇기에 초반에는 ArgoCD를 원인 후보에서 제외하고 있었다.
“애플리케이션 자체에 변경이 없는데, ArgoCD가 배포를 트리거한다면 뭔가 다른 문제가 있는 것이 아닌가?”라고 판단했던 것이다.
왜 ArgoCD가 의도치 않게 애플리케이션을 배포하였을까?
🔍 ArgoCD 구조 이해: App of Apps 패턴
우리 조직의 개발 환경은 Helm 기반의 App of Apps 패턴으로 구성되어 있다.
최상위 애플리케이션은 여러 개의 하위 Helm Chart를 포함하며, 각 하위 애플리케이션은 AutoSync가 활성화되어 있다.
app-of-apps
├── charts
│ ├── mgm
│ │ └── ...
│ └── oss
│ ├── templates
│ │ ├── harbor.yaml
│ │ └── ...
├── dev
│ └── oss-application.yaml
└── ...


🔎 정말 ArgoCD가 문제였을까?
혹시나 하는 마음에, Harbor 애플리케이션의 AutoSync를 비활성화한 후 관찰을 시작했다.
그런데 정말 놀랍게도, 그 이후부터 파드가 더 이상 재기동되지 않았다.
그리고 ArgoCD의 상태는 OutOfSync로 표시되고 있었다.

이쯤 되니 ArgoCD가 재배포를 트리거한 주체라는 점은 분명해졌다.
하지만 왜? 애초에 Helm Chart의 코드나 values 파일에는 변경사항(애플리케이션 변경사항)이 없었는데?
Helm Template 결과가 매번 달라진다면?
Harbor 차트의 템플릿 내용을 확인해 보았고, 다음과 같은 점을 발견했다.
• helm template을 실행할 때마다 결과가 달라지는 필드가 있었다.
• 예: randomAlphaNum, timestamp 기반의 값 등
• MinIO의 경우에는 해당 필드가 고정되도록 템플릿에서 무효화(disable) 옵션을 제공하고 있었지만, Harbor는 그렇지 않았다.

helm template 명령어를 수행할 때마다 값이 달라지는 것을 확인할 수 있다.

주석을 보면 , GitOps 도구를 사용할 때 이러한 동작이 불필요한 재시작을 유발할 수 있다고 설명한다. 이러한 경우 minio 같은 경우는 비활성화를 할 수 있도록 옵션으로 제공하고 있다.

ArgoCD는 Helm Template을 사용해 Diff를 비교한다
그렇다면 ArgoCD는 어떻게 변경을 감지할까?
공식 문서를 보면, Helm 기반 애플리케이션인 경우 ArgoCD는 내부적으로 helm template 명령어를 사용하여 현 상태 vs 선언된 상태의 차이(Diff)를 감지한다.

이를 뒷받침하듯, argocd-repo-server의 로그를 확인해 보면 각 애플리케이션에서 등록된 레포지토리의 차트를 helm template으로 렌더링 하는 과정을 볼 수 있었다..

ArgoCD는 레포지토리 단위로 변경점이 발생했는지 확인한다.
"ArgoCD는 레포지토리에 단위로 변경점이 발생하면, ArgoCD는 해당 레포지토리를 참조하는 모든 애플리케이션에 대해 helm template을 수행한다."
(개인적인 생각)
왜 이렇게 동작할까 고민을 해보았는데, ArgoCD의 Webhook을 사용하면서 문득 생각이 들었다.
예를 들어 Jenkins의 경우, 각 Job(파이프라인) 별로 Webhook을 개별 설정한다.

반면 ArgoCD는 /api/webhook이라는 단일 엔드포인트를 통해 동작하며, 별도로 애플리케이션 단위로 나누어 Webhook을 설정하지 않는다.

왜 그럴까?
ArgoCD는 하나의 Git 저장소를 기반으로 수십~수백 개의 애플리케이션(Application 리소스)을 관리할 수 있다.
만약 Jenkins처럼 각각의 Application에 대해 Webhook을 따로 설정해야 한다면,
Webhook 설정이 지나치게 방대해지고 관리 복잡도도 급격히 증가했을 것이다.
추가적으로.
동일 레포지토리를 여러 환경에서 사용할 경우
우리 환경은 values-dev.yaml
, values-stg.yaml
, values-prd.yaml
파일을 사용하여 동일한 레포지토리를 환경별로 재사용하고 있다
이 말은 곧, 하나의 레포지토리에서 template 결과에 변동이 발생하면, 운영, 스테이징, 개발 환경 모두에 영향을 줄 수 있다는 것이다.
다행히도 운영과 stg 환경은 AutoSync가 비활성화되어 있어 실제로 문제는 피할 수 있었다.
하지만 개발 환경에서만 이슈가 발생했기에 ArgoCD가 원인이라는 판단을 늦게 하게 되었다
😱 Readme 파일만 수정해도 배포가 된다?
ArgoCD가 레포지토리 단위로 동작하다 보니, 예상하지 못했던 상황도 발생했다.
실제로 테스트를 위해 README.md 파일만 수정해서 커밋을 올려봤는데도, 해당 레포지토리를 참조하는 모든 애플리케이션에 대해 ArgoCD는 template → diff → 배포 과정을 실행했다.
이러한 동작은 GitOps 워크플로우의 본질인 “소스 코드 상태 = 실제 클러스터 상태”라는 개념과는 잘 맞지만, (declaritve)
운영 측면에서는 예상치 못한 부작용을 초래할 수 있다.
그러면 bitnami처럼 대형 Helm Chart 레포에선?
만약 bitnami처럼 많은 차트를 담고 있는 Helm Chart 레포지토리에서, randomAlphaNum 등 template 결과가 변동될 수 있는 필드가 존재하고, ArgoCD에 같은 레포를 참조하는 여러 애플리케이션이 등록되어 있다면, 누군가 레포에 푸시만 해도, 하위의 모든 애플리케이션이 “변경됨”으로 감지되는 것은 아닐까?
그렇게 된다면, bitnami 차트를 사용하는 전 세계의 모든 애플리케이션이 변경감지가 되는 것인가? 🤔
증상을 재현해 보자.
앞서 분석한 바와 같이, Helm Chart 내부의 randAlphaNum
과 같은 동적 템플릿 요소가 존재할 경우,
레포지토리 내에 코드와 무관한 변경(예: README.md 수정)만 발생해도 ArgoCD는 Diff를 감지하고 재배포를 수행할 수 있다.
다음과 같은 실습을 통해 증상을 재현해 보자.
1. 테스트용 Helm Chart 준비
먼저, 서로 다른 환경(dev, prd)을 위한 두 개의 Nginx Helm Chart 기반 애플리케이션을 구성했다.
이를 위해 개인 GitHub 레포지토리를 활용해 Helm Chart를 작성하고, 아래와 같이 랜덤 문자열을 생성하는 randAlphaNum 값을 추가했다:
# nginx-deployment.yaml
env:
- name: RANDOM_VALUE
value: {{ randAlphaNum 8 | quote }}
📦 GitHub 레포지토리 클론
# Clone
git clone https://github.com/hackjap/cicd
cd cicd
2. ArgoCD 레포지토리 등록
ArgoCD UI에서 위 레포지토리를 등록하였다.
사내에는 깃 레포지토리로 Bitbucket을 사용하는데. 혹시 내가 모르는 내부의 Webhook 설정이나 정책에 영향을 받을 수도 있다는 생각에, 확실한 테스트를 위해 bitbucket이 아닌 외부의 개인 GitHub 레포지토리를 생성하여 사용하였다.

레포지토리 연결 확인

3. Nginx 애플리케이션 배포 (dev / prd 환경)
각 환경에 대해 동일한 Chart 경로와 소스를 참조하되, values-dev.yaml, values-prd.yaml 파일로 구분된 Application을 생성했다.
📄 Application 리소스
# dev
cat << EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: dev-nginx
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
helm:
valueFiles:
- values-dev.yaml
path: argocd/helm-chartsnginx-chart
repoURL: https://github.com/hackjap/cicd
targetRevision: HEAD
syncPolicy:
automated:
prune: true
syncOptions:
- CreateNamespace=true
destination:
namespace: dev-nginx
server: https://kubernetes.default.svc
EOF
# prd
cat << EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: prd-nginx
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
destination:
namespace: prd-nginx
server: https://kubernetes.default.svc
project: default
source:
helm:
valueFiles:
- values-prd.yaml
path: argocd/helm-chartsnginx-chart
repoURL: https://github.com/hackjap/cicd
targetRevision: HEAD
syncPolicy:
automated:
prune: true
syncOptions:
- CreateNamespace=true
EOF
4. 실습: README.md 파일만 수정해 보기
이제 실질적인 Kubernetes 리소스에는 전혀 영향을 주지 않는
README.md 파일만 수정한 후 커밋 & 푸시를 진행했다
pwd
~/cicd
# README.md 파일 수정 후 커밋&푸시
echo "add" >> README.md && git add -A && git commit -m "update" && git push -u origin main

5. 결과 확인
푸시 직후, ArgoCD에서는 해당 레포지토리를 참조하는 두 애플리케이션 모두에서
OutOfSync 상태가 감지되었고, dev 환경 애플리케이션은 자동으로 동기화(Sync)가 실행되었다.

실습 GIF

정리하자면,
• Helm Chart 내 randAlphaNum과 같은 동적 랜덤 요소가 존재할 경우,
• Git 레포지토리 내 어떤 파일이든 변경되면 helm template 결과가 달라지고,
• ArgoCD는 이를 변경사항으로 감지 → 자동 동기화까지 수행하게 된다.
이러한 동작은 GitOps 철학상 자연스러운 행동일 수 있지만,
실제로는 비즈니스 영향도가 없는 변경에 대해 불필요한 재배포를 발생시킬 수 있다.
해결방법 - Diffing Customization
ArgoCD는 기본적으로 helm template 결과를 바탕으로 쿠버네티스 리소스와 Git 소스 간의 차이를 감지한다.
하지만, 공식문서에서도 명시하듯이 다음과 같이 변경을 무시하는 것이 바람직한 경우도 존재한다.
• randAlphaNum
, now
, timestamp
등 매번 결과가 달라지는 Helm 템플릿 함수
• HPA의 동적 조정 값
• Mutating Admission Webhook이 리소스에 삽입하는 필드
• Secret, TLS 등 자동 생성되는 인증 관련 값
이런 변경을 방지하기 위해 ArgoCD는 ignoreDifferences 옵션을 제공한다.
특정 리소스의 일부 필드를 diff 대상에서 제외시켜 불필요한 배포를 방지할 수 있다.

✍️ Application 설정 예시
다음은 Harbor 애플리케이션의 Diffing Customization 설정 예시이다.
randAlphaNum으로 생성된 secret 값이나, 체크섬 기반의 annotation 등 동적 변경이 잦은 필드들을 제외하는 설정
- 아래 필드를 추가
- group 설정 시, secret의 경우 core group에 해당하지만, 인식하지 못하는 에러 발생, 따라서 *로 일단 설정.(group, kind, name)은 필수 값이 아닌 것으로 보임.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: harbor
spec:
...
ignoreDifferences:
- group: "*"
kind: "*"
namespace: {{ .Values.application.destination.namespace }}
jsonPointers:
- /data/CSRF_KEY
- /data/secret
- /data/tls.crt
- /data/tls.key
- /data/JOBSERVICE_SECRET
- /data/JOBSERVICE_SECRET
- /data/REGISTRY_HTPASSWD
- /data/REGISTRY_HTTP_SECRET
- /spec/template/metadata/annotations/checksum~1secret
- /spec/template/metadata/annotations/checksum~1secret-core
- /spec/template/metadata/annotations/checksum~1secret-jobservice
⚠️ 참고
• group, kind, namespace는 필수 값은 아님.
• Secret의 group은 "core"이나 *로 설정하였다. (인식 이슈 있음).
결론
ArgoCD의 AutoSync는 GitOps 자동화를 구현하는 핵심 기능이지만, 모든 환경에서 무조건 활성화하는 것은 위험할 수 있다. 기본적으로 ArgoCD는 Git 저장소를 주기적으로 Pull 방식으로 감시하며 변경사항을 감지하고 자동으로 배포를 수행한다. 이 방식은 편리하지만, README.md와 같은 비업무 파일의 수정이나 Helm 템플릿 내 randAlphaNum과 같은 동적 요소로 인해 의도하지 않은 재배포가 발생할 수 있다.
특히 관리하는 애플리케이션 수가 많아질수록 ArgoCD의 repo-server는 모든 애플리케이션에 대해 주기적으로 템플릿 렌더링 작업을 수행하게 되며, 이로 인해 CPU 및 메모리 자원을 과도하게 소모하거나 과부하가 발생할 수 있다. 다수의 레포지토리를 동시에 관리하는 구조라면 이러한 부하는 더욱 심각해질 수 있다.
따라서, Push 방식을 적극 고려해 봐야겠다.
Webhook을 활용한 Push 방식은 특정 이벤트(예: 커밋, PR 병합, 태그 푸시) 발생 시에만 동기화를 트리거할 수 있어, 명확한 배포 타이밍 제어와 안전한 운영 환경 구축에 더욱 적합하다.
이번 경험을 통해, GitOps를 운영하는 데 있어 ArgoCD의 동작 원리와 Helm 템플릿의 특성, 특히 helm template 기반의 변경 감지 방식에 대해 보다 깊이 이해할 수 있게 되었다.
'CICD' 카테고리의 다른 글
GoCD 설치 및 기본 사용법 정리 (Feat. Kubernetes) (0) | 2025.04.04 |
---|---|
GitLab CI/CD 기본 사용 방법 및 CI/CD 워크플로우 구성 (0) | 2024.12.21 |
ArgoCD Autopilot: ArgoCD 및 GitOps 환경 자동 구축하기 (0) | 2024.12.19 |
Github Actions 기본 사용 방법 및 CI/CD 워크플로우 구성하기 (2) | 2024.12.14 |
jib를 활용하여 젠킨스 파이프라인 구성하기(GitOps + SpringBoot + Gradle ) (2) | 2024.12.08 |