11 KiB
Kustomize 기준
목적
Kustomize는 Kubernetes 리소스를 template-free로 조합하고 환경별 차이를 overlay로 표현하는 도구다. 1000+ 서비스 prod 스케일에서 기본 배포 도구로 사용하며, Helm 차트는 특정 플랫폼 컴포넌트(Prometheus Operator, cert-manager 등)에만 제한적으로 쓴다.
목표:
- base / overlay / component 세 축을 명확히 구분한다
commonLabels의 selector immutability 함정을 피한다kubectl apply --server-side를 전제로 field manager ownership을 관리한다- GitOps (ArgoCD/Flux) 또는 CI
kubectl apply -k어느 쪽이든 같은 원본을 쓴다
공식 의미 (근거)
- 공식 문서:
https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/,https://kubectl.docs.kubernetes.io/references/kustomize/ kubectl kustomize <dir>렌더,kubectl apply -k <dir>apply,kubectl diff -k <dir>diff.- Kustomize v5+
labels:필드: label을 리소스에 추가하되 기본적으로 selector에 주입하지 않는다 (includeSelectors: false). 공식 문서 인용: "A field that allows adding labels without also automatically injecting corresponding selectors. This can be used instead of thecommonLabelsfield, which always adds selectors." commonLabels: 모든 리소스의metadata.labels+spec.selector.matchLabels+ Pod template labels에 주입된다. Deployment/StatefulSet의selector.matchLabels는 immutable 이므로, 이미 apply된 리소스에commonLabels로 label을 추가하면field is immutable에러로 apply 실패.components:(v4+): 재사용 가능한 cross-cutting overlay 단위.kind: Component. resource 집합 + patch 집합을 하나의 단위로 묶어 여러 overlay에서components:키로 참조.configMapGenerator/secretGenerator: 이름 끝에 hash suffix가 자동으로 붙어 rollout trigger.generatorOptions.disableNameSuffixHash: true로 비활성 가능.patches:(v5 권장):target:선택 +patch:inline 또는path:파일. strategic merge / JSON patch 양쪽 지원.images:: image name/tag/digest 교체.replicas:: resource별 replica 수 override.namespace:/namePrefix:/nameSuffix:: overlay에서 공통 변환.- Server-Side Apply (
kubectl apply --server-side --field-manager=<id> -k)가 GitOps 기본.
기본 규칙
1. Kustomize 디렉터리가 선언형 source of truth
- 렌더:
kubectl kustomize <dir> - diff:
kubectl diff --server-side -k <dir> - apply:
kubectl apply --server-side --field-manager=<ci-id> -k <dir>
kubectl apply -f 단일 파일 apply는 금지 (bootstrap 예외 제외).
2. base는 환경 중립
허용:
- Deployment/StatefulSet/DaemonSet/Job/CronJob 기본 shape
app.kubernetes.io/{name,instance,component,part-of,managed-by}(version은 overlay에서 image tag와 함께 주입)- 공통 container spec (resources, probes, securityContext)
- 공통 volume mount / ConfigMap reference
금지:
- replicas 고정값 (overlay
replicas:에서 결정) - 환경별 host / domain / issuer 이름
- 환경별 secret / ConfigMap 이름
- 환경별 resources requests/limits
example.com/environmentlabel (overlay에서labels:로 주입)
3. overlay는 환경 차이만, patches는 파일로 분리
overlay 한 디렉터리의 kustomization.yaml은 짧아야 한다. diff가 몇 백 줄을 넘으면 base 설계 실패 신호.
권장 구조:
overlays/prod/
kustomization.yaml
patches/
auth-replicas.yaml
auth-resources.yaml
auth-topology-spread.yaml
ingress-host.yaml
postgres-storage.yaml
4. 디렉터리 구조는 base / components / overlays 3축
k8s/
base/
app/units/<domain>/<service>/
managing/<job>/
plugins/<platform>/
components/
<reusable-cross-cutting>/
overlays/
<env>/[region/]
components/는 "Kustomize Components"로, 여러 overlay에서 재사용.
5. commonLabels 금지, labels: 사용
신규 코드에서는 commonLabels 사용을 금지한다.
# DO
labels:
- pairs:
example.com/environment: prod
example.com/region: kr-main
includeSelectors: false
includeTemplates: true
이유:
commonLabels는selector.matchLabels에 자동 주입 → live Deployment/StatefulSet apply 시field is immutable실패labels:는includeSelectors: false가 기본 → safeincludeTemplates: true로 Pod template labels에는 전파되므로 관찰성은 유지
기존 commonLabels 사용 코드는 migration plan을 세워 교체. selector에 이미 들어간 label이 있다면 해당 리소스를 재배포 (delete + recreate) 없이는 변경 불가.
6. selector에는 불변 3종만
overlay에서 selector를 건드리지 않는다. selector에 허용되는 label은:
app.kubernetes.io/nameapp.kubernetes.io/instanceapp.kubernetes.io/component
이 3종은 base에서 고정. overlay가 labels:로 추가하는 label은 반드시 includeSelectors: false.
7. patches: (v5 스타일) 사용, patchesStrategicMerge / patchesJson6902 금지
patches:
- target:
kind: Deployment
name: auth
path: patches/auth-resources.yaml
- target:
kind: Ingress
name: auth-public
patch: |-
- op: replace
path: /spec/rules/0/host
value: auth.example.com
이유:
- 단일 키로 strategic merge + JSON patch 양쪽 지원
target:selector로 여러 리소스에 적용 가능- 레거시
patchesStrategicMerge/patchesJson6902는 v5에서 deprecated (여전히 작동하지만 신규 사용 금지)
8. components:로 cross-cutting 재사용
multiple overlay에서 공통으로 끼워야 하는 변경(예: mTLS 활성화, sidecar 주입, monitoring label 추가)은 component로.
components/
with-istio-sidecar/
kustomization.yaml # kind: Component
patches/
inject-sidecar.yaml
with-service-monitor/
kustomization.yaml
service-monitor.yaml
with-pdb-tier1/
kustomization.yaml
pdb-patch.yaml
overlay에서:
components:
- ../../components/with-service-monitor
- ../../components/with-pdb-tier1
9. namePrefix / nameSuffix는 꼭 필요할 때만
리소스 이름이 바뀌면 ConfigMap/Secret 참조 (envFrom, volumes.configMap.name)도 모두 바뀐다. namespace 격리가 기본이고, 같은 cluster 안에서 같은 이름 리소스를 여러 번 생성할 때만 prefix/suffix를 쓴다.
10. generator 기준
configMapGenerator: 비민감 설정만. 기본 hash suffix로 rollout 자동 트리거.secretGenerator: 로컬/테스트/bootstrap 에만. prod secret은 External Secrets Operator / Vault Secrets Operator / SealedSecrets로 관리.generatorOptions.disableNameSuffixHash: true는 GitOps 외부 컨슈머가 이름을 하드코딩해야 할 때만 (예외).
11. images:로 image tag/digest 고정
images:
- name: registry.example.com/auth
newTag: "1.24.3"
- name: registry.example.com/keycloak
digest: "sha256:abcd1234..."
- prod에서는 digest 권장 (tag는 mutable)
- CI가 overlay의
images:섹션을 빌드 후 새 digest로 patch (kustomize edit set image)
12. replicas:는 overlay에서 resource별 값 주입
replicas:
- name: auth
count: 6
- name: keycloak
count: 3
HPA 주도 rollout 환경에서는 replicas: override가 HPA와 충돌할 수 있다. HPA 활성 리소스는 base replicas를 HPA minReplicas와 일치시키고 overlay에서는 건드리지 않는다.
13. kubectl apply --server-side --field-manager=<id> 기본
- ArgoCD: field manager
argocd-controller - Flux: field manager
kustomize-controller - CI manual: field manager
ci-<pipeline-id>
field manager 이름을 환경별로 통일해야 managedFields 충돌이 예측 가능해진다.
14. render 전 검증
CI가 아래를 순서대로 실행:
kubectl kustomize overlays/prod > /tmp/rendered.yaml
kubeconform -strict -summary -schema-location default -schema-location 'https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json' /tmp/rendered.yaml
kubectl diff --server-side --field-manager=ci -k overlays/prod
- kubeconform / kubeval: schema validation
- kyverno / OPA Gatekeeper: policy validation (post-render)
- conftest: opa policy bundle 실행
15. base는 overlay를 모른다
공식 원칙. base kustomization.yaml은 overlay에서만 의미 있는 설정(환경 host / issuer / region label)을 전제하지 않는다. 위반 시 base가 더 이상 재사용 가능한 unit이 아니다.
16. Kustomize를 템플릿 엔진으로 남용하지 않는다
분기 / 조건 / 반복이 필요하면:
- 리소스 분리
- component 도입
- overlay 추가
- (마지막 수단) Helm / jsonnet / cdk8s
Kustomize는 patch/overlay 도구다. Go template이 아니다.
17. scripts는 Kustomize 보조, 대체 아님
scripts/render.sh, scripts/diff.sh, scripts/apply.sh는 Kustomize 명령의 wrapper에 그치고 overlay 구조를 우회하지 않는다.
18. resources: vs bases: — v5에서는 resources: 통일
v2.1에서 bases:가 resources:로 통합됨. 신규 파일에서 bases: 금지.
19. overlay에서 StatefulSet PVC retention 변경 주의
persistentVolumeClaimRetentionPolicy는 StatefulSet spec 필드 (GA 1.27). 환경별로 값이 다르면 overlay patch로 조정하되 prod는 기본 {whenDeleted: Retain, whenScaled: Retain} 유지.
추천 폴더 구조
k8s/
base/
app/
kustomization.yaml
units/
identity/
auth/
kustomization.yaml
deployment.yaml
service.yaml
servicemonitor.yaml
pdb.yaml
hpa.yaml
keycloak/
kustomization.yaml
data/
postgres-identity/
kustomization.yaml
statefulset.yaml
service-headless.yaml
service.yaml
managing/
flyway-migrate-identity/
kustomization.yaml
job.yaml
backup-postgres/
kustomization.yaml
cronjob.yaml
plugins/
ingress-nginx/
cert-manager/
external-secrets/
kube-prometheus-stack/
fluent-bit/
components/
with-service-monitor/
with-pdb-tier1/
with-topology-spread-zone/
with-network-policy-deny-default/
overlays/
dev/
kustomization.yaml
staging/
kustomization.yaml
prod/
kr-main/
kustomization.yaml
patches/
kr-dr/
kustomization.yaml
patches/
scripts/
render.sh
diff.sh
apply.sh
validate.sh
프로젝트 기준 요약
- Kustomize v5 문법 기준,
commonLabels금지,labels:사용 patches:단일 키,target:+path:또는patch:inlinecomponents:로 cross-cutting 재사용- selector에는 불변 3종만 (name / instance / component)
- generator는 configMap만 기본, secret은 External Secrets
kubectl apply --server-side --field-manager=<id>전제- render + schema + policy 검증을 CI에서 강제
- base / components / overlays 3축 디렉터리
- overlay diff는 짧아야 한다 (base 재작성 금지)