デプロイ戦略

SRE Workbook 第16章(カナリアリリース)、Pete Hodgson の Feature Toggles、 Martin Fowler の Blue Green Deployment を取得して読み、 理解した内容を自分の言葉で書いたもの。本文の引用はしていない。

リリースとデプロイ出す仕組みの話なら、こちらは出し方の話。

前提: リリースプロセスの条件

カナリアを成立させるには、その手前が整っている必要がある。

同じ入力から同じ成果物が出る(再現可能なビルド)
コミットを引き金に自動でビルドされる
成果物が自動テストで検証される
人ではなくシステムがデプロイする
1回のデプロイに含まれる変更が小さく、自己完結している

リリースとデプロイの密閉ビルドとセルフサービスが、そのまま前提になる。

カナリア

変更を部分的かつ期間を区切って投入し、評価すること。

母集団の大きさと期間はトレードオフ

制約内容
開発速度が期間を決める日次リリースなら週次より短い。1日20回以上の継続的デプロイなら、窓は大幅に圧縮される
同時に複数走らせない認知負荷が上がり、シグナルが混ざる。同時に走らせるカナリアは1つだけにする
代表性には量が要る処理レートが高いほど、代表的な標本を得るのに必要な時間は短くて済む。クエリの種類が多様なら母集団を大きく、均質なら少なくてよい
時間帯が効く性能の欠陥はピーク時にしか現れない。 閑散時のカナリアは取り逃す

何を見るか

指標の条件は3つ。

実際の問題を示すこと      CPU使用率のような周辺のシグナルより SLI を優先する
その変更に帰属できること   インフラのばらつきや外部要因ではなく、投入した変更に起因すること
前後比較をしないこと      時間による変化とデプロイの影響が混ざる

指標が多すぎると効果が逓減する。 上位10数個に絞る。

ノイズに埋もれる問題

具体例が分かりやすい。

カナリアをトラフィックの 5% に投入
カナリア側のエラー率が 20%

→ 全体のエラー率は 1% にしかならない
→ 平常時のノイズと区別がつかない

対処はカナリア群と対照群で指標を分けて見ること。 全体の平均で見ない。

集計の粒度にも注意する。 カナリアを30分走らせるなら、 指標も30分以下の粒度で集計する。1時間粒度だと無関係な事象が混ざる。

影響範囲(blast radius)

上の数字は、裏返せばカナリアの利点そのもの。 **5%の母集団なら、そこが20%失敗しても全体では1%**にとどまる。 エラーバジェットの大半が残る。

段階を分ける

確信度の増加に合わせて広げる。

段階母集団見る指標
第1段階最小明確な失敗のみ。クラッシュ、リクエストの失敗
第2段階より大きくレイテンシ、資源使用量などを追加

依存と分離

カナリアと対照群は、バックエンド・データストア・ネットワークを共有していることが多い。 カナリア側の悪い挙動が、対照群にも悪影響を与えうる。

だから A/B の比較だけでなく、SLO の絶対値でも全体の健全性を確認する。

取り逃すもの

  • 性能の欠陥は高負荷でしか現れないので、閑散時のカナリアでは見えない
  • 時間は観測される指標の最大の変動要因なので、前後比較は当てにならない
  • 非対話的なシステム(レンダリング、動画エンコード)では、 1つの作業単位の処理時間以上、カナリアを走らせる必要がある

ロールバック

カナリアの指標が対照群から大きく外れたら、自動でロールバックする。

小さいカナリアなら、検知とロールバックが低いコストで完了する。 復旧までの総時間は結局それなりにかかるとしても、 露出を制限しているので累積の被害が小さい

閾値は「これ」という規定がなく、技術的・事業的な目的を満たす最も単純なモデルを、 組織のリスク許容度に合わせて選ぶ、とされている。

フィーチャートグル

デプロイと機能公開を切り離す仕組み。 トランクベース開発で 「実行時に無効化した状態でコミットする」と書いたものの実体。

4種類

寿命と動的さの2軸で分類する。 これが実装方法を決める。

種類目的寿命動的さ
リリーストグル未完成の機能を隠し、トランクベース開発を可能にする数日〜数週静的
実験トグルA/B テスト、多変量解析。利用者をコホートに分ける数時間〜数週リクエスト単位で動的、設定は静的
運用トグル高負荷時に重要でない機能を落とす、障害時に無効化する短命。ただし恒久的なキルスイッチも持つ再デプロイなしで即座に切り替わる必要がある
権限トグル特定の利用者群にだけ公開(有料顧客、ベータ、社内)数年利用者ごとに動的
  • 寿命が短いなら単純な if/else で足りる
  • 寿命が長いなら、コードの散乱を防ぐ抽象化が要る
  • 動的さが高いなら、コホートや権限のロジックを持つルーターが要る

種類ごとに違う扱いをするのが要点。全部を同じ仕組みで管理しない。

実装

条件分岐をコードのあちこちに撒かない。

判断点をロジックから切り離す
  トグルの判断を集約したモジュール(FeatureDecisions)を作る
  トグル名の文字列がシステム中に散らばるのを防ぐ

依存性注入にする
  コンポーネントがグローバルなトグルルーターを見に行くのではなく、
  生成時に判断結果を注入する
  業務ロジックとトグルの基盤が分離され、テストも楽になる

Strategy パターンを使う
  条件分岐を、差し替え可能な関数に置き換える
  生成時にどちらの関数を渡すかを、トグルの判断で決める

設定の置き場所

可能なら、ソース管理下の静的な設定にする。 そうすればトグルの状態もコードと同じように配信パイプラインを通り、 環境をまたいで一貫した検証ができる

実行時の再設定が要る場合は、必要性に応じて段階的に上げる。

環境変数によるパラメータ化
設定ファイル
管理UIを持つデータベース
Consul のような、全ノードに変更を通知する分散システム

テスト用にリクエスト単位で上書きする(Cookie や HTTP ヘッダ)手もあるが、 セキュリティのリスクが上がる

在庫として管理する

トグルは在庫であり、保有コストがかかる。 在庫はできるだけ少なく保つ。

1つ増えるごとに、テストの負担が増え、条件分岐が増え、保守の義務が生まれる。

対策。

トグルを作るときに、削除タスクをバックログに積む
トグルに有効期限を設定する
期限を超えたらテストを失敗させる「時限爆弾」を仕込む
システム全体で許容するトグルの総数に上限を置く

Knight Capital Group の4億6千万ドルの損失には、フィーチャーフラグの管理不備が関わっている。 トグルの負債は実害になる。

これは廃止の 「コードは資産ではなく債務」と同じ話で、トグルはその中でも特に腐りやすい。

テストの組み合わせ

すべての組み合わせをテストしない。 3通りで足りる。

1. 本番で想定される構成    現在の本番のトグル + これからリリースするものを ON
2. フォールバック          同じトグルを OFF
3. 全部 ON                 予期しない相互作用を捕まえる

前提として、OFF が旧挙動、ON が新挙動という意味づけを一貫させる。

テストからプロセス再起動なしにトグルを切り替えられるエンドポイントを用意すると、 検証のサイクルが速くなる。

ブルーグリーン

同一の本番環境を2つ持ち、ルーターの向き先を切り替える。

blue   いま本番のトラフィックを受けている
green  次のリリースを載せて、最後の検証をしている

検証が通ったら、ルーターを green に向ける。blue が待機側になる

カナリアが割合を少しずつ動かすのに対し、こちらは一度に全部切り替える。 違いは、戻し方にそのまま出る。

切り戻し
カナリア新バージョンへ向けた割合を戻す。既に受けた分の影響は残る
ブルーグリーンルーターを元に戻すだけ。 旧環境がそのまま生きている

旧環境を消さずに待機側として残すことが本体で、環境が2つあること自体ではない。

役割は巡回する

green が安定したら、blue が次のリリースの検証環境になる。 同じ2つの環境が、本番 → 直前バージョンの退避先 → 次の検証環境、と役割を回していく。

環境は別のハードウェアでも仮想マシンでもよく、 1つの環境を区画に分けたものでも成立する。必要なのは切り替えられることだけ。

副産物としての災害復旧訓練

ここが見落としやすい利点で、この仕組みはホットスタンバイと同じ仕組みになる。

つまりリリースのたびに、災害復旧の手順を実際に動かしていることになる。 「切り替えられるはずだが試したことがない」という状態にならない。

切り替えの瞬間にあるもの

green が本番になった後、切り替え中に blue 側で取りこぼした取引が問題になる。

対処は2つ。

両方の環境に取引を流し込み、blue を追随させて退避先として保っておく
切り替え前に読み取り専用にし、安定してから読み書きに戻す

後者は単純だが、その間サービスが書き込みを受け付けない。 どちらを取るかは、書き込みを止められる時間で決まる。

スキーマ変更を分離する

データベースのスキーマ変更は、アプリケーションの入れ替えと別のデプロイにする。

1. 新旧どちらのバージョンでも動くようにスキーマを変更し、これだけをデプロイする
2. 問題なく動いていることを確認する ← ここが切り戻し可能な地点
3. 新しいアプリケーションをデプロイする

2の時点で切り戻し先が確保されるのが要点。 スキーマとアプリを同時に出すと、アプリを戻してもスキーマが戻らないので、 ルーターを戻すだけでは復旧しない。ブルーグリーンの利点がそこで消える。

関連