Notes
K8s 04. Helm
Helm을 Kubernetes package manager로 이해하고, Chart, Values, Template, Release, Repository를 중심으로 애플리케이션 배포와 운영 lifecycle을 정리한다.
- Published
- Updated
- Area
- Cloud Infrastructure
- Type
- concept
- Series
- Kubernetes Essentials
- Category
- Notes
K8s 04. Helm
Kubernetes는 containerized application을 선언형으로 배포하고 운영하기 위한 강력한 platform이다. 하지만 실제 애플리케이션을 Kubernetes에 올리다 보면 Deployment 하나만으로 끝나는 경우는 많지 않다. 보통 하나의 서비스에도 여러 Kubernetes resource가 함께 필요하다.
Deployment
Service
Ingress
ConfigMap
Secret
ServiceAccount
Role
RoleBinding
PersistentVolumeClaim
HorizontalPodAutoscaler
NetworkPolicy
작은 API 서버 하나를 배포하더라도 Deployment, Service, ConfigMap, Secret 정도는 자주 함께 등장한다. 여기에 외부 노출이 필요하면 Ingress, 권한 제어가 필요하면 ServiceAccount와 RoleBinding, autoscaling이 필요하면 HorizontalPodAutoscaler가 추가된다.
환경이 dev, staging, production으로 나뉘면 문제는 더 커진다. 구조는 거의 같은데 image tag, replica 수, domain, resource limit, secret name, ingress annotation만 달라지는 YAML이 반복된다.
Helm은 이 반복을 줄이기 위한 Kubernetes package manager다. 여러 Kubernetes manifest를 하나의 reusable package인 Chart로 묶고, 환경별 차이는 Values로 분리하며, 실제 cluster에 설치된 인스턴스는 Release 단위로 관리한다.
반복되는 Kubernetes YAML 묶음
↓
template + values로 일반화
↓
Chart라는 package로 관리
↓
Release라는 설치 인스턴스로 cluster에 배포
Helm을 한 문장으로 정의하기
Helm은 Kubernetes application을 구성하는 여러 manifest를 Chart라는 package로 묶고, Values를 통해 환경별 설정을 주입하며, Release 단위로 install, upgrade, rollback, uninstall을 관리하는 Kubernetes package manager다.
중요한 점은 Helm이 container image를 build하는 도구가 아니라는 것이다. 애플리케이션 코드는 보통 Dockerfile이나 다른 build process를 통해 container image로 만들어지고, Helm은 그 image를 Kubernetes에 어떤 resource 형태로 배포할지 정의한다.
Dockerfile / Container Image
→ 애플리케이션 실행 단위
Helm Chart
→ 그 image를 Kubernetes에 어떻게 배포할지 정의하는 package
즉 Helm은 Kubernetes를 대체하지 않는다. Helm은 결국 Kubernetes YAML을 rendering하고 Kubernetes API Server에 적용한다. Helm을 사용하더라도 Deployment, Service, Pod, Ingress, ConfigMap, Secret 같은 Kubernetes object를 이해해야 한다.
Helm이 필요한 이유
Kubernetes YAML은 강력하지만 반복이 많다
Kubernetes는 선언형 API를 사용한다. 사용자는 YAML manifest로 원하는 상태를 선언하고, Kubernetes는 실제 cluster 상태를 그 선언에 맞추려 한다.
예를 들어 API 서버 하나를 배포하려면 최소한 Deployment가 필요하다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 3
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: registry.example.com/api:1.0.0
ports:
- containerPort: 3000
내부 접근을 위해 Service를 추가할 수 있다.
apiVersion: v1
kind: Service
metadata:
name: api
spec:
selector:
app: api
ports:
- port: 80
targetPort: 3000
애플리케이션 설정은 ConfigMap으로 분리할 수 있다.
apiVersion: v1
kind: ConfigMap
metadata:
name: api-config
data:
LOG_LEVEL: "info"
민감한 정보는 Secret으로 관리할 수 있다.
apiVersion: v1
kind: Secret
metadata:
name: api-secret
type: Opaque
stringData:
DB_PASSWORD: "[REDACTED]"
외부 접근이 필요하면 Ingress도 필요하다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api
spec:
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api
port:
number: 80
여기까지는 단순해 보인다. 하지만 실제 운영에서는 파일이 빠르게 늘어난다.
deployment.yaml
service.yaml
ingress.yaml
configmap.yaml
secret.yaml
serviceaccount.yaml
role.yaml
rolebinding.yaml
hpa.yaml
pdb.yaml
networkpolicy.yaml
pvc.yaml
그리고 환경별로 같은 파일을 복사하기 시작한다.
dev/
deployment.yaml
service.yaml
ingress.yaml
staging/
deployment.yaml
service.yaml
ingress.yaml
prod/
deployment.yaml
service.yaml
ingress.yaml
이 구조의 문제는 YAML 자체가 아니라 반복과 drift다.
복사-붙여넣기 YAML의 문제
환경별 manifest를 복사해 관리하면 초반에는 빠르다. 하지만 시간이 지나면 다음과 같은 문제가 생긴다.
dev에는 image tag가 1.1.0인데 staging은 1.0.8이다.
prod에는 readinessProbe가 있는데 dev에는 없다.
staging에만 resource limit이 빠져 있다.
ingress annotation이 환경마다 조금씩 다르다.
service name을 바꾸면서 selector label을 같이 못 바꿨다.
대표적인 문제는 다음과 같다.
| 문제 | 설명 |
|---|---|
| label mismatch | Service.selector와 Deployment.template.metadata.labels가 달라 traffic이 가지 않음 |
| image tag drift | 환경별 manifest에 서로 다른 tag가 남아 배포 상태 추적이 어려움 |
| config drift | dev/staging/prod 간 설정 차이가 의도인지 실수인지 구분하기 어려움 |
| duplicate YAML | 거의 같은 파일이 여러 환경에 중복되어 변경 누락 발생 |
| rollback 어려움 | 여러 YAML 변경이 하나의 application 단위로 묶이지 않아 이전 상태 복원이 복잡 |
| dependency 누락 | app은 설치했지만 필요한 DB, exporter, config, RBAC 등이 빠짐 |
Helm은 이런 문제를 template, values, chart, release라는 구조로 완화한다.
Helm을 package manager로 이해하기
Linux에서 apt, yum, dnf, brew 같은 package manager를 사용하면 소스 다운로드, 의존성 확인, 설치 경로 구성, 업데이트, 삭제를 매번 직접 하지 않아도 된다.
apt install nginx
Helm도 Kubernetes에서 비슷한 위치에 있다.
helm install my-nginx bitnami/nginx
다만 Helm은 OS package manager처럼 binary를 운영체제에 설치하는 도구가 아니다. Helm은 Kubernetes resource manifest를 rendering하고, 그 결과를 Kubernetes API Server에 적용한다.
Chart + Values
↓
Kubernetes YAML render
↓
Kubernetes API Server에 적용
↓
Release record 저장
따라서 Helm의 본질은 다음에 가깝다.
- Kubernetes manifest를 package화한다.
- 반복되는 YAML을 template으로 일반화한다.
- 환경별 차이를 values로 분리한다.
- 설치된 application instance를 release로 추적한다.
- upgrade와 rollback의 단위를 application release로 묶는다.
Helm의 핵심 구성요소
Helm을 이해하려면 다음 다섯 개념을 먼저 잡아야 한다.
Chart
Template
Values
Release
Repository
| 개념 | 의미 |
|---|---|
Chart | Kubernetes application을 설치하기 위한 Helm package |
Template | Kubernetes YAML을 생성하기 위한 parameterized manifest |
Values | template에 주입되는 설정값 |
Release | chart를 특정 values로 cluster에 설치한 인스턴스 |
Repository | chart를 저장하고 공유하는 저장소 |
Chart
Chart는 Helm package다. Kubernetes application을 설치하는 데 필요한 template, 기본 values, metadata를 담는다.
간단한 chart 구조는 다음과 같다.
myapp/
├── Chart.yaml
├── values.yaml
├── templates/
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ └── configmap.yaml
└── charts/
각 파일과 디렉터리의 역할은 다음과 같다.
| 파일/디렉터리 | 역할 |
|---|---|
Chart.yaml | chart 이름, version, appVersion, description 같은 metadata |
values.yaml | chart의 기본 설정값 |
templates/ | Kubernetes manifest template |
charts/ | dependency chart 저장 위치 |
.helmignore | chart package에 포함하지 않을 파일 지정 |
실제 chart는 더 많은 template을 포함할 수 있다.
myapp/
├── Chart.yaml
├── values.yaml
├── templates/
│ ├── _helpers.tpl
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── configmap.yaml
│ ├── secret.yaml
│ ├── serviceaccount.yaml
│ ├── hpa.yaml
│ └── tests/
│ └── test-connection.yaml
└── charts/
Chart.yaml
Chart.yaml은 chart의 metadata를 담는다.
apiVersion: v2
name: myapp
description: A Helm chart for my API application
type: application
version: 0.1.0
appVersion: "1.0.0"
여기서 version과 appVersion은 구분해야 한다.
| 필드 | 의미 |
|---|---|
version | chart 자체의 version |
appVersion | chart가 배포하는 application version |
apiVersion | chart specification version |
type | application 또는 library |
예를 들어 chart template만 변경되어도 version은 올라갈 수 있다. 반면 application image가 그대로라면 appVersion은 유지될 수 있다.
반대로 application image tag가 바뀌면 appVersion도 함께 올리는 것이 일반적이다.
Template
Template은 고정된 Kubernetes YAML이 아니라, values를 주입받아 최종 manifest로 변환되는 파일이다.
일반 Kubernetes manifest에서는 image가 고정된다.
image: registry.example.com/api:1.0.0
Helm template에서는 값을 변수화할 수 있다.
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
예를 들어 Deployment template은 다음처럼 작성할 수 있다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-api
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ .Release.Name }}-api
template:
metadata:
labels:
app: {{ .Release.Name }}-api
spec:
containers:
- name: api
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
ports:
- containerPort: {{ .Values.service.targetPort }}
이 template은 아직 Kubernetes가 직접 이해할 수 있는 YAML이 아니다. Helm이 values를 주입해 rendering한 뒤 최종 YAML이 된다.
values.yaml이 다음과 같다고 하자.
replicaCount: 3
image:
repository: registry.example.com/api
tag: "1.0.0"
service:
targetPort: 3000
렌더링 결과는 다음처럼 된다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-release-api
spec:
replicas: 3
selector:
matchLabels:
app: my-release-api
template:
metadata:
labels:
app: my-release-api
spec:
containers:
- name: api
image: "registry.example.com/api:1.0.0"
ports:
- containerPort: 3000
즉 Helm template은 Kubernetes YAML을 생성하기 위한 parameterized manifest다.
_helpers.tpl
templates/_helpers.tpl는 반복되는 이름 생성 로직, label, selector 등을 함수처럼 정의할 때 자주 사용한다.
{{- define "myapp.name" -}}
{{ .Chart.Name }}
{{- end }}
{{- define "myapp.fullname" -}}
{{ .Release.Name }}-{{ .Chart.Name }}
{{- end }}
다른 template에서 사용할 수 있다.
metadata:
name: {{ include "myapp.fullname" . }}
이런 helper를 쓰면 release name, chart name, label naming을 일관되게 유지할 수 있다.
Values
Values는 template에 주입되는 설정값이다. 기본값은 chart 내부의 values.yaml에 들어간다.
replicaCount: 2
image:
repository: registry.example.com/api
tag: "1.0.0"
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
targetPort: 3000
ingress:
enabled: false
host: ""
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
환경별 설정은 별도 values 파일로 분리할 수 있다.
chart/
Chart.yaml
values.yaml
values-dev.yaml
values-staging.yaml
values-prod.yaml
templates/
설치할 때 values 파일을 지정한다.
helm install api ./myapp -f values-prod.yaml
특정 값만 command line에서 덮어쓸 수도 있다.
helm install api ./myapp --set image.tag=1.1.0 --set replicaCount=5
다만 --set을 과도하게 사용하면 배포 재현성이 떨어진다. 대부분의 환경 설정은 values 파일에 두고, CI/CD에서 매번 바뀌는 image tag 정도만 --set으로 넘기는 방식이 관리하기 쉽다.
helm upgrade --install api ./chart \
-f values-prod.yaml \
--set image.tag=${GIT_SHA}
Values 설계는 chart의 API가 된다
Chart를 다른 팀이나 다른 사람이 사용하게 되면 values.yaml 구조는 사실상 API가 된다.
처음에 이렇게 만들었다고 하자.
imageTag: "1.0.0"
나중에 구조를 이렇게 바꾸면 기존 사용자에게 breaking change가 될 수 있다.
image:
tag: "1.0.0"
기존 사용자는 다음 명령을 사용하고 있었을 수 있다.
helm upgrade api ./chart --set imageTag=1.1.0
따라서 chart를 배포할 때는 values 구조를 신중하게 설계하고, chart versioning을 제대로 해야 한다.
Release
Release는 chart를 cluster에 설치한 실제 인스턴스다. Chart는 package이고, Release는 그 package를 특정 values로 설치한 결과다.
Chart: nginx
Release #1: frontend-nginx
Release #2: admin-nginx
Release #3: internal-nginx
같은 chart를 여러 번 설치할 수 있다.
helm install frontend ./nginx-chart -f values-frontend.yaml
helm install admin ./nginx-chart -f values-admin.yaml
release 목록은 다음 명령으로 확인한다.
helm list
예시:
NAME NAMESPACE REVISION STATUS CHART APP VERSION
frontend default 1 deployed nginx-1.2.0 1.25.0
admin default 1 deployed nginx-1.2.0 1.25.0
Helm이 단순 YAML rendering 도구와 다른 점은 release history를 관리한다는 것이다.
helm history frontend
release는 upgrade와 rollback의 기준이 된다.
Repository
Repository는 chart를 저장하고 공유하는 장소다. Docker image가 container registry에 저장되는 것처럼, Helm chart는 chart repository나 OCI registry에 저장될 수 있다.
Container image → Container registry
Helm chart → Chart repository / OCI registry
repository를 추가할 수 있다.
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
chart를 검색한다.
helm search repo nginx
chart를 설치한다.
helm install my-nginx bitnami/nginx
이 구조를 사용하면 chart를 직접 작성하지 않아도, 이미 공개되어 있거나 조직 내부에서 관리하는 chart를 재사용할 수 있다.
Helm의 동작 흐름
Helm의 기본 동작은 다음 순서로 이해할 수 있다.
1. Chart 선택
2. Values 결정
3. Template rendering
4. Kubernetes manifest 생성
5. Kubernetes API Server에 적용
6. Release record 저장
helm install
helm install api ./myapp -f values-prod.yaml
이 명령을 실행하면 Helm은 대략 다음을 수행한다.
Chart 디렉터리 읽기
values.yaml 읽기
values-prod.yaml로 값 override
templates/ 내부 파일 rendering
최종 Kubernetes YAML 생성
Kubernetes API Server에 resource 생성 요청
release 정보를 cluster에 저장
설치 전 렌더링 결과를 확인하려면 dry run을 사용한다.
helm install api ./myapp -f values-prod.yaml --dry-run --debug
이 명령은 실제 cluster에 반영하기 전에 어떤 manifest가 생성되는지 확인할 때 유용하다.
helm template
helm template은 chart를 rendering하지만 cluster에는 적용하지 않는다.
helm template api ./myapp -f values-prod.yaml
이 명령은 CI에서 특히 유용하다.
Chart template 문법 확인
최종 Kubernetes YAML 확인
Git diff에서 manifest 변화 확인
정책 검사 도구와 연결
예를 들어 다음 흐름을 만들 수 있다.
helm template
↓
kubeconform / kubeval
↓
conftest / OPA
↓
kubectl apply 또는 GitOps sync
helm upgrade
이미 설치된 release를 변경하려면 helm upgrade를 사용한다.
helm upgrade api ./myapp -f values-prod.yaml --set image.tag=1.1.0
설치와 업그레이드를 하나의 idempotent한 명령으로 처리할 수도 있다.
helm upgrade --install api ./myapp -f values-prod.yaml
이 방식은 CI/CD에서 자주 사용된다.
release가 없으면 install
release가 있으면 upgrade
helm rollback
새 버전 배포 후 문제가 생기면 이전 revision으로 rollback할 수 있다.
helm history api
예시:
REVISION UPDATED STATUS CHART APP VERSION
1 2026-06-01 10:00:00 superseded myapp-0.1.0 1.0.0
2 2026-06-01 11:00:00 deployed myapp-0.1.1 1.1.0
이전 revision으로 되돌린다.
helm rollback api 1
주의할 점은 Helm rollback이 application data schema까지 안전하게 되돌리는 것은 아니라는 점이다.
app v1.1.0 배포
↓
DB schema 변경
↓
app만 v1.0.0으로 rollback
↓
구버전 app이 변경된 schema와 호환되지 않을 수 있음
따라서 Helm rollback은 Kubernetes resource 상태를 되돌리는 데 유용하지만, database migration까지 자동으로 안전하게 되돌린다고 보면 안 된다.
helm uninstall
release를 제거할 때는 helm uninstall을 사용한다.
helm uninstall api
주의할 점은 resource 종류에 따라 삭제 결과가 다를 수 있다는 것이다.
Deployment, Service, ConfigMap 등은 삭제됨
PVC는 reclaim policy나 chart 설계에 따라 남을 수 있음
CRD는 Helm이 일반 resource처럼 upgrade/delete하지 않는 경우가 있음
외부 cloud resource는 별도 정리 필요할 수 있음
Helm이 Kubernetes YAML을 줄이는 방식
환경별 replica 수가 다르다고 하자.
dev: replicas = 1
staging: replicas = 2
prod: replicas = 5
Helm 없이 관리하면 환경별 deployment YAML이 생긴다.
deployment-dev.yaml
deployment-staging.yaml
deployment-prod.yaml
Helm에서는 template 하나와 values 파일 세 개로 나눌 수 있다.
templates/deployment.yaml
values-dev.yaml
values-staging.yaml
values-prod.yaml
templates/deployment.yaml:
spec:
replicas: {{ .Values.replicaCount }}
values-dev.yaml:
replicaCount: 1
values-staging.yaml:
replicaCount: 2
values-prod.yaml:
replicaCount: 5
설치 또는 업그레이드는 다음처럼 수행한다.
helm upgrade --install api ./myapp -f values-prod.yaml
이렇게 하면 “구조는 같고 값만 다른 manifest”를 깔끔하게 관리할 수 있다.
Helm과 Kubernetes object의 관계
Helm을 쓰면 Kubernetes object가 사라지는 것이 아니다. Helm은 결국 Kubernetes manifest를 생성한다.
예를 들어 chart에 Deployment, Service, Ingress template이 있으면 Helm은 최종적으로 다음 resource를 cluster에 만든다.
Deployment/api
Service/api
Ingress/api
ConfigMap/api-config
Secret/api-secret
Helm release는 이 resource들을 하나의 application 단위로 묶어 관리한다.
Release: api
├─ Deployment/api
├─ Service/api
├─ Ingress/api
├─ ConfigMap/api-config
└─ Secret/api-secret
따라서 문제를 디버깅할 때는 Helm과 Kubernetes 양쪽을 모두 봐야 한다.
Helm 관점에서 확인할 명령:
helm list
helm status api
helm history api
helm get values api
helm get manifest api
Kubernetes 관점에서 확인할 명령:
kubectl get pods
kubectl get deploy
kubectl get svc
kubectl describe pod <pod-name>
kubectl logs <pod-name>
역할을 구분하면 다음과 같다.
| 도구 | 주로 보는 것 |
|---|---|
helm | chart, values, release, revision, rendered manifest |
kubectl | 실제 Kubernetes resource 상태, Pod 상태, logs, events |
Helm의 upgrade와 Kubernetes rollout의 차이
자칫 실수하기 쉬운 부분이 있다.
helm upgrade와 Kubernetes rolling update는 같은 것인가?
정확히는 다르다.
helm upgrade는 release의 chart/values를 바꾸고, rendering된 manifest를 Kubernetes API에 적용하는 작업이다.
Kubernetes rolling update는 Deployment controller가 새 Pod와 old Pod를 교체하는 작업이다.
helm upgrade
↓
새 Deployment manifest 적용
↓
Deployment spec.template 변경
↓
Kubernetes Deployment controller가 rollout 수행
예를 들어 image tag를 바꾼다.
helm upgrade api ./myapp --set image.tag=1.1.0
그 후 rollout 확인은 Kubernetes 명령으로 봐야 한다.
kubectl rollout status deployment/api
Helm 상태도 함께 확인한다.
helm status api
운영에서는 다음 네 가지를 함께 확인해야 한다.
Helm release status = deployed
Kubernetes rollout = 성공
Pod readiness = 통과
Application metric = 정상
helm upgrade가 성공했다고 해서 애플리케이션이 정상 동작한다는 뜻은 아니다. manifest 적용은 성공했지만 Pod가 CrashLoopBackOff일 수 있다.
Helm과 CI/CD
Helm은 CI/CD pipeline에서 자주 사용된다. 일반적인 흐름은 다음과 같다.
1. Source code commit
2. CI test
3. Container image build
4. Image push to registry
5. Helm values 또는 chart version 업데이트
6. helm upgrade --install
7. rollout 확인
8. 문제 시 rollback
예시 pipeline 명령:
docker build -t registry.example.com/api:${GIT_SHA} .
docker push registry.example.com/api:${GIT_SHA}
helm upgrade --install api ./chart \
-f values-prod.yaml \
--set image.tag=${GIT_SHA} \
--namespace production
여기서 중요한 것은 image tag와 release revision이 연결된다는 점이다.
Git commit → image tag → Helm release revision → Kubernetes deployment
이 연결이 명확하면 장애 발생 시 추적이 쉬워진다.
helm history api
helm get values api --revision 3
helm get manifest api --revision 3
운영자는 언제 어떤 chart와 values로 배포되었는지 확인할 수 있다.
Secret 관리 주의점
values 파일에 secret을 평문으로 두면 안 된다.
피해야 할 예시는 다음과 같다.
database:
password: my-real-password
Git에 올라가는 values 파일에는 secret을 직접 넣지 않는 것이 좋다. 대안은 다음과 같다.
Kubernetes Secret을 별도로 관리
External Secrets Operator 사용
Sealed Secrets 사용
SOPS로 암호화
CI/CD secret store에서 주입
Helm chart에서는 secret 값 자체가 아니라 secret name만 받게 만들 수 있다.
database:
existingSecret: api-db-secret
template에서는 해당 secret을 참조한다.
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Values.database.existingSecret }}
key: password
이 방식은 chart와 values에서 민감한 값을 분리하는 데 도움이 된다.
Helm release lifecycle
Helm은 application lifecycle을 release 단위로 관리한다.
install
↓
upgrade
↓
rollback
↓
uninstall
| 작업 | 명령 |
|---|---|
| 설치 | helm install <release> <chart> |
| 설치 또는 업그레이드 | helm upgrade --install <release> <chart> |
| 업그레이드 | helm upgrade <release> <chart> |
| 상태 확인 | helm status <release> |
| 목록 확인 | helm list |
| history 확인 | helm history <release> |
| rollback | helm rollback <release> <revision> |
| 제거 | helm uninstall <release> |
| 렌더링 확인 | helm template <release> <chart> |
| dry run | helm install <release> <chart> --dry-run --debug |
Helm 2와 Helm 3의 구분
현재 Helm을 사용할 때는 Helm 3 기준으로 이해하는 것이 일반적이다. Helm 2와 Helm 3의 중요한 차이는 Tiller 제거다.
Helm 2에는 cluster 안에 Tiller라는 server-side component가 있었다.
Helm 2
helm client
↓
Tiller in cluster
↓
Kubernetes API Server
Helm 3에서는 Tiller가 제거되었다.
Helm 3+
helm client
↓
Kubernetes API Server
이 차이는 보안 모델에서 중요하다. Helm 3에서는 사용자의 kubeconfig와 Kubernetes RBAC 권한을 기준으로 Helm 작업이 수행된다. 따라서 Helm을 실행하는 사용자가 어떤 namespace와 resource에 대해 어떤 권한을 갖는지 명확하게 설계해야 한다.
Helm과 Kustomize의 차이
Kubernetes manifest를 재사용하는 도구로 Helm만 있는 것은 아니다. 대표적으로 Kustomize도 있다.
간단히 구분하면 다음과 같다.
| 구분 | Helm | Kustomize |
|---|---|---|
| 핵심 방식 | template + values | base + overlay patch |
| package 개념 | Chart | 명시적 package manager보다는 customization |
| release 관리 | 있음 | 없음 |
| rollback/history | Helm release revision 기반 | 별도 Git/CI/CD로 관리 |
| 외부 chart 설치 | 강함 | 주 목적은 아님 |
| template 언어 | Go template | YAML patch 중심 |
| 복잡한 조건문 | 가능 | 제한적 |
| 단순 환경 차이 관리 | 가능 | 매우 적합 |
| app lifecycle 관리 | 강함 | kubectl apply 흐름에 가까움 |
Helm은 조건문을 사용할 수 있다.
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "myapp.fullname" . }}
{{- end }}
Kustomize는 기존 YAML에 patch를 덧씌우는 방식에 가깝다.
base/
deployment.yaml
service.yaml
overlays/
dev/
kustomization.yaml
prod/
kustomization.yaml
선택 기준은 다음처럼 잡을 수 있다.
| 상황 | 적합한 도구 |
|---|---|
| 외부 애플리케이션 설치, release 관리, packaging | Helm |
| 내부 manifest의 환경별 patch, GitOps 단순화 | Kustomize |
| Helm chart 결과를 검토한 뒤 후처리 | Helm template + Kustomize |
| Argo CD/Flux로 chart 직접 배포 | Helm + GitOps |
Helm이 편하지만 위험해지는 지점
Template이 너무 복잡해지는 문제
처음에는 간단한 변수 치환으로 시작한다.
replicas: {{ .Values.replicaCount }}
하지만 시간이 지나면 조건문과 반복문이 많아질 수 있다.
{{- if .Values.ingress.enabled }}
{{- range .Values.ingress.hosts }}
...
{{- end }}
{{- end }}
chart가 application 배포 정의가 아니라 작은 programming language처럼 변하면 유지보수가 어려워진다.
주의할 점은 다음과 같다.
조건문이 지나치게 많으면 chart 이해가 어려워진다.
values schema가 불명확하면 사용자가 잘못된 값을 넣기 쉽다.
template rendering 결과를 보지 않으면 실제 manifest를 예측하기 어렵다.
검증 명령:
helm lint ./chart
helm template api ./chart -f values-prod.yaml
Helm release와 수동 kubectl edit 충돌
Helm으로 관리되는 resource를 사람이 직접 수정하면 drift가 생길 수 있다.
kubectl edit deployment api
이후 helm upgrade를 실행하면 수동 변경이 덮어써질 수 있다.
운영 원칙은 명확해야 한다.
Helm이 관리하는 resource는 Helm values/chart로 변경한다.
긴급 수동 변경이 있었다면 chart/values에 반드시 반영한다.
helm get manifest와 kubectl get yaml 차이를 비교한다.
확인 명령:
helm get manifest api
kubectl get deployment api -o yaml
CRD 관리
일부 Helm chart는 CRD(CustomResourceDefinition)를 포함한다. 예를 들어 operator 계열 chart에서는 CRD가 중요한 역할을 한다.
CRD는 일반 resource와 lifecycle이 다르게 다뤄질 수 있다. CRD를 잘못 삭제하면 그 CRD를 기반으로 생성된 custom resource에도 영향이 생길 수 있다.
운영에서는 다음을 확인해야 한다.
chart가 CRD를 포함하는가?
CRD upgrade는 chart upgrade로 자동 처리되는가?
CRD 삭제 시 custom resource가 어떻게 되는가?
GitOps 도구가 CRD lifecycle을 어떻게 다루는가?
chart를 설치하기 전에 chart 문서와 CRD 포함 여부를 확인하는 습관이 필요하다.
설치 전 검증 포인트
Helm chart를 적용하기 전에 다음 명령으로 기본 검증을 수행한다.
helm lint ./chart
helm template api ./chart -f values-prod.yaml
helm install api ./chart -f values-prod.yaml --dry-run --debug
확인할 항목은 다음과 같다.
template 문법 오류가 없는가?
렌더링된 YAML이 예상과 같은가?
Secret 값이 평문으로 노출되지 않는가?
resource requests/limits가 설정되어 있는가?
Service selector와 Pod label이 일치하는가?
Ingress host가 올바른가?
특히 Service.selector와 Pod label이 맞지 않으면 Service endpoint가 비어 traffic이 전달되지 않는다.
kubectl get endpoints api
endpoint가 비어 있다면 label mismatch를 우선 의심해야 한다.
설치 후 검증 포인트
설치 또는 upgrade 후에는 Helm 상태와 Kubernetes 상태를 함께 확인해야 한다.
helm status api
helm list
kubectl get all
kubectl get pods
kubectl get svc
kubectl describe pod <pod-name>
kubectl logs <pod-name>
확인할 항목은 다음과 같다.
Helm release status가 deployed인가?
Deployment rollout이 완료되었는가?
Pod readiness가 통과했는가?
Service endpoint가 존재하는가?
Ingress가 expected host로 연결되는가?
ConfigMap/Secret이 올바르게 주입되었는가?
helm status가 정상이어도 실제 Pod가 CrashLoopBackOff일 수 있다. 따라서 release status, rollout, Pod 상태, logs, metrics를 함께 확인해야 한다.
간단한 Helm chart 작성 예시
간단한 API chart를 만든다고 하자.
helm create api
Helm은 기본 chart 구조를 생성한다.
api/
├── Chart.yaml
├── values.yaml
├── templates/
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── serviceaccount.yaml
│ ├── hpa.yaml
│ └── tests/
└── charts/
직접 최소 구조를 만든다면 다음 정도로 시작할 수 있다.
api/
├── Chart.yaml
├── values.yaml
└── templates/
├── deployment.yaml
└── service.yaml
Chart.yaml:
apiVersion: v2
name: api
description: API service chart
type: application
version: 0.1.0
appVersion: "1.0.0"
values.yaml:
replicaCount: 2
image:
repository: registry.example.com/api
tag: "1.0.0"
service:
port: 80
targetPort: 3000
templates/deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-api
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ .Release.Name }}-api
template:
metadata:
labels:
app: {{ .Release.Name }}-api
spec:
containers:
- name: api
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
ports:
- containerPort: {{ .Values.service.targetPort }}
templates/service.yaml:
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-api
spec:
selector:
app: {{ .Release.Name }}-api
ports:
- port: {{ .Values.service.port }}
targetPort: {{ .Values.service.targetPort }}
렌더링:
helm template myrelease ./api
설치:
helm install myrelease ./api
업그레이드:
helm upgrade myrelease ./api --set image.tag=1.1.0
삭제:
helm uninstall myrelease
운영에서 권장하는 Helm 사용 흐름
운영에서는 대략 다음 흐름이 안전하다.
1. chart와 values를 Git에 저장한다.
2. secret은 평문으로 저장하지 않는다.
3. CI에서 helm lint를 수행한다.
4. CI에서 helm template 결과를 생성한다.
5. 생성된 manifest에 대해 policy/schema 검증을 수행한다.
6. image tag는 immutable하게 관리한다.
7. 배포는 helm upgrade --install로 수행한다.
8. rollout과 readiness를 확인한다.
9. 장애 발생 시 helm history와 rollback 경로를 확인한다.
10. 수동 kubectl 변경은 최소화하고 chart/values로 되돌려 반영한다.
예시:
helm lint ./chart
helm template api ./chart \
-f values-prod.yaml \
--set image.tag=${GIT_SHA}
helm upgrade --install api ./chart \
-f values-prod.yaml \
--set image.tag=${GIT_SHA} \
--namespace production \
--create-namespace
kubectl rollout status deployment/api -n production
Helm이 해결하는 문제와 해결하지 않는 문제
Helm이 잘 해결하는 문제
| 문제 | Helm의 역할 |
|---|---|
| 반복 YAML | template과 values로 중복 감소 |
| 애플리케이션 묶음 설치 | chart 단위로 여러 Kubernetes resource 배포 |
| 환경별 설정 | values 파일로 dev/staging/prod 차이 관리 |
| version 관리 | chart version과 release revision 관리 |
| rollback | release revision 기준 rollback |
| 공유 | chart repository로 package 배포 |
| CI/CD 통합 | helm upgrade --install 기반 배포 자동화 |
Helm이 직접 해결하지 않는 문제
| 문제 | 설명 |
|---|---|
| 잘못된 Kubernetes 설계 | chart가 잘못된 manifest를 만들면 그대로 잘못 배포된다. |
| 애플리케이션 bug | Helm은 app code를 고치지 않는다. |
| DB migration 안전성 | rollback 시 schema 호환성은 별도 설계가 필요하다. |
| Secret 보안 | values에 평문 secret을 넣으면 Helm이 보호해주지 않는다. |
| Observability | Helm으로 설치할 수는 있지만, 운영 관찰성 설계는 별도 문제다. |
| RBAC 정책 | Helm 실행자의 Kubernetes 권한 설계가 필요하다. |
| Drift 방지 | 수동 변경을 완전히 막지는 못한다. GitOps 정책과 함께 관리해야 한다. |
자칫 실수하기 쉬운 부분
Helm chart를 container image로 착각하기
Chart는 application binary가 아니다.
Container image
→ app code와 runtime을 담는 실행 패키지
Helm chart
→ 그 image를 Kubernetes에 어떻게 배포할지 정의하는 패키지
Helm을 쓰면 Kubernetes를 몰라도 된다고 생각하기
Helm은 Kubernetes manifest를 생성한다. 결국 문제가 생기면 Deployment, Service, Pod, Ingress, ConfigMap, Secret을 이해해야 한다.
Helm은 Kubernetes를 숨기는 도구가 아니라,
Kubernetes manifest 관리를 구조화하는 도구다.
helm install 성공을 애플리케이션 정상으로 착각하기
helm install이 성공했다는 것은 Kubernetes API에 resource 생성 요청이 성공했다는 의미에 가깝다. 애플리케이션 정상 여부는 따로 확인해야 한다.
kubectl get pods
kubectl rollout status deployment/api
kubectl logs <pod-name>
kubectl get endpoints api
values 파일을 정리하지 않고 계속 덧붙이기
오래된 values가 남아 있으면 chart upgrade 시 문제가 생길 수 있다.
deprecated value 사용
더 이상 쓰지 않는 field 잔존
chart version과 values schema 불일치
환경별 values drift
upgrade 전에는 현재 chart의 values 구조와 실제 배포 values를 비교해야 한다.
helm show values <chart>
helm get values api
요약
Helm은 Kubernetes application을 구성하는 여러 manifest를 Chart로 묶고, 환경별 차이는 Values로 분리하며, 실제 cluster에 설치된 인스턴스를 Release로 관리한다.
핵심 구조는 다음과 같다.
Kubernetes YAML 반복
↓
Helm template으로 일반화
↓
values로 환경별 차이 분리
↓
chart로 packaging
↓
release로 lifecycle 관리
Helm을 잘 사용하려면 단순히 helm install 명령만 알면 부족하다. 다음 구분을 명확히 이해해야 한다.
Chart는 package다.
Values는 configuration API다.
Template은 manifest generator다.
Release는 cluster에 설치된 chart instance다.
Upgrade는 manifest 변경을 Kubernetes에 적용하는 작업이다.
Rollback은 Helm revision 기준으로 resource 상태를 되돌리는 작업이다.
실제 Pod 교체와 self-healing은 Kubernetes controller가 수행한다.
결국 Helm은 Kubernetes의 복잡도를 없애는 도구라기보다, 반복되는 manifest와 application lifecycle을 package와 release라는 단위로 구조화하는 도구다. 따라서 Helm을 사용할수록 chart 구조, values 설계, secret 관리, rollout 검증, RBAC, drift 관리까지 함께 신경 써야 한다.