TestForge Blog
← 전체 포스트

Kubernetes 개발·운영 실전 5편 — Helm 실전 관리

Helm Chart 구조 설계, values 파일로 환경별 설정 분리, 안전한 배포와 롤백 전략. 프로덕션에서 Helm을 체계적으로 관리하는 실전 가이드.

TestForge Team ·

Helm이 필요한 이유

Kubernetes 매니페스트를 그냥 kubectl apply -f로 관리하면 한계가 옵니다.

  • dev/staging/prod 환경마다 이미지 태그, replicas, 도메인이 다름
  • 여러 파일에 걸쳐 반복되는 설정
  • 어떤 버전이 클러스터에 배포됐는지 추적이 어려움
  • 롤백 시 이전 상태로 돌아가는 방법이 불명확

Helm은 이 문제를 Chart(템플릿) + Values(환경별 설정) + Release(배포 이력) 구조로 해결합니다.


1. Chart 구조

my-app/
├── Chart.yaml           # Chart 메타데이터
├── values.yaml          # 기본값
├── values-dev.yaml      # 개발 환경 오버라이드
├── values-staging.yaml  # 스테이징 오버라이드
├── values-prod.yaml     # 프로덕션 오버라이드
└── templates/
    ├── _helpers.tpl     # 공통 helper 함수
    ├── deployment.yaml
    ├── service.yaml
    ├── ingress.yaml
    ├── configmap.yaml
    ├── hpa.yaml
    └── NOTES.txt        # 배포 후 출력 메시지

Chart.yaml

apiVersion: v2
name: my-app
description: My Application Helm Chart
type: application
version: 0.3.1       # Chart 버전 (SemVer)
appVersion: "1.2.3"  # 앱 버전
dependencies:
  - name: redis
    version: "19.x.x"
    repository: https://charts.bitnami.com/bitnami
    condition: redis.enabled

2. values.yaml — 기본값 설계

# values.yaml
replicaCount: 1

image:
  repository: my-registry/my-app
  tag: ""  # Chart.yaml의 appVersion 사용 (비워두면 자동)
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 80
  targetPort: 8080

ingress:
  enabled: false
  className: nginx
  host: ""
  tls: false

resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 500m
    memory: 512Mi

autoscaling:
  enabled: false
  minReplicas: 1
  maxReplicas: 10
  targetCPUUtilizationPercentage: 70

env: {}
  # DATABASE_URL: ""
  # REDIS_URL: ""

redis:
  enabled: false

nodeSelector: {}
tolerations: []
affinity: {}

values-prod.yaml — 프로덕션 오버라이드

replicaCount: 3

image:
  pullPolicy: Always

ingress:
  enabled: true
  host: api.example.com
  tls: true

resources:
  requests:
    cpu: 500m
    memory: 512Mi
  limits:
    cpu: "2"
    memory: 2Gi

autoscaling:
  enabled: true
  minReplicas: 3
  maxReplicas: 20

redis:
  enabled: true
  auth:
    enabled: true

3. 템플릿 작성 실전

_helpers.tpl — 공통 이름 함수

{{/* 앱 이름 */}}
{{- define "my-app.name" -}}
{{- .Chart.Name | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/* 전체 이름 (릴리즈명-차트명) */}}
{{- 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: {{ include "my-app.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

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:
      app.kubernetes.io/name: {{ include "my-app.name" . }}
  template:
    metadata:
      labels:
        {{- include "my-app.labels" . | nindent 8 }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - containerPort: {{ .Values.service.targetPort }}
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
          {{- if .Values.env }}
          env:
            {{- range $key, $value := .Values.env }}
            - name: {{ $key }}
              value: {{ $value | quote }}
            {{- end }}
          {{- end }}

4. 배포 명령 실전

설치

# 기본 설치
helm install my-release ./my-app

# 환경별 values 적용
helm install my-release ./my-app \
  -f values.yaml \
  -f values-prod.yaml \
  --namespace production \
  --create-namespace

# 이미지 태그 동적 주입 (CI/CD에서 사용)
helm install my-release ./my-app \
  -f values-prod.yaml \
  --set image.tag=1.2.3 \
  --namespace production

업그레이드

# 업그레이드 (없으면 설치)
helm upgrade --install my-release ./my-app \
  -f values-prod.yaml \
  --set image.tag=$NEW_TAG \
  --namespace production \
  --atomic \          # 실패 시 자동 롤백
  --timeout 5m \
  --wait              # 모든 Pod Ready 대기

배포 이력 확인 및 롤백

# 배포 이력 확인
helm history my-release -n production

REVISION  STATUS     CHART         APP VERSION  DESCRIPTION
1         superseded my-app-0.3.0  1.2.1        Initial install
2         superseded my-app-0.3.1  1.2.2        Upgrade complete
3         deployed   my-app-0.3.1  1.2.3        Upgrade complete

# 이전 버전으로 롤백
helm rollback my-release 2 -n production

# 한 단계 전으로 롤백
helm rollback my-release -n production

5. Helm Secrets — 민감 정보 관리

Helm values에 직접 비밀번호를 넣는 것은 위험합니다. 두 가지 방법을 사용합니다.

방법 1: 외부 Secret 참조

# values.yaml에는 Secret 이름만 기록
database:
  secretName: db-credentials  # 실제 값은 Kubernetes Secret에 저장

# deployment.yaml 템플릿
env:
  - name: DATABASE_URL
    valueFrom:
      secretKeyRef:
        name: {{ .Values.database.secretName }}
        key: url

방법 2: helm-secrets 플러그인 (SOPS)

helm plugin install https://github.com/jkroepke/helm-secrets

# 암호화된 values 파일 사용
helm secrets upgrade my-release ./my-app \
  -f values-prod.yaml \
  -f secrets.prod.yaml  # SOPS로 암호화된 파일

6. Chart 테스트

# 템플릿 렌더링만 확인 (실제 배포 없음)
helm template my-release ./my-app -f values-prod.yaml

# 린트 검사
helm lint ./my-app -f values-prod.yaml

# 드라이런 (서버 검증 포함)
helm upgrade --install my-release ./my-app \
  -f values-prod.yaml \
  --dry-run \
  --namespace production

7. Helm Repository 관리

# 공식 Charts 레포 추가
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

# 사용 가능한 버전 확인
helm search repo bitnami/redis --versions | head -10

# OCI 기반 레포 (최신 방식)
helm push my-app-0.3.1.tgz oci://registry.example.com/charts
helm install my-release oci://registry.example.com/charts/my-app --version 0.3.1

정리

단계명령설명
검증helm lint / helm template배포 전 문법/렌더링 확인
배포helm upgrade --install --atomic실패 시 자동 롤백
이력helm history버전별 배포 상태
롤백helm rollback특정 revision으로 복구

다음 편에서는 멀티 환경 배포 전략을 다룹니다. Kustomize overlay로 환경별 설정을 분리하고 ArgoCD App of Apps 패턴으로 전체 클러스터를 GitOps로 관리하는 방법을 정리합니다.