326 lines
11 KiB
Markdown
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 재작성 금지)
|