Files
2nd/10_Wiki/Topics/Coding/DevOps_Helm_Deep.md
T
2026-05-09 21:08:02 +09:00

10 KiB

id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
id title category status source_trust_level verification_status created_at updated_at tags tech_stack applied_in aliases
devops-helm-deep Helm 깊이 — Chart / Templating / Hook Coding draft B conceptual 2026-05-09 2026-05-09
devops
helm
kubernetes
vibe-coding
language applicable_to
YAML / Go template
DevOps
Helm chart
values.yaml
template
hook
dependency
helmfile

Helm Deep

Kubernetes package manager. Chart = template + values. 환경별 다른 values + dependency + hook + rollback. 큰 프로덕션 K8s 의 표준.

📖 핵심 개념

  • Chart: package (templates / values / metadata).
  • Values: 환경별 config.
  • Template: Go template + Helm functions.
  • Release: cluster 안 deployed instance.

💻 코드 패턴

Chart 구조

my-app/
├── Chart.yaml
├── values.yaml
├── values-prod.yaml
├── values-dev.yaml
├── templates/
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── ingress.yaml
│   ├── configmap.yaml
│   ├── secret.yaml
│   ├── serviceaccount.yaml
│   ├── _helpers.tpl
│   └── tests/
│       └── connection-test.yaml
└── charts/                      # dependency

Chart.yaml

apiVersion: v2
name: my-app
description: Acme API service
version: 1.0.0       # chart version
appVersion: "1.5.0"  # app version
type: application

dependencies:
  - name: postgresql
    version: 13.2.0
    repository: https://charts.bitnami.com/bitnami
    condition: postgresql.enabled
  - name: redis
    version: 18.0.0
    repository: https://charts.bitnami.com/bitnami

values.yaml (default)

replicaCount: 2

image:
  repository: ghcr.io/myorg/my-app
  tag: ""  # default = appVersion
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 80

ingress:
  enabled: true
  className: nginx
  hosts:
    - host: api.example.com

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

postgresql:
  enabled: true
  auth:
    username: app
    database: app

env:
  NODE_ENV: production

values-prod.yaml (override)

replicaCount: 5

resources:
  requests:
    cpu: 500m
    memory: 1Gi
  limits:
    cpu: 2
    memory: 4Gi

postgresql:
  enabled: false  # External RDS

env:
  DATABASE_URL: postgresql://prod...

Deployment template

# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "my-app.fullname" . }}
  labels:
    {{- include "my-app.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  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 | default .Chart.AppVersion }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - containerPort: 8080
          env:
            {{- range $k, $v := .Values.env }}
            - name: {{ $k }}
              value: {{ $v | quote }}
            {{- end }}
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8080
          readinessProbe:
            httpGet:
              path: /readyz
              port: 8080

Helpers (_helpers.tpl)

{{/* Common labels */}}
{{- define "my-app.labels" -}}
helm.sh/chart: {{ include "my-app.chart" . }}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

{{/* Selector labels */}}
{{- define "my-app.selectorLabels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

{{/* Fullname */}}
{{- define "my-app.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}

명령

# Render local
helm template my-app . -f values-prod.yaml

# Lint
helm lint .

# Install
helm install my-app . -f values-prod.yaml -n prod

# Upgrade
helm upgrade my-app . -f values-prod.yaml -n prod

# Diff (변경 미리)
helm plugin install https://github.com/databus23/helm-diff
helm diff upgrade my-app . -f values-prod.yaml -n prod

# Rollback
helm rollback my-app 5 -n prod  # revision 5

# Uninstall
helm uninstall my-app -n prod

Conditional render

{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
...
{{- end }}

{{- if .Values.podAnnotations }}
annotations:
  {{- toYaml .Values.podAnnotations | nindent 4 }}
{{- end }}

Loop

spec:
  template:
    spec:
      containers:
        {{- range .Values.sidecars }}
        - name: {{ .name }}
          image: {{ .image }}
        {{- end }}

Secret (sealed / external)

# templates/secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: {{ include "my-app.fullname" . }}
type: Opaque
stringData:
  {{- range $k, $v := .Values.secrets }}
  {{ $k }}: {{ $v | quote }}
  {{- end }}

⚠️ Secret in values.yaml prod = bad. Sealed Secrets / External Secrets Operator.

Hooks (lifecycle)

# Pre-install / pre-upgrade — DB migration
apiVersion: batch/v1
kind: Job
metadata:
  name: {{ include "my-app.fullname" . }}-migrate
  annotations:
    "helm.sh/hook": pre-upgrade,pre-install
    "helm.sh/hook-weight": "1"
    "helm.sh/hook-delete-policy": before-hook-creation
spec:
  template:
    spec:
      containers:
        - name: migrate
          image: ...
          command: ["yarn", "migrate:up"]
      restartPolicy: Never

→ Helm install / upgrade 전 자동 migration.

Test

# templates/tests/connection.yaml
apiVersion: v1
kind: Pod
metadata:
  name: "{{ .Release.Name }}-test-connection"
  annotations:
    "helm.sh/hook": test
spec:
  containers:
    - name: wget
      image: busybox
      command: ['wget', '{{ include "my-app.fullname" . }}:{{ .Values.service.port }}/healthz']
  restartPolicy: Never
helm test my-app

Dependency

# Chart.yaml
dependencies:
  - name: postgresql
    version: ~13.0.0
    repository: oci://registry-1.docker.io/bitnamicharts
helm dependency update
helm install my-app . -f values.yaml

→ Postgres 자동 같이 install.

Helmfile (multiple chart)

# helmfile.yaml
releases:
  - name: my-app
    namespace: prod
    chart: ./charts/my-app
    values: [values-prod.yaml]
  
  - name: monitoring
    namespace: monitoring
    chart: prometheus-community/kube-prometheus-stack
    values: [monitoring-values.yaml]
helmfile sync
helmfile diff

→ 여러 chart 한 번에.

Multi-environment

values.yaml         # baseline / dev
values-staging.yaml # staging
values-prod.yaml    # prod

helm upgrade my-app . -f values-staging.yaml -n staging
helm upgrade my-app . -f values-prod.yaml -n prod

Chart museum / OCI registry

# Push to OCI
helm package .
helm push my-app-1.0.0.tgz oci://registry.example.com/charts

# Install from OCI
helm install my-app oci://registry.example.com/charts/my-app --version 1.0.0

Sub-chart override

# 자식 chart 의 values
postgresql:
  primary:
    persistence:
      size: 100Gi
  auth:
    database: app

Common functions

{{ .Values.x | default "fallback" }}
{{ .Values.x | quote }}
{{ .Values.x | nindent 4 }}
{{ tpl .Values.x . }}              # template within value
{{ include "common.template" . }}
{{ required "x is required" .Values.x }}
{{ printf "%s-%s" .a .b }}
{{ toYaml .Values.complex | nindent 4 }}
{{ b64enc "secret" }}
{{ randAlphaNum 32 }}

Values schema validation

// values.schema.json
{
  "$schema": "https://json-schema.org/draft-07/schema#",
  "properties": {
    "replicaCount": { "type": "integer", "minimum": 1 },
    "image": {
      "type": "object",
      "required": ["repository"],
      "properties": {
        "repository": { "type": "string" },
        "tag": { "type": "string" }
      }
    }
  },
  "required": ["replicaCount"]
}

→ helm install 시 자동 검증.

CI

- run: helm lint .
- run: helm template . -f values-prod.yaml | kubectl --dry-run=server apply -f -
- run: helm upgrade --install my-app . -f values-prod.yaml -n prod --atomic --timeout 5m

--atomic flag

helm upgrade --install --atomic

→ 실패 시 자동 rollback. 안전.

Chart vs Kustomize

Helm:        Template engine. Complex logic.
Kustomize:   Overlay (patch) — declarative, no logic.
KCL / CUE:   New schema lang.

→ Helm 가 가장 인기. Kustomize 가 단순.

ArgoCD + Helm (GitOps)

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
spec:
  source:
    repoURL: https://github.com/myorg/charts
    chart: my-app
    targetRevision: 1.0.0
    helm:
      valueFiles: [values-prod.yaml]
  destination:
    server: https://kubernetes.default.svc
    namespace: prod

→ Git = truth. Argo 가 sync.

🤔 의사결정 기준

상황 추천
K8s app 배포 Helm
단순 / overlay Kustomize
Multi-cluster / GitOps ArgoCD + Helm
매우 dynamic Helm + Helmfile
사내 chart 공유 OCI registry
Public chart Artifact Hub

안티패턴

  • Secret in values.yaml + git: leak.
  • --atomic 없이 prod: 실패 시 broken state.
  • Chart version + appVersion 같음: chart 변경도 app version bump 필요.
  • Single huge values.yaml: 분리.
  • Hook 가 idempotent X: 재시도 시 깨짐.
  • No values.schema: 잘못된 values 통과.
  • 모든 env 같은 chart: dev / prod 차이 안 됨.

🤖 LLM 활용 힌트

  • Chart + values per env + atomic upgrade.
  • Sealed Secrets / External Secrets.
  • ArgoCD GitOps.
  • Helmfile 여러 chart.

🔗 관련 문서