2025년 11월 8일
수동 배포는 이제 그만! Git Push 한 번으로 자동 배포되는 마법
Day 7에서 Ceph 분산 스토리지로 진정한 동적 프로비저닝을 경험했습니다. 이제 Day 8에서는 GitOps 패러다임의 핵심 도구인 Helm과 ArgoCD를 마스터하여 선언적 배포 자동화를 완성했습니다.
오늘 배운 것:
1. Helm의 철학과 Custom Chart 생성 (Chart.yaml, templates/, values)
2. 환경별 Values 관리 (values-dev.yaml, values-staging.yaml, values-prod.yaml)
3. ArgoCD 설치 및 아키텍처 이해 (Application Controller, Repo Server)
4. GitOps 워크플로우 (Git → ArgoCD → Kubernetes 자동 동기화)
5. Self-Heal 기능 (수동 변경 자동 복구)
6. Sync Waves와 Hooks (순차적 배포, DB 마이그레이션)
7. 다중 환경 배포 (하나의 차트, 세 가지 환경)
🤔 내가 이해한 것:
Kubernetes YAML을 직접 관리하다 보면 이런 문제가 생깁니다:
문제점:
❌ YAML 파일 수십 개 (Deployment, Service, ConfigMap, Secret, Ingress...)
❌ 환경별 복사본 (dev, staging, prod 각각 관리)
❌ 변수 관리 어려움 (이미지 태그, 포트, 리소스 등)
❌ 롤백 복잡함 (어떤 YAML을 되돌릴지?)
❌ 재사용 불가 (다른 프로젝트에서 복사-붙여넣기)
Helm의 해결책:
✅ 패키지 단위 관리 (Chart = 모든 리소스를 하나로)
✅ 템플릿화 ({{ .Values.image.tag }} 같은 변수)
✅ 환경별 Values 파일 (values-dev.yaml, values-prod.yaml)
✅ 버전 관리 (helm rollback으로 즉시 복구)
✅ Chart 재사용 (공식 차트 저장소, 자체 차트)
Kubernetes YAML vs Helm:
기존 YAML 방식:
myapp/
├─ deployment-dev.yaml
├─ deployment-staging.yaml
├─ deployment-prod.yaml
├─ service-dev.yaml
├─ service-staging.yaml
├─ service-prod.yaml
├─ configmap-dev.yaml
└─ ... (복사본 지옥!)
→ 환경 추가 시 모든 파일 복사
→ 이미지 태그 변경 시 3개 파일 수정
Helm 방식:
myapp/
├─ Chart.yaml
├─ values.yaml (기본값)
├─ values-dev.yaml
├─ values-staging.yaml
├─ values-prod.yaml
└─ templates/
├─ deployment.yaml (템플릿)
├─ service.yaml (템플릿)
└─ configmap.yaml (템플릿)
→ 환경 추가 시 values-*.yaml 하나만 추가
→ 이미지 태그 변경 시 values 파일 한 줄만 수정!
Chart (차트):
helm create로 생성Values (값):
# values.yaml (기본값)
replicaCount: 2
image:
repository: nginx
tag: "1.25.3"
service:
port: 80
# values-prod.yaml (프로덕션 오버라이드)
replicaCount: 5 # ← 프로덕션은 5개로!
resources:
limits:
memory: "512Mi"
템플릿 (Template):
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name }}
spec:
replicas: {{ .Values.replicaCount }} # ← 값 주입!
template:
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
ports:
- containerPort: {{ .Values.service.port }}
Release (릴리스):
Chart를 특정 환경에 설치한 인스턴스
예시:
myapp-dev (myapp 차트의 dev 릴리스)
myapp-staging (myapp 차트의 staging 릴리스)
myapp-prod (myapp 차트의 prod 릴리스)
Helm 설치 (이미 설치됨):
$ helm version
version.BuildInfo{Version:"v3.16.3", GitCommit:"cfd07493f46efc9debd9cc1b02a0961186df7fdf", GitTreeState:"clean", GoVersion:"go1.22.7"}
기본 차트 스캐폴딩:
# 차트 뼈대 생성
$ helm create myapp
Creating myapp
# 디렉토리 구조 확인
$ tree myapp
myapp/
├── Chart.yaml # 차트 메타데이터
├── values.yaml # 기본 설정값
├── templates/ # Kubernetes 리소스 템플릿
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── _helpers.tpl # 헬퍼 함수
│ ├── hpa.yaml
│ ├── ingress.yaml
│ ├── serviceaccount.yaml
│ └── NOTES.txt # 설치 후 출력 메시지
└── charts/ # 의존성 차트
Chart.yaml 이해:
apiVersion: v2 # Helm 3 = v2
name: myapp # 차트 이름
description: A Helm chart for myapp
type: application # application or library
version: 0.1.0 # 차트 버전 (SemVer)
appVersion: "1.25.3" # 앱 버전 (nginx 1.25.3)
문제: 피곤한 작업이지만 한 번만 하면 계속 재사용 가능!
# values.yaml (기본값)
replicaCount: 2
image:
repository: nginx
tag: "1.25.3"
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 50m
memory: 64Mi
env: "default"
환경별 오버라이드:
# values-dev.yaml (개발 환경 - 최소 리소스)
replicaCount: 1
resources:
limits:
cpu: 50m
memory: 64Mi
requests:
cpu: 25m
memory: 32Mi
env: "development"
# values-staging.yaml (스테이징 - 중간 리소스)
replicaCount: 2
resources:
limits:
cpu: 200m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
env: "staging"
# values-prod.yaml (프로덕션 - 고가용성)
replicaCount: 5
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
env: "production"
service:
type: NodePort # 외부 접근
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 70
🎯 핵심: 템플릿은 한 번만 작성, 환경별 설정만 변경!
전통적인 배포:
개발자 로컬 PC
↓
kubectl apply -f deployment.yaml
↓
Kubernetes 클러스터
문제점:
❌ 누가 언제 무엇을 배포했는지 추적 어려움
❌ 환경마다 다른 상태 (drift)
❌ 롤백 복잡함
❌ 권한 관리 어려움 (개발자마다 kubectl 권한 필요)
GitOps 방식:
개발자 → Git Push
↓
Git Repository (단일 진실 소스, Single Source of Truth)
↓
ArgoCD (자동 감지)
↓
Kubernetes 클러스터 (자동 동기화)
장점:
✅ Git = 모든 변경 이력 추적
✅ Pull Request 기반 코드 리뷰
✅ 롤백 = Git Revert
✅ 선언적 상태 (Desired State in Git)
✅ 중앙 집중식 배포 (ArgoCD만 kubectl 권한 필요)
GitHub Repository
├─ myapp/ (Helm Chart)
│ ├─ templates/
│ └─ values-*.yaml
└─ argocd-apps/
├─ myapp-dev.yaml
├─ myapp-staging.yaml
└─ myapp-prod.yaml
↓
↓ (Git Poll/Webhook)
↓
ArgoCD 컴포넌트
├─ Application Controller
│ - Git 저장소 모니터링
│ - 실제 상태 vs 원하는 상태 비교
│ - 동기화 실행
│
├─ Repo Server
│ - Git Clone
│ - Helm Template 렌더링
│ - Kubernetes 매니페스트 생성
│
├─ API Server
│ - Web UI / CLI 제공
│ - RBAC 인증/인가
│
└─ Redis
- 캐시 (Cluster State, Git Commit)
↓
↓ (kubectl apply)
↓
Kubernetes 클러스터
├─ myapp-dev (Namespace)
├─ myapp-staging (Namespace)
└─ myapp-prod (Namespace)
네임스페이스 생성 및 배포:
# 네임스페이스 생성
$ kubectl create namespace argocd
namespace/argocd created
# ArgoCD 설치 (공식 YAML)
$ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
customresourcedefinition.apiextensions.k8s.io/applications.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/applicationsets.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/appprojects.argoproj.io created
serviceaccount/argocd-application-controller created
...
deployment.apps/argocd-server created
deployment.apps/argocd-repo-server created
deployment.apps/argocd-applicationset-controller created
# Pod 상태 확인
$ kubectl get pods -n argocd
NAME READY STATUS
argocd-application-controller-0 1/1 Running
argocd-applicationset-controller-xxxxx 1/1 Running
argocd-dex-server-xxxxx 1/1 Running
argocd-notifications-controller-xxxxx 1/1 Running
argocd-redis-xxxxx 1/1 Running
argocd-repo-server-xxxxx 1/1 Running
argocd-server-xxxxx 1/1 Running
ArgoCD가 알아서 Helm Chart 인식!
helm.valueFiles 파라미터로 환경별 values 선택NodePort로 외부 노출:
# 서비스 타입 변경
$ kubectl patch svc argocd-server -n argocd -p '{"spec":{"type":"NodePort"}}'
service/argocd-server patched
# NodePort 확인
$ kubectl get svc -n argocd argocd-server
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
argocd-server NodePort 10.103.51.239 <none> 80:31080/TCP,443:31443/TCP
# 접속 URL
http://172.30.1.38:31080
초기 비밀번호 확인:
$ kubectl get secret -n argocd argocd-initial-admin-secret -o jsonpath='{.data.password}' | base64 -d
hZa4rP9qK7mE3nF2
로그인:
Username: admin
Password: hZa4rP9qK7mE3nF2
GitHub 저장소 구조:
ArgoCD-gitops/
├─ myapp/ # Helm Chart
│ ├─ Chart.yaml
│ ├─ values.yaml
│ ├─ values-dev.yaml
│ ├─ values-staging.yaml
│ ├─ values-prod.yaml
│ └─ templates/
│ ├─ deployment.yaml
│ ├─ service.yaml
│ └─ configmap.yaml
│
└─ sync-waves-demo/ # Sync Waves 실습
├─ database.yaml
├─ migration-job.yaml
└─ application.yaml
Git Push:
$ cd /root/argocd-demo
$ git add myapp/
$ git commit -m "Add Helm chart with multi-env values"
$ git push origin main
ArgoCD Application CRD 이해:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp-dev
namespace: argocd
spec:
project: default
# Git 소스
source:
repoURL: https://github.com/hansungmoon/ArgoCD-gitops.git
targetRevision: main
path: myapp # Helm Chart 경로
helm:
valueFiles:
- values-dev.yaml # ← 환경별 values 선택!
# 배포 대상
destination:
server: https://kubernetes.default.svc
namespace: myapp-dev
# 동기화 정책
syncPolicy:
automated:
prune: true # Git에서 삭제된 리소스 자동 제거
selfHeal: true # 수동 변경 자동 복구
syncOptions:
- CreateNamespace=true
helm.valueFiles는 어디 있지?
/root/argocd-helm-apps.yaml 파일의 15-17, 44-46, 73-75 라인!3개 환경 배포:
$ kubectl apply -f /root/argocd-helm-apps.yaml
application.argoproj.io/myapp-dev created
application.argoproj.io/myapp-staging created
application.argoproj.io/myapp-prod created
# Application 상태 확인
$ kubectl get applications -n argocd
NAME SYNC STATUS HEALTH STATUS
myapp-dev Synced Healthy
myapp-staging Synced Healthy
myapp-prod Synced Healthy
네임스페이스별 Pod 확인:
$ kubectl get pods -n myapp-dev
NAME READY STATUS RESTARTS AGE
myapp-xxxxxxxxx-xxxxx 1/1 Running 0 2m
$ kubectl get pods -n myapp-staging
NAME READY STATUS RESTARTS AGE
myapp-xxxxxxxxx-xxxxx 1/1 Running 0 2m
myapp-xxxxxxxxx-xxxxx 1/1 Running 0 2m # ← Replica 2
$ kubectl get pods -n myapp-prod
NAME READY STATUS RESTARTS AGE
myapp-xxxxxxxxx-xxxxx 1/1 Running 0 2m
myapp-xxxxxxxxx-xxxxx 1/1 Running 0 2m
myapp-xxxxxxxxx-xxxxx 1/1 Running 0 2m
myapp-xxxxxxxxx-xxxxx 1/1 Running 0 2m
myapp-xxxxxxxxx-xxxxx 1/1 Running 0 2m # ← Replica 5
🎉 성공! 하나의 Helm Chart, 세 가지 환경!
Self-Heal:
작동 원리:
1. Git: replicas: 2
2. ArgoCD: Kubernetes에 Deployment 배포 (replicas=2)
3. 개발자: kubectl scale deployment myapp --replicas=10
4. ArgoCD: "어? Git에는 2인데 실제는 10이네?" (Drift 감지)
5. ArgoCD: kubectl apply (replicas=2로 복구)
→ 약 5초 후 자동 복구!
시나리오: 개발자가 실수로 Replica를 수동 변경
# 현재 상태 (Git: 2, 실제: 2)
$ kubectl get deployment -n myapp-dev
NAME READY UP-TO-DATE AVAILABLE AGE
myapp 2/2 2 2 5m
# 수동 변경 (kubectl로 직접 수정!)
$ kubectl scale deployment myapp -n myapp-dev --replicas=10
deployment.apps/myapp scaled
# 즉시 확인
$ kubectl get deployment -n myapp-dev
NAME READY UP-TO-DATE AVAILABLE AGE
myapp 10/10 10 10 5m
# 5초 대기...
$ sleep 5
# Self-Heal 작동! (Git 상태로 복구)
$ kubectl get deployment -n myapp-dev
NAME READY UP-TO-DATE AVAILABLE AGE
myapp 2/2 2 2 5m # ← 자동으로 2로 복구!
ArgoCD UI에서 확인:
Application: myapp-dev
Status: OutOfSync → Syncing → Synced
Message: "Deployment replicas reverted to 2 (Git state)"
🎯 교훈:
문제 상황:
Application 배포 시:
1. Database Pod 생성 중... (아직 준비 안 됨)
2. Application Pod 시작 → DB 연결 실패! (CrashLoopBackOff)
3. DB 준비 완료
4. Application 계속 재시작...
→ 배포 순서가 랜덤!
Sync Waves 해결책:
annotations:
argocd.argoproj.io/sync-wave: "0" # ← 낮은 번호부터 배포!
배포 순서:
Wave 0: Database (postgres)
↓ (DB 준비 완료 대기)
Wave 1: DB Migration Job (PreSync Hook)
↓ (마이그레이션 완료)
Wave 2: Application (webapp)
↓ (모든 리소스 Healthy)
Wave 0: Database (먼저 배포)
# database.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
annotations:
argocd.argoproj.io/sync-wave: "0" # ← Wave 0
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
spec:
containers:
- name: postgres
image: postgres:14-alpine
env:
- name: POSTGRES_PASSWORD
value: "password123"
- name: POSTGRES_DB
value: "myapp"
---
apiVersion: v1
kind: Service
metadata:
name: postgres
annotations:
argocd.argoproj.io/sync-wave: "0"
spec:
selector:
app: postgres
ports:
- port: 5432
Wave 1: DB Migration (PreSync Hook)
# migration-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: db-migration
annotations:
argocd.argoproj.io/sync-wave: "1" # ← Wave 1
argocd.argoproj.io/hook: PreSync # ← 앱 배포 전 실행!
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
spec:
template:
spec:
restartPolicy: Never
containers:
- name: migration
image: postgres:14-alpine
command:
- /bin/sh
- -c
- |
echo "=== DB Migration 시작 ==="
echo "데이터베이스 연결 대기 중..."
sleep 5
echo "테이블 생성 중..."
PGPASSWORD=password123 psql -h postgres -U postgres -d myapp -c "
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
created_at TIMESTAMP DEFAULT NOW()
);
"
echo "샘플 데이터 삽입 중..."
PGPASSWORD=password123 psql -h postgres -U postgres -d myapp -c "
INSERT INTO users (name) VALUES ('Alice'), ('Bob'), ('Charlie')
ON CONFLICT DO NOTHING;
"
echo "=== DB Migration 완료 ==="
Hook 종류:
Wave 2: Application (마지막 배포)
# application.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
annotations:
argocd.argoproj.io/sync-wave: "2" # ← Wave 2
spec:
replicas: 2
selector:
matchLabels:
app: webapp
template:
spec:
initContainers:
- name: wait-for-db
image: postgres:14-alpine
command:
- /bin/sh
- -c
- |
echo "데이터베이스 준비 대기 중..."
until PGPASSWORD=password123 psql -h postgres -U postgres -d myapp -c '\l'; do
echo "데이터베이스 연결 대기..."
sleep 2
done
echo "데이터베이스 준비 완료!"
containers:
- name: webapp
image: nginx:1.25.3
Git Push 및 ArgoCD Application 생성:
# Git Push
$ cd /root/argocd-demo/sync-waves-demo
$ git add .
$ git commit -m "Add Sync Waves demo with DB migration"
$ git push origin main
# ArgoCD Application 생성
$ kubectl apply -f /root/sync-waves-app.yaml
application.argoproj.io/sync-waves-demo created
# 배포 순서 확인 (실시간 모니터링)
$ kubectl get pods -n sync-waves -w
NAME READY STATUS RESTARTS AGE
postgres-xxxxxxxxx-xxxxx 0/1 ContainerCreating 0 3s # ← Wave 0
postgres-xxxxxxxxx-xxxxx 1/1 Running 0 15s
db-migration-xxxxx 0/1 ContainerCreating 0 5s # ← Wave 1 (PreSync)
db-migration-xxxxx 1/1 Running 0 8s
db-migration-xxxxx 0/1 Completed 0 25s
webapp-xxxxxxxxx-xxxxx 0/1 Init:0/1 0 3s # ← Wave 2
webapp-xxxxxxxxx-xxxxx 0/1 PodInitializing 0 12s
webapp-xxxxxxxxx-xxxxx 1/1 Running 0 15s
최종 상태:
$ kubectl get pods -n sync-waves
NAME READY STATUS RESTARTS AGE
db-migration-dvlft 0/1 Completed 0 2m # ← Job 완료
postgres-6b9c5b7d9c-4kvxn 1/1 Running 0 2m
webapp-7f8b6c9d8f-7kxqm 1/1 Running 0 1m
webapp-7f8b6c9d8f-9hnpz 1/1 Running 0 1m
$ kubectl get application -n argocd sync-waves-demo
NAME SYNC STATUS HEALTH STATUS
sync-waves-demo Synced Healthy # ← 모두 성공!
🎉 순차적 배포 성공! DB → Migration → App 순서 보장!
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp-dev
namespace: argocd # ← ArgoCD는 argocd 네임스페이스에서 실행
spec:
# 1. 프로젝트 (RBAC, 리소스 격리)
project: default
# 2. Git 소스
source:
repoURL: https://github.com/user/repo.git
targetRevision: main # 브랜치, 태그, 커밋 SHA
path: myapp # Chart 경로
# Helm 설정
helm:
valueFiles:
- values-dev.yaml # ← 여러 개 가능!
- secrets-dev.yaml
parameters: # CLI 오버라이드
- name: replicaCount
value: "3"
releaseName: myapp-dev
# 3. 배포 대상
destination:
server: https://kubernetes.default.svc # 클러스터 URL
namespace: myapp-dev
# 4. 동기화 정책
syncPolicy:
automated:
prune: true # Git 삭제 → Kubernetes 삭제
selfHeal: true # Drift 자동 복구
allowEmpty: false # 빈 커밋 거부
syncOptions:
- CreateNamespace=true # 네임스페이스 자동 생성
- PruneLast=true # 삭제는 마지막에
- ApplyOutOfSyncOnly=true # OutOfSync만 적용
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
# 5. Health 체크 (선택)
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicas # HPA 사용 시 replicas 무시
automated.prune:
Git에서 deployment.yaml 삭제
↓
ArgoCD: "Deployment가 Git에 없네?"
↓
kubectl delete deployment myapp # ← 자동 삭제!
automated.selfHeal:
kubectl scale deployment myapp --replicas=10
↓
ArgoCD: "Git에는 2인데 실제는 10이네?" (5초마다 체크)
↓
kubectl apply (Git 상태로 복구)
syncOptions:
- CreateNamespace=true # 네임스페이스 없으면 생성
- PrunePropagationPolicy=foreground # 삭제 순서 제어
- Validate=false # kubectl validation 건너뛰기
retry:
1차 시도 실패
↓ 5초 대기
2차 시도 실패
↓ 10초 대기 (factor=2)
3차 시도 실패
↓ 20초 대기
...
최대 3분까지 재시도
1. 개발자: 코드 수정
$ vi src/app.py
$ docker build -t myapp:v1.2.3 .
$ docker push ghcr.io/user/myapp:v1.2.3
2. Helm Values 업데이트
$ vi myapp/values-prod.yaml
---
image:
tag: "v1.2.3" # ← 새 버전
---
3. Git Push
$ git add myapp/values-prod.yaml
$ git commit -m "Update to v1.2.3"
$ git push origin main
4. Pull Request (선택)
- Code Review
- CI 테스트 (GitHub Actions)
- Approve & Merge
5. ArgoCD 자동 감지
- 3분마다 Git Poll (또는 Webhook)
- "main 브랜치 커밋 감지!"
6. ArgoCD Sync
- Helm Template 렌더링
- Kubernetes 매니페스트 생성
- kubectl apply
7. Kubernetes 롤링 업데이트
- Pod v1.2.2 → v1.2.3 교체
- Readiness Probe 확인
- 무중단 배포 완료!
8. ArgoCD 상태 업데이트
- Sync Status: Synced
- Health Status: Healthy
- Slack 알림 (설정 시)
Git Push
↓
GitHub (즉시)
↓
ArgoCD Polling (최대 3분) or Webhook (즉시)
↓
Helm Rendering (5초)
↓
kubectl apply (10초)
↓
Pod 롤링 업데이트 (30초~2분)
↓
Healthy 상태 확인 (10초)
↓
Total: 약 1~5분 (Webhook 사용 시 더 빠름!)
Custom Chart 생성:
처음엔 피곤한 작업:
❌ Chart.yaml 작성
❌ templates/ 디렉토리 구조 설계
❌ values.yaml 변수 정의
❌ {{ .Values.xxx }} 템플릿 문법
하지만 한 번 만들면:
✅ 무한 재사용
✅ 환경 추가 = values 파일 하나
✅ 버전 관리 (helm rollback)
✅ 공유 가능 (Helm Repository)
실무 팁:
helm create로 시작_helpers.tpl로 분리연동 설정 없이 동작:
Git 저장소에 Chart.yaml 있으면?
↓
ArgoCD: "아, Helm Chart구나!"
↓
자동으로:
- helm template 실행
- values 파일 머지
- Kubernetes 매니페스트 생성
- kubectl apply
helm.valueFiles 위치:
spec.source.helm.valueFiles작동 원리:
매 5초마다:
1. Git에서 최신 매니페스트 가져오기
2. kubectl get으로 실제 상태 확인
3. Diff 계산 (Desired vs Actual)
4. OutOfSync 발견 시 kubectl apply
→ 수동 변경은 무의미!
→ Git만 수정하세요!
예외 상황:
ignoreDifferences 설정ignoreDifferences 설정왜 필요한가:
일반 배포:
DB, App, Migration Job 동시 생성
→ 랜덤 순서!
→ App이 먼저 뜨면 DB 연결 실패
Sync Waves:
Wave 0: DB
Wave 1: Migration (PreSync Hook)
Wave 2: App
→ 순서 보장!
→ 안정적 배포!
Hook 활용:
하나의 Chart, 여러 환경:
myapp/ (Helm Chart)
├─ values.yaml (공통 기본값)
├─ values-dev.yaml (개발: replica=1, resources 최소)
├─ values-staging.yaml (스테이징: replica=2, 중간)
└─ values-prod.yaml (프로덕션: replica=5, HPA, NodePort)
Application CRD:
myapp-dev → valueFiles: [values-dev.yaml]
myapp-staging → valueFiles: [values-staging.yaml]
myapp-prod → valueFiles: [values-prod.yaml]
→ 템플릿 중복 없음!
→ 환경별 차이만 values에!
증상: 템플릿 하나하나 다 작성해야 함
해결:
helm create myapp로 스캐폴딩 활용증상: ArgoCD UI에서 어떤 values 쓰는지 안 보임
해결:
# Application YAML 확인
$ kubectl get application myapp-dev -n argocd -o yaml
spec:
source:
helm:
valueFiles:
- values-dev.yaml # ← 여기!
증상: kubectl edit으로 수정해도 5초 후 복구됨
교훈:
Self-Heal이 켜져 있으면 Git만 수정!
긴급 수정 필요 시:
# Self-Heal 임시 비활성화
$ kubectl patch application myapp-dev -n argocd --type=merge \
-p '{"spec":{"syncPolicy":{"automated":{"selfHeal":false}}}}'
# 수정 작업
$ kubectl edit deployment myapp -n myapp-dev
# 다시 활성화
$ kubectl patch application myapp-dev -n argocd --type=merge \
-p '{"spec":{"syncPolicy":{"automated":{"selfHeal":true}}}}'
증상: Wave 2가 Wave 1보다 먼저 배포됨
원인: 숫자가 아닌 문자열 정렬
❌ sync-wave: 10 # "10"은 "2"보다 앞!
✅ sync-wave: "10"
교훈:
-1 (Wave 0보다 먼저)Day 8에서 Helm + ArgoCD로 GitOps 파이프라인을 완성했습니다. 이제 Day 9부터는 마이크로서비스 통신을 관리하는 Istio Service Mesh를 단계별로 학습합니다.
마이크로서비스 문제점과 Service Mesh 필요성
Istio 아키텍처 이해
실습 환경 구축
관측 도구 설치
Istio 심화 학습 (단계별):
실전 도전 과제:
GitOps는 단순한 도구가 아니라 철학입니다. Git을 단일 진실 소스로 삼고, 모든 변경을 코드로 관리하는 것!
핵심 요약:
Day 9에서 만나요! 🚀