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

480 lines
10 KiB
Markdown

---
id: devops-helm-deep
title: Helm 깊이 — Chart / Templating / Hook
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [devops, helm, kubernetes, vibe-coding]
tech_stack: { language: "YAML / Go template", applicable_to: ["DevOps"] }
applied_in: []
aliases: [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
```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)
```yaml
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)
```yaml
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
```yaml
# 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)
```yaml
{{/* 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 }}
```
### 명령
```bash
# 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
```yaml
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
...
{{- end }}
{{- if .Values.podAnnotations }}
annotations:
{{- toYaml .Values.podAnnotations | nindent 4 }}
{{- end }}
```
### Loop
```yaml
spec:
template:
spec:
containers:
{{- range .Values.sidecars }}
- name: {{ .name }}
image: {{ .image }}
{{- end }}
```
### Secret (sealed / external)
```yaml
# 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)
```yaml
# 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
```yaml
# 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
```
```bash
helm test my-app
```
### Dependency
```yaml
# Chart.yaml
dependencies:
- name: postgresql
version: ~13.0.0
repository: oci://registry-1.docker.io/bitnamicharts
```
```bash
helm dependency update
helm install my-app . -f values.yaml
```
→ Postgres 자동 같이 install.
### Helmfile (multiple chart)
```yaml
# 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]
```
```bash
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
```bash
# 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
```yaml
# 자식 chart 의 values
postgresql:
primary:
persistence:
size: 100Gi
auth:
database: app
```
### Common functions
```yaml
{{ .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
```json
// 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
```yaml
- 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
```bash
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)
```yaml
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.
## 🔗 관련 문서
- [[DevOps_Kubernetes_Basics]]
- [[DevOps_ArgoCD_GitOps]]
- [[DevOps_Service_Mesh_Deep]]