480 lines
10 KiB
Markdown
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]]
|