왜 환경별 values 파일이 문제가 되는가

가장 흔한 시작점은 values-dev.yaml, values-staging.yaml, values-prod.yaml을 각각 통째로 복제하는 방식이다. 처음엔 편하지만 시간이 지나면 세 파일이 서로 드리프트(drift)한다. dev에서만 바꾼 리소스 리밋이 prod에 반영되지 않거나, 공통 설정 하나를 고치려고 세 파일을 모두 편집하다가 하나를 빠뜨린다. 결국 "환경별로 다른 값이 정확히 무엇인가"가 파일 전체에 묻혀버려서 리뷰가 불가능해진다.

핵심은 공통 기본값(base)과 환경별 차이(overlay)를 분리하는 것이다. Helm은 -f 플래그를 여러 번 받을 수 있고, 뒤에 오는 파일이 앞의 값을 병합/덮어쓰기한다. 이 병합 규칙을 이용하면 환경 파일에는 "차이"만 남길 수 있다.

기본 오버레이 병합 동작

values.yaml에 공통값을 두고, 환경 파일에는 달라지는 키만 적는다. Helm은 스칼라/맵은 깊게 병합하지만 리스트는 통째로 교체한다는 점이 가장 중요한 함정이다.

# values.yaml (base)
replicaCount: 2
image:
  repository: registry.int4.io/api
  tag: "1.0.0"
  pullPolicy: IfNotPresent
resources:
  requests:
    cpu: 100m
    memory: 128Mi
# values-prod.yaml (overlay — 차이만)
replicaCount: 6
image:
  tag: "1.4.2"          # repository/pullPolicy는 base 유지
resources:
  requests:
    cpu: 500m
    memory: 512Mi

배포는 base를 먼저, overlay를 나중에 지정한다. 순서가 곧 우선순위다.

helm upgrade --install api ./chart \
  -f values.yaml \
  -f values-prod.yaml \
  --namespace prod --create-namespace

레이어 순서와 우선순위

실무에서는 보통 세 겹으로 쌓는다. 공통 → 환경 → 인스턴스/시크릿 순이며, 마지막 --set이 최우선이다.

레이어내용커밋 대상
values.yaml모든 환경 공통 기본값Git
values-<env>.yaml환경별 차이(리소스, 레플리카, 도메인)Git
--set / 외부 시크릿이미지 태그, 동적 값, 민감정보CI 파이프라인 주입

이미지 태그처럼 배포마다 바뀌는 값을 values 파일에 넣지 말자. CI에서 --set image.tag=$GIT_SHA로 주입해야 파일 커밋 없이 롤백/추적이 깔끔해진다.

리스트 병합 함정 다루기

환경별로 env 배열이나 ingress.hosts를 조금만 추가하려 해도, overlay에서 리스트를 쓰면 base 리스트 전체가 사라진다. 해결책은 두 가지다. 첫째, 리스트를 맵으로 모델링해서 깊은 병합이 되게 한다.

# base: 리스트 대신 맵으로 설계
extraEnv:
  LOG_LEVEL: info
  POOL_SIZE: "10"
# overlay-prod: 특정 키만 덮어쓰기
extraEnv:
  LOG_LEVEL: warn

템플릿에서 range $k, $v := .Values.extraEnv로 순회하면 두 레이어가 안전하게 합쳐진다. 둘째, 정말 리스트가 필요하면 환경 파일에 전체 리스트를 명시적으로 다시 적고, 그 사실을 주석으로 남겨 리뷰어가 인지하게 한다.

렌더링 검증을 CI에 넣기

오버레이는 실제 배포 전에 최종 결과를 눈으로 확인할 수 있어야 한다. helm template으로 병합 결과를 렌더링하고 --dry-run으로 서버 검증까지 파이프라인에 건다.

# CI: 병합 결과 diff 확인 + 서버 유효성 검사
helm template api ./chart -f values.yaml -f values-prod.yaml \
  | kubectl diff -f - || true

helm upgrade --install api ./chart \
  -f values.yaml -f values-prod.yaml \
  --namespace prod --dry-run=server

kubectl diff는 클러스터 현재 상태와 렌더 결과의 차이만 보여주므로, PR 리뷰에서 "이 배포가 무엇을 바꾸는가"를 정확히 판단할 수 있다.

주의점 정리

  • 시크릿을 values 파일에 넣지 않는다. External Secrets, SOPS, 또는 CI 변수로 주입하고 Git에는 참조만 남긴다.
  • 환경 파일은 최대한 짧게. overlay가 100줄을 넘으면 base 설계가 잘못됐다는 신호다. 공통값을 base로 끌어올려라.
  • 레이어 순서를 스크립트로 고정한다. 사람이 매번 -f를 손으로 나열하면 순서 실수가 난다. Makefile이나 헬퍼 스크립트로 감싼다.
  • 리스트 병합 규칙을 팀 규약으로. 리스트 통째 교체 동작을 모르는 팀원이 base 값을 날리는 사고가 가장 흔하다.

정리하면, values 파일을 복제하지 말고 base+overlay로 쪼개되 리스트 병합만 주의하면, 환경별 차이가 diff 한 줄로 드러나는 관리 가능한 배포 구조를 얻는다.