1. Helm이란?
Helm은 Kubernetes의 패키지 매니저입니다. 복잡한 Kubernetes 리소스를 하나의 **Chart**로 패키징하여 설치, 업그레이드, 롤백을 간편하게 관리할 수 있습니다.
왜 Helm인가?
- 반복적인 YAML 작성 제거
- 환경별 설정 분리 (dev/staging/production)
- 버전 관리와 롤백
- 의존성 관리
2. Chart 디렉토리 구조
mychart/
├── Chart.yaml # Chart 메타데이터
├── Chart.lock # 의존성 잠금 파일
├── values.yaml # 기본 설정값
├── values-prod.yaml # 프로덕션 오버라이드
├── templates/ # Kubernetes 매니페스트 템플릿
│ ├── _helpers.tpl # 공통 헬퍼 함수
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── hpa.yaml
│ ├── configmap.yaml
│ ├── secret.yaml
│ ├── serviceaccount.yaml
│ ├── NOTES.txt # 설치 후 안내 메시지
│ └── tests/
│ └── test-connection.yaml
├── charts/ # 의존성 Chart
└── .helmignore # 패키징 제외 파일
Chart.yaml 작성
apiVersion: v2
name: my-web-app
description: A production-ready web application
type: application
version: 1.2.0 # Chart 버전 (SemVer)
appVersion: '3.1.0' # 앱 버전
maintainers:
- name: youngjukim
email: fjvbn2003@gmail.com
dependencies:
- name: postgresql
version: '12.x.x'
repository: 'https://charts.bitnami.com/bitnami'
condition: postgresql.enabled
- name: redis
version: '17.x.x'
repository: 'https://charts.bitnami.com/bitnami'
condition: redis.enabled
3. values.yaml 설계 패턴
values.yaml - 기본 설정
replicaCount: 2
image:
repository: myregistry.io/my-web-app
tag: '' # Chart appVersion 사용
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
targetPort: 8080
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: app.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: app-tls
hosts:
- app.example.com
resources:
limits:
cpu: 500m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
autoscaling:
enabled: false
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 80
postgresql:
enabled: true
auth:
database: myapp
username: appuser
redis:
enabled: false
4. Templates 작성
\_helpers.tpl - 공통 헬퍼
{{- define "mychart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- define "mychart.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{- define "mychart.labels" -}}
helm.sh/chart: {{ include "mychart.chart" . }}
app.kubernetes.io/name: {{ include "mychart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{- define "mychart.selectorLabels" -}}
app.kubernetes.io/name: {{ include "mychart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "mychart.fullname" . }}
labels:
{{- include "mychart.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "mychart.selectorLabels" . | nindent 6 }}
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
labels:
{{- include "mychart.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.targetPort }}
protocol: TCP
livenessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 15
readinessProbe:
httpGet:
path: /ready
port: http
initialDelaySeconds: 5
resources:
{{- toYaml .Values.resources | nindent 12 }}
envFrom:
- configMapRef:
name: {{ include "mychart.fullname" . }}-config
5. Helm Hooks
Hook을 사용하면 릴리스 라이프사이클의 특정 시점에 작업을 실행할 수 있습니다:
templates/pre-install-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "mychart.fullname" . }}-db-migrate
annotations:
"helm.sh/hook": pre-install,pre-upgrade
"helm.sh/hook-weight": "-5"
"helm.sh/hook-delete-policy": before-hook-creation
spec:
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
command: ["python", "manage.py", "migrate"]
envFrom:
- secretRef:
name: {{ include "mychart.fullname" . }}-db-secret
backoffLimit: 3
Hook 종류
| Hook | 실행 시점 |
| ------------ | ----------------- |
| pre-install | 설치 전 |
| post-install | 설치 후 |
| pre-upgrade | 업그레이드 전 |
| post-upgrade | 업그레이드 후 |
| pre-rollback | 롤백 전 |
| pre-delete | 삭제 전 |
| test | helm test 실행 시 |
6. CI/CD 파이프라인 연동
GitHub Actions + Helm
.github/workflows/deploy.yml
name: Deploy Helm Chart
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ap-northeast-2
- name: Update kubeconfig
run: aws eks update-kubeconfig --name my-cluster
- name: Install Helm
uses: azure/setup-helm@v3
with:
version: v3.14.0
- name: Helm lint
run: helm lint ./charts/my-web-app
- name: Helm upgrade
run: |
helm upgrade --install my-app ./charts/my-web-app \
--namespace production \
--create-namespace \
-f ./charts/my-web-app/values-prod.yaml \
--set image.tag=${{ github.sha }} \
--wait --timeout 5m
helm template로 로컬 검증
렌더링된 매니페스트 확인
helm template my-release ./mychart -f values-prod.yaml
특정 값 오버라이드
helm template my-release ./mychart --set replicaCount=3
kubeval로 유효성 검증
helm template my-release ./mychart | kubeval --strict
7. Chart 테스트
templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "mychart.fullname" . }}-test"
annotations:
"helm.sh/hook": test
spec:
containers:
- name: curl
image: curlimages/curl:8.5.0
command: ['curl']
args: ['{{ include "mychart.fullname" . }}:{{ .Values.service.port }}/healthz']
restartPolicy: Never
테스트 실행
helm test my-release -n production
8. 퀴즈
- **version**: Chart 자체의 버전입니다. Chart 구조, 템플릿, values가 변경될 때 올립니다. SemVer를 따릅니다.
- **appVersion**: Chart가 배포하는 애플리케이션의 버전입니다. 앱 코드가 변경될 때 올립니다.
예: Chart version 1.2.0이 appVersion 3.1.0을 배포할 수 있습니다.
**hook-weight**는 같은 타입의 hook이 여러 개 있을 때 실행 순서를 결정합니다. 낮은 숫자가 먼저 실행됩니다.
예: weight -5인 DB 마이그레이션이 weight 0인 캐시 초기화보다 먼저 실행됩니다.
ConfigMap이 변경되면 sha256sum이 바뀌어 Pod의 annotation이 달라집니다. 이로 인해 Deployment가 rolling update를 트리거합니다.
ConfigMap만 변경하면 기본적으로 Pod가 재시작되지 않는 문제를 해결하는 패턴입니다.
현재 단락 (1/237)
Helm은 Kubernetes의 패키지 매니저입니다. 복잡한 Kubernetes 리소스를 하나의 **Chart**로 패키징하여 설치, 업그레이드, 롤백을 간편하게 관리할 수 있습니...