팀마다 비슷한 Deployment YAML을 복사해 쓰다가 포트, 라벨, probe 설정이 제각각이 되었습니다. 공통 패턴을 Chart로 만들면 환경별 값만 바꾸고 검증된 템플릿을 재사용할 수 있습니다. Helm Chart 작성은 플랫폼팀이 배포 표준을 코드로 제공하는 방식입니다.
Helm Chart 직접 작성하기
팀에 새 마이크로서비스가 추가될 때마다 누군가 밤새 YAML을 복붙하며 수정하고 있습니다. 환경마다 이미지 태그가 달라야 하고, 스테이징과 프로덕션의 레플리카 수가 다르고, 시크릿 이름도 제각각입니다. 처음 한 번만 고생하면 그 이후에는 helm install my-service ./charts/my-service -f values-prod.yaml 한 줄로 끝나는 구조를 만들 수 있습니다. Helm Chart를 직접 작성하면 인프라 변경이 코드 리뷰를 거치고, 히스토리가 남고, 롤백이 가능해집니다. 이 모듈에서는 빈 디렉토리에서 시작해 프로덕션에서 실제로 사용하는 수준의 Chart를 직접 만들어봅니다.
직접 작성한 Helm Chart로 Kubernetes 배포를 코드로 관리하는 방법을 익힙니다. Go 템플릿 함수부터 서브차트 의존성 관리까지, 팀 전체가 사용하는 공용 Chart를 만들 수 있게 됩니다.
- 1Helm Chart 디렉토리 구조 — Chart.yaml, values.yaml, templates/ 역할
- 2Go 템플릿 기본 문법 — 변수 참조, 조건문, 반복문
- 3핵심 함수 — include, toYaml, required, default, nindent
- 4_helpers.tpl로 named template 재사용 패턴 구현
- 5서브차트(dependency)로 외부 Chart 의존성 관리
- 6helm template / helm lint으로 로컬 디버깅
Helm v3와 kubectl이 설치되어 있고 클러스터에 연결되어 있어야 합니다. minikube나 kind 등 로컬 클러스터로도 충분합니다.
helm version --shortkubectl cluster-infokubectl create namespace helm-devmkdir -p ~/helm-workshop && cd ~/helm-workshophelm uninstall my-webapp -n helm-dev && kubectl delete namespace helm-dev
Helm Chart 디렉토리 구조
팀마다 비슷한 Deployment YAML을 각자 관리하다 보면 probe 설정이 누락되거나 리소스 제한이 제각각이 되는 문제가 생깁니다. 직접 Chart를 작성하면 이 공통 패턴을 한 곳에서 관리하고 환경별 값만 values.yaml로 분리할 수 있습니다. helm create가 자동 생성하는 구조를 이해하지 않고 쓰면 불필요한 파일이 많아 혼란스럽습니다. Chart.yaml, values.yaml, templates/ 세 요소의 역할 분담을 먼저 파악하면 어떤 로직이 어디에 있어야 하는지 판단이 생겨 유지보수 가능한 Chart를 만들 수 있습니다.

Chart 기본 구조
my-webapp/
├── Chart.yaml # Chart 메타데이터 (필수)
├── values.yaml # 기본값 정의 (필수)
├── charts/ # 서브차트(dependency) 저장 디렉토리
└── templates/ # Kubernetes manifest 템플릿
├── _helpers.tpl # 재사용 named template (렌더링 대상 아님)
├── deployment.yaml
├── service.yaml
├── ingress.yaml
├── configmap.yaml
└── NOTES.txt # helm install 후 출력될 안내 메시지
Chart.yaml — Chart 메타데이터
# Chart.yaml
apiVersion: v2 # Helm v3는 반드시 v2
name: my-webapp
description: A production-ready web application chart
type: application # application | library
version: 0.1.0 # Chart 버전 (SemVer)
appVersion: "1.0.0" # 실제 앱 버전 (참조용, 이미지 태그와는 별도)
# 외부 Chart 의존성 (서브차트)
dependencies:
- name: redis
version: "18.x.x"
repository: "https://charts.bitnami.com/bitnami"
condition: redis.enabled # values.yaml의 값으로 선택적 설치
values.yaml — 기본값 정의
# values.yaml
replicaCount: 1
image:
repository: nginx
tag: "1.25"
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
targetPort: 8080
ingress:
enabled: false
host: ""
annotations: {}
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
env: []
# - name: DATABASE_URL
# valueFrom:
# secretKeyRef:
# name: db-secret
# key: url
redis:
enabled: false # 서브차트 활성화 여부
auth:
enabled: false
templates/deployment.yaml — 핵심 템플릿
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "my-webapp.fullname" . }}
labels:
{{- include "my-webapp.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "my-webapp.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "my-webapp.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: {{ .Values.service.targetPort }}
{{- if .Values.env }}
env:
{{- toYaml .Values.env | nindent 12 }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
Go 템플릿 핵심 함수 — include, toYaml, required, nindent
Helm 템플릿을 처음 작성할 때 가장 많이 만나는 오류가 들여쓰기 불일치나 "wrong type for value" 입니다. YAML은 들여쓰기가 문법의 일부이기 때문에, 함수 호출 결과를 올바르게 삽입하지 않으면 유효하지 않은 YAML이 만들어집니다. nindent로 들여쓰기를 맞추고, toYaml로 구조체를 직렬화하며, required로 필수 값 누락을 배포 전에 잡는 패턴은 실무 Chart에서 반복적으로 나타납니다. 이 4가지 함수의 동작을 이해하면 다른 사람의 Chart를 읽고 디버깅하는 속도도 빨라집니다.

include — named template 호출 (pipeline 지원)
# template 함수는 pipeline에 연결할 수 없어서 indent가 안 됩니다.
# include를 사용하면 결과를 pipeline으로 넘길 수 있습니다.
# 잘못된 방법 (indent 불가)
{{template "my-webapp.labels" .}}
# 올바른 방법 (nindent로 들여쓰기 적용)
{{- include "my-webapp.labels" . | nindent 4 }}
# 예: 여러 manifest에서 동일한 label 블록 재사용
metadata:
labels:
{{- include "my-webapp.labels" . | nindent 4 }}
spec:
selector:
matchLabels:
{{- include "my-webapp.selectorLabels" . | nindent 6 }}
toYaml — 값 객체를 YAML로 직렬화
# values.yaml에 정의한 구조체를 그대로 YAML로 출력
resources:
{{- toYaml .Values.resources | nindent 2 }}
# 결과:
# resources:
# requests:
# cpu: 100m
# memory: 128Mi
# limits:
# cpu: 500m
# memory: 512Mi
# env 배열 처리 (아이템이 있을 때만 출력)
{{- if .Values.env }}
env:
{{- toYaml .Values.env | nindent 2 }}
{{- end }}
# annotations 맵 처리
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
required — 필수 값 검증
# 값이 비어 있으면 오류 메시지와 함께 렌더링 중단
image:
repository: {{ required "image.repository는 필수 값입니다" .Values.image.repository }}
tag: {{ required "image.tag는 필수 값입니다" .Values.image.tag | quote }}
# Ingress host 검증
{{- if .Values.ingress.enabled }}
{{- $host := required "ingress.enabled가 true이면 ingress.host를 지정해야 합니다" .Values.ingress.host }}
rules:
- host: {{ $host }}
{{- end }}
default — 기본값 설정
# .Values에 값이 없거나 비어 있을 때 기본값 사용
replicas: {{ .Values.replicaCount | default 1 }}
pullPolicy: {{ .Values.image.pullPolicy | default "IfNotPresent" }}
# 조건부 기본값 (string 빈 값 처리)
{{- $name := .Values.nameOverride | default .Chart.Name }}
name: {{ $name | trunc 63 | trimSuffix "-" }}
nindent vs indent
# indent: 지정한 수만큼 공백 추가 (앞에 줄바꿈 없음)
# nindent: newline + indent (앞에 줄바꿈 포함) ← 주로 이것을 사용
# indent 사용 시 (줄바꿈을 직접 관리해야 함)
labels:
{{ include "my-webapp.labels" . | indent 4 }}
# nindent 사용 시 (더 깔끔함, - 로 앞 공백 제거)
labels:
{{- include "my-webapp.labels" . | nindent 2 }}
_helpers.tpl — named template 재사용 패턴
여러 template 파일에서 동일한 label 블록을 반복 작성하면 유지보수가 어렵습니다. _helpers.tpl에 named template을 정의하고 include로 불러 쓰면 한 곳만 수정해도 모든 manifest에 반영됩니다.
_helpers.tpl 전체 예시
{{/*
Expand the name of the chart.
*/}}
{{- define "my-webapp.name" -}}
{{- .Values.nameOverride | default .Chart.Name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
Release 이름이 Chart 이름을 포함하면 중복 제거
*/}}
{{- define "my-webapp.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := .Values.nameOverride | default .Chart.Name }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
공통 label — 모든 리소스에 붙는 표준 label
*/}}
{{- define "my-webapp.labels" -}}
helm.sh/chart: {{ include "my-webapp.chart" . }}
{{ include "my-webapp.selectorLabels" . }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
{{- end }}
{{/*
Selector label — Deployment의 selector.matchLabels에 사용
(한 번 배포 후 변경하면 안 됨!)
*/}}
{{- define "my-webapp.selectorLabels" -}}
app.kubernetes.io/name: {{ include "my-webapp.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Chart 이름+버전 문자열
*/}}
{{- define "my-webapp.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
named template에서 컨텍스트 전달
# . (현재 컨텍스트 전체)를 전달하는 것이 일반적
{{- include "my-webapp.labels" . | nindent 4 }}
# 특정 값만 전달할 때 (dict 사용)
{{- include "my-webapp.serviceAccountName" (dict "Values" .Values "Release" .Release) }}
# range 블록 내부에서 루트 컨텍스트 전달 ($ 사용)
{{- range .Values.ingress.hosts }}
- host: {{ .host }}
paths:
{{- range .paths }}
- path: {{ .path }}
# range 블록 내부에서 .Values는 현재 스코프(path 객체)를 가리킴
# $.Values로 루트 컨텍스트의 Values에 접근
pathType: {{ $.Values.ingress.pathType | default "Prefix" }}
{{- end }}
{{- end }}
서브차트(Dependency)로 외부 Chart 관리
my-webapp이 Redis를 필요로 한다면, Redis Chart를 직접 관리하는 대신 Helm의 dependency 메커니즘으로 가져올 수 있습니다. 서브차트는 선택적으로 활성화/비활성화할 수 있어 로컬 개발(Redis 불필요)과 프로덕션(Redis 필요) 환경을 동일한 Chart로 처리할 수 있습니다.
서브차트 설정 흐름
# 1단계: Chart.yaml에 dependency 추가 (이미 앞에서 작성함)
# dependencies:
# - name: redis
# version: "18.x.x"
# repository: "https://charts.bitnami.com/bitnami"
# condition: redis.enabled
# 2단계: repository 추가 및 dependency 다운로드
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm dependency update ./my-webapp
# 결과: charts/ 디렉토리에 redis-18.x.x.tgz 생성
ls ./my-webapp/charts/
# redis-18.6.1.tgz
# 3단계: 서브차트 values 오버라이드 (values.yaml에서)
# 서브차트 이름(redis)을 키로 사용하여 values 전달
서브차트 values 오버라이드
# values.yaml — 서브차트 설정은 서브차트 이름 아래에 작성
redis:
enabled: true # condition 값 (이 키로 서브차트 활성화 제어)
auth:
enabled: true
password: "my-secret-pw" # 프로덕션에서는 --set으로 전달
master:
persistence:
size: 5Gi
replica:
replicaCount: 1
서브차트 내부 Service 이름 참조
# templates/deployment.yaml — Redis 서비스에 연결하는 환경변수
env:
{{- if .Values.redis.enabled }}
- name: REDIS_HOST
# 서브차트 Service 이름은 "<release-name>-redis-master" 형식
value: "{{ .Release.Name }}-redis-master"
- name: REDIS_PORT
value: "6379"
{{- end }}
Chart 설치 및 확인
# Redis 포함하여 설치
helm install my-webapp ./my-webapp \
--namespace helm-dev \
--set redis.enabled=true \
--set redis.auth.password=secret123
# 렌더링 결과 확인 (클러스터 불필요)
helm template my-webapp ./my-webapp \
--set redis.enabled=true \
--namespace helm-dev | less
# Redis 없이 설치 (로컬 개발)
helm install my-webapp-dev ./my-webapp \
--namespace helm-dev \
--set redis.enabled=false
실습 — Helm Chart 처음부터 만들기
1단계: Chart 골격 생성
mkdir -p ~/helm-workshop/my-webapp/{templates,charts}
cd ~/helm-workshop/my-webapp
# Chart.yaml 작성
cat > Chart.yaml << 'EOF'
apiVersion: v2
name: my-webapp
description: Production-ready web application Helm Chart
type: application
version: 0.1.0
appVersion: "1.0.0"
EOF
2단계: values.yaml 작성
cat > values.yaml << 'EOF'
replicaCount: 2
image:
repository: nginx
tag: "1.25-alpine"
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
targetPort: 80
ingress:
enabled: false
host: ""
resources:
requests:
cpu: 100m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
env: []
EOF
3단계: _helpers.tpl 작성
cat > templates/_helpers.tpl << 'EOF'
{{- define "my-webapp.name" -}}
{{- .Values.nameOverride | default .Chart.Name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- define "my-webapp.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := .Values.nameOverride | default .Chart.Name }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{- define "my-webapp.labels" -}}
helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version }}
{{ include "my-webapp.selectorLabels" . }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{- define "my-webapp.selectorLabels" -}}
app.kubernetes.io/name: {{ include "my-webapp.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
EOF
4단계: Deployment, Service 템플릿 작성
cat > templates/deployment.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "my-webapp.fullname" . }}
labels:
{{- include "my-webapp.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "my-webapp.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "my-webapp.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ required "image.tag는 필수입니다" .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.targetPort }}
{{- if .Values.env }}
env:
{{- toYaml .Values.env | nindent 12 }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
EOF
cat > templates/service.yaml << 'EOF'
apiVersion: v1
kind: Service
metadata:
name: {{ include "my-webapp.fullname" . }}
labels:
{{- include "my-webapp.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: {{ .Values.service.targetPort }}
protocol: TCP
selector:
{{- include "my-webapp.selectorLabels" . | nindent 4 }}
EOF
5단계: lint와 template으로 검증
# Chart 문법 검증
helm lint ./my-webapp
# ==> Linting ./my-webapp
# [INFO] Chart.yaml: icon is recommended
# 1 chart(s) linted, 0 chart(s) failed
# 로컬 렌더링 (클러스터 불필요)
helm template test-release ./my-webapp --namespace helm-dev
# 예상 출력:
# ---
# # Source: my-webapp/templates/service.yaml
# apiVersion: v1
# kind: Service
# metadata:
# name: test-release-my-webapp
# labels:
# helm.sh/chart: my-webapp-0.1.0
# app.kubernetes.io/name: my-webapp
# app.kubernetes.io/instance: test-release
# app.kubernetes.io/managed-by: Helm
# ...
# values 오버라이드 확인
helm template test-release ./my-webapp \
--set replicaCount=3 \
--set image.repository=myapp \
--set image.tag=v2.0.0 \
--namespace helm-dev
6단계: 클러스터에 설치
# 설치 (--dry-run으로 먼저 확인)
helm install my-webapp ./my-webapp \
--namespace helm-dev \
--dry-run
# 실제 설치
helm install my-webapp ./my-webapp --namespace helm-dev
# 결과 확인
helm list -n helm-dev
# NAME NAMESPACE REVISION STATUS CHART
# my-webapp helm-dev 1 deployed my-webapp-0.1.0
kubectl get all -n helm-dev
# NAME READY STATUS RESTARTS
# pod/my-webapp-my-webapp-7d9f6b8d4c-xk2p9 1/1 Running 0
# pod/my-webapp-my-webapp-7d9f6b8d4c-r5mn2 1/1 Running 0
#
# NAME TYPE CLUSTER-IP PORT(S)
# service/my-webapp-my-webapp ClusterIP 10.96.173.42 80/TCP
# values 변경 후 업그레이드
helm upgrade my-webapp ./my-webapp \
--namespace helm-dev \
--set replicaCount=3
# 롤백
helm rollback my-webapp 1 --namespace helm-dev
- NAME—조회 대상 리소스 이름이 예상한 대상과 일치하는지 확인합니다.
- STATUS/READY—Running, Ready, Available처럼 정상 상태를 나타내는 필드가 있는지 봅니다.
- RESTARTS/EVENTS—재시작 횟수나 Warning 이벤트가 증가하지 않는지 확인합니다.
문제 상황
$ helm template test ./my-webapp
Error: template: my-webapp/templates/deployment.yaml:12:7: \
executing "my-webapp/templates/deployment.yaml" at <include "my-webapp.labels" .>: \
error calling include: template: my-webapp/templates/_helpers.tpl:8:3: \
executing "my-webapp.labels" at <include "my-webapp.selectorLabels" .>: \
wrong type for value; expected string; got template
원인 분석
_helpers.tpl에서 named template을 정의할 때 define 블록 끝에 불필요한 공백이나 줄바꿈이 포함되면 이 오류가 납니다. Helm template은 공백에 매우 민감합니다.
# 잘못된 _helpers.tpl (줄바꿈이 반환값에 포함됨)
{{- define "my-webapp.selectorLabels" }}
app.kubernetes.io/name: {{ include "my-webapp.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{ end }}
# ^ end 앞 줄바꿈, define 뒤 줄바꿈이 문제
# 올바른 _helpers.tpl (- 로 공백 제거)
{{- define "my-webapp.selectorLabels" -}}
app.kubernetes.io/name: {{ include "my-webapp.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
# ^ define과 end 양쪽에 - 붙여 앞뒤 공백 제거
디버깅 방법
# 1단계: 어느 파일의 몇 번째 줄인지 오류 메시지에서 확인
# "my-webapp/templates/deployment.yaml:12" → 12번째 줄
# "my-webapp/templates/_helpers.tpl:8" → _helpers.tpl 8번째 줄
# 2단계: 문제 template을 단독으로 렌더링 시도
helm template test ./my-webapp --show-only templates/deployment.yaml
# 3단계: --debug 플래그로 렌더링 전 전처리 결과 확인
helm template test ./my-webapp --debug 2>&1 | head -50
# 4단계: 공백 문제 확인 — define/end 양쪽에 반드시 - 붙이기
# {{- define "name" -}} ... {{- end }}
# ^ ^ ^
# 앞 공백 제거 뒤 공백 제거 앞 공백 제거
자주 나오는 template 오류 패턴
# 오류 1: undefined template
# Error: template: ...at <include "my-webapp.fullname" .>: function "my-webapp.fullname" not defined
# → _helpers.tpl 파일이 없거나 define 이름이 일치하지 않음
# 오류 2: wrong number of args
# Error: ...wrong number of args for include: want 2 got 1
# → include에 두 번째 인수(컨텍스트) 누락
# 잘못됨: {{ include "my-webapp.labels" }}
# 올바름: {{ include "my-webapp.labels" . }}
# 오류 3: can't evaluate field X in type interface {}
# Error: ...can't evaluate field replicaCount in type interface {}
# → .Values 접근 경로 오타
# values.yaml: replicaCount → template: .Values.replicaCount (대소문자 확인)
배경
스타트업 C사의 DevOps 엔지니어. 현재 10개 마이크로서비스가 각자 deployment.yaml, service.yaml을 별도로 관리하고 있습니다. 환경마다 수작업으로 이미지 태그를 바꾸고, 레플리카 수를 수정하다가 실수가 반복됩니다. Chart를 도입하여 표준화하는 작업을 맡았습니다.
전환 전략
1단계: 가장 단순한 서비스 하나를 Chart로 전환 (파일럿)
2단계: 공통 패턴 추출 → 팀 공용 _helpers.tpl 정립
3단계: 나머지 서비스를 순차적으로 전환
4단계: CI/CD 파이프라인에 helm upgrade 통합
기존 YAML에서 Chart로 변환하는 패턴
# 기존 deployment.yaml의 하드코딩 값을 values.yaml로 추출
# Before:
# replicas: 2
# image: gcr.io/myproject/user-service:abc123def
# After values.yaml:
# replicaCount: 2
# image:
# repository: gcr.io/myproject/user-service
# tag: abc123def
CI/CD 통합 예시 (GitHub Actions)
# .github/workflows/deploy.yml
- name: Deploy to Kubernetes
run: |
helm upgrade --install ${{ env.SERVICE_NAME }} ./charts/${{ env.SERVICE_NAME }} \
--namespace ${{ env.NAMESPACE }} \
--set image.tag=${{ github.sha }} \
--set replicaCount=${{ env.REPLICA_COUNT }} \
--atomic \
--timeout 5m
# --atomic: 실패 시 자동 롤백
# --timeout: 배포 완료 대기 시간
현장에서 배운 교훈
1. selectorLabels는 한 번 배포 후 절대 변경하지 말 것
→ 변경 시 Deployment 삭제 후 재생성 필요 (다운타임 발생)
2. Chart 버전(Chart.yaml의 version)과 앱 버전(appVersion)을 분리할 것
→ Chart 구조 변경은 version 올리기, 앱 코드 변경은 appVersion 올리기
3. 시크릿은 values.yaml에 직접 쓰지 말 것
→ helm install ... --set secret.password=$(cat /tmp/secret)
→ 또는 외부 시크릿 관리 도구(External Secrets Operator) 사용
핵심 요약
| 개념 | 설명 |
|---|---|
| Chart.yaml | Chart 이름, 버전, dependency 정의 |
| values.yaml | 기본값 정의 — 환경별 오버라이드 가능 |
| templates/ | Kubernetes manifest Go 템플릿 파일 |
| _helpers.tpl | named template 정의 — _로 시작하여 렌더링 대상 제외 |
include | named template 호출 + pipeline 지원 |
toYaml | 값 구조체를 YAML 문자열로 직렬화 |
required | 필수 값 없을 때 명확한 오류로 렌더링 중단 |
nindent N | 앞에 줄바꿈 + N칸 들여쓰기 |
helm template | 클러스터 없이 로컬 렌더링 결과 확인 |
helm lint | Chart 문법·구조 검증 |
| dependency | 서브차트 — helm dependency update로 다운로드 |