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

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

フックはリリースの外に住んでいる

TT Labで続きを見る

一言でいうと

フックのリソースは、releaseが管理しません。そのため、ロールバックしても消えず、削除ポリシーを掛けないと、クラスターに積み上がります。

なぜ必要なのか

DBマイグレーションをデプロイに組み込む最もよくある方法が、pre-upgradeフックのJobです。ところが、このJobはreleaseの一部ではありません。Helmは、フックを適用して、完了を待ち、そのあとで、本体のマニフェストを適用します。フックで作られたJobオブジェクトは、releaseのSecretに入りません。

結果は2つです。

どう動くのか

metadata:
  annotations:
    "helm.sh/hook": pre-upgrade,pre-install
    "helm.sh/hook-weight": "-5"          # 작을수록 먼저
    "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded

hook-delete-policyの値3つは、意味が違います。

値 いつ削除するか
before-hook-creation 次のデプロイで、同じフックを作る直前(失敗したJobを残して、調査できる)
hook-succeeded 成功したら、すぐに
hook-failed 失敗したら、すぐに(ログが消える。たいてい悪い選択です)

実務の基本形は、before-hook-creation,hook-succeededです。成功したものは片づけ、失敗したものは次のデプロイまで残して、ログを見られるようにします。

よくある勘違い

フックが失敗するとロールバックされるという勘違いです。pre-upgradeフックが失敗すると、アップグレードが中断され、releaseはfailedのまま残ります。--atomicがあれば、ロールバックが掛かりますが、すでに実行されたマイグレーションは元に戻りません。

フックでやってはいけないこと

フックは便利なので、あらゆるものを入れたくなります。入れてはいけないものが3つあります。

時間のかかるデータ移行です。数百万行を移す作業は、デプロイを数十分間拘束します。その間、releaseがpending-upgradeでロックされて、ほかのデプロイが止まります。こうしたものは、デプロイと分離して別のJobで実行し、アプリケーションが2つのスキーマを両方読めるようにします。

外部システムに知らせる作業です。post-installフックでSlackに知らせるのはよくありますが、フックが失敗すると、デプロイ全体が失敗と表示されます。通知が届かなかったからといって、デプロイを失敗と見る理由はありません。こうしたものは、CI側に置きます。

テストです。helm testは別のコマンドで、helm.sh/hook: testを使います。デプロイの過程に混ぜると、テストが不安定になるたびに、デプロイが止まります。

フックが引っかかるとデプロイが止まる

フックのJobが終わらなければ、helm upgradeはずっと待ちます。--timeoutのデフォルトは5分で、その後は失敗として扱います。ところが、タイムアウトになっても、Jobはクラスターで動き続けます。Helmは、待つのをやめるだけで、Jobを殺しません。

ここで危険な状況が生じます。失敗と判断してもう一度デプロイすると、前のマイグレーションがまだ動いているのに、2つ目が始まります。同じテーブルにALTERを2つ同時に掛けると、ロック待ちでサービス全体が止まることがあります。

3つの方法で防ぎます。

spec:
  activeDeadlineSeconds: 240      # helm --timeout 5m 보다 짧게
  backoffLimit: 0
  template:
    spec:
      restartPolicy: Never

フックと通常のマニフェストの順序

1つのチャートの中にフックと通常のリソースが混ざっていると、順序が混乱します。実際の順序は、次のとおりです。

pre-install 훅  →  일반 매니페스트 적용  →  post-install 훅
   (weight 순)        (Helm 의 종류별 순서)      (weight 순)

ここで、よく踏む落とし穴があります。pre-installフックは、SecretやConfigMapがまだない状態で動きます。フックのJobがreleaseのSecretを参照すると、「そんなsecretはない」で失敗します。フックが使う値は、フック自身が作る(同じフックグループに、weightを低くしたSecret)か、releaseの外で事前に作っておく必要があります。

hook-weightは、文字列ですが、数値でソートされます。"10"と"9"を渡すと、9が先です。負の数を使う慣例が、ここから出てきました。-5を基本として、さらに先に動く必要があるものに-10を与えます。

実務で本当に大切なこと

元に戻せないマイグレーションは、デプロイと分離します。カラムを削除する変更は、3回に分けてデプロイします。まず新しいカラムを追加し(古いコードも動作)、次に新しいコードをデプロイし、最後に古いカラムを削除します。こうすれば、どの段階でロールバックしても、前後のバージョンがどちらも耐えられます。これを拡張-縮小(expand-contract)パターンと呼びます。