# 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 ` 렌더, `kubectl apply -k ` apply, `kubectl diff -k ` 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 the `commonLabels` field, 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= -k`)가 GitOps 기본. ## 기본 규칙 ### 1. Kustomize 디렉터리가 선언형 source of truth - 렌더: `kubectl kustomize ` - diff: `kubectl diff --server-side -k ` - apply: `kubectl apply --server-side --field-manager= -k ` `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/environment` label (overlay에서 `labels:`로 주입) ### 3. overlay는 환경 차이만, patches는 파일로 분리 overlay 한 디렉터리의 `kustomization.yaml`은 짧아야 한다. diff가 몇 백 줄을 넘으면 base 설계 실패 신호. 권장 unit overlay 구조: ``` gitops/apps/auth-server/overlays/prod/ kustomization.yaml patches/ auth-replicas.yaml auth-resources.yaml auth-topology-spread.yaml ingress-host.yaml postgres-storage.yaml ``` ### 4. catalog unit과 cluster entrypoint를 분리 ``` gitops/ apps// base/ components/ overlays// platform// base/ components/ overlays// policies// tenants// clusters/// ``` `components/`는 소유 unit 내부의 Kustomize Component입니다. 여러 catalog를 조립하는 최종 경계는 `clusters/`이며 catalog 디렉터리를 controller root로 직접 사용하지 않습니다. ### 5. `commonLabels` 금지, `labels:` 사용 신규 코드에서는 `commonLabels` 사용을 금지한다. ```yaml # 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`가 기본 → safe - `includeTemplates: true`로 Pod template labels에는 전파되므로 관찰성은 유지 기존 `commonLabels` 사용 코드는 migration plan을 세워 교체. selector에 이미 들어간 label이 있다면 해당 리소스를 **재배포** (delete + recreate) 없이는 변경 불가. ### 6. selector에는 불변 2종만 overlay에서 selector를 건드리지 않는다. selector에 허용되는 label은: - `app.kubernetes.io/name` - `app.kubernetes.io/instance` 이 2종은 base에서 고정. overlay가 `labels:`로 추가하는 label은 반드시 `includeSelectors: false`. ### 7. `patches:` (v5 스타일) 사용, `patchesStrategicMerge` / `patchesJson6902` 금지 ```yaml 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로. ``` gitops/apps/auth-server/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에서: ```yaml 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 고정 ```yaml 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별 값 주입 ```yaml 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=` 기본 - ArgoCD: field manager `argocd-controller` - Flux: field manager `kustomize-controller` - CI manual: field manager `ci-` field manager 이름을 환경별로 통일해야 `managedFields` 충돌이 예측 가능해진다. ### 14. render 전 검증 CI가 아래를 순서대로 실행: ```bash kubectl kustomize gitops/clusters/prod/kr-main/all > /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 gitops/clusters/prod/kr-main/stages/50-apps ``` `all`은 schema/policy 감사용이고 diff/apply는 의존성이 준비된 개별 stage를 대상으로 합니다. 여러 stage 변경은 승인된 orchestrator/controller가 순서와 health gate를 보장해야 합니다. - 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를 템플릿 엔진으로 남용하지 않는다 분기 / 조건 / 반복이 필요하면: 1. 리소스 분리 2. component 도입 3. overlay 추가 4. (마지막 수단) 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}` 유지. ## 추천 폴더 구조 ```text gitops/ apps/ auth/ base/ kustomization.yaml deployment.yaml service.yaml servicemonitor.yaml pdb.yaml hpa.yaml components/ with-service-monitor/ with-pdb-tier1/ with-topology-spread-zone/ overlays/{lab,staging,prod}/ postgres-identity/ base/ overlays/{lab,staging,prod}/ flyway-migrate-identity/ base/ overlays/{lab,staging,prod}/ platform/ ingress-nginx/ cert-manager/ secret-delivery/ policies/ network-policy-deny-default/ clusters/ lab/main/stages/ staging/main/stages/ prod/kr-main/stages/ prod/kr-dr/stages/ scripts/ bin/ ci/ ``` ## 프로젝트 기준 요약 - Kustomize v5 문법 기준, `commonLabels` 금지, `labels:` 사용 - `patches:` 단일 키, `target:` + `path:` 또는 `patch:` inline - `components:`로 cross-cutting 재사용 - selector에는 불변 2종만 (name / instance) - generator는 configMap만 기본, secret은 External Secrets - `kubectl apply --server-side --field-manager=` 전제 - render + schema + policy 검증을 CI에서 강제 - catalog unit의 base/components/overlays와 cluster entrypoint를 분리 - `stages/`만 apply하고 `all/`은 render/schema/policy audit에만 사용 - overlay diff는 짧아야 한다 (base 재작성 금지)