Kustomize란? — 템플릿 없이 YAML을 커스터마이징
Kubernetes 매니페스트를 환경별(dev/staging/prod)로 관리할 때, 같은 Deployment를 복사해서 replica 수와 이미지 태그만 바꾸는 일이 반복된다. Helm은 Go 템플릿으로 이 문제를 해결하지만, 학습 곡선이 높고 템플릿이 복잡해진다. Kustomize는 원본 YAML을 수정하지 않고 overlay(덧씌우기)로 환경별 차이만 선언하는 도구다.
kubectl v1.14부터 kubectl apply -k로 내장되어 별도 설치가 불필요하다. 이 글에서는 base/overlay 구조, patches, configMapGenerator, 이미지 태그 오버라이드, components, 그리고 Helm vs Kustomize 선택 기준까지 정리한다.
1. 디렉터리 구조 — base와 overlays
k8s/
├── base/ # 공통 매니페스트
│ ├── kustomization.yaml
│ ├── deployment.yaml
│ ├── service.yaml
│ └── hpa.yaml
└── overlays/
├── dev/ # 개발 환경 오버레이
│ ├── kustomization.yaml
│ └── patch-replicas.yaml
├── staging/
│ ├── kustomization.yaml
│ └── patch-resources.yaml
└── prod/
├── kustomization.yaml
├── patch-replicas.yaml
└── patch-resources.yaml
base/kustomization.yaml
# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
- hpa.yaml
commonLabels:
app: order-service
managed-by: kustomize
base/deployment.yaml
# base/deployment.yaml — 환경 무관한 기본 설정
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 1 # 기본값 — overlay에서 오버라이드
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order
image: order-service:latest
ports:
- containerPort: 8080
name: http
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
memory: 512Mi
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: http
periodSeconds: 5
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: http
initialDelaySeconds: 30
2. Overlay — 환경별 차이를 덧씌우기
# overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base # base 참조
namespace: production # 네임스페이스 오버라이드
namePrefix: prod- # 리소스 이름에 prefix 추가
nameSuffix: ""
commonLabels:
env: production
commonAnnotations:
team: backend
# 이미지 태그 오버라이드 — 가장 많이 쓰는 기능
images:
- name: order-service
newName: registry.example.com/order-service
newTag: v2.3.1
# 패치 적용
patches:
- path: patch-replicas.yaml
- path: patch-resources.yaml
# overlays/prod/patch-replicas.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 6 # prod: 6개 replica
# overlays/prod/patch-resources.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
template:
spec:
containers:
- name: order
resources:
requests:
cpu: "1"
memory: 1Gi
limits:
memory: 2Gi
# 빌드 및 적용
kubectl kustomize overlays/prod # 최종 YAML 미리보기
kubectl apply -k overlays/prod # 클러스터에 적용
kubectl diff -k overlays/prod # 변경사항 diff
3. Patch 전략 — Strategic Merge vs JSON 6902
3-1. Strategic Merge Patch (기본)
Kubernetes의 Strategic Merge Patch를 사용한다. 지정한 필드만 덮어쓰고 나머지는 유지한다.
# 위에서 본 patch-replicas.yaml이 Strategic Merge Patch
# metadata.name으로 대상을 매칭하고, spec.replicas만 오버라이드
3-2. JSON 6902 Patch
더 정밀한 제어가 필요할 때 사용한다. 배열의 특정 인덱스를 수정하거나, 필드를 삭제할 수 있다.
# overlays/prod/kustomization.yaml
patches:
- target:
kind: Deployment
name: order-service
patch: |
- op: replace
path: /spec/replicas
value: 6
- op: add
path: /spec/template/spec/containers/0/env/-
value:
name: SPRING_PROFILES_ACTIVE
value: prod
- op: remove
path: /spec/template/spec/containers/0/resources/limits/cpu
3-3. 인라인 Patch
# kustomization.yaml에 직접 패치 내용 작성
patches:
- target:
kind: Deployment
name: order-service
patch: |
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 6
template:
spec:
tolerations:
- key: "dedicated"
operator: "Equal"
value: "app"
effect: "NoSchedule"
| 패치 타입 | 장점 | 사용 시점 |
|---|---|---|
| Strategic Merge | 직관적, YAML 형식 그대로 | 필드 추가/수정 (대부분의 경우) |
| JSON 6902 | 배열 인덱스 제어, 필드 삭제 가능 | 정밀한 수정, 필드 삭제 |
| 인라인 | 별도 파일 불필요 | 간단한 1~2줄 패치 |
4. ConfigMap·Secret 자동 생성 — Generator
Kustomize의 Generator는 파일이나 리터럴에서 ConfigMap/Secret을 자동 생성하고, 내용이 바뀌면 이름에 해시 suffix를 붙여 Deployment가 자동으로 롤아웃된다. Terraform ConfigMap 체크섬 패턴과 동일한 효과를 Kustomize가 기본 제공한다.
# kustomization.yaml
configMapGenerator:
# 파일에서 생성
- name: app-config
files:
- application.yml
- logback-spring.xml
options:
disableNameSuffixHash: false # 해시 suffix 활성화 (기본값)
# 리터럴에서 생성
- name: app-env
literals:
- DATABASE_HOST=db.production.svc.cluster.local
- DATABASE_PORT=5432
- REDIS_HOST=redis.production.svc.cluster.local
# .env 파일에서 생성
- name: app-dotenv
envs:
- .env.production
secretGenerator:
- name: db-credentials
literals:
- DB_USERNAME=order_svc
- DB_PASSWORD=s3cur3p@ss
type: Opaque
# 파일에서 TLS Secret
- name: tls-cert
files:
- tls.crt=certs/server.crt
- tls.key=certs/server.key
type: kubernetes.io/tls
해시 suffix 동작:
# 생성된 ConfigMap 이름: app-config-k5hm2f8
# application.yml 내용이 바뀌면: app-config-d9h3k7g (새 해시)
# → Deployment의 configMapRef도 자동으로 새 이름으로 교체
# → Pod이 자동 롤아웃 (재시작)
# 해시 suffix 비활성화 (비권장 — 변경 감지 불가)
generatorOptions:
disableNameSuffixHash: true
5. Components — 재사용 가능한 기능 단위
Kustomize v3.7+에서 도입된 Components는 여러 overlay에서 공통으로 쓰는 패치 세트를 모듈화한다.
k8s/
├── base/
├── components/
│ ├── monitoring/ # Prometheus 사이드카 추가
│ │ └── kustomization.yaml
│ ├── istio-sidecar/ # Istio 어노테이션 추가
│ │ └── kustomization.yaml
│ └── high-availability/ # PDB + topology spread 추가
│ ├── kustomization.yaml
│ └── pdb.yaml
└── overlays/
├── dev/
│ └── kustomization.yaml # components 없음
└── prod/
└── kustomization.yaml # 3개 component 모두 사용
# components/high-availability/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1alpha1
kind: Component
resources:
- pdb.yaml
patches:
- target:
kind: Deployment
patch: |
apiVersion: apps/v1
kind: Deployment
metadata:
name: not-used # Component에서는 target으로 매칭
spec:
template:
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: order-service
# overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
components:
- ../../components/monitoring
- ../../components/istio-sidecar
- ../../components/high-availability
images:
- name: order-service
newTag: v2.3.1
6. CI/CD에서 이미지 태그 주입
CI 파이프라인에서 빌드된 이미지 태그를 Kustomize에 주입하는 패턴이다.
# GitHub Actions 예시
- name: Build & Push Image
run: |
IMAGE_TAG=${{ github.sha }}
docker build -t registry.example.com/order-service:$IMAGE_TAG .
docker push registry.example.com/order-service:$IMAGE_TAG
- name: Update Kustomize Image Tag
run: |
cd k8s/overlays/prod
kustomize edit set image
order-service=registry.example.com/order-service:${{ github.sha }}
- name: Deploy
run: |
kubectl apply -k k8s/overlays/prod
# ArgoCD + Kustomize — GitOps 방식
# ArgoCD Application에서 kustomize를 소스로 지정
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: order-service-prod
spec:
source:
repoURL: https://github.com/example/k8s-manifests.git
targetRevision: main
path: k8s/overlays/prod
kustomize:
images:
- order-service=registry.example.com/order-service:v2.3.1
7. Helm vs Kustomize — 선택 기준
| 기준 | Kustomize | Helm |
|---|---|---|
| 접근 방식 | 패치(overlay) — 원본 유지 | 템플릿(Go template) — 값 주입 |
| 학습 곡선 | 낮음 (순수 YAML) | 높음 (Go template 문법) |
| 패키지 배포 | 없음 (Git 기반) | Chart Repository 지원 |
| 릴리스 관리 | 없음 (kubectl 기반) | helm install/upgrade/rollback |
| 조건부 리소스 | Component로 부분 가능 | if/else로 자유롭게 |
| kubectl 내장 | 예 (v1.14+) | 아니오 (별도 설치) |
| 추천 사용처 | 자체 서비스 환경별 배포 | 공유 차트, 복잡한 조건부 로직 |
실무 가이드:
- 자체 서비스 배포: Kustomize — base/overlay로 환경별 관리가 간단
- 오픈소스 설치(nginx-ingress, prometheus): Helm — 공식 Chart 사용
- 둘 다 쓰기: Helm으로 설치한 Chart를 Kustomize로 후처리 가능 (
helm template | kustomize)
8. 실전 트러블슈팅
# 최종 결과물 미리보기 (적용 전 필수)
kubectl kustomize overlays/prod
# 특정 리소스만 확인
kubectl kustomize overlays/prod | grep -A 20 "kind: Deployment"
# dry-run으로 서버 측 검증
kubectl apply -k overlays/prod --dry-run=server
# diff로 변경사항 확인
kubectl diff -k overlays/prod
# 흔한 실수: base의 리소스가 resources에 누락
# Error: accumulating resources: ... not found
# → kustomization.yaml의 resources에 파일 추가 확인
# 흔한 실수: patch target이 매칭 안 됨
# → metadata.name이 base의 리소스 이름과 정확히 일치하는지 확인
# → namePrefix/nameSuffix 적용 전 이름으로 매칭
9. 운영 체크리스트
| 항목 | 권장 사항 | 위반 시 증상 |
|---|---|---|
| 디렉터리 구조 | base/ + overlays/{env}/ 분리 | 환경별 YAML 복사 → 동기화 실패 |
| ConfigMap 해시 | Generator + suffix hash 활성화 | 설정 변경해도 Pod이 재시작 안 됨 |
| 이미지 태그 | images 필드로 오버라이드 | base에 :latest가 prod에 배포됨 |
| 적용 전 검증 | kubectl diff -k로 변경사항 확인 | 의도치 않은 리소스 삭제/변경 |
| 공통 패치 | Components로 모듈화 | 같은 패치를 여러 overlay에 복사 |
| Secret 관리 | secretGenerator + 외부 시크릿 관리 | 평문 Secret이 Git에 커밋 |
마무리 — Kustomize는 YAML의 Git이다
Kustomize의 철학은 단순하다. 원본 YAML은 건드리지 않고, 환경별 차이만 overlay로 선언한다. base가 Git의 main 브랜치라면, overlay는 환경별 브랜치의 diff와 같다.
핵심은 세 가지다. 첫째, base에는 환경 무관한 기본값만 두고 overlay에서 덮어써라. 둘째, configMapGenerator의 해시 suffix로 설정 변경 시 자동 롤아웃을 보장하라. 셋째, Components로 재사용 가능한 패치 세트를 모듈화하여 overlay 간 중복을 제거하라. kubectl에 내장된 도구만으로 환경별 배포를 깔끔하게 관리할 수 있다.