콘텐츠로 이동
Study NoteCKA

Kustomize — YAML과 오버레이

결론부터
Kustomize는 공통 YAML에 환경별 변경을 겹쳐 최종 매니페스트를 만든다.

dev와 prod의 매니페스트를 복사해 관리하면 공통 수정이 빠지기 쉽다. base와 overlay로 공통 내용과 차이를 나누고, kubectl로 결과를 확인하고 적용한다. 설치 이력 관리는 Helm과 비교한다.

Helm은 재사용 문제를 템플릿으로 풀었는데, 그 대가가 있다 — 차트의 templates/를 열면 YAML이 아니라 Go 템플릿 코드라 그냥 읽히지 않고, helm template을 돌려 봐야 결과를 안다. 남이 배포한 컴포넌트라면 감수할 만하지만, 내가 쓴 매니페스트를 dev·prod로 나누는 데 그 대가를 치를 이유는 없다.

Kustomize는 같은 문제를 반대로 푼다 — 원본 YAML은 끝까지 평범한 YAML로 두고, 바꿀 부분만 따로 적어 위에 얹는다. 그래서 kubectl apply -f로 쓰던 파일을 한 줄도 고치지 않고 그대로 베이스로 삼을 수 있다.

Kustomize — 템플릿 없는 커스터마이징

섹션 제목: “Kustomize — 템플릿 없는 커스터마이징”
  • 디렉터리base/
    • kustomization.yaml
    • deployment.yaml
    • service.yaml
  • 디렉터리overlays/
    • 디렉터리dev/
      • kustomization.yaml
      • replica-patch.yaml
    • 디렉터리prod/
      • kustomization.yaml
      • replica-patch.yaml
base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
labels:
- pairs: { app: web }
includeSelectors: true # 셀렉터에도 넣는다 — 옛 commonLabels와 같은 효과
  • 템플릿 언어가 없다. 전부 유효한 YAML이다
  • 라벨은 labels로 단다 — 옛 commonLabels는 v5.0.0에서 deprecated. labels는 기본으로 셀렉터를 건드리지 않으니, 셀렉터까지 맞추려면 includeSelectors: true를 준다
  • 베이스는 그대로 두고 오버레이가 패치를 얹는다
  • kubectl에 내장되어 있다 — 별도 설치가 필요 없다

kustomization.yaml의 필드가 기억나지 않으면, 시험 중에는 공식 Declarative Management of Kubernetes Objects Using Kustomize 페이지를 열어 예제를 복사해 오는 게 정석이다.

하나의 base를 dev·prod 오버레이가 각각 패치해 apply되는 구조

오버레이 — 이름·네임스페이스·이미지 바꾸기

섹션 제목: “오버레이 — 이름·네임스페이스·이미지 바꾸기”
overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base # 베이스를 가져온다
namespace: prod # 전부 이 네임스페이스로
namePrefix: prod- # 모든 이름 앞에 붙인다
commonAnnotations: { owner: platform-team }
images:
- { name: nginx, newTag: "1.28" } # 이미지 태그만 교체
replicas:
- { name: web, count: 5 } # 레플리카 수만 교체
  • images / replicas 는 패치 파일 없이 가장 자주 바꾸는 값을 직접 지정하는 단축이다
  • namePrefix를 쓰면 참조하는 Service·볼륨 이름도 함께 갱신된다
configMapGenerator:
- name: app-config
literals: ["LOG_LEVEL=warn"]
secretGenerator:
- name: db-secret
literals: ["password=prodsecret"]
patches:
- path: resource-patch.yaml # 파일로 패치
target: { kind: Deployment, name: web }
  • generator는 ConfigMap/Secret을 오버레이에서 새로 만든다 (베이스에 둘 필요가 없다)
  • patches 는 베이스의 임의 필드를 고친다. target으로 대상을 고른다
터미널 창
kubectl kustomize overlays/prod # 결과를 출력만 (미리보기)
kubectl diff -k overlays/prod # 현재 클러스터와 적용 예정 결과 비교
kubectl apply -k overlays/prod # 적용 (delete -k 로 제거)
kubectl get -k overlays/prod # 이 kustomization이 가리키는 최종 리소스 확인
kustomize build overlays/prod | kubectl apply -f - # 독립 바이너리를 쓸 때

독립 바이너리 kustomize와 kubectl -k는 같은 코드지만, kubectl에 박혀 있는 쪽이 대체로 버전이 낮다. 최신 필드를 썼는데 kubectl -k가 모른다고 하면 그 차이일 수 있다. 시험에서는 kubectl -k 하나로 충분하다.

패치 두 가지 방식

바꿀 부분만 같은 구조로 쓴다. 읽기 쉽다.

resource-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata: { name: web }
spec:
template:
spec:
containers:
- name: nginx
resources: { limits: { memory: 512Mi } }

설정과 리소스에서 본 문제 하나가 여기서 풀린다 — ConfigMap의 내용을 바꿔도 Deployment의 spec은 그대로라, 루프가 볼 때 “달라진 것이 없으니” 롤아웃이 일어나지 않는다. 결국 손으로 재시작을 걸어야 했다. generator는 이 문제를 이름을 바꿔 버리는 방식으로 푼다.

터미널 창
kubectl kustomize overlays/prod | grep -A2 'kind: ConfigMap'
# name: prod-app-config-7g5bd8f2mk

이것이 설정과 리소스의 “ConfigMap을 바꿔도 Pod이 안 바뀐다” 문제를 구조적으로 푼다.

configMapGenerator의 이름 해시가 값 변경 시 자동 롤아웃을 일으키는 원리
  • configMapGenerator/secretGenerator가 만든 이름에는 내용 해시가 붙는다
  • 그리고 Deployment 안의 ConfigMap 참조 이름도 자동으로 갱신된다
  • 결과: ConfigMap이 바뀌면 Deployment가 자동으로 롤아웃된다
generatorOptions:
disableNameSuffixHash: true # 해시를 끄고 싶다면
Helm의 차트·값·릴리스와 Kustomize의 베이스·오버레이 흐름 대비
HelmKustomize
방식템플릿 + 값 주입베이스 YAML + 오버레이 패치
배포 형태패키지(차트) 저장소그냥 디렉터리
상태 관리릴리스를 추적한다없음 (kubectl apply와 동일)
설치별도 바이너리kubectl에 내장 (-k)
남이 만든 것은 Helm, 내 앱은 Kustomize라는 선택 기준과 둘의 조합
터미널 창
helm template prom prometheus/kube-prometheus-stack > base/all.yaml
# → 이후 Kustomize 오버레이로 환경별 변형
  • Kustomize는 공통 YAML에 환경별 변경을 겹쳐 최종 매니페스트를 만든다.
  • kubectl kustomize로 최종 YAML을 확인한 뒤 kubectl apply -k로 적용한다.
  • generator의 해시 접미사는 설정 변경을 참조 이름에 반영한다.