Helm 패키지 매니저
마지막 업데이트: 2026년 9월 12일 실제 로컬 검증: Helm 3.21.3 / Helm 4.3.0
Helm은 chart를 렌더링하고 Kubernetes 리소스와 release 이력을 관리합니다. chart version, appVersion, image tag/digest, release revision은 서로 다른 값입니다. Helm 4도 기존 apiVersion:v2 chart를 사용하지만 CLI·적용·대기 방식의 차이는 정확한 버전에서 확인해야 합니다.
핵심 개념과 권한
Helm 3부터 Tiller 없이 client가 자신의 Kubernetes credential/RBAC로 API를 사용합니다. chart repository/OCI registry와의 통신은 Kubernetes API 호출과 별도입니다. Tiller가 없다고 위험한 chart나 광범위한 권한이 안전해지는 것은 아닙니다.
기본 release storage는 release namespace의 Secret이며 ConfigMap/SQL 등 다른 backend를 구성할 수도 있습니다. release 기록에는 렌더링된 manifest와 values 등 민감 정보가 포함될 수 있습니다. Base64는 암호화가 아니며 release Secret을 읽는 권한도 제한해야 합니다.
완전한 로컬 chart 예제
저장소의 examples/platform/helm/reviewed-app에는 아래 8개 파일이 있습니다. Helm 3/4에서 lint와 렌더링 결과를 비교했고 패키징·values override·잘못된 replicaCount 거부를 검증했습니다. 실제 Kubernetes 설치나 컨테이너 실행은 이번 검토에서 하지 않았습니다. 운영 전에 image digest와 실제 namespace·장치·정책을 확인하세요.
Chart.yaml
apiVersion: v2
name: reviewed-app
description: Offline Helm teaching chart
type: application
version: 0.1.0
appVersion: "1.30.4"values.yaml
replicaCount: 1
image:
repository: nginxinc/nginx-unprivileged
tag: "1.30.4-alpine"
service:
port: 8080
resources:
requests:
cpu: 100m
memory: 64Mi
limits:
cpu: 500m
memory: 128Mi
env:
LOG_LEVEL: infovalues.schema.json
{
"$schema": "https://json-schema.org/draft-07/schema#",
"type": "object",
"required": [
"replicaCount",
"image",
"service"
],
"properties": {
"replicaCount": {
"type": "integer",
"minimum": 0,
"maximum": 5
},
"image": {
"type": "object",
"required": [
"repository",
"tag"
],
"properties": {
"repository": {
"type": "string",
"minLength": 1
},
"tag": {
"type": "string",
"minLength": 1
}
}
},
"service": {
"type": "object",
"required": [
"port"
],
"properties": {
"port": {
"type": "integer",
"minimum": 1,
"maximum": 65535
}
}
},
"env": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}templates/_helpers.tpl
{{- define "reviewed-app.fullname" -}}
{{- printf "%s-%s" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "reviewed-app.selectorLabels" -}}
app.kubernetes.io/name: {{ .Chart.Name | quote }}
app.kubernetes.io/instance: {{ .Release.Name | quote }}
{{- end -}}templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "reviewed-app.fullname" . }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "reviewed-app.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "reviewed-app.selectorLabels" . | nindent 8 }}
spec:
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 101
runAsGroup: 101
fsGroup: 101
seccompProfile:
type: RuntimeDefault
containers:
- name: web
image: {{ printf "%s:%s" .Values.image.repository .Values.image.tag | quote }}
ports:
- name: http
containerPort: 8080
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: [ALL]
resources:
{{- toYaml .Values.resources | nindent 10 }}
env:
{{- range $key, $value := .Values.env }}
- name: {{ $key | quote }}
value: {{ $value | quote }}
{{- end }}
readinessProbe:
httpGet:
path: /
port: http
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir:
sizeLimit: 64Mitemplates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: {{ include "reviewed-app.fullname" . }}
spec:
type: ClusterIP
selector:
{{- include "reviewed-app.selectorLabels" . | nindent 4 }}
ports:
- name: http
port: {{ .Values.service.port }}
targetPort: httptemplates/NOTES.txt
Inspect the rendered resources and prepare namespace/image compatibility before installation.
Release: {{ .Release.Name }}
Namespace: {{ .Release.Namespace }}.helmignore
*.private모든 helper를 정의했고 Service와 container의 named port를 연결했습니다. securityContext는 manifest에 포함되며 resources·env 설정은 values에서 template으로 전달됩니다. values에만 옵션을 적고 template에서 참조하지 않으면 효과가 없습니다. 기본 예제는 DB·Ingress·autoscaler를 생성하지 않습니다.
로컬 검사
저장소 root에서 실행합니다. helm 명령이 가리키는 버전은 helm version --short로 확인합니다.
helm lint examples/platform/helm/reviewed-app
helm template demo examples/platform/helm/reviewed-app --namespace example
helm template demo examples/platform/helm/reviewed-app --set replicaCount=3 --set-string env.MAX_CONNECTIONS=100
helm package examples/platform/helm/reviewed-app --destination ./chart-packageslint/template 성공은 admission, CEL, RBAC, image 실행, Service 연결이나 readiness의 검증이 아닙니다. native test hook도 클러스터에서 실제 실행해야 합니다. helm template --api-versions는 오프라인 capability를 알려줄 뿐 CRD를 설치하지 않습니다.
명령과 Helm 3/4 차이
| 목적 | 예시와 주의점 |
|---|---|
| Repository | helm repo add, update, list, remove, search repo; OCI registry는 별도 login/pull 경로 |
| 설치 | helm install demo ./chart -n example --create-namespace; namespace와 동일 release 존재 여부 확인 |
| 생성/업그레이드 | helm upgrade --install; hooks·random values·외부 상태까지 무조건 멱등적이라는 뜻은 아님 |
| 조회 | helm list -n example, status, history, get values, get manifest; 민감 값 출력 주의 |
| Computed values | helm get values demo -n example --all; chart 기본값을 포함 |
| 롤백 | helm rollback demo REVISION -n example; image tag가 아닌 release revision |
| 삭제 | helm uninstall demo -n example; PVC/CRD/hooks/외부 자원 lifecycle은 별도 확인 |
기존 stable repository는 보관용이며 새로운 기본 배포 경로로 소개하지 않습니다. 외부 chart와 database image는 현재 공급·라이선스·지원·보안 조건을 확인하고 chart version을 고정합니다. 과거 Bitnami PostgreSQL12/Redis17 의존성을 현재 예제의 기본값으로 유지하지 않았습니다.
Dry run과 대기
Helm 4.3은 --dry-run=client와 --dry-run=server를 구분합니다. 이번 환경에서 4.3 client 모드는 클러스터 없이 통과했지만 3.21.3 install의 client dry run은 클러스터 접근을 시도하며 실패했습니다. 오프라인 템플릿 검사는 실제로 통과한 helm template 경로를 사용하세요. server 모드도 실제 cluster와 권한이 필요하며 모든 webhook·외부 side effect가 검증되는 것은 아닙니다.
Helm 4.3에서 --wait를 생략하면 기본 hookOnly, 지정하면 기본 watcher를 사용하며 legacy도 선택할 수 있습니다. --rollback-on-failure는 실패한 upgrade를 이전 성공 release로 되돌리는 옵션입니다. Helm 3의 --atomic과 동일한 이름이 아니며 해당 버전 도움말을 확인합니다. --force-replace와 --force-conflicts도 replacement와 server-side apply conflict를 다루는 서로 다른 기능입니다.
rollback은 DB migration·외부 API side effect·이미 발생한 데이터 삭제를 되돌리는 transaction이 아닙니다. timeout과 Pod readiness, job 완료, application SLO를 구분하세요.
템플릿과 values
Chart, Release, Values, Capabilities는 context 객체입니다. range/with 내부에서는 점(.) context가 바뀌므로 root가 필요할 때 $를 사용합니다. .Capabilities.APIVersions.Has는 주어진 discovery/capability 정보이며 모든 운영 호환성을 보장하지 않습니다.
include는 named template 결과를 string으로 반환하므로 nindent와 조합할 수 있습니다. nindent는 새 줄도 추가합니다. helper 이름은 subchart와 충돌하지 않도록 chart prefix를 사용하고 selector는 release 업그레이드 시 불필요하게 바꾸지 않습니다.
default와 coalesce의 empty에는 false,0,빈 문자열·목록 등이 포함됩니다. 명시한 false/0을 보존해야 한다면 존재 여부와 타입을 따로 검사합니다. 부모 map이 없는 nested lookup의 오류를 모든 default가 자동 방지하지는 않습니다.
values.yaml은 기본적으로 데이터이며 내부의 {{ .Values... }}가 자동으로 다시 렌더링되지 않습니다. 필요한 경우 chart 작성자가 tpl을 명시적으로 사용하지만, 평가 가능한 template 권한과 입력 신뢰를 검토해야 합니다. 이전 subchart storageClass와 Blue/Green selector 예제의 literal template 문자열은 자동 연결이 아니었습니다.
여러 -f 파일과 같은 계열의 override는 오른쪽 값이 우선하며 map과 list의 merge/교체 동작을 확인합니다. dev/staging/prod는 별도 파일로 저장하세요. 한 YAML 문서에 중복 key를 나열하는 것은 환경별 파일을 만드는 것과 다릅니다. 숫자처럼 보이는 문자열에는 --set-string, 구조화 값에는 해당 버전의 --set-json 등을 사용합니다.
--reuse-values, --reset-values, --reset-then-reuse-values는 이전 release와 새 chart 기본값을 합치는 방식이 다릅니다. 변경 전에 computed values와 렌더링 diff를 검토하고 암묵적 default에 의존하지 않습니다.
의존성 관리
Chart.yaml의 dependencies는 chart 이름·버전·repository와 선택적인 alias/condition을 선언합니다. 아래는 로컬 helper subchart가 준비된 경우의 fragment입니다.
dependencies:
- name: helper
alias: cache
version: 0.1.0
repository: file://../dependency-child
condition: cache.enabledalias를 사용하면 values도 cache 아래로 전달하며 condition도 일치시킵니다. condition 경로가 존재하지 않는 경우의 기본 동작까지 테스트해야 합니다. global 값은 subchart가 해당 값을 실제 참조할 때만 효과가 있습니다. import-values는 child/parent export 구조가 맞아야 합니다.
dependency update는 Chart.yaml의 버전 제약을 해석하고 Chart.lock을 작성합니다. build는 lock의 버전을 사용하며 lock이 없으면 update와 유사하게 해석할 수 있습니다. lock만으로 변조 방지·runtime image 고정·완전한 재현성이 보장되지는 않습니다. chart artifact digest/서명, 공급 경로와 image revision도 관리합니다. 검토에서는 로컬 file dependency의 update/build와 alias condition on/off를 Helm 3/4에서 실행했습니다.
Hooks, CRDs와 테스트
pre/post install·upgrade·rollback·delete와 test hook은 특정 lifecycle 단계에 실행됩니다. 같은 단계에서는 낮은 weight가 먼저 실행되고 동률의 kind/name 순서도 검토합니다. pre-install DB migration은 일반 chart의 DB가 아직 생성되지 않았을 수 있으므로 의존성을 확인해야 합니다.
hook Job/Pod는 실제 executable·image·Service·Secret과 권한, timeout·재실행의 멱등성을 갖춰야 합니다. before-hook-creation/hook-succeeded/hook-failed와 Job TTL로 cleanup을 설계하며 uninstall이 모든 hook 자원을 정리한다고 가정하지 않습니다. post-install 의미도 --wait 설정에 따라 준비 상태와 구분합니다.
crds/ 아래 CRD는 일반 template과 lifecycle이 다릅니다. 자동 upgrade/delete나 rollback으로 CRD schema가 원복된다고 가정하지 말고 별도 migration·CR 보존 계획을 사용하세요. CRD를 삭제하면 해당 custom resource 데이터에 영향을 줄 수 있습니다.
helm test는 정의한 test hook을 실행합니다. 단순 HTTP 연결 성공은 DB, 보안, load·복구를 검증하지 않습니다. Blue/Green이나 canary를 구현하려면 실제 Deployment·Service/mesh route와 controller·metric/rollback 조건을 함께 준비해야 합니다. Helm values만으로 점진적 배포가 실행되지는 않습니다.
GitOps와 보안
Argo CD는 보통 Helm을 template renderer로 사용하며 Helm release lifecycle을 직접 소유하는 방식과 다릅니다. Flux helm-controller는 HelmRelease를 reconcile합니다. Git/source chart version, valuesFrom namespace와 precedence, hook mapping·prune·reconciliation 주체를 확인하고 두 controller가 같은 자원을 경쟁 관리하지 않게 합니다.
비밀은 chart 기본값이나 --set 인자, debug 출력에 넣지 않습니다. --hide-secret은 dry-run의 Kubernetes Secret 출력 범위이며 모든 log나 values에 대한 일반 마스킹을 보장하지 않습니다. 기존 Secret 참조도 app의 환경 변수로 전달하면 파일 credential 정책을 충족하지 않습니다. 승인된 Secret volume과 파일 재읽기/회전 경로를 사용하세요.
ESO v1 등 현재 API와 controller를 별도로 준비해야 합니다. Sealed Secrets와 helm-secrets는 선택한 controller/plugin·key/KMS 접근과 복호화 과정이 필요하며 Helm core 기능이 아닙니다. 복호화한 values가 최종 release 기록이나 log에 남는지도 검사합니다.
ServiceAccount/Role만 정의해서 권한이 연결되지는 않습니다. 필요한 경우 RoleBinding과 workload serviceAccountName을 연결하고, 단순 secret volume mount를 위해 app에 모든 Secrets get/list/watch 권한을 주지 않습니다. 예제 web chart는 Kubernetes API 권한이 필요하지 않아 token 자동 mount를 끕니다.
문제 해결 순서
| 증상 | 확인과 수정 |
|---|---|
| 이름 재사용 | namespace와 release 상태/history 확인 후 의도한 upgrade 또는 새 이름 선택 |
| 기존 자원 충돌 | owner annotation/label과 관리 주체 확인; 승인된 adoption/migration 또는 이름 변경 |
| 실패 release | 실패 원인·events·history를 확인하고 검증된 revision/설정으로 재시도 |
| helper 누락 | helper 정의·이름·scope와 root context 확인 |
| schema 실패 | values 타입·required·범위와 실제 최종 병합값 확인 |
기존 자원 삭제나 --force를 보편적 해결책으로 권하지 않습니다. 실제 변경 diff, immutable field, 데이터 보존과 다른 controller 영향을 검토한 뒤 필요한 조치를 선택합니다.
검증 범위와 참고 자료
원문 764줄·퀴즈 462줄씩과 58개 고유 block을 읽었습니다. 완전한 chart의 Helm 3/4 lint/template/package, values override·negative schema, 로컬 dependency/alias를 검사했고 4.3 client dry run이 통과했습니다. 3.21.3 install dry run의 cluster access 실패도 기록했습니다. 실제 Kubernetes install/upgrade/rollback/hooks나 app HTTP 실행을 검증한 것은 아닙니다.