新卒インフラエンジニア向け「Kubernetes 実践教科書① CKAD アプリケーション開発編」の第17回です。第16回までで、fanclub-api は Pod・Service・StatefulSet から ConfigMap・Secret・RBAC・NetworkPolicy まで、20 以上のマニフェストを kubectl apply で 1 枚ずつ積み上げてきました。今回はこの大量のマニフェストを Helm v4 で 1 つの chart にまとめ、install / upgrade / rollback / uninstall のフルサイクルを実機で回します。fanclub-api を「パッケージ」として一括管理できるようにするのが到達点です。CKAD ドメイン D2(Application Deployment)の中心テーマです。
動作確認バージョン:Helm v4.1.4 / kind v0.31.0(K8s v1.35.0)/ kubectl v1.35.6 / fanclub-backend:0.3.2(再ビルドなし)/ fanclub-frontend:0.1.0 / Chart 版 1.0.0(appVersion 0.3.2)(2026-07-03 実機検証時点)
今ここマップ(全 19 回中の現在地)
現在地は第 6 部「パッケージ管理と HTTPS 公開」の第 17 回、第 6 部の入口です。第 1〜5 部で組み上げたアプリを、ここからパッケージ化して公開まで進めます。
- 第 1 部 コンテナと Docker(第 1〜4 回)
- 第 2 部 Kubernetes 基礎(第 5〜6 回)
- 第 3 部 アプリリソース(第 7〜11 回)
- 第 4 部 ワークロード戦略(第 12〜14 回)
- 第 5 部 セキュリティ基礎(第 15〜16 回)
- 第 6 部 パッケージ管理と HTTPS 公開(第 17〜19 回)← 今ここ
この回のゴール
- 個別
kubectl applyの限界を理解し、Chart / Release / Values / Repository の 4 概念を説明できる。 - Helm v4 をバイナリで導入し、fanclub-api の全マニフェストを 1 つの chart にまとめられる。
install/upgrade/rollback/uninstallのフルサイクルと、lint/template/historyを実機で回せる。
なぜ Helm か
ここまで fanclub-api を構成するマニフェストは、Namespace・ResourceQuota・ConfigMap・Secret・ServiceAccount・Role・Deployment・Service・StatefulSet・DaemonSet・CronJob・NetworkPolicy と 20 枚を超えました。1 枚ずつ kubectl apply -f で管理する方式には、次の限界があります。
- 適用順序を人間が覚える必要がある:Secret より先に Deployment を当てると起動に失敗するなど、依存を手作業で管理することになる。
- 一括更新・一括削除がしづらい:イメージのタグを 1 つ上げるだけでも該当ファイルを探して書き換え、消すときは 20 枚を
deleteして回る。 - 環境差分をコピーで作りがち:dev と prod で replica 数やリソースだけ違うのに、ほぼ同じマニフェスト一式を 2 セット持つことになる。
- まとめて元に戻せない:更新して問題が出ても、変更前の状態へワンコマンドで戻す仕組みが無い。
Helm は Kubernetes のパッケージマネージャーで、これらをまとめて解決します。中心になる概念は 4 つです。
- Chart:関連するマニフェスト群と既定値をまとめたパッケージ。fanclub-api の全リソースを 1 つの chart にする。
- Values:chart に外から差し込む値(イメージのタグ・replica 数・リソース量など)。環境差分はここだけ変える。
- Release:chart をクラスタに install した「インスタンス」。同じ chart から名前違いで複数の Release を作れる。Release はリビジョンを持ち、更新のたびに番号が増え、過去へ戻せる。
- Repository:chart を配布する置き場。公開 chart を
helm repo addで取り込める(本回は自作 chart をローカルディレクトリから直接使う)。

Helm v4 の導入
以降の作業はすべて、作業端末 k8s-ops に一般ユーザー developer で SSH した状態で行います。まず Helm v4 を入れます。バージョンを固定したいので、配布スクリプト(get.helm.sh/get_helm.sh)ではなくバージョン指定のバイナリを直接取得します。配布スクリプトは Helm v3 系の導入用で、v4 のピン留めには使いません。取得と展開は developer のホーム(~)で行い、バイナリを全ユーザー共通の /usr/local/bin に置くところだけ sudo を使います。実行コマンド:
$ curl -sSLO https://get.helm.sh/helm-v4.1.4-linux-amd64.tar.gz
$ tar xzf helm-v4.1.4-linux-amd64.tar.gz
$ sudo install -m 0755 linux-amd64/helm /usr/local/bin/helm
$ helm version
実行結果(例):v4.1.4 が入り、対応する Kubernetes クライアントが v1.35 であることも確認できます。
version.BuildInfo{Version:"v4.1.4", GitCommit:"05fa37973dc9e42b76e1d2883494c87174b6074f", GitTreeState:"clean", GoVersion:"go1.25.9", KubeClientVersion:"v1.35"}
/usr/local/bin/helm は sudo install で置いたので root 所有のシステム共通コマンドになりますが、この後に作る chart のファイル群はすべて developer が自分のホームに置く一般ユーザー所有のファイルです(root は不要)。また Helm は、第6回から使ってきた kubectl と同じ kubeconfig(~/.kube/config)を読むので、追加設定なしで同じ kind クラスタを操作します。
Helm v3 は 2026 年 11 月でセキュリティ修正が終わるため、新規に学ぶなら v4 が前提です。v3 からの主な破壊的変更は次の 3 点です。実務で v3 の資料を見ながら v4 を触ると食い違うので、頭に入れておきます。
- OCI レジストリが既定の配布方式:v3 で必要だった環境変数
HELM_EXPERIMENTAL_OCI=1は廃止され、OCI 関連コマンドがそのまま使える。 - プラグイン互換の変化:v3 向けプラグインの多くは v4 で動かず、v4 対応版が要る。
- Go モジュールパスの変更:
helm.sh/helm/v3からhelm.sh/helm/v4へ。Helm を Go から呼ぶツールは追従が要る。
chart の構造
chart は決まったディレクトリ構造を持ちます。fanclub-api 用に fanclub-chart を作ります。最小の骨格は次の 3 要素です。
Chart.yaml:chart のメタデータ。名前・chart 自身のバージョン(version)・中身のアプリのバージョン(appVersion)を持つ。values.yaml:Values の既定値。テンプレートから{{ .Values.* }}で参照する。templates/:K8s マニフェストのテンプレート群。Go テンプレート記法でvalues.yamlの値を差し込む。
この chart は第18回・第19回でも使い続けるので、developer のホーム直下に fanclub-chart/ として作ります。まずディレクトリだけ用意します(これ以降のファイルはすべて developer が一般ユーザーとして作成します。root は不要です)。実行コマンド:
$ mkdir -p ~/fanclub-chart/templates
以降、Chart.yaml と values.yaml は ~/fanclub-chart/ 直下に、各マニフェストは ~/fanclub-chart/templates/ の中に置きます。
まず Chart.yaml です。version(chart のバージョン)と appVersion(同梱アプリのバージョン)は別物です。本 chart のバージョンは 1.0.0、appVersion は fanclub-backend の完走タグ 0.3.2 にします。chart を直したら version を上げ、アプリのイメージを差し替えたら appVersion を上げる、という使い分けです。
apiVersion: v2
name: fanclub-api
description: fanclub-api 三層アプリ(Frontend / Backend / PostgreSQL)を一括管理する Helm chart
type: application
version: 1.0.0
appVersion: "0.3.2"
次に values.yaml です。環境で変えたい値(namespace・イメージのタグ・replica 数・リソース量)と、第18回で有効化する Gateway API のトグルを外に出します。gateway.enabled を false にしておくと、Gateway 関連のテンプレートは出力されません。
namespace: fanclub
backend:
replicaCount: 1
image:
repository: k8s-registry:5000/fanclub-backend
tag: "0.3.2"
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "768Mi"
frontend:
replicaCount: 1
image:
repository: k8s-registry:5000/fanclub-frontend
tag: "0.1.0"
db:
image:
repository: postgres
tag: "18"
storage: 1Gi
gateway:
enabled: false
host: fanclub.local
fanclub-api を chart 化する
テンプレートの元ネタは、第7〜16回で developer のホームに作ってきた各マニフェスト(backend-deploy.yaml / db-statefulset.yaml / fanclub-config.yaml / np-*.yaml など)です。これらを ~/fanclub-chart/templates/ にリソース単位で置き直し、次の 2 つの機械的な変更を加えれば chart になります。
- 各リソースの
metadata.namespaceを{{ .Values.namespace }}に置き換える(fanclub名前空間へまとめるため)。 - 環境で変えたい値(イメージのタグ・replica 数・リソース量・PVC 容量)を
{{ .Values.* }}参照にする。
加えて、Frontend だけは第8回以来 Pod で動かしてきたので、この機に Deployment(frontend-deployment.yaml)へ書き換えて Backend と形式をそろえます。fanclub-chart の全体構成は次のとおりです(templates/ 配下の 21 ファイル)。第11回で足した CronJob・DaemonSet、第16回の NetworkPolicy まで、すべてこの chart に収めます。
fanclub-chart/Chart.yaml・values.yamltemplates/土台:namespace.yaml/resourcequota.yaml/limitrange.yamltemplates/設定:configmap.yaml/secret.yaml/serviceaccount.yaml/role.yaml/rolebinding.yamltemplates/DB:db-statefulset.yaml/db-service.yaml/db-migrate-job.yamltemplates/アプリ:backend-deployment.yaml/backend-service.yaml/frontend-deployment.yaml/frontend-service.yamltemplates/バッチ・ログ:cronjob.yaml/daemonset.yamltemplates/通信制御:networkpolicy.yamltemplates/公開(第18回で有効化):gateway.yaml/httproute.yaml/certificate.yaml
移植のポイントは、ハードコードしていた値を {{ .Values.* }} に置き換えることです。代表として Backend の Deployment を見ます。イメージ・replica 数・リソース量・namespace を Values 参照にしています(第12回で作った 3 Probe と第15回の SecurityContext はそのまま維持)。実機で使っている backend-deployment.yaml は次のとおりです。
apiVersion: apps/v1
kind: Deployment
metadata:
name: fanclub-backend
namespace: {{ .Values.namespace }}
labels:
app: fanclub-backend
spec:
replicas: {{ .Values.backend.replicaCount }}
selector:
matchLabels:
app: fanclub-backend
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: fanclub-backend
spec:
serviceAccountName: fanclub-backend
automountServiceAccountToken: false
initContainers:
- name: wait-for-db
image: busybox:1.37
command: ["sh","-c","until nslookup fanclub-db.{{ .Values.namespace }}.svc.cluster.local >/dev/null 2>&1; do echo waiting; sleep 2; done; echo resolved"]
- name: log-shipper
image: busybox:1.37
restartPolicy: Always
command: ["sh","-c","echo sidecar; tail -F /var/log/app/app.log 2>/dev/null || sleep infinity"]
volumeMounts:
- name: applog
mountPath: /var/log/app
containers:
- name: backend
image: {{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag }}
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: fanclub-config
- secretRef:
name: fanclub-db-secret
securityContext:
runAsNonRoot: true
runAsUser: 10001
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
startupProbe:
httpGet:
path: /health/started
port: 8080
failureThreshold: 30
periodSeconds: 10
livenessProbe:
httpGet:
path: /health/live
port: 8080
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /health/ready
port: 8080
periodSeconds: 10
failureThreshold: 3
resources:
requests:
memory: {{ .Values.backend.resources.requests.memory | quote }}
cpu: {{ .Values.backend.resources.requests.cpu | quote }}
limits:
memory: {{ .Values.backend.resources.limits.memory | quote }}
cpu: {{ .Values.backend.resources.limits.cpu | quote }}
volumeMounts:
- name: applog
mountPath: /var/log/app
- name: payara-tmp
mountPath: /opt/payara/tmp
volumes:
- name: applog
emptyDir: {}
- name: payara-tmp
emptyDir: {}
先に触れた frontend-deployment.yaml も同じ要領で、image と replicas を {{ .Values.frontend.* }} 参照にします。これで Backend・Frontend とも replica 数を Values 側でそろえて制御できます。
第18回で有効化する Gateway をトグルで無効化する
Gateway API のリソース(gateway.yaml / httproute.yaml / certificate.yaml)は chart に含めますが、第17回時点ではまだ Gateway API の CRD を入れていないため、そのまま install すると「未知のリソース種別」で失敗します。そこで {{- if .Values.gateway.enabled }} で囲み、gateway.enabled: false のあいだは出力しないようにします。第18回で CRD を入れてから --set gateway.enabled=true で有効化します。gateway.yaml の冒頭はこの形です。
{{- if .Values.gateway.enabled }}
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: fanclub-gateway
namespace: {{ .Values.namespace }}
spec:
gatewayClassName: traefik
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: {{ .Values.gateway.host | quote }}
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: fanclub-tls
{{- end }}
DB マイグレーションはフックにする
第11回で作った DB マイグレーション Job は、Helm フックにします。フックにしない Job は、内容を変えて helm upgrade すると「Job は変更不可」で失敗しがちです。helm.sh/hook: post-install,post-upgrade を付けると、install と upgrade のたびに Helm が Job を作り直し、完了を待ってから処理を進めます。マイグレーションは DB が起動してから走る必要があるので、先頭に pg_isready の待ち合わせを入れています。
apiVersion: batch/v1
kind: Job
metadata:
name: fanclub-db-migrate
namespace: {{ .Values.namespace }}
annotations:
"helm.sh/hook": post-install,post-upgrade
"helm.sh/hook-weight": "5"
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
backoffLimit: 3
template:
metadata:
labels:
app: fanclub-batch
spec:
restartPolicy: Never
containers:
- name: migrate
image: {{ .Values.db.image.repository }}:{{ .Values.db.image.tag }}
envFrom:
- configMapRef:
name: fanclub-config
- secretRef:
name: fanclub-db-secret
command:
- sh
- -c
- |
set -e
until PGPASSWORD="$DB_PASSWORD" pg_isready -h "$DB_HOST" -U "$DB_USER" >/dev/null 2>&1; do
echo "waiting for db"; sleep 2
done
PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -U "$DB_USER" -d "$DB_NAME" -v ON_ERROR_STOP=1 \
-c "ALTER TABLE members ADD COLUMN IF NOT EXISTS updated_at TIMESTAMP" \
-c "INSERT INTO members (name,email,plan) VALUES ('鈴木花子','hanako@example.com','standard'),('佐藤一郎','ichiro@example.com','free') ON CONFLICT (email) DO NOTHING"
Pod テンプレートに app: fanclub-batch というラベルを付けた点に注目してください。この理由は、次の NetworkPolicy の移植で分かります。
NetworkPolicy を移植すると、バッチが DB に届かなくなる
第16回の NetworkPolicy(default-deny + 隣接層の許可 6 枚)をそのまま networkpolicy.yaml に移すと、マイグレーション Job とレポート CronJob が DB に接続できなくなります。実際、この状態で helm install すると、post-install フックの Job が waiting for db を延々と出したまま完了せず、タイムアウトします。
Error: INSTALLATION FAILED: failed post-install: resource Job/fanclub/fanclub-db-migrate not ready. status: InProgress, message: Job in progress
context deadline exceeded
原因は明快です。第16回の許可ルールは、DB への通信を app: fanclub-backend のラベルを持つ Pod だけに開けていました。マイグレーション Job やレポート CronJob の Pod にはそのラベルが無いため、default-deny-all により DB への Egress も、DB 側の Ingress も塞がれます。「アプリ本体は通信できるのに、バッチ処理だけ DB に届かない」——NetworkPolicy を締めた現場でよく起きる詰まり方です。
解決は、バッチ用の経路を明示的に開けることです。Backend の Service セレクタ(app: fanclub-backend)を汚さないよう、バッチには専用ラベル app: fanclub-batch を付け(先ほどの Job のラベルがこれです)、次の 2 点を networkpolicy.yaml に足します。
app: fanclub-batchの Pod から DB への Egress を許可するallow-egress-batch-to-dbを新設する。- DB 側の Ingress 許可
allow-db-from-backendの送信元に、app: fanclub-batchを追加する。
該当箇所は次のとおりです(DNS への Egress は第16回の allow-dns が podSelector: {} で全 Pod に効いているので、バッチもそのまま名前解決できます)。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-db-from-backend
namespace: {{ .Values.namespace }}
spec:
podSelector:
matchLabels:
app: fanclub-db
policyTypes: ["Ingress"]
ingress:
- from:
- podSelector:
matchLabels:
app: fanclub-backend
- podSelector:
matchLabels:
app: fanclub-batch
ports: [{protocol: TCP, port: 5432}]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-egress-batch-to-db
namespace: {{ .Values.namespace }}
spec:
podSelector:
matchLabels:
app: fanclub-batch
policyTypes: ["Egress"]
egress:
- to:
- podSelector:
matchLabels:
app: fanclub-db
ports: [{protocol: TCP, port: 5432}]
レポート CronJob の Pod テンプレートにも同じ app: fanclub-batch を付けておきます。これで default-deny を保ったまま、バッチ経路だけを最小限に開けられます。
lint と template で事前確認する
クラスタへ当てる前に、chart を静的に確認します。helm lint は構文と推奨事項をチェックし、helm template は Values を差し込んだ実際に生成される YAML を手元で確認できます。なお、以降の helm コマンドは chart を ./fanclub-chart の相対パスで指すので、developer のホーム(~)で実行します(cd ~/fanclub-chart の中では相対パスがずれるので注意してください)。実行コマンド:
$ helm lint ./fanclub-chart
実行結果(例):エラー 0 件です(icon の推奨は情報レベルなので無視して問題ありません)。
==> Linting ./fanclub-chart
[INFO] Chart.yaml: icon is recommended
1 chart(s) linted, 0 chart(s) failed
次に helm template で、生成されるオブジェクトの種類と数を確認します。gateway.enabled が false なので Gateway・HTTPRoute・Certificate は出力されないはずです。実行コマンド:
$ helm template fanclub ./fanclub-chart | grep -E "^kind:" | sort | uniq -c
実行結果(例):ConfigMap 3・Deployment 2(Backend / Frontend)・Service 3・NetworkPolicy 7・StatefulSet 1 など、想定どおりの一式が出ます。NetworkPolicy が第16回の 6 枚より 1 多い 7 枚なのは、先ほど足したバッチ用の allow-egress-batch-to-db が含まれるためです。Gateway 系は 0 件で、トグルが効いています。
3 kind: ConfigMap
1 kind: CronJob
1 kind: DaemonSet
2 kind: Deployment
1 kind: Job
1 kind: LimitRange
1 kind: Namespace
7 kind: NetworkPolicy
1 kind: ResourceQuota
1 kind: Role
1 kind: RoleBinding
1 kind: Secret
3 kind: Service
1 kind: ServiceAccount
1 kind: StatefulSet
install する
chart は専用の namespace fanclub にまとめてデプロイします。ここまで default に個別適用してきたリソースは、chart 管理へ移すので先に削除します。二重に起動させないためと、kind の CPU 予算(2 vCPU)を空けるためです。ConfigMap や ServiceAccount には app ラベルが無く一括セレクタで取りこぼすので、名前を明示して確実に消します(テンプレートの元にした ~/*.yaml はホームに残るため、消えて困るものはありません)。実行コマンド:
$ kubectl delete deployment/fanclub-backend statefulset/fanclub-db pod/fanclub-frontend \
daemonset/fanclub-logcollector cronjob/fanclub-report --ignore-not-found
$ kubectl delete service/fanclub-backend service/fanclub-frontend service/fanclub-db --ignore-not-found
$ kubectl delete configmap/fanclub-config configmap/fanclub-frontend-config configmap/fanclub-db-init \
secret/fanclub-db-secret serviceaccount/fanclub-backend --ignore-not-found
$ kubectl delete role/fanclub-backend-reader rolebinding/fanclub-backend-reader-binding --ignore-not-found
$ kubectl delete networkpolicy --all
$ kubectl delete pvc/data-fanclub-db-0 --ignore-not-found
掃除ができたら install します。chart 内の namespace.yaml が fanclub を作り、各リソースは namespace: fanclub を宣言しているので、実体は fanclub に作られます。実行コマンド:
$ helm install fanclub ./fanclub-chart --timeout 5m
実行結果(例):STATUS: deployed・REVISION: 1 で install が完了します。post-install フックのマイグレーションもこの中で完了しています。
NAME: fanclub
LAST DEPLOYED: Fri Jul 3 12:51:35 2026
NAMESPACE: default
STATUS: deployed
REVISION: 1
DESCRIPTION: Install complete
TEST SUITE: None
Release の情報自体は現在の default に記録されますが(NAMESPACE: default はこの意味)、アプリの実体は fanclub です。Pod の状態を見ます。実行コマンド:
$ kubectl get pods -n fanclub
実行結果(例):Backend(2/2=サイドカー込み)・DB・Frontend・ログコレクタがすべて Running です。
NAME READY STATUS RESTARTS AGE
fanclub-backend-6896d48bd9-6kxzh 2/2 Running 0 31s
fanclub-db-0 1/1 Running 0 31s
fanclub-frontend-7c98b748bc-g9b59 1/1 Running 0 31s
fanclub-logcollector-47tqs 1/1 Running 0 31s
NetworkPolicy 下で 3 層が通るかを、DB 参照を伴う /api/members で確認します。Frontend Pod から Backend を叩きます。実行コマンド:
$ kubectl exec -n fanclub deploy/fanclub-frontend -- \
wget -qO- http://fanclub-backend:8080/api/members
実行結果(例):マイグレーションフックが投入した会員 2 件の JSON が返り、Frontend → Backend → DB が chart 一式で通っています。先ほど PVC も削除したため DB は初期化されており、schema.sql(テーブル作成)とマイグレーションフック(この 2 件の投入)で作り直された状態です。これまでの回で入れた会員データは引き継がれない点に注意してください(学習環境のため作り直しで問題ありません)。
[{"createdAt":"2026-07-03T03:49:05.855174","email":"hanako@example.com","id":1,"name":"鈴木花子","plan":"standard"},{"createdAt":"2026-07-03T03:49:05.855174","email":"ichiro@example.com","id":2,"name":"佐藤一郎","plan":"free"}]
フルサイクル運用
install した Release を、値を変えて更新し、問題があれば戻し、最後に消す——この一連が Helm の要です。まず upgrade です。Frontend の replica を 1 から 2 に増やします(Backend を増やすと kind の CPU 予算を超えて Pending になるため、軽い Frontend で試します)。値は --set でその場だけ上書きできます。実行コマンド:
$ helm upgrade fanclub ./fanclub-chart --set frontend.replicaCount=2
実行結果(例):REVISION: 2 に上がります。
Release "fanclub" has been upgraded. Happy Helming!
NAME: fanclub
STATUS: deployed
REVISION: 2
DESCRIPTION: Upgrade complete
Frontend が 2 つに増えたことを確認したら、rollback で 1 つ前(リビジョン 1)へ戻します。rollback は「戻す」操作自体も新しいリビジョンとして記録されます。実行コマンド:
$ helm rollback fanclub 1
実行結果(例):戻しが成功し、Frontend は 1 replica に戻ります。
Rollback was a success! Happy Helming!
ここまでの履歴を helm history で見ます。実行コマンド:
$ helm history fanclub
実行結果(例):install(リビジョン 1)→ upgrade(2)→ rollback(3)と、変更が積み重なって記録されています。リビジョン 3 は「1 の状態に戻したもの」です。
REVISION STATUS CHART APP VERSION DESCRIPTION
1 superseded fanclub-api-1.0.0 0.3.2 Install complete
2 superseded fanclub-api-1.0.0 0.3.2 Upgrade complete
3 deployed fanclub-api-1.0.0 0.3.2 Rollback to 1

最後に uninstall です。Release に属するリソースがまとめて削除されます。chart には namespace.yaml も含まれるので、fanclub namespace ごと消えます。実行コマンド:
$ helm uninstall fanclub
実行結果(例):1 コマンドで一式が片付きます。20 枚以上を kubectl delete で消して回っていた手間が、これ 1 行になりました。
release "fanclub" uninstalled
次回に備えて、chart をもう一度クリーンに install しておきます(helm install fanclub ./fanclub-chart)。ただし uninstall の直後に再 install するときは 1 点注意があります。この chart は namespace.yaml で fanclub namespace 自体も作るため、uninstall でその namespace が削除されますが、削除は非同期で、DB の PVC(volumeClaimTemplates 由来で Helm 管理外のため残存)などが片付くまで Terminating がしばらく続きます。この Terminating 中に再 install すると、namespace 作成が競合して INSTALLATION FAILED(cannot reuse a name や namespace not found)になります。kubectl get ns fanclub が NotFound になるのを待ってから再 install してください。これで fanclub-api は Helm 管理下の 1 つの Release として扱えるようになりました。
やってみよう
helm lint ./fanclub-chartとhelm template fanclub ./fanclub-chartを実行し、生成される YAML に Gateway・HTTPRoute・Certificate が含まれないこと(トグル無効)を確認する。helm install fanclub ./fanclub-chartを実行し、kubectl get pods -n fanclubで 4 つの Pod が Running になること、/api/membersが会員 JSON を返すことを確認する。--set frontend.replicaCount=2でhelm upgradeし、Frontend が 2 つに増えることとリビジョンが 2 になることを確認する。helm rollback fanclub 1で戻し、helm history fanclubで install → upgrade → rollback の 3 リビジョンが並ぶことを確認する。helm uninstall fanclubで一式が消えることを確認したのち、再度helm installして元に戻す。
理解度チェック
次の各文が正しいか(○)誤りか(×)を判断してください。解答は下にまとめています。
- Chart は関連マニフェストと既定値をまとめたパッケージで、install するとクラスタ上に Release ができる。
Chart.yamlのversionは同梱アプリのバージョンを表し、appVersionは chart 自身のバージョンを表す。helm templateは Values を差し込んだ後の YAML を手元で確認でき、クラスタには何も適用しない。helm rollbackで過去のリビジョンへ戻すと、その戻し操作は新しいリビジョンとして履歴に追加される。values.yamlの値はhelm install/upgrade時に--setで上書きできる。- default-deny の NetworkPolicy 下では、アプリ本体に許可があればマイグレーション Job も追加設定なしで DB に接続できる。
- Helm v4 では OCI レジストリが既定の配布方式で、
HELM_EXPERIMENTAL_OCI=1は不要になった。
解答
- 1. ○:chart は install により Release としてクラスタへ展開される。
- 2. ×:逆。
versionが chart 自身、appVersionが同梱アプリのバージョン。 - 3. ○:
templateはレンダリング結果を出すだけで、クラスタへは適用しない。 - 4. ○:rollback も 1 つの操作として新リビジョンに記録される(本回では 3 になった)。
- 5. ○:
--set key=valueで個別に上書きできる。 - 6. ×:バッチ Pod には
app: fanclub-backendが無いため、専用の許可(app: fanclub-batchの Egress と DB Ingress)を足さないと届かない。 - 7. ○:v4 で OCI が既定になり、実験フラグは廃止された。
まとめ
本記事では、20 枚以上に増えた fanclub-api のマニフェストを Helm v4 の chart にまとめ、パッケージとして扱えるようにしました。Chart / Values / Release / Repository の 4 概念を押さえ、ハードコードしていた値を {{ .Values.* }} に置き換え、環境で変えたい値だけを values.yaml に外出ししました。Gateway API のリソースは gateway.enabled のトグルで無効化し、DB マイグレーションは post-install / post-upgrade フックにしました。NetworkPolicy を移植するとバッチが DB に届かなくなる詰まりは、専用ラベル app: fanclub-batch と許可 2 枚で解きました。lint / template で事前確認し、install / upgrade / rollback / history / uninstall のフルサイクルを実機で回して、20 枚以上の手作業を 1 コマンドに置き換えられることを確認しました。
次回予告
次回(第18回)は、chart にトグルで仕込んでおいた Gateway API を有効化します。Gateway API の CRD を導入し、Traefik をゲートウェイ実装に、cert-manager で TLS 証明書を発行して、ブラウザから https://fanclub.local へ HTTPS でアクセスできる状態を作ります。第16回で保留した「外部 → Frontend」の NetworkPolicy 許可も、このとき足して 4 層のホワイトリストを完成させます。
シリーズ一覧
第1部:コンテナと Docker
- 第1回 コンテナ技術概念 + Docker 環境準備
- 第2回 Docker 基本操作
- 第3回 Dockerfile + マルチステージ + JDK 25 / Payara Micro イメージビルド
- 第4回 コンテナレジストリ + イメージタグ戦略 + Trivy スキャン
第2部:Kubernetes 基礎
第3部:アプリリソース
- 第7回 Pod + Multi-container パターン
- 第8回 Service とネットワーキング
- 第9回 ストレージ(PVC + StatefulSet)+ PostgreSQL DB 追加
- 第10回 ConfigMap + Secret + ServiceAccount 基礎
- 第11回 Job + CronJob + DaemonSet
第4部:ワークロード戦略
- 第12回 Deployment + 3 Probe + Rolling Update + Probe デバッグ実践
- 第13回 Deployment 戦略補完(Blue/Green + Canary + Recreate)
- 第14回 ResourceQuota + LimitRange + Multi-tenant Namespace
第5部:セキュリティ基礎
第6部:パッケージ管理 + HTTPS 公開
- 第17回 Helm v4 + chart 化 + フルサイクル ← 今ここ
- 第18回 Gateway API + HTTPRoute + Ingress 対比 + Traefik + cert-manager
- 第19回 fanclub-api 全機構連携の実技総ざらい総合演習 + 第1巻完走宣言 ★
