Deployment, Service, ConfigMap, Ingress... 리소스마다 YAML을 작성하고 환경별로 관리하려면 파일이 끝없이 늘어나는데, 더 좋은 방법은 없을까요?

Helm은 Kubernetes의 패키지 매니저입니다. 여러 리소스를 하나의 Chart로 묶고, 환경별 설정은 values로 분리하며, 버전 관리와 롤백까지 제공합니다. 복잡한 애플리케이션을 한 줄의 명령어로 설치하고 관리할 수 있게 해줍니다.

Chart 구조

PLAINTEXT
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

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

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

리소스 제한, 오토스케일링, 서브차트 설정도 같은 values.yaml에 정의하여 환경별로 오버라이드할 수 있습니다.

YAML
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 패키지를 사용합니다.

기본 문법

YAML
# 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 섹션에서는 .Values.Chart 객체를 활용해 이미지, 포트, 리소스 등을 동적으로 생성합니다.

YAML
  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

재사용 가능한 템플릿을 정의합니다.

YAML
# 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 }}

핵심 명령어

SHELL
# 설치
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

릴리즈 관리와 디버깅에 사용하는 명령어들입니다.

SHELL
# 롤백
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 오버라이드

SHELL
# 파일로 오버라이드
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 파일 패턴

PLAINTEXT
my-app/
├── values.yaml            # 기본값
├── values-dev.yaml        # 개발 환경 오버라이드
├── values-staging.yaml    # 스테이징 환경
└── values-prod.yaml       # 프로덕션 환경
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 등을 실행합니다.

YAML
# 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롤백 후
testhelm test 실행 시

Chart 리포지토리

SHELL
# 리포지토리 추가
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와 조합하세요
SHELL
# helm-diff 플러그인 설치
helm plugin install https://github.com/databus23/helm-diff

# 업그레이드 전 변경 확인
helm diff upgrade my-release ./my-app -f values-prod.yaml

주의할 점

1. helm upgrade --install 시 values 파일을 빠뜨리면 기존 설정이 기본값으로 덮어씌워진다

Helm은 upgrade할 때 이전 릴리즈의 values를 자동으로 유지하지 않습니다. -f values-prod.yaml을 빼먹으면 chart의 기본 values로 배포되어 프로덕션 설정(replica 수, 리소스, 환경변수 등)이 날아갑니다. --reuse-values 플래그를 쓸 수도 있지만, chart 버전이 바뀌면 새 기본값이 적용되지 않는 부작용이 있으므로 항상 명시적으로 values 파일을 지정하세요.

2. Chart 의존성 업데이트 없이 배포하면 구 버전 서브차트가 사용된다

Chart.yaml에서 의존성 버전을 올렸더라도 helm dependency update를 실행하지 않으면 charts/ 디렉토리에 캐시된 구 버전이 그대로 사용됩니다. CI 파이프라인에서 helm dependency build를 빌드 스텝에 반드시 포함하세요.

3. 템플릿의 들여쓰기 오류가 런타임에서야 발견된다

Helm 템플릿에서 {{ .Values.config | nindent 4 }}의 들여쓰기를 잘못 설정하면 유효하지 않은 YAML이 생성됩니다. helm template이나 helm install --dry-run으로 문법은 통과하지만, 실제 Kubernetes가 적용할 때 파싱 에러가 납니다. 배포 전에 helm template | kubectl apply --dry-run=server -f -로 서버 측 검증까지 하세요.

정리

Helm은 복잡한 Kubernetes 리소스를 Chart로 패키징하고, values로 환경별 설정을 분리하며, 릴리즈 관리와 롤백을 제공합니다. Go 템플릿 문법으로 유연한 YAML 생성이 가능하고, hooks로 배포 전후의 작업을 자동화할 수 있습니다. helm templatehelm diff를 활용하면 배포 실수를 크게 줄일 수 있습니다.

댓글 로딩 중...