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

326 lines
11 KiB
Markdown

# 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.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/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` 사용을 금지한다.
```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에는 불변 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` 금지
```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로.
```
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=<id>` 기본
- ArgoCD: field manager `argocd-controller`
- Flux: field manager `kustomize-controller`
- CI manual: field manager `ci-<pipeline-id>`
field manager 이름을 환경별로 통일해야 `managedFields` 충돌이 예측 가능해진다.
### 14. render 전 검증
CI가 아래를 순서대로 실행:
```bash
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}` 유지.
## 추천 폴더 구조
```text
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 재작성 금지)