Helm Chartの作成と管理
HelmはKubernetesのパッケージマネージャーであり、複雑なKubernetesアプリケーションの定義、インストール、アップグレードを容易にします。本ドキュメントでは、Helmの基本概念からカスタムChartの作成、そして運用におけるベストプラクティスについて解説します。
Helmとは
Kubernetesのマニフェストファイル(YAML)は、アプリケーションが複雑になるにつれて管理が困難になります。Helmを使用することで、これらのマニフェストを「Chart」と呼ばれるパッケージとしてまとめ、パラメータ化して再利用可能にすることができます。
主な利点
- 複雑性の管理: 多数のYAMLファイルを1つのパッケージとして扱えます。
- 簡単な更新:
helm upgradeコマンドでアプリケーションの更新やロールバックが可能です。 - 共有と再利用: 公開されているChartを利用したり、自作のChartを組織内で共有したりできます。
- 環境ごとの構成:
values.yamlを切り替えることで、開発・ステージング・本番環境などで異なる設定を適用できます。
Helm Chartの基本構造
Helm Chartは特定のディレクトリ構造を持っています。
mychart/
├── Chart.yaml # Chartのメタデータ
├── values.yaml # デフォルト設定値
├── charts/ # 依存するChart
├── templates/ # Kubernetesマニフェストテンプレート
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── _helpers.tpl # テンプレートヘルパー関数
│ └── NOTES.txt # インストール後の説明
└── .helmignore # パッケージングから除外するファイル
Chart.yaml - Chartメタデータ
Chart.yamlはChartの基本情報を定義します。
apiVersion: v2
name: myapp
description: カスタムアプリケーションのHelm Chart
type: application
# Chartのバージョン(Chart自体のバージョン)
version: 1.0.0
# アプリケーションのバージョン
appVersion: "2.1.0"
# キーワード(検索用)
keywords:
- web
- api
- microservice
# メンテナー情報
maintainers:
- name: Developer Name
email: developer@example.com
url: https://example.com
# ホームページURL
home: https://github.com/example/myapp
# ソースコードリポジトリ
sources:
- https://github.com/example/myapp
# 依存するChart
dependencies:
- name: postgresql
version: "12.1.0"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled
- name: redis
version: "17.3.0"
repository: "https://charts.bitnami.com/bitnami"
condition: redis.enabled
# アイコンURL
icon: https://example.com/icon.png
# 非推奨の場合
# deprecated: true
# KubeVersionの制約
kubeVersion: ">=1.24.0-0"
Chart.yamlの重要フィールド
| フィールド | 必須 | 説明 |
|---|---|---|
apiVersion | ✓ | Chart APIバージョン(v2推奨) |
name | ✓ | Chart名(ディレクトリ名と一致) |
version | ✓ | Chart自体のバージョン(SemVer) |
appVersion | デプロイされるアプリのバージョン | |
type | applicationまたはlibrary | |
dependencies | 依存するChartのリスト |
values.yaml - デフォルト設定値
values.yamlは、テンプレートで使用されるデフォルト値を定義します。
# レプリカ数
replicaCount: 3
# Dockerイメージ設定
image:
repository: myregistry.azurecr.io/myapp
pullPolicy: IfNotPresent
tag: "2.1.0"
# イメージPull Secret
imagePullSecrets:
- name: acr-secret
# サービスアカウント
serviceAccount:
create: true
annotations: {}
name: ""
# Pod Annotations
podAnnotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
# Pod Security Context
podSecurityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
# Container Security Context
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
# Service設定
service:
type: ClusterIP
port: 80
targetPort: 8080
annotations: {}
# Ingress設定
ingress:
enabled: true
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: true
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 80
targetMemoryUtilizationPercentage: 80
# ヘルスチェック
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
# 環境変数
env:
- name: APP_ENV
value: "production"
- name: LOG_LEVEL
value: "info"
# ConfigMapからの環境変数
envFrom:
- configMapRef:
name: myapp-config
# シークレット
secrets:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: myapp-secrets
key: database-url
# ボリューム
volumes:
- name: config
configMap:
name: myapp-config
- name: cache
emptyDir: {}
volumeMounts:
- name: config
mountPath: /app/config
readOnly: true
- name: cache
mountPath: /tmp/cache
# Node Selector
nodeSelector: {}
# Tolerations
tolerations: []
# Affinity
affinity: {}
テンプレートファイル
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "myapp.fullname" . }}
labels:
{{- include "myapp.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "myapp.selectorLabels" . | nindent 6 }}
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
{{- with .Values.podAnnotations }}
{{- 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
{{- with .Values.livenessProbe }}
livenessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.readinessProbe }}
readinessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.env }}
env:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.envFrom }}
envFrom:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.volumeMounts }}
volumeMounts:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.volumes }}
volumes:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
service.yaml
apiVersion: v1
kind: Service
metadata:
name: {{ include "myapp.fullname" . }}
labels:
{{- include "myapp.labels" . | nindent 4 }}
{{- with .Values.service.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: {{ .Values.service.targetPort }}
protocol: TCP
name: http
selector:
{{- include "myapp.selectorLabels" . | nindent 4 }}
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 }}
_helpers.tpl - ヘルパーテンプレート
_helpers.tplは再利用可能なテンプレート関数を定義します。このファイルはKubernetesリソースとしてレンダリングされません。
{{/*
Chart名を展開
*/}}
{{- define "myapp.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
完全修飾アプリ名を作成
リリース名が63文字を超える場合は切り詰められます
*/}}
{{- 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 }}
{{/*
Chart名とバージョンをチャートラベルとして使用
*/}}
{{- define "myapp.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
共通ラベル
*/}}
{{- 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 }}
{{/*
セレクタラベル
*/}}
{{- define "myapp.selectorLabels" -}}
app.kubernetes.io/name: {{ include "myapp.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
サービスアカウント名を作成
*/}}
{{- define "myapp.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "myapp.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{/*
環境変数のテンプレート
*/}}
{{- define "myapp.env" -}}
- name: APP_NAME
value: {{ include "myapp.fullname" . }}
- name: APP_VERSION
value: {{ .Chart.AppVersion | quote }}
- name: RELEASE_NAME
value: {{ .Release.Name }}
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
{{- end }}
{{/*
条件付きラベル
*/}}
{{- define "myapp.podLabels" -}}
{{- if .Values.podLabels }}
{{- toYaml .Values.podLabels }}
{{- end }}
{{- end }}
ヘルパー関数の主な用途
- 命名規則の統一: リソース名を一貫した方法で生成
- ラベルの標準化: Kubernetesのベストプラクティスに従ったラベル
- 条件付きロジック: 設定に基づいてテンプレートをカスタマイズ
- コードの重複排除: 共通のテンプレート部分を再利用
ヘルパー関数の使用例
定義したヘルパー関数は、include関数を使用して他のテンプレートファイルから呼び出します。
定義 (_helpers.tpl):
{{- define "myapp.fullname" -}}
{{- printf "%s-%s" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
使用 (deployment.yaml):
apiVersion: apps/v1
kind: Deployment
metadata:
# 定義した関数を呼び出して名前を展開
name: {{ include "myapp.fullname" . }}
include関数は出力を文字列として返すため、パイプラインで他の関数(indentやnindentなど)に渡すことができます。これはYAMLのインデント構造を維持するために非常に重要です。
labels:
# 出力を4スペースでインデント
{{- include "myapp.labels" . | nindent 4 }}
テンプレート関数とパイプライン
HelmはGo TemplateとSprig関数ライブラリを使用します。
基本的なテンプレート構文
# 値の参照
{{ .Values.replicaCount }}
# デフォルト値
{{ .Values.image.tag | default .Chart.AppVersion }}
# 条件分岐
{{- if .Values.ingress.enabled }}
# Ingressリソースを作成
{{- end }}
# 範囲ループ
{{- range .Values.env }}
- name: {{ .name }}
value: {{ .value }}
{{- end }}
# パイプライン(複数の関数を連鎖)
{{ .Values.name | upper | quote }}
# インデント調整
{{- toYaml .Values.resources | nindent 12 }}
# ヘルパー関数の呼び出し
{{ include "myapp.fullname" . }}
よく使う関数
| 関数 | 説明 | 例 |
|---|---|---|
default | デフォルト値を提供 | {{ .Values.tag | default "latest" }} |
quote | 文字列をクォート | {{ .Values.name | quote }} |
upper/lower | 大文字/小文字変換 | {{ .Values.env | upper }} |
trunc | 文字列を切り詰め | {{ .Values.name | trunc 63 }} |
trimSuffix | 接尾辞を削除 | {{ .Values.name | trimSuffix "-" }} |
nindent | インデントを追加 | {{ toYaml .Values | nindent 4 }} |
toYaml | YAMLに変換 | {{ toYaml .Values.resources }} |
sha256sum | SHA256ハッシュ | {{ .Values.config | sha256sum }} |
Chartのパッケージング - .tgzファイル
Helm Chartは.tgz(tar.gz)形式のアーカイブとしてパッケージ化されます。このファイルは、Chartリポジトリへの公開や、エアギャップ環境(インターネット接続のない環境)への転送に使用されます。
ファイル名の規則
パッケージファイル名は、Chart.yamlで定義されたnameとversionに基づいて自動的に生成されます。
形式: <chart-name>-<chart-version>.tgz
例:
- Chart名:
myapp - バージョン:
1.0.0 - 生成されるファイル名:
myapp-1.0.0.tgz
パッケージの作成
# Chartをパッケージ化
helm package mychart/
# 出力例: myapp-1.0.0.tgz
パッケージの内容
.tgzファイルには、レンダリングされたKubernetesマニフェストではなく、Chartのソースファイルそのものが含まれています。インストール時に、Helmはこのパッケージを展開し、その場でテンプレートをレンダリングします。
# パッケージの中身を確認
tar -tzf myapp-1.0.0.tgz
# 出力:
# myapp/Chart.yaml
# myapp/values.yaml
# myapp/templates/deployment.yaml
# myapp/templates/service.yaml
# ...
.helmignore
.helmignoreでパッケージから除外するファイルを指定します。
# 開発ファイル
*.swp
*.bak
*.tmp
*.orig
*~
# Git関連
.git/
.gitignore
# CI/CD
.github/
.gitlab-ci.yml
# テスト
tests/
*.test
# ドキュメント
README.md
CONTRIBUTING.md
# 環境固有のファイル
values-dev.yaml
values-staging.yaml
Chart開発のワークフロー
1. Chartの作成
# 新しいChartを作成
helm create myapp
# ディレクトリ構造が生成されます
2. テンプレートの開発とテスト
# テンプレートのレンダリング結果を確認
helm template myapp ./myapp
# 特定の値ファイルを使用
helm template myapp ./myapp -f values-prod.yaml
# デバッグモードで詳細出力
helm template myapp ./myapp --debug
3. Chartの検証
# Chartの構文チェック
helm lint myapp/
# 依存関係の更新
helm dependency update myapp/
# ドライラン(実際にはインストールしない)
helm install myapp ./myapp --dry-run --debug
4. Chartのパッケージング
# Chartをパッケージ化
helm package myapp/
# 署名付きパッケージ(オプション)
helm package myapp/ --sign --key 'keyname' --keyring path/to/keyring
5. Chartのインストール
# ローカルChartからインストール
helm install my-release ./myapp
# パッケージからインストール
helm install my-release myapp-1.0.0.tgz
# カスタム値を指定
helm install my-release ./myapp -f custom-values.yaml
# コマンドラインで値をオーバーライド
helm install my-release ./myapp --set replicaCount=5
# 特定のnamespaceにインストール
helm install my-release ./myapp -n production --create-namespace
環境別の設定管理
異なる環境(開発、ステージング、本番)で異なる設定を使用します。
values-dev.yaml
replicaCount: 1
image:
tag: "dev"
ingress:
enabled: false
resources:
limits:
cpu: 200m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
autoscaling:
enabled: false
env:
- name: APP_ENV
value: "development"
- name: LOG_LEVEL
value: "debug"
values-prod.yaml
replicaCount: 5
image:
tag: "2.1.0"
ingress:
enabled: true
hosts:
- host: myapp.production.com
paths:
- path: /
pathType: Prefix
resources:
limits:
cpu: 1000m
memory: 1Gi
requests:
cpu: 500m
memory: 512Mi
autoscaling:
enabled: true
minReplicas: 5
maxReplicas: 20
env:
- name: APP_ENV
value: "production"
- name: LOG_LEVEL
value: "warn"
使用方法
# 開発環境
helm install myapp-dev ./myapp -f values-dev.yaml
# 本番環境
helm install myapp-prod ./myapp -f values-prod.yaml -n production
# 複数の値ファイルを結合(後のファイルが優先)
helm install myapp ./myapp -f values.yaml -f values-prod.yaml -f values-override.yaml
Umbrella Chartパターン
Umbrella Chart(アンブレラチャート)は、複数のマイクロサービスやアプリケーションをまとめて管理するためのHelmの設計パターンです。
概要
Umbrella Chart自体はテンプレート(templates/)をほとんど持たず、Chart.yamlのdependenciesセクションで他のChart(サブチャート)を定義することに特化しています。これにより、複雑なシステム全体を1つのリリースとしてデプロイ・管理できます。
主な用途
- マイクロサービス構成: 複数のサービス(Frontend, Backend, DBなど)を一括管理
- 依存関係の統合: アプリケーションと必要なミドルウェア(Redis, PostgreSQLなど)をセットで提供
構造例
umbrella-app/
├── Chart.yaml # 依存関係を定義
├── values.yaml # 全体の設定(サブチャートの設定をオーバーライド)
├── charts/ # 依存Chartがダウンロードされる場所
└── templates/ # 通常は空、または共通のConfigMap/Secretのみ
Chart.yamlの設定
apiVersion: v2
name: my-complex-app
version: 1.0.0
type: application
dependencies:
- name: frontend
version: 1.2.0
repository: http://my-charts.com
- name: backend
version: 2.3.0
repository: http://my-charts.com
- name: postgresql
version: 12.1.0
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
values.yamlでの設定オーバーライド
親Chart(Umbrella Chart)のvalues.yamlから、サブチャートの設定を上書きできます。サブチャート名をキーとして使用します。
# グローバル設定(全チャートで共有)
global:
imageRegistry: myregistry.azurecr.io
# frontendサブチャートの設定
frontend:
replicaCount: 3
service:
type: LoadBalancer
# backendサブチャートの設定
backend:
env:
- name: DB_HOST
value: "my-complex-app-postgresql"
# postgresqlサブチャートの設定
postgresql:
enabled: true
auth:
database: mydb
username: myuser
コマンド
依存関係を解決してChartを準備するには、以下のコマンドを使用します。
# 依存Chartをダウンロードして charts/ ディレクトリに配置
helm dependency build ./umbrella-app
# または
helm dependency update ./umbrella-app
Chartリポジトリへの公開
ChartMuseumを使用
# ChartMuseumにアップロード
curl --data-binary "@myapp-1.0.0.tgz" http://chartmuseum.example.com/api/charts
# リポジトリの追加
helm repo add myrepo http://chartmuseum.example.com
helm repo update
# リポジトリからインストール
helm install my-release myrepo/myapp
Azure Container Registry (ACR)を使用
# ACRにログイン
az acr login --name myregistry
# ChartをOCIアーティファクトとしてプッシュ
helm push myapp-1.0.0.tgz oci://myregistry.azurecr.io/helm
# ACRからインストール
helm install my-release oci://myregistry.azurecr.io/helm/myapp --version 1.0.0
ベストプラクティス
1. 命名規則
- Chart名: 小文字、ハイフン区切り(例:
my-app) - リソース名:
{{ include "myapp.fullname" . }}を使用 - ラベル: Kubernetesの推奨ラベルを使用
2. セキュリティ
# Security Contextを常に定義
securityContext:
runAsNonRoot: true
runAsUser: 1000
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
# Resource Limitsを設定
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
3. ConfigとSecretの管理
# ConfigMapのハッシュをアノテーションに追加(設定変更時にPodを再起動)
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
4. 条件付きリソース
# 機能のオン/オフを values.yaml で制御
{{- if .Values.ingress.enabled }}
# Ingressリソース
{{- end }}
5. ドキュメント
README.md: Chartの概要と使用方法NOTES.txt: インストール後にユーザーに表示される情報values.yaml: すべての設定オプションにコメントを記載
NOTES.txt の例
Thank you for installing {{ .Chart.Name }}.
Your release is named {{ .Release.Name }}.
To learn more about the release, try:
$ helm status {{ .Release.Name }}
$ helm get all {{ .Release.Name }}
To access your application:
{{- if .Values.ingress.enabled }}
Visit: https://{{ (index .Values.ingress.hosts 0).host }}
{{- else }}
Run: kubectl port-forward svc/{{ include "myapp.fullname" . }} 8080:{{ .Values.service.port }}
Then visit: http://localhost:8080
{{- end }}
トラブルシューティング
デバッグコマンド
# レンダリングされたテンプレートを確認
helm template myapp ./myapp --debug
# インストールのドライラン
helm install myapp ./myapp --dry-run --debug
# インストールされたChartの値を確認
helm get values my-release
# インストールされたマニフェストを確認
helm get manifest my-release
# リリースの履歴を確認
helm history my-release
よくある問題
-
テンプレートのインデントエラー
nindent関数を使用してインデントを正確に制御
-
値が反映されない
helm get valuesで実際の値を確認--setと-fの優先順位を確認
-
依存関係の問題
helm dependency updateを実行charts/ディレクトリを確認
まとめ
カスタムHelm Chartを作成する際の重要なポイント:
- Chart.yaml: メタデータとバージョン管理
- values.yaml: 設定可能なすべてのパラメータ
- templates/: Kubernetesマニフェストのテンプレート
- _helpers.tpl: 再利用可能なテンプレート関数
- .tgzファイル: パッケージ化されたChart
- 環境別設定: 異なる環境での柔軟なデプロイ
Helm Chartは、Kubernetesアプリケーションのデプロイを標準化し、再利用可能にする強力なツールです。適切に構造化されたChartは、チーム全体の生産性を大幅に向上させます。