Files
project-infra/docs/standards/infra/kustomize.md
T

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 the commonLabels field, which always adds selectors."
  • commonLabels: 모든 리소스의 metadata.labels + spec.selector.matchLabels + Pod template labels에 주입된다. Deployment/StatefulSet의 selector.matchLabelsimmutable 이므로, 이미 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/environment label (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

이유:

  • commonLabelsselector.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에는 불변 3종만

overlay에서 selector를 건드리지 않는다. selector에 허용되는 label은:

  • app.kubernetes.io/name
  • app.kubernetes.io/instance
  • app.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를 템플릿 엔진으로 남용하지 않는다

분기 / 조건 / 반복이 필요하면:

  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} 유지.

추천 폴더 구조

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: inline
  • components:로 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 재작성 금지)