TT Lab
はじめる
学ぶ 学習パス コース

Helmのデプロイとロールバック

リリースはシークレット一つだ

TT Labで続きを見る

一言でいうと

Helmのreleaseは魔法ではなく、ネームスペースの中のSecret1つです。その事実を知れば、ロールバックが何を元に戻すのか、なぜ元に戻せないものがあるのかが、すべて説明できます。

なぜ必要なのか

helm rollbackを実行したのに、データベースのマイグレーションが元に戻らず、サービスが壊れた経験は、よくあります。逆に、「ロールバックしたのに、何も変わらなかった」という報告もよくあります。どちらも、同じ誤解から来ています。ロールバックが時間を巻き戻すと思っていることです。

Helmがやっていることは、はるかに単純です。helm installをすると、レンダリングされたマニフェスト全体をgzipで圧縮して、Secretに保存します。名前はsh.helm.release.v1.<릴리스>.v<리비전>(プレースホルダーはrelease名とリビジョンです)です。helm upgradeをすると、新しいリビジョンのSecretをもう1つ作ります。helm rollback 3は、リビジョン3のSecretを取り出して、そのマニフェストをもう一度applyすることです。

そのため、規則が導かれます。マニフェストに書かれていたものは、元に戻ります。マニフェストの外で起きたこと、つまり、マイグレーションが変えたデータ、Jobが作ったファイル、外部APIに送ったリクエストは、元に戻りません。

どう動くのか

自分で確認できます。

kubectl get secret -l owner=helm
kubectl get secret sh.helm.release.v1.demo.v1 -o jsonpath='{.data.release}' \
  | base64 -d | base64 -d | gzip -d | head -40

base64を2回デコードするのは変に見えますが、正しいです。Kubernetesが値を1回エンコードし、Helmがその中でもう1回エンコードしておいたのです。

helm historyは、これらのSecretの一覧です。リビジョンごとに状態があります。

状態 意味
deployed 今生きているリビジョン。常に1つだけです
superseded 以前デプロイされたあと、次のリビジョンに席を譲ったもの
failed 適用に失敗したリビジョン
pending-upgrade アップグレードを開始したのに終わっていないもの。ここに閉じ込められると、次のデプロイが止まります

よくある勘違い

ロールバックは、リビジョン番号を戻しません。helm rollback demo 1をすると、リビジョン1に戻るのではなく、リビジョン1の内容で新しいリビジョン3を作ります。historyを見ると、3行目にRollback to 1と書かれています。これは、監査記録を消さないための設計です。何があったかが、履歴に残る必要があります。

保持する個数には上限があります。デフォルトは10個です(--history-max)。古いリビジョンは削除されるので、「6か月前にロールバック」は、たいてい不可能です。元に戻せる範囲は、思ったより狭いです。

releaseのSecretを直接のぞいてみる

Helm 3は、releaseごとにSecretを1つ作ります。この構造を知っていれば、事故のときに手で復旧できます。

kubectl -n labhub-prod get secret -l owner=helm,name=labhub
NAME                             TYPE                 DATA   AGE
sh.helm.release.v1.labhub.v247   helm.sh/release.v1   1      2d
sh.helm.release.v1.labhub.v248   helm.sh/release.v1   1      1d
sh.helm.release.v1.labhub.v249   helm.sh/release.v1   1      3h

# 안을 풀어 본다 — base64 → gzip → JSON
kubectl get secret sh.helm.release.v1.labhub.v249 -o jsonpath='{.data.release}'   | base64 -d | base64 -d | gzip -d | jq '.info, .chart.metadata.version'

base64が2回あるのが落とし穴です。KubernetesのSecret自体が1層、Helmが保存するときにもう1層です。

ここから出てくる結論が2つあります。

3つの状態を区別する

helm list          → deployed 만 보인다
helm list --all    → failed, pending-upgrade, superseded 까지
helm history <릴리스> → 리비전별 상태와 설명

supersededは正常です。新しいリビジョンが出て、退いたのです。問題はpending-*です。この状態で残っていると、次のデプロイが拒否され、プロセスはすでに死んでいて、表示だけが残っているのです。

failedも、そのままにしてはいけません。次のデプロイはできますが、--atomicのロールバックの基準点がぼやけます。原因を直して、成功したデプロイを1回作っておくことが、整理です。

release名とリソース名

チャートのリソース名は、たいてい{{ .Release.Name }}-{{ .Chart.Name }}で作られます。そのため、release名を変えると、リソースがすべて新しく作られます。古いものは残り、新しいものができて、2つが共存します。

同じ理由で、release名は最初に慎重に決め、変える必要があるなら、古いreleaseを削除して新しくインストールする手順を計画します。PVCを使うワークロードなら、その間にデータを移すステップが入ります。

実務で本当に大切なこと

releaseがSecretだということは、サイズの上限があるという意味でもあります。etcdのオブジェクトの上限は1MiBです。チャートが大きくなると(特にCRDをたくさん入れると)、releaseのSecretがその上限にぶつかって、アップグレードが失敗します。そのとき出るエラーメッセージは、原因をまったく教えてくれないので、この構造を知っていることが、そのまま解決の速さです。