Helm 심화 — Chart 구조, 템플릿 문법, 릴리즈 관리까지
Deployment, Service, ConfigMap, Ingress... 리소스마다 YAML을 작성하고 환경별로 관리하려면 파일이 끝없이 늘어나는데, 더 좋은 방법은 없을까요?
Helm은 Kubernetes의 패키지 매니저입니다. 여러 리소스를 하나의 Chart로 묶고, 환경별 설정은 values로 분리하며, 버전 관리와 롤백까지 제공합니다. 복잡한 애플리케이션을 한 줄의 명령어로 설치하고 관리할 수 있게 해줍니다.
Chart 구조
my-app/
├── Chart.yaml # Chart 메타데이터
├── values.yaml # 기본 설정값
├── charts/ # 의존성 Chart (서브차트)
├── templates/ # Kubernetes 매니페스트 템플릿
│ ├── _helpers.tpl # 재사용 템플릿 함수
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── configmap.yaml
│ ├── hpa.yaml
│ └── NOTES.txt # 설치 후 출력 메시지
└── .helmignore # 패키징 제외 파일
Chart.yaml
apiVersion: v2
name: my-app
description: 나의 웹 애플리케이션
version: 1.2.0 # Chart 버전
appVersion: "2.0.0" # 애플리케이션 버전
type: application # application 또는 library
dependencies:
- name: postgresql
version: "12.x.x"
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
values.yaml
# 기본 설정값 — 템플릿에서 .Values로 참조
replicaCount: 3
image:
repository: my-app
tag: "2.0.0"
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
ingress:
enabled: true
hostname: app.example.com
tls: true
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
memory: 512Mi
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilization: 70
postgresql:
enabled: true
auth:
database: mydb
Go 템플릿 문법
Helm 템플릿은 Go의 text/template 패키지를 사용합니다.
기본 문법
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "my-app.fullname" . }}
labels:
{{- include "my-app.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "my-app.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "my-app.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: 8080
{{- with .Values.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
자주 쓰는 문법
| 문법 | 설명 | 예제 |
|---|---|---|
{{ .Values.x }} | values 참조 | {{ .Values.replicaCount }} |
{{ .Release.Name }} | 릴리즈 이름 | my-release |
{{ .Chart.Name }} | Chart 이름 | my-app |
{{- if }}...{{- end }} | 조건문 | {{- if .Values.ingress.enabled }} |
{{- range }}...{{- end }} | 반복문 | {{- range .Values.env }} |
{{- with }}...{{- end }} | 스코프 변경 | {{- with .Values.resources }} |
{{ include "x" . }} | 헬퍼 함수 호출 | {{ include "my-app.labels" . }} |
{{ toYaml . }} | YAML 변환 | {{ toYaml .Values.resources }} |
| ` | nindent N` | N칸 들여쓰기 |
| ` | default "x"` | 기본값 |
| ` | quote` | 따옴표 추가 |
_helpers.tpl
재사용 가능한 템플릿을 정의합니다.
# templates/_helpers.tpl
{{- define "my-app.fullname" -}}
{{- printf "%s-%s" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- define "my-app.labels" -}}
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{- define "my-app.selectorLabels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
핵심 명령어
# 설치
helm install my-release ./my-app
helm install my-release ./my-app -f values-prod.yaml
helm install my-release ./my-app --set replicaCount=5
# 업그레이드
helm upgrade my-release ./my-app -f values-prod.yaml
# 설치 또는 업그레이드 (CI/CD에서 자주 사용)
helm upgrade --install my-release ./my-app -f values-prod.yaml
# 릴리즈 목록
helm list -n production
# 릴리즈 이력
helm history my-release
# 롤백
helm rollback my-release 2 # revision 2로 롤백
# 삭제
helm uninstall my-release
# 템플릿 확인 (설치 없이)
helm template my-release ./my-app -f values-prod.yaml
# 문법 검증
helm lint ./my-app
# Chart 패키징
helm package ./my-app
Values 오버라이드
# 파일로 오버라이드
helm install my-release ./my-app -f values-prod.yaml -f values-secret.yaml
# 명령줄로 오버라이드 (최우선)
helm install my-release ./my-app --set image.tag=v2.1.0
# 우선순위: --set > 마지막 -f 파일 > 이전 -f 파일 > values.yaml
환경별 values 파일 패턴
my-app/
├── values.yaml # 기본값
├── values-dev.yaml # 개발 환경 오버라이드
├── values-staging.yaml # 스테이징 환경
└── values-prod.yaml # 프로덕션 환경
# values-prod.yaml
replicaCount: 5
image:
tag: "2.0.0"
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
memory: 1Gi
ingress:
hostname: app.production.example.com
Helm Hooks
릴리즈 라이프사이클의 특정 시점에 Job 등을 실행합니다.
# templates/db-migrate.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "my-app.fullname" . }}-migrate
annotations:
"helm.sh/hook": pre-upgrade # 업그레이드 전에 실행
"helm.sh/hook-weight": "0" # 실행 순서 (작은 수 먼저)
"helm.sh/hook-delete-policy": hook-succeeded # 성공 시 Job 삭제
spec:
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
command: ["./migrate", "--up"]
Hook 타입
| Hook | 실행 시점 |
|---|---|
pre-install | 설치 전 |
post-install | 설치 후 |
pre-upgrade | 업그레이드 전 |
post-upgrade | 업그레이드 후 |
pre-delete | 삭제 전 |
post-delete | 삭제 후 |
pre-rollback | 롤백 전 |
post-rollback | 롤백 후 |
test | helm test 실행 시 |
Chart 리포지토리
# 리포지토리 추가
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
# Chart 검색
helm search repo nginx
# 리포지토리에서 설치
helm install my-nginx bitnami/nginx
# OCI 레지스트리 (Helm 3.8+)
helm push my-app-1.0.0.tgz oci://myregistry.io/charts
helm install my-release oci://myregistry.io/charts/my-app --version 1.0.0
실무 팁
helm template으로 먼저 확인: 설치 전에 생성될 YAML을 반드시 검토하세요helm diff플러그인: 업그레이드 전 변경사항을 diff로 확인합니다- Chart 버전과 앱 버전을 분리: Chart.yaml의 version과 appVersion을 구분하세요
- 시크릿은 values에 넣지 마세요: Sealed Secrets이나 ESO와 조합하세요
# helm-diff 플러그인 설치
helm plugin install https://github.com/databus23/helm-diff
# 업그레이드 전 변경 확인
helm diff upgrade my-release ./my-app -f values-prod.yaml
정리
Helm은 복잡한 Kubernetes 리소스를 Chart로 패키징하고, values로 환경별 설정을 분리하며, 릴리즈 관리와 롤백을 제공합니다. Go 템플릿 문법으로 유연한 YAML 생성이 가능하고, hooks로 배포 전후의 작업을 자동화할 수 있습니다. helm template과 helm diff를 활용하면 배포 실수를 크게 줄일 수 있습니다.
댓글 로딩 중...