매니페스트 50장을 복붙하던 날들 — Helm이 템플릿으로 만든 재사용
한 팀이 세 환경(dev/stage/prod)에 같은 앱을 띄운다. 매니페스트가 이미지 태그·replica 수·도메인만 다르고 나머진 동일한데, 환경마다 디렉터리를 복사해 50장씩 유지했다. 이미지를 바꾸면 세 디렉터리를 일일이 고쳤다. 어느 날 prod만 고치는 걸 깜빡해 stage와 prod가 달라졌다. Helm은 이 "매니페스트의 복붙 지옥"을 템플릿 하나로 끝낸다.
이 글이 푸는 것은: Helm이 매니페스트를 어떻게 재사용 가능한 패키지(chart)로 만드는가, 그리고 kubectl apply와 무엇이 근본적으로 다른가다.
Helm은 Kubernetes의 패키지 매니저
Helm은 Kubernetes 매니페스트의 패키지 매니저다. apt/yarn/npm이 소프트웨어 패키지를 다루듯, Helm은 차트(chart)라는 매니페스트 묶음을 다룬다.
- chart: 템플릿 + 설정 값(values) 묶음. "재사용 가능한 매니페스트 패키지".
- release: chart를 특정 이름으로 설치한 인스턴스. 같은 chart를 여러 release로(예: dev/stage) 따로 둘 수 있다.
- values: chart를 구체화하는 변수. 환경마다 다른 부분(이미지 태그, replica, 도메인)만 여기로 뺀다.
flowchart LR
C["chart<br/>(템플릿 + 기본 values)"] -->|"helm install"| R["release: myapp-prod"]
V["values-prod.yaml<br/>(오버라이드)"] --> R
C -->|"helm install"| R2["release: myapp-dev"]
V2["values-dev.yaml"] --> R2
복붙의 끝: 같은 chart에 다른 values만 줘서 dev/prod를 만든다. 차트는 하나, 환경별 차이는 values 파일로.
템플릿 — 매니페스트에 변수를 넣는다
chart의 핵심은 Go template이다. 매니페스트 안에 {{ }} 변수를 넣고, values로 치환한다.
# templates/deployment.yaml (chart 안)
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}
spec:
replicas: {{ .Values.replicaCount }}
template:
spec:
containers:
- name: {{ .Chart.Name }}
image: {{ .Values.image.repo }}:{{ .Values.image.tag }}
# values.yaml (기본값)
replicaCount: 1
image: {repo: myapp, tag: "1.0"}
# my-prod.yaml (오버라이드)
replicaCount: 5
image: {repo: myapp, tag: "2.0"}
helm install myapp-prod ./mychart -f my-prod.yaml
이 과정에서 템플릿이 렌더링돼 최종 매니페스트가 나오고, 그것이 apiserver에 적용된다. 결국 Helm도 kubectl apply를 자동화한 것이지만 — 변수화 + 패키징 + 릴리스 관리가 더해진 버전이다.
chart 구조
mychart/
├── Chart.yaml # 차트 메타데이터(이름, 버전)
├── values.yaml # 기본 설정값
├── templates/ # 템플릿 파일들
│ ├── deployment.yaml
│ ├── service.yaml
│ └── _helpers.tpl # 재사용 템플릿 조각
└── charts/ # 하위 의존 차트Chart.yaml이 차트 정체성(이름, 버전)을 갖고, templates/가 렌더링 대상, values.yaml이 기본값이다.
helm install / upgrade / rollback — 릴리스 생명주기
helm install myapp-prod ./mychart -f prod.yaml # 설치
helm upgrade myapp-prod ./mychart -f prod.yaml # 업그레이드(values/차트 변경 시)
helm rollback myapp-prod 2 # 이전 리비전으로 롤백
helm list # 설치된 release 목록
helm uninstall myapp-prod # 제거
Helm은 release마다 리비전(revision) 히스토리를 유지한다(기본적으로 Secret/ConfigMap에 저장). 그래서 rollback이 가능하다 — kubectl apply로는 못 하는 일. 이것이 Helm이 단순 템플릿 렌더러가 아니라 릴리스 관리자인 이유다.
Helm 3의 중요한 변화: Helm 2엔 Tiller라는 클러스터 내 서버 컴포넌트가 있어 보안 문제가 있었다(클러스터 권한을 크게 가짐). Helm 3는 Tiller를 없애고, 설치하는 사용자의 권한(kubeconfig)으로만 동작. RBAC 모델이 단순해졌다.
subchart와 의존성 — 복잡한 앱 조립
복잡한 앱은 여러 chart(예: 앱 chart + DB chart + 모니터링 chart)를 조립할 수 있다. Chart.yaml의 dependencies로 하위 chart(subchart)를 선언하고, 각각의 values를 부모 chart에서 오버라이드.
Helm vs Kustomize — 두 접근
Helm 외에 Kustomize(kubectl에 내장)라는 또 다른 접근이 있다. 차이:
- Helm: 템플릿 + 변수. 동적 렌더링.
- Kustomize: 오버레이. 기본 매니페스트(base)에 패치(overlay)를 겹쳐 환경별 차이를 만듦. 템플릿 변수 없이 순수 YAML 조합.
| Helm | Kustomize | |
|---|---|---|
| 접근 | 템플릿 + 변수 | 오버레이(패치) |
| 학습 곤란도 | Go template 문법 | YAML 겹치기(비교적 단순) |
| 릴리스 관리 | 있음(revision/rollback) | 없음(순수 렌더링) |
| 배포 | kubectl 필요 없음 | kubectl에 내장 |
선택은 취향/생태계. 공식 차트가 많은(e.g. 데이터플랫폼) 생태계엔 Helm이 유리하고, 단순 환경 분리엔 Kustomize가 가볍다.
helm template의 함정 — 렌더링 후엔 kubectl과 같다
helm template(렌더링만 하고 설치 안 함)으로 나온 매니페스트를 kubectl apply하는 패턴이 있다(GitOps). 이때 중요한 점: Helm은 매니페스트를 렌더링해 apiserver에 보낼 뿐, 그 이후 상태는 Kubernetes가 관리한다. Helm hook(pre-install, post-upgrade 등)으로 일부 작업을 끼울 순 있지만, 본질은 "렌더링 + apply"다.
그래서 Helm으로 설치한 리소스를 직접 kubectl edit로 고치면, 다음 helm upgrade가 그것을 덮어쓴다. Helm이 release 상태를 추적하므로 — "Helm이 관리하는 리소스는 Helm으로만 고쳐라"가 원칙.
직접 확인하기
# Kubernetes 1.36 — Helm 설치 후 (공식 chart repo 추가)
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install my-nginx bitnami/nginx --set replicaCount=3
helm list
확인할 것: my-nginx release가 있고, Deployment가 3 replica로 생김.
kubectl get deploy -l app.kubernetes.io/instance=my-nginx
helm upgrade my-nginx bitnami/nginx --set replicaCount=5
helm history my-nginx
helm rollback my-nginx 1
확인할 것: upgrade로 리비전이 올라가고, rollback으로 이전 리비전으로 돌아간다.
Helm CLI 별도 설치 필요. kind 클러스터에서 동작. 미검증 환경은 노트.
chart 의존성과 subchart — 복잡한 앱 조립
복잡한 앱은 여러 chart를 조립할 수 있다. Chart.yaml의 dependencies로 하위 chart(subchart)를 선언:
# Chart.yaml
dependencies:
- name: postgresql
version: 12.x.x
repository: https://charts.bitnami.com/bitnami
- name: redis
version: 17.x.x
repository: https://charts.bitnami.com/bitnami
helm dependency update가 subchart를 다운로드해 charts/에 배치. 이제 부모 chart를 설치하면 subchart(postgres, redis)도 같이 설치. 부모 chart의 values.yaml에서 subchart의 값을 오버라이드:
# 부모 values.yaml
postgresql:
auth: {password: "strongpw"}
redis:
replica: {replicaCount: 3}
이것이 "한 chart에 전체 스택(앱 + DB + 캐시)"을 패키징하는 패턴. 복잡한 앱을 한 번에 배포.
Helm hooks — 설치/업그레이드에 끼어들기
Helm hooks가 특정 시점에 임의 작업을 끼워 넣게 한다:
pre-install: 설치 전 (예: DB 마이그레이션).post-install: 설치 후 (예: 초기 데이터 시딩).pre-upgrade,post-upgrade,pre-rollback등.
# pre-install hook (Job)
metadata:
annotations:
"helm.sh/hook": pre-install
"helm.sh/hook-weight": "-5" # 여러 hook 중 순서
"helm.sh/hook-delete-policy": hook-succeeded
hook은 일반 리소스와 달리 설치 과정에 끼어들어 실행. 예: "DB 스키마 마이그레이션 Job을 업그레이드 전에 돌려라". 단 hook 실패 시 설치/업그레이드가 어떻게 되는지(--wait/hook 정책)를 이해해야 — hook이 실패하면 전체 배포가 멈출 수 있다.
OCI registry — chart를 컨테이너 레지스트리에
최근 Helm은 chart를 OCI 레지스트리(container registry)에 저장 지원. 기존 Helm repo(HTTP) 대신 OCI:
helm install myapp oci://registry.example.com/charts/myapp --version 1.2.3
이점: chart와 컨테이너 이미지를 같은 레지스트리에서 관리. 서명(cosign, 03-k8s-security 08장)으로 chart 무결성도 검증. OCI가 Helm chart 배포의 차세대 표준 방향.
흔히 묻는 것, 흔히 틀리는 것
| 오해 | 정정 |
|---|---|
| "Helm은 kubectl을 대체한다" | 아니다. 렌더링해 apiserver에 보내는 건 같다. 변수화+패키징+릴리스 관리가 차이 |
| "chart를 설치하면 영원히 Helm 관리다" | uninstall하면 Helm이 만든 리소스가 같이 지워짐. 단 직접 만든 건 남음 |
| "Helm으로 설치 후 kubectl edit 해도 된다" | 다음 upgrade가 덮어쓴다. Helm 관리 리소스는 Helm으로만 |
| "Helm 2와 3은 같다" | Tiller 제거 등 큰 변화. Helm 3는 사용자 권한으로만 동작 |
| "values.yaml은 고정이다" | -f로 여러 오버라이드 파일, --set으로 단일 값 주입 가능 |
| "Kustomize는 Helm의 하위 호환이다" | 다른 철학(오버레이 vs 템플릿). 대체가 아니라 선택 |
요약 — 이 글의 결론
- Helm은 매니페스트의 패키지 매니저. chart(템플릿+values 묶음) + release(설치 인스턴스)로 복붙 지옥을 끝낸다.
- Go template + values가 변수화의 핵심. 같은 chart에 다른 values로 dev/prod를 만든다 — 매니페스트는 하나.
- 릴리스 관리(revision/rollback)가 Helm을 단순 렌더러와 구분. kubectl apply로는 못 하는 롤백을 제공.
- Helm 3는 Tiller 제거로 보안 단순화 — 사용자의 kubeconfig 권한으로만 동작.
- Helm 관리 리소스는 Helm으로만 고쳐야. 직접 kubectl edit하면 다음 upgrade가 덮어쓴다.
- Kustomize는 다른 철학(오버레이). 둘 중 선택이지 하위/상위가 아니다. 공식 chart 생태계엔 Helm, 단순 환경 분리엔 Kustomize가 자주 선택.
생각해 볼 문제
helm upgrade가 내부적으로 하는 일을 "렌더링 → apply"로 설명하라. rollback은 어떻게 구현되나?- 같은 chart를 dev/stage/prod로 세 release로 띄운다. 각각 어떻게 격리되는가? (namespace, release name)
- Helm으로 설치한 Deployment를
kubectl scale로 replica를 바꿨다. 다음helm upgrade에 어떻게 되나? - Helm hook(
pre-install)으로 DB 마이그레이션을 넣었다. 실패하면 설치는 어떻게 되나? - GitOps(ArgoCD/Flux)에서 Helm을 쓸 때 "helm install" 대신 쓰는 패턴은? (helm template + apply)
- Kustomize가 Helm보다 나은 구체적 상황을 하나 설계하라.
참고
- Helm 공식 문서 - Charts - 접근 2026-07-12 (chart 구조)
- Helm 공식 문서 - Templates - 접근 2026-07-12 (Go template)
- Helm 공식 문서 - helm upgrade/rollback - 접근 2026-07-12 (릴리스 관리)
- Kubernetes 공식 문서 - Helm 2→3 변화 (Tiller 제거) - 접근 2026-07-12
- Kustomize - kubernetes-sigs/kustomize - 접근 2026-07-12 (오버레이 접근 비교)
'Tech Artifacts > Kubernetes' 카테고리의 다른 글
| Kubernetes - 16. crd controllers (0) | 2026.07.13 |
|---|---|
| Kubernetes - 14. kubeadm (0) | 2026.07.13 |
| Kubernetes - 13. node administration (0) | 2026.07.13 |
| Kubernetes - 12. autoscaling (0) | 2026.07.13 |
| Kubernetes - 11. resource qos (0) | 2026.07.13 |
