Kubernetes Helmチャート実践ガイド — テンプレート、Values、依存関係管理
Helmは、Kubernetesアプリケーションをパッケージ化・管理するための事実上の標準ツールです。
この記事では、Helm 3の基礎から、チャート作成、テンプレート設計、本番運用まで、実践的な内容を詳しく解説します。
Helmとは
Helmの役割
Helmがない場合:
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl apply -f ingress.yaml
kubectl apply -f configmap.yaml
kubectl apply -f secret.yaml
# 環境ごとに手動で値を書き換える...
Helmがある場合:
helm install myapp ./mychart -f values-prod.yaml
# 一発でデプロイ、バージョン管理、ロールバックも可能
Helm 3の主な変更点
- Tillerの廃止: Helm 2のサーバーコンポーネントが不要に
- 3-way merge: より安全なアップグレード
- Secrets管理の改善: リリース情報がSecretに保存
- ライブラリチャート: 共通テンプレートの再利用
Helmのインストール
インストール方法
# macOS (Homebrew)
brew install helm
# Linux (スクリプト)
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
# Windows (Chocolatey)
choco install kubernetes-helm
# バージョン確認
helm version
リポジトリの追加
# 公式リポジトリを追加
helm repo add stable https://charts.helm.sh/stable
helm repo add bitnami https://charts.bitnami.com/bitnami
# リポジトリの更新
helm repo update
# リポジトリ一覧
helm repo list
チャートの基本構造
チャートの作成
helm create myapp
生成されるディレクトリ構造:
myapp/
├── Chart.yaml # チャートのメタデータ
├── values.yaml # デフォルト設定値
├── charts/ # 依存チャート
├── templates/ # Kubernetesマニフェストテンプレート
│ ├── NOTES.txt # インストール後のメッセージ
│ ├── _helpers.tpl # テンプレートヘルパー
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── serviceaccount.yaml
│ └── hpa.yaml
└── .helmignore # パッケージ化時の除外ファイル
Chart.yaml の構成
# Chart.yaml
apiVersion: v2
name: myapp
description: A Helm chart for my application
type: application # application or library
version: 0.1.0 # チャートのバージョン
appVersion: "1.0" # アプリケーションのバージョン
maintainers:
- name: Your Name
email: you@example.com
url: https://example.com
keywords:
- web
- nodejs
- microservice
home: https://github.com/example/myapp
sources:
- https://github.com/example/myapp
dependencies:
- name: postgresql
version: 12.1.0
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
values.yaml の設計
基本的な構造
# values.yaml
replicaCount: 2
image:
repository: myapp
pullPolicy: IfNotPresent
tag: "1.0.0"
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
create: true
annotations: {}
name: ""
podAnnotations: {}
podSecurityContext:
runAsNonRoot: true
runAsUser: 1000
securityContext:
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
service:
type: ClusterIP
port: 80
targetPort: 3000
ingress:
enabled: false
className: "nginx"
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
hosts:
- host: myapp.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: myapp-tls
hosts:
- myapp.example.com
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
autoscaling:
enabled: false
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}
env:
- name: NODE_ENV
value: production
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: url
postgresql:
enabled: true
auth:
username: myapp
password: changeme
database: myapp
環境別のvalues
# values-dev.yaml
replicaCount: 1
image:
tag: "latest"
resources:
limits:
cpu: 200m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
ingress:
enabled: true
hosts:
- host: myapp-dev.example.com
paths:
- path: /
pathType: Prefix
postgresql:
enabled: true
auth:
password: dev-password
# values-prod.yaml
replicaCount: 3
image:
tag: "1.0.0"
resources:
limits:
cpu: 1000m
memory: 1Gi
requests:
cpu: 500m
memory: 512Mi
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 20
targetCPUUtilizationPercentage: 70
ingress:
enabled: true
hosts:
- host: myapp.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: myapp-tls
hosts:
- myapp.example.com
postgresql:
enabled: false # 外部のマネージドDBを使用
テンプレート構文
基本的な変数展開
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "myapp.fullname" . }}
labels:
{{- include "myapp.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "myapp.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "myapp.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "myapp.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.targetPort }}
protocol: TCP
env:
{{- toYaml .Values.env | nindent 8 }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
条件分岐
# templates/ingress.yaml
{{- if .Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "myapp.fullname" . }}
labels:
{{- include "myapp.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.className }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
backend:
service:
name: {{ include "myapp.fullname" $ }}
port:
number: {{ $.Values.service.port }}
{{- end }}
{{- end }}
{{- end }}
ヘルパーテンプレート
# templates/_helpers.tpl
{{/*
Expand the name of the chart.
*/}}
{{- define "myapp.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
*/}}
{{- define "myapp.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "myapp.labels" -}}
helm.sh/chart: {{ include "myapp.chart" . }}
{{ include "myapp.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "myapp.selectorLabels" -}}
app.kubernetes.io/name: {{ include "myapp.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "myapp.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "myapp.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
依存関係の管理
Chart.yaml での依存定義
# Chart.yaml
dependencies:
- name: postgresql
version: "12.1.0"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled
tags:
- database
- name: redis
version: "17.3.0"
repository: "https://charts.bitnami.com/bitnami"
condition: redis.enabled
tags:
- cache
- name: common
version: "2.x.x"
repository: "https://charts.bitnami.com/bitnami"
依存チャートの更新
# 依存関係をダウンロード
helm dependency update
# 依存関係の確認
helm dependency list
# charts/ ディレクトリが生成される
myapp/
├── charts/
│ ├── postgresql-12.1.0.tgz
│ └── redis-17.3.0.tgz
└── Chart.lock
依存チャートの設定
# values.yaml
postgresql:
enabled: true
auth:
username: myapp
password: changeme
database: myapp
primary:
persistence:
enabled: true
size: 10Gi
redis:
enabled: true
auth:
enabled: false
master:
persistence:
enabled: false
リリースの管理
インストール
# 基本的なインストール
helm install myapp ./myapp
# values ファイルを指定
helm install myapp ./myapp -f values-prod.yaml
# コマンドラインで値を上書き
helm install myapp ./myapp \
--set replicaCount=3 \
--set image.tag=2.0.0
# 名前空間を指定
helm install myapp ./myapp -n production --create-namespace
# ドライラン(実際にはインストールしない)
helm install myapp ./myapp --dry-run --debug
アップグレード
# 基本的なアップグレード
helm upgrade myapp ./myapp
# values を更新してアップグレード
helm upgrade myapp ./myapp -f values-prod.yaml
# インストールされていなければインストール
helm upgrade --install myapp ./myapp
# 強制的に再作成
helm upgrade myapp ./myapp --force
# アップグレード前に待機
helm upgrade myapp ./myapp --wait --timeout 5m
ロールバック
# 直前のバージョンにロールバック
helm rollback myapp
# 特定のリビジョンにロールバック
helm rollback myapp 3
# ドライランで確認
helm rollback myapp 3 --dry-run
履歴の確認
# リリース履歴を表示
helm history myapp
# 出力例:
# REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION
# 1 Mon Jan 1 00:00:00 2026 superseded myapp-0.1.0 1.0 Install complete
# 2 Mon Jan 2 00:00:00 2026 superseded myapp-0.1.1 1.1 Upgrade complete
# 3 Mon Jan 3 00:00:00 2026 deployed myapp-0.2.0 2.0 Upgrade complete
アンインストール
# リリースを削除
helm uninstall myapp
# 履歴を保持して削除
helm uninstall myapp --keep-history
テストとデバッグ
テンプレートの検証
# テンプレートをレンダリング
helm template myapp ./myapp
# 特定の values で確認
helm template myapp ./myapp -f values-prod.yaml
# 出力を保存
helm template myapp ./myapp > rendered.yaml
# 特定のテンプレートのみ表示
helm template myapp ./myapp -s templates/deployment.yaml
Lintチェック
# チャートの構文チェック
helm lint ./myapp
# 出力例:
# ==> Linting ./myapp
# [INFO] Chart.yaml: icon is recommended
# [WARNING] templates/: directory not found
# 1 chart(s) linted, 0 chart(s) failed
デバッグモード
# 詳細なデバッグ情報を表示
helm install myapp ./myapp --debug
# ドライランと組み合わせ
helm install myapp ./myapp --dry-run --debug
ベストプラクティス
1. 値の階層構造
# ❌ 悪い例
deploymentReplicas: 3
deploymentImage: myapp:1.0
deploymentPort: 3000
# ✅ 良い例
deployment:
replicas: 3
image: myapp:1.0
port: 3000
2. デフォルト値の提供
# values.yaml
replicaCount: 2 # 必ず妥当なデフォルト値を設定
# templates/deployment.yaml
replicas: {{ .Values.replicaCount | default 1 }}
3. リソース制限の設定
# 必ずリソース制限を設定
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
4. セキュリティ設定
# セキュリティコンテキストを設定
podSecurityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
securityContext:
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
5. 環境変数の管理
# ConfigMapとSecretを分離
env:
- name: NODE_ENV
value: production
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: url
まとめ
Helmは、Kubernetesアプリケーションの管理を大幅に簡素化します。
主なメリット:
- パッケージ化とバージョン管理
- 環境別の設定管理
- 依存関係の自動解決
- ロールバック機能
- 再利用可能なテンプレート
ベストプラクティス:
- 明確なvalues.yaml構造
- 環境別のvaluesファイル
- セキュリティ設定の徹底
- リソース制限の設定
- 適切なラベリング
Helmをマスターすれば、Kubernetesアプリケーションの運用が格段に楽になります。