インフラエンジニアの羅針盤

インフラエンジニア1〜3年目のための技術ガイド

本番移行計画|戻せる期限から書く

公開

第12回で、設計どおり動くことを検証しました。次に決めるのは、それを本番のトラフィックにどう乗せ換えるかです。

この工程には、実務でよく見る失敗が2つあります。1つは手順書から書き始めること。もう1つは、上長に「無停止で移行します」と説明したものの、何が何分止まるのか、何時まで元に戻せるのかを自分で計算できていないことです。

本記事は、この2つを同じ方法で潰します。移行計画で最初に決めるのは手順ではなく、切り戻せなくなる時点です。そこを特定してから、逆算してタイムラインを組みます。

本記事はGoogle Cloud(GKE)を前提としますが、第1回から第12回までと1つ条件が違います。移行元がGoogle Cloudの外にあることです。旧環境はオンプレミスか既存のVMであり、新旧を同時に動かす期間が必ず存在します。

目次
  1. 移行計画は、戻せる時間から書く
    1. この記事で作る成果物
  2. 移行戦略は3つあり、選ぶ軸も3つある
    1. 3つの戦略を、切り戻しに必要な時間で並べる
    2. Strangler Fig の前提は、継ぎ目を作れること
    3. 継ぎ目は、共有データベースの外側にしか引けない
    4. セッションを外部化していないと、段階移行は選べない
    5. 並行稼働の月数 × 二重費用が、安全策の値札
  3. 切り替えは、DNSではなくロードバランサで行う
    1. TTLはクライアントが守るとは限らない
    2. 同じロードバランサの下に、新旧を並べる
    3. DNSを使うのは、IPアドレスが変わるときだけ
    4. 重み0は「送らない」を意味するとは限らない
    5. 切り替わるのは、入ってくる経路だけではない
  4. 段階移行の刻み幅は、率ではなく観測できる母数で決める
    1. 1%で何が分かるかを、計算で出す
    2. 重みを上げる速度は、新環境が追いつく速度で制限される
    3. 各段で見る値を、先に決める
    4. 重みを0にしても、旧環境への通信は即座には止まらない
    5. 戻す条件を、進む条件と同時に書く
  5. ダウンタイムの起点は、トラフィックではなくデータ
    1. 時間が始まるのは、旧環境の書き込みを止めた瞬間
    2. 遅延がゼロでなくても進める、という選択がある
    3. プロモートした時点で、切り戻せなくなる
    4. アプリケーションを変えながら移行すると、切り戻せなくなる
    5. 切り戻し経路は、プロモートより前に作ってテストする
    6. 読み取り専用という中間状態を設計する
  6. Go-Live判定は、移行日には作れない
    1. 判定基準は、第12回の受け入れ基準から作る
    2. 所要時間の数値は、リハーサルでしか出ない
    3. 判定項目に「戻せる時間が残っているか」を入れる
    4. 判定の時刻・権限・中止の宣言方法を先に決める
  7. ロールバック手順は、2つに分けて書く
    1. トラフィックの切り戻し(分単位)
    2. データの切り戻し(時間単位・期限あり)
    3. 戻さないという選択肢を、判定の選択肢に入れる
  8. 【実務テンプレート】本番移行・トラフィック切り替え計画書
    1. セクション1〜4:前提と準備
    2. セクション5〜6:段階移行とタイムライン(表)
    3. セクション7〜10:判定・切り戻し・移行後
  9. 次回予告

移行計画は、戻せる時間から書く

第12回で作った受け入れ基準は、繰り返す検証のためのものでした。本記事の判定は一度きりです。使う数値は同じですが、不合格だったときに起きることが違います。

第12回(カオス・E2Eテスト)本記事(本番移行)
実施回数繰り返す一度きり
基準の名前受け入れ基準Go-Live判定基準
不合格のとき設定を直して再実験する中止して切り戻す
時間の制約次の実験日まで待てる切り戻せる時間に期限がある

右の列に「期限」という語が入っている点が、本記事の全体を規定します。手順書から書き始めると、この期限が最後まで空欄のまま移行日を迎えます。そして当日、障害が出てから「これは戻せるのか」を初めて考えることになります。

この記事で作る成果物

3つです。

  1. 移行計画書。戦略の選択、切り替え機構、カットオーバーのタイムライン
  2. ロールバック手順書。トラフィックとデータの2本立て(理由は後述します)
  3. Go-Live判定チェックリスト。移行日より前に作るもの

2番目が2本立てになる理由が、本記事の中核です。「即時ロールバック」の即時は、トラフィックにしか当てはまりません。データには期限があり、その期限は設計で動かせます。

移行戦略は3つあり、選ぶ軸も3つある

移行戦略の選択は、好みでも新しさでもありません。3つの軸で決まります。

  • 継ぎ目(seam)を作れるか。経路を分けられる単位がシステムに存在するか
  • データの向き。新旧のどちらが書き込みの正本か。両方が書き込む構成を選ぶと、切り戻しの難易度が跳ね上がります
  • 切り戻しに必要な時間。許容できる時間に収まるか

先に用語を1つ固定します。本記事のBlue-Greenは「新旧2つの環境を並行させる移行戦略」の意味で使います。平時のデプロイ方式としてのBlue-Greenやカナリアリリースは第9回の管轄であり、本記事では扱いません。本記事が扱うのは、一度きりのカットオーバーです。

3つの戦略を、切り戻しに必要な時間で並べる

移行戦略の比較。判断材料は切り戻し時間と費用の形です
戦略切り替えの単位並行稼働の期間トラフィックの切り戻しデータの切り戻し費用の形
Big Bang全体を一度になし、またはごく短時間経路を戻すだけ(分)新環境で書き込みが発生した分だけ困難二重費用の期間が最短
Strangler Fig機能・エンドポイント単位数か月単位ごとに戻せる単位ごとに小さい二重費用が長期間
Blue-Green環境まるごと2系統数日から数週間経路を戻すだけ(分)データの同期方式に依存一時的に2倍

どれも成立します。Strangler Fig を提唱した Martin Fowler が Big Bang について書いているのは、「重要なITシステムの置き換えには長い時間がかかり、その間ユーザーは新機能を待てない」「既存の挙動の詳細を把握するのが難しい」という点です。切り替え方式そのものの否定ではありません。

したがって判断はこうなります。切り戻しに必要な時間が許容範囲に収まるなら、Big Bang は最も安い選択肢です。収まらないとき、そして収まらない理由がデータの量や複雑さにあるとき、初めて分割を検討します。

Strangler Fig の前提は、継ぎ目を作れること

Strangler Fig は Fowler が2001年に提示した考え方です(当初は Strangler Application という名称で、後に植物学上の由来を強調して Strangler Fig Application に改称されました)。原典が前提に置いているのは、分割を可能にする継ぎ目(seam)をシステムに挿入できることです。

実務では、継ぎ目を引ける場所は次のどれかです。

  • URLのパス。特定のパス配下だけを新環境へ向ける
  • ホスト名。サブドメイン単位で分ける
  • イベント・メッセージの購読単位。特定のキューの消費側だけを新環境に置く
  • バッチジョブの単位。ジョブ単位で実行環境を移す

このどれにも当てはまらないなら、Strangler Fig は選べません。「段階的に移行したい」という要望に対して最初に確認するのは、組織の合意ではなく、経路を分けられる単位が存在するかどうかです。存在しないなら、選択肢は Big Bang と Blue-Green の2つに減ります。

ここは第10回とつながります。第10回でマニフェストの抽象化の単位を決めるとき、カタログの単位・ラベルの単位・責任の単位を一致させると書きました。移行の継ぎ目も同じ単位に揃えられるなら、移行後の運用がそのまま第10回の設計に乗ります。揃えられないなら、移行のためだけの一時的な分割が残ります。

継ぎ目は、共有データベースの外側にしか引けない

継ぎ目の候補を4つ挙げましたが、実務ではもう1段の判定が要ります。その単位を新環境へ移したとき、データベースはどちらに残るのかという判定です。

URLのパスで分割できたとしても、新旧の両方が同じデータベースを見ている状態では、移したのはアプリケーションだけで、データは1つのままです。この構成自体は動きますが、次の制約が付きます。

  • データベースの移行は、最後に一度だけ、全体で実施することになります。段階的に移せるのはアプリケーションだけです
  • 新環境から旧環境のデータベースへ接続する経路が、移行期間中ずっと必要です。オンプレミスにデータベースが残るなら、その通信のレイテンシがそのまま新環境の応答時間に乗ります
  • スキーマ変更は、新旧の両方のアプリケーションが動く形でしか実施できません

2つ目は見積もりに直結します。オンプレミスとGoogle Cloudの間の往復が1回あたり数ミリ秒だとしても、1リクエストで数十回のクエリを発行する画面では、その数十倍が加算されます。移行前に問題なかった画面が、移行後に目に見えて遅くなる典型的な原因です。段階移行の各段で「レイテンシの分位点」を見る理由の1つがこれです。

したがって、継ぎ目を選ぶときの優先順位はこうなります。データベースごと切り出せる単位が最も望ましく、次がアプリケーションだけを移せる単位です。前者に該当する単位が1つもないなら、Strangler Fig を選んでも「アプリだけ先に移して、データベースは最後にBig Bang」という構成になります。それは戦略の名前が違うだけで、最も難しい部分を先送りしただけです。計画書には、その事実を書いてください。

経路の確保も忘れないでください。新環境から旧環境のデータベースへ向かう通信は、第5回で設計したNetworkPolicyの送信規則を通過する必要があります。NetworkPolicyを既定拒否で組んでいるなら、移行期間中だけ許可する規則が要ります。そして移行が終わったら、その規則を削除するところまでを計画に含めてください。「移行のときに開けた穴」は、恒久的な例外として残りやすい種類の設定です。

なお、StatefulSet で新環境にデータベースを置くか、マネージドサービスを使うかという選定は第7回の管轄です。本記事で決めるのは、移行の単位としてデータベースをどちら側に置くかだけです。

セッションを外部化していないと、段階移行は選べない

ここが、移行戦略の選択に最も直接的に効く制約です。Google Cloud の公式ドキュメントは、重み付きのトラフィック分割について次のように明記しています。

Don’t configure session affinity if you’re using weighted traffic splitting. If you do, the weighted traffic splitting configuration takes precedence.

セッションアフィニティを効かせながら、重みで新旧に分けることはできません。両方を設定した場合、優先されるのは重み付きの分割のほうです。

結果として何が起きるかというと、セッションをアプリケーションのメモリに持っているシステムでは、リクエストごとに新旧を行き来し、ログイン状態が失われます。1%だけ新環境に流すつもりが、利用者から見ると「操作の途中で何度もログアウトする」という障害になります。

したがってセッションの外部化は、段階移行の前提条件です。移行日より前に完了していなければならない作業であり、完了していないなら選べる戦略は Big Bang だけになります。

第1回を読み返してください。条件3(水平分散不可)で問題にした「状態を自分の内部に抱えたまま複製に耐えられない設計」という論点が、ここでは移行計画の制約として再登場します。採用の判定を通ったシステムでも、セッションの持ち方は移行の直前まで手が付かないまま残りがちです。移行計画の最初のタスクとして、セッションの保持先を実装を見て確認してください。設計書ではなく実装です。

並行稼働の月数 × 二重費用が、安全策の値札

Strangler Fig は安全ですが、代償があります。新旧が同時に動く期間が長いということは、その期間ずっと二重の費用を払うということです。

第1回で確定したK8s税は、リージョンクラスタ1環境で月約$160、Prod / Stg / Dev の3環境で月約$407でした。移行期間中は、これに旧環境の費用がそのまま乗ります。加えて、ハイブリッド接続(後述)の費用も並行稼働の期間だけ発生します。

したがってStrangler Fig を選ぶなら、並行稼働の月数を先に決めて費用を出してください。出さないまま始めると、移行が終わらないまま予算が尽きるという結末になります。実務でよく見るのは、最初の2つか3つの継ぎ目を移した時点で優先度が下がり、新旧が並んだまま数年が経過する状態です。

これを防ぐために、計画書に旧環境の停止日と削除日を、日付で書いてください。第8回で「一時環境は削除まで手順に含める」と決めたのと同じ形です。日付が書けないなら、その戦略は選ばないほうがよい、という判定に使えます。

切り替えは、DNSではなくロードバランサで行う

「移行の切り替え」と聞いてDNSを思い浮かべる読者が多いはずです。本記事はそれを第一選択にしません。理由は好みではなく、DNSで切り替えると、旧環境をいつ停止してよいかを判定できなくなるためです。

TTLはクライアントが守るとは限らない

Java の InetAddress のドキュメントを読むと、名前解決のキャッシュについて次のように書かれています。

  • networkaddress.cache.ttl の既定は「an implementation specific period of time」=実装依存の期間
  • セキュリティマネージャが導入されている環境では「the result of positive host name resolutions are cached forever」=成功した名前解決は永久にキャッシュされる
  • -1 は「cache forever」を意味する
  • 失敗した解決のキャッシュ(networkaddress.cache.negative.ttl)の既定は10秒

注目すべきは1点目です。既定の説明に「DNSのTTLに従う」とは書かれていません。実装依存の期間、と書かれています。そして2点目のとおり、条件によっては永久にキャッシュします。

したがって、TTLを60秒に下げても、旧環境へのアクセスが止まる時刻は計算できません。これは設定ミスでも、リゾルバの不具合でもありません。クライアント実装の仕様です。「TTLを下げたので5分で切り替わります」という説明は、接続してくるクライアントの実装をすべて確認していない限り成り立ちません。そして外部の利用者が接続してくるシステムでは、確認は原理的に不可能です。

誤解のないように書くと、TTLを下げること自体は有効です。ただし目的が違います。

  • 切り替えを速くするため、ではありません。キャッシュを持ち続けるクライアントには効きません
  • 切り戻しを速くするため、です。切り戻しの所要時間の上限は、TTLを下げていなければ元のTTLの値になります

そして下げる作業は、移行の数日前に済ませてください。下げた値がリゾルバに行き渡るまでに、元のTTLの時間がかかるためです。TTLを86400秒(24時間)で運用していたなら、下げた当日には効きません。

同じロードバランサの下に、新旧を並べる

第一選択はこちらです。Google Cloud にはハイブリッド接続NEGという仕組みがあり、「オンプレミスのデータセンターや他のパブリッククラウド」のエンドポイントを、Google Cloud のロードバランサのバックエンドにできます。ネットワークエンドポイントの種類は NON_GCP_PRIVATE_IP_PORT です。

公式ドキュメントは、この機能の用途として移行を明記しています。

temporarily set up a hybrid deployment that lets you route traffic to both your current on-premises service and a corresponding Google Cloud service endpoint

この方式の利点は1つに集約されます。利用者から見えるIPアドレスとDNSレコードが変わりません。切り替えはロードバランサの重みの変更だけで完結し、戻すのも重みの変更だけです。クライアント側のキャッシュは関係しません。

重みの仕様も確認しておきます。グローバル外部アプリケーションロードバランサでは、バックエンドサービスごとに0から1000の重みを設定できます。公式の例示は「99%を安定版のサービスへ、1%を新しいバージョンのサービスへ」という段階的な展開です。

ただし、この方式には準備が要ります。見落とすと計画が丸ごと崩れる前提条件なので、先に挙げます。

前提条件内容
接続手段Cloud Interconnect の VLAN アタッチメント、Cloud Router を伴う Cloud VPN トンネル、または Router appliance VM のいずれか
ルーティングglobal dynamic routing を有効にした Cloud Router が必須。regional dynamic routing は非対応
エンドポイントの種類1つのNEGには同じ種類のエンドポイントしか登録できない

接続の開通そのものに日数がかかります。Cloud Interconnect であれば、物理的な回線の手配が入ります。したがって移行日から逆算して、接続の開通を計画の最初のマイルストーンに置いてください。ここが遅れると、後続のすべてが動きません。

なお、移行元がGoogle Cloud上の既存VMなら、この前提条件は不要です。同じVPC内でインスタンスグループをバックエンドに追加するだけで済みます。移行元がどこにあるかで、準備期間が数週間単位で変わります。

DNSを使うのは、IPアドレスが変わるときだけ

切り替え点は、経路のどこで新旧を分けるかで決まります。層は3つあります。

切り替え点にはクライアントの設定、DNSレコード、ロードバランサのバックエンドという3つの層があり、旧環境への到達が止まる時刻を見積もれるのはロードバランサで切り替えたときだけであることを示した図
上の2層で切り替えると、旧環境への到達が止まる時刻を確定できません。IPアドレスが変わらない切り替えを選べば、その2層に触れずに済みます。

3行目を忘れないでください。社内システムからの連携で、接続先のIPアドレスを設定ファイルに直接書いている相手が存在するシステムでは、DNSもロードバランサも切り替えの完了を保証しません。この場合、移行計画には「連携元の一覧」と「それぞれの設定変更の依頼先と期限」が必要になります。技術ではなく調整の作業であり、所要期間が最も読めない部分です。早い段階で洗い出してください。

DNSで段階的に移す必要がある場合の仕様も、確認しておきます。Cloud DNS のルーティングポリシーは重み付きラウンドロビン(WRR)に対応しており、0から1000の重みを設定できます。ヘルスチェックを併用でき、その間隔は30秒から300秒です。パブリックゾーンでヘルスチェックする場合、対象のエンドポイントがインターネットから到達可能である必要があります。

設計上の制約が1つあります。

For a resource record set, you set either a routing policy (routingPolicy) or DNS data (rrdatas), not both.

同じ名前・タイプで、通常のレコードとルーティングポリシーは併用できません。段階移行を始める時点でレコードの形式そのものが変わり、終わったら通常のレコードに戻すことになります。この変更自体が2回の切り替え操作になる点を、タイムラインに書いてください。

重み0は「送らない」を意味するとは限らない

Cloud DNS の重み付きラウンドロビンには、知らないと計画が崩れる挙動があります。

If you configure multiple targets, all with weight 0, traffic is distributed equally among the targets.

すべてのターゲットの重みが0なら、トラフィックは均等に分散されます。0は「送らない」ではなく、この場合は「全部に等しく送る」として扱われます。

さらにヘルスチェックを併用した場合、重み0のターゲットは常に健全とみなされ、重みが0でないターゲットがすべて不健全になったときにトラフィックが送られます。つまり重み0は、実質的にフォールバック先として機能します。新環境で障害が起きたとき、意図せず旧環境へ流れる構成になっている可能性があります。

この挙動から導かれる実務上の指針は1つです。計画書の完了条件を、設定値ではなく観測値で書いてください。

  • 不可:旧環境の重みを0に変更した
  • 可:旧環境のアクセスログが0件になったことを確認した

第7回で「バックアップの完了条件は、復元テストに成功したこと」と決めたのと同じ形です。設定したことと、そうなったことは別の事実です。

切り替わるのは、入ってくる経路だけではない

ここまでは利用者から入ってくる経路の話でした。移行では、新環境から外に出ていく経路も変わります。そして出ていく側のほうが、準備に時間がかかります。

変わるものは2つです。

  • 送信元IPアドレス。外部システムへ接続するときの見え方が変わります
  • TLS証明書の提示元。新環境で同じFQDNの証明書を用意する必要があります

1つ目が実務上の難所です。GKEのPodのように内部IPアドレスしか持たないリソースが外部へ通信すると、Cloud NAT を経由して共有の外部IPアドレスに変換されます。Google Cloud のドキュメントは、NATのIPアドレスを手動で割り当てる理由として次の状況を挙げています。

a destination service might only allow connections from known external IP addresses

接続先が送信元IPアドレスで許可リストを組んでいる場合、新環境のIPアドレスを事前に登録してもらう必要があります。そして相手が社外であれば、この依頼は書面の手続きになり、数週間かかることがあります。

ここは第1回とつながります。第1回の条件3(水平分散不可)で、判定観点の1つに「相手システムが送信元IPを許可リストに登録している」を挙げました。その観点が、移行ではリードタイムの問題として再登場します。技術的には手動割り当てを設定するだけの作業ですが、依頼と回答の往復が計画の最長経路になることがあります。

したがって移行計画の初期に、次の一覧を作ってください。

  • 新環境から接続する外部システムの一覧
  • そのうち、送信元IPアドレスで制限しているものはどれか
  • 登録の依頼先と、依頼から反映までの期間
  • 移行期間中、旧環境と新環境の両方のIPアドレスを許可してもらえるか

最後の項目が抜けると、段階移行そのものが成立しません。どちらか一方しか許可されないなら、その連携についてはBig Bangしか選べないということです。継ぎ目を引けるかどうかの判定に、この条件を加えてください。

段階移行の刻み幅は、率ではなく観測できる母数で決める

段階移行の計画では「1% → 10% → 50% → 100%」という刻みが広く使われます。この数列をそのまま採用しないでください。1%が意味を持つのは、母数が足りているときだけです。

1%で何が分かるかを、計算で出す

毎分1,000リクエストのシステムで1%を新環境に流すと、新環境には毎分10リクエストしか届きません。この状態で10分観測しても、届いたのは100リクエストです。エラー率が0.5%悪化していたとしても、期待される失敗は0.5件であり、検出できません。

各段の滞留時間の下限は、次の割り算で出します。

滞留時間の下限 = その劣化を検出するのに必要なリクエスト数
                 ÷ その段で新環境に流れる毎分のリクエスト数

分子は、検出したい劣化の大きさから決めます。「エラー率が1%悪化したら止める」という基準なら、数百件では判断できません。分子を決められないなら、その段に置く意味がありません。

この計算から導かれる結論は、多くのシステムで同じ形になります。母数の小さいシステムでは、1%という段を置くこと自体に意味がありません。最初の段を10%や25%に置くほうが、同じ時間で判断材料が揃います。刻み幅は慣習ではなく、システムのリクエスト量から決まります。

これは第12回の観測窓と同じ構造です。第12回では、ノード障害の実験の観測窓に5分という下限があり、それを下回る実験は「復旧しない」という誤った結論を出すと書きました。段階移行の滞留時間も同じで、下限を計算で出し、それを下回る計画は結論を出せません。

重みを上げる速度は、新環境が追いつく速度で制限される

刻み幅には、もう1つの下限があります。新環境が負荷に追いつけるかどうかです。

重みを10%から50%へ上げると、新環境へのリクエストは5倍になります。このとき何が起きるかは、第11回で設計した値で決まります。

  • HPAのスケールアウトは、安定化ウィンドウ0秒で即時に判断されます。ただし判断が即時なだけで、新しいPodが応答を返せるようになるまでには起動時間がかかります
  • 起動直後のPodは、--horizontal-pod-autoscaler-cpu-initialization-period(5分)の対象になり、CPU使用率がスケールの判断から除外されます
  • ノードに空きがなければ、ノードの追加から始まります。第4回で設計したノードプールの構成と、Spot VMの比率がそのまま効きます

したがって、各段の滞留時間には新環境が定常状態に落ち着くまでの時間も含める必要があります。前の段の数値が安定してから次の段へ進むのは、判定のためだけではありません。スケールが追いついた状態で測るためです。

実務上の指針としては、1回の変更で重みを5倍以上に上げないでください。上げるなら、その段の滞留時間をPodの起動時間の数倍以上に取ります。「深夜のうちに100%まで持っていく」という計画は、最も負荷の低い時間帯にスケールの検証を行い、翌朝の本番の負荷を未検証のまま迎えることになります。

逆に、移行の時間帯を負荷の低い時間に置くこと自体は正しい判断です。矛盾するようですが、両立させる方法があります。深夜に切り替えを完了させたうえで、翌営業日のピークを通過するまでを移行期間とみなし、その間は切り戻し可能な状態を維持することです。判定点を「切り替え直後」と「翌営業日のピーク通過後」の2つに分けてください。

各段で見る値を、先に決める

見る値は4つです。

  1. エラー率。新環境と旧環境を分けて計測します
  2. レイテンシの分位点。平均ではなく95パーセンタイル以上を見ます
  3. 新旧の応答の差分。同じ入力に対して同じ結果が返るか
  4. 切り戻し可能性。まだ戻せる状態か(次章で詳述します)

1つ目に条件が付いている点が重要です。新旧を合算した数値を見ていると、10%の段では劣化がほとんど見えません。新環境のエラー率が10%に達していても、全体では1%の上昇にしか見えないためです。アラートの閾値が全体に対して設定されていれば、発報もしません。

したがって新旧を分けて計測できる状態になっていることを、移行日の前提条件(前日までの完了項目)に入れてください。移行当日に「新環境だけのエラー率が見られない」と気づくと、その日は判定ができません。メトリクスとダッシュボードの設計そのものは第15回の管轄ですが、移行中に何を分けて見るかの指定は、本記事の計画書に書く項目です。

重みを0にしても、旧環境への通信は即座には止まらない

各段で重みを下げるたびに、時間差が発生します。この時間差を計画に織り込んでいないと、判定そのものが狂います。

コネクションドレイニングの仕様を確認します。タイムアウトは0から3600秒の範囲で設定でき、公式は次のように書いています。

It can take up to 60 seconds after your specified timeout duration has passed for the instance to be terminated.

複数のバックエンドサービスに設定がある場合は、最大値が適用されます。そして、最も見落とされるのが次の記述です。

If you’re using connection pooling, you might see that new requests … are still being received on VMs that are getting drained.

接続プールを使うクライアントは、ドレイン中のバックエンドへ新しいリクエストを送り続けます。接続の使い回しが目的の仕組みなので、当然の挙動です。多くのアプリケーションフレームワークとHTTPクライアントが、既定で接続プールを持ちます。

ここから2つの帰結が出ます。

  • 旧環境を停止してよい時刻は、重みを変更した時刻から計算できません。計画には「重み変更から30分後に停止」ではなく、「旧環境のアクセスログが30分間0件であることを確認してから停止」と書きます。待機時間の下限は、ドレインの設定値 + 60秒 + クライアント側の接続プールの寿命です
  • 重みを下げた直後の数値で判定しないでください。まだ旧環境に流れている状態で新環境を評価することになります。滞留時間の下限には、この時間差も含めます

戻す条件を、進む条件と同時に書く

各段には「次の段へ進む条件」と「前の段へ戻す条件」を両方書きます。戻す条件を書いていない計画では、劣化が見えたときに「もう少し様子を見る」という選択が発生します。そして様子を見ている間に、切り戻せる時間が減ります。

第12回で「実験の中止条件を先に書く」と決めたのと同じ理由です。作業中は、注入した変化と本物の障害を区別できません。移行中も同じで、目の前のエラーが移行によるものか、たまたま同時刻に起きた別の問題かは、その場では判別できません。判別を待つのではなく、閾値を超えたら戻すと決めておきます。

段ごとに記入する項目は次の6つです。

  • 段(重みの値)
  • その段で新環境に流れる毎分のリクエスト数
  • 滞留時間の下限(計算値)
  • 進む条件
  • 戻す条件
  • 判断者

ダウンタイムの起点は、トラフィックではなくデータ

ここまでトラフィックの話をしてきました。しかし止まる時間を決めているのは、経路の切り替えではありません。データです。

時間が始まるのは、旧環境の書き込みを止めた瞬間

Cloud SQL への移行手順(Database Migration Service)を読むと、カットオーバーの節に次の一文があります。

Stop all writes, running scripts, and client connections to the source database. The downtime period begins here.

ダウンタイムはDNSを切り替えた瞬間ではなく、旧環境のデータベースへの書き込みを止めた瞬間から始まります。カットオーバーのタイムラインは、経路の操作からではなく、この時刻から書き始めてください。

停止時間の内訳は5つです。

  1. 書き込みの停止(停止の確認を含む)
  2. レプリケーション遅延がゼロになるまでの待機
  3. 移行先の昇格(プロモート)
  4. 接続先の切り替え
  5. 動作確認

「無停止で移行します」と説明する前に、この5つの合計が何分になるかを計算してください。合計が出せないなら、無停止という説明は根拠を欠いています。

そして1つ目に落とし穴があります。「書き込みを止める」は、アプリケーションを止めることと同じではありません。止まっていない経路が残ります。

  • 定期実行のバッチジョブ(cron、ジョブスケジューラ)
  • 運用チームが手動で流す集計スクリプト
  • 外部システムからの連携(ファイル取り込み、Webhook)
  • 管理画面からの操作
  • 監視やバックアップのツールが書き込むテーブル

止める対象を一覧にして、それぞれの停止方法と、停止したことの確認方法を書いてください。確認方法まで書かないと、当日「たぶん止まっている」という状態でプロモートすることになります。

遅延がゼロでなくても進める、という選択がある

2つ目の待機について、公式は次のように書いています。

You can promote a migration even if the replication delay isn’t at zero. This can reduce the database downtime, but may affect the accuracy of the data in the destination.

待つか進むかは、ダウンタイムとデータの正確性の交換です。この判断を移行日に初めて考えることになると、時間に追われた状態で正確性を捨てる決定をすることになります。会議室に人が集まり、利用者への告知時刻が迫っている状況で、冷静な判断は期待できません。

したがって計画書に、遅延が何秒以下ならプロモートしてよいかを数値で先に書いてください。そして、その数値を超えていたらどうするか(待つのか、中止して日を改めるのか)も同時に書きます。中止という選択肢を書いておかないと、実質的に「進む」しか残りません。

プロモートした時点で、切り戻せなくなる

本記事で最も重要な一文です。

At this point, you cannot stop or undo the promotion process.

プロモートによって、移行先のインスタンスはソースから切り離され、レプリカからプライマリへ昇格します。そして、この操作は止めることも取り消すこともできません。

この一点の前後で、「切り戻す」という言葉の意味が変わります。

プロモート前プロモート後
データの正本旧環境新環境
切り戻しの作業経路を戻すだけ新環境で発生した書き込みを旧環境へ反映する
所要時間時間、または不可能
事前準備不要逆方向の経路が必要

移行計画のタイムラインには、プロモートの時刻を明示し、「ここより後の切り戻しは別手順」と書いてください。タイムラインの行に「この時点で切り戻せるか」という列を持たせるのが確実です。当日、その列を見れば判断できます。

カットオーバーのダウンタイムは旧環境への書き込みを止めた時刻から始まり、データベースの昇格を境に、切り戻しの手段がトラフィックを戻すだけの操作から逆方向の同期を要する作業へ変わることを時間軸で示した図
切り戻せなくなる時点を先に決め、そこから逆算してタイムラインを組みます。「即時ロールバック」が成り立つのは、この線の左側にいる間だけです。

アプリケーションを変えながら移行すると、切り戻せなくなる

切り戻せる期限を短くする要因が、もう1つあります。移行と同時にアプリケーションを変更することです。

移行の機会に、積み残していた改修をまとめて入れたくなります。フレームワークの更新、テーブル定義の見直し、設定の整理。しかし、データベースのスキーマを変更した瞬間に、旧環境のアプリケーションはそのデータを読めなくなります。トラフィックを戻しても、旧環境は動きません。

この状態は、切り戻し手順の有無とは無関係に成立します。逆方向のレプリケーションを用意していても、戻した先のアプリケーションが新しいスキーマを解釈できないなら、戻す意味がありません。

したがって原則は次のようになります。

  • 移行では、動かす場所だけを変える。アプリケーションとスキーマは変えない
  • 改修は、移行が完了して旧環境を停止した後に、通常のリリースとして実施する
  • どうしても同時に入れる必要があるなら、新旧の両方のアプリケーションが動くスキーマ(列の追加のみ、削除と改名は行わない)に限定する

「移行のついでに直す」は、作業量を減らしているように見えて、切り戻しの選択肢を消しています。移行の失敗が許されない理由の多くは、技術的な難しさではなく、この選択肢の消し方にあります。

計画書には、移行に含める変更と、移行後に回す変更の一覧を書いてください。境界を書いておかないと、移行日が近づくにつれて「ついでに」の項目が増えます。

切り戻し経路は、プロモートより前に作ってテストする

では、プロモート後に戻す必要が出たらどうするか。公式ドキュメントには、逆方向のレプリケーション(フェイルバック)の設定手順が独立したページとして用意されています。そこに書かれている推奨は次のとおりです。

After data is migrated, don’t promote the migration job. We recommend that you create and test the failback migration job before the promotion step.

プロモートの前に、逆方向の移行ジョブを作成してテストしておくこと。これが公式の推奨です。そしてこの手順は自動ではなく、複数の構成作業からなります。

ここから、本記事で最も実務に効く結論が出ます。ロールバック手順は、移行日には作れません。逆方向の経路は、移行日より前に作り、実際に動かして確認しておく必要があります。第12回で「実験の復旧手順を先に書く」と決めたのと同じ構造ですが、本記事ではそれが文書ではなく構築作業になります。

正直に見積もると、逆方向の経路を用意することは移行の作業量を実質的に2倍にします。用意しないという選択も成立します。ただしその場合は、計画書に「プロモート後は切り戻せない」と明記してください。書かせることに意味があります。その一文が、承認する立場の人間に届きます。口頭で「まあ何とかなります」と伝わるより、はるかに正確です。

読み取り専用という中間状態を設計する

停止時間を短くする方法が1つあります。書き込みを止めてから切り替えが完了するまでの間、サービスを完全に停止するのではなく、読み取り専用で提供するという中間状態を置くことです。

利用者から見える停止時間が、この分だけ短くなります。参照が主体のサービスであれば、体感上はほぼ止まっていない状態になります。

ただし前提があります。読み取り専用モードが、アプリケーション側に実装されていることです。実装されていないなら、それは移行日までに開発チームが作る機能であり、移行計画のタスクとして開発チームに割り当てる対象になります。第2回のRACIでいえば、実行責任は開発チームにあり、運用チームは要求を出す側です。

ここで指摘したいのは、読み取り専用モードそのものより一般的な構造です。移行計画には、インフラ側だけで完結しない項目が必ず混ざります。セッションの外部化、読み取り専用モード、連携元の設定変更。これらを計画の後半で発見すると、移行日が動きます。計画の最初の1週間で、他チームに依頼が必要な項目を洗い出してください。

Go-Live判定は、移行日には作れない

判定基準を当日に作ると、判定は正当化になります。目の前のグラフを見ながら基準を決めることになるためです。

判定基準は、第12回の受け入れ基準から作る

新しく作る必要はありません。第12回で作った受け入れ基準(定常状態の定義、観測窓、合否の分岐)が、そのまま判定の材料になります。移行の判定に使う数値の出どころは、第12回のシナリオマトリクスです。

第12回を実施していない場合、ここで戻る必要があります。これは「本番移行の前にテストをしましょう」という一般論ではありません。判定基準の数値を、他のどこからも持ってこられないという依存関係です。エラー率の閾値も、レイテンシの基準も、平常時の値を測っていなければ決められません。

所要時間の数値は、リハーサルでしか出ない

カットオーバーのタイムラインには「所要時間」の列があります。この列を見積もりで埋めないでください。実測でしか埋まりません。

実測を取る方法は1つです。本番と同じ手順を、本番以外の環境で通しで実施することです。部分ごとの確認ではなく、書き込みの停止から動作確認までを、実際の順序と実際の担当者で通します。

ここで第12回と第8回がつながります。第12回で「テスト環境は規模ではなく構成を揃える。常設は縮小構成、本番規模は一時的に作成する」と決めました。移行のリハーサルは、その一時環境を使う代表的な用途です。そして一時環境を短時間で作れることは、第8回のIaCが「空のプロジェクトから再現できること」を完了条件にしていた理由でもあります。

リハーサルで取る値は次の4つです。

  • 各操作の所要時間。タイムラインの列を埋める値
  • レプリケーション遅延がゼロになるまでの時間。データ量に比例するため、本番相当のデータ量で測る必要があります
  • 逆方向の同期にかかる時間。切り戻し手順書に書く実測値
  • 手順の抜け。書いていない操作、担当者が決まっていない操作

4つ目が実際には最大の成果になります。第12回でカオステストの成果が「不具合」ではなく「手順の不足」だったのと同じ構造です。リハーサルで出てくるのは、多くの場合「この操作の権限を持っている人が当日いない」「この確認をする手段がない」という種類の発見です。

データ量が本番と違う環境でリハーサルする場合は、2つ目の値だけが使えないと明記してください。所要時間の合計をそのまま本番に当てはめると、待機時間を過小に見積もることになります。

判定項目に「戻せる時間が残っているか」を入れる

判定チェックリストの骨格は6項目です。

Go-Live判定チェックリストの骨格。5番目が本記事で追加する項目です
項目見る内容基準値の出どころ
機能主要な利用者操作が通ること第12回のE2E
性能レイテンシの分位点が基準内第12回の負荷テスト
エラー率新環境単独で計測した値第12回の定常状態
データの整合新旧の突き合わせが完了移行計画で定義
切り戻し可能性逆方向の経路が動作確認済みか。期限まで何時間残っているか本記事
運用の準備監視とアラートが新環境を向いているか。ランブックが更新されているか第15回・第16回

5番目がない判定会議は、「問題が起きていないか」だけを見ます。しかし、問題が起きていないことと、問題が起きたときに戻せることは別の条件です。前者だけを確認して次の段へ進むと、戻せる時間を消費していることに誰も気づかないまま時間が過ぎます。

この項目は「残り時間」を数値で持たせてください。「まだ戻せます」ではなく「あと4時間20分」と書きます。数値にすると、判定会議の議論の長さがコストとして見えます。

判定の時刻・権限・中止の宣言方法を先に決める

判定の責任者は、移行日に決めるものではありません。第2回のRACIで A(Accountable)を持つチームから導きます。第2回で「実行権限のないチームにAを置かない」と決めたのと同じ原則で、中止を宣言できるのは、実際に切り戻しの操作を実行できる立場の人間です。

計画書に記入する項目は5つです。

  • 判定の時刻(複数の判定点
  • 判定者
  • 中止を宣言できる人
  • 宣言の連絡経路
  • 中止後に最初にやること

1つ目に条件が付いています。判定点を移行の最後に1つだけ置かないでください。段階移行では、段ごとに判定点があります。最後の1回だけを判定と呼ぶと、途中の段で劣化が見えても止まりません。「まだ判定の時間ではない」という理由で進み続けることになります。

なお、恒常的なインシデント対応の体制(オンコール、Severity定義、エスカレーション)は第16回の管轄です。本記事で決めるのは、移行という計画された作業の中での判断権限だけです。この2つは分けて設計してください。移行当日の連絡経路が、平常時のエスカレーションフローと同じである必要はありません。

ロールバック手順は、2つに分けて書く

冒頭で予告した内容に戻ります。即時なのはトラフィックだけです。1つの手順書に混ぜると、「即時ロールバック可能」と書いてあるのに実際には戻せない、という状態が生まれます。

用語を1つ区別しておきます。本記事の切り戻しは、事前に用意した経路をたどる計画された操作です。予期しない災害からの復旧(RPO / RTO の定義、別リージョンでの復旧)は第17回の管轄であり、必要な設計も所要時間も別物です。切り戻し手順書に災害復旧の手順を混ぜないでください。移行当日に読む文書は、当日に実行する操作だけで構成します。

トラフィックの切り戻し(分単位)

手順は3ステップです。

  1. 重みを元に戻す
  2. 旧環境の健全性を確認する
  3. 新環境への流入が0になったことをログで確認する

3つ目が前章と同じ形になっている点に注意してください。設定値ではなく観測値で確認します。切り戻しの方向でも、時間差は同じように発生します。

所要時間の見積もりは次のとおりです。重みの変更操作そのものは即座に反映されますが、新環境への通信が完全に止まるまでには、コネクションドレイニングの設定値 + 60秒 + クライアント側の接続プールの寿命がかかります。DNSで切り替えている場合は、これに元のTTLが加わります。「即時」と書くなら、この合計を横に書いてください。

データの切り戻し(時間単位・期限あり)

前章のとおり、プロモートの前後で必要な作業が変わります。

  • プロモート前。旧環境が正本のままなので、トラフィックを戻すだけで完了します
  • プロモート後。新環境で発生した書き込みを旧環境へ反映する必要があり、事前に作った逆方向の経路が要ります

手順書に記入する項目は4つです。

  • 切り戻せなくなる時刻
  • 逆方向の経路の有無と、動作確認を実施した日付
  • 逆方向の同期にかかる時間の実測値(見積もりではなく、確認したときに測った値)
  • 同期できないデータの一覧と、その手当て

4つ目は必ず発生します。逆方向のレプリケーションを用意していても、戻せないものが残ります。

  • 新環境でのみ発行された識別子。採番の連続性が壊れます
  • 外部システムへ送信済みの通知。メール、Webhook、プッシュ通知は取り消せません
  • 決済のように、取り消しが別の取引になる処理
  • 外部システムに書き込んだ内容。自分たちのデータベースを戻しても、相手側は戻りません

この一覧を先に作らないと、切り戻しの判断が「戻せるかどうか分からない」という状態で行われます。そして分からないまま切り戻すと、旧環境と外部システムの間に不整合が残ります。障害対応としては、移行の失敗より始末が悪い結果です。

戻さないという選択肢を、判定の選択肢に入れる

障害が出たときの選択肢は2つではなく3つです。

  1. 切り戻す。旧環境へ戻す
  2. 前進復旧する。新環境のまま直す
  3. 待つ。判定を次の判定点へ持ち越す

判断基準は明確です。切り戻しの所要時間が、前進復旧の所要時間を上回るなら、戻さないほうが速いという場面があります。設定値の誤りのように原因が特定できていて修正が数分で済むなら、切り戻しは過剰です。

特にプロモート後は、この判断が逆転します。プロモート後の切り戻しは、前進復旧より危険な操作です。逆方向の同期という、当日まで一度しか動かしていない経路を本番で使うことになるためです。

したがって、障害の種類ごとに、どの選択肢を既定とするかを先に決めておいてください。当日に3択を考えることになると、時間が判断ではなく議論に消えます。そして議論している間も、切り戻せる時間は減り続けます。

【実務テンプレート】本番移行・トラフィック切り替え計画書

ここまでの内容を、そのまま記入できる形にまとめます。10のセクションのうち、表が必要なのは5と6の2つだけです。

セクション1〜4:前提と準備

  1. 前提。対象システム/移行元の環境(オンプレミスか既存VMか)/移行先(第3回の環境構成)/利用者から見える接続先(FQDNとIPアドレス)
  2. 移行戦略の選択。選んだ戦略/継ぎ目の単位/データの向き(正本はどちらか)/並行稼働の期間/二重費用の月額/旧環境の停止日と削除日
  3. 前提条件の完了確認(移行日より前に終わっていること)。セッションの外部化/新旧を分けて計測できる監視/ハイブリッド接続の開通/DNSのTTLの引き下げ(実施日)/逆方向の切り戻し経路の構築と動作確認日/連携元の設定変更の依頼と回答
  4. 切り替え機構。切り替え点(ロードバランサ/DNS/クライアント設定)/重みの変更手順/コネクションドレイニングの設定値/セッションアフィニティの設定状況

セクション5〜6:段階移行とタイムライン(表)

セクション5:段階移行の計画(1行1段)

重み新環境の毎分リクエスト数滞留時間の下限(計算値)進む条件戻す条件判断者
1

セクション6:カットオーバーのタイムライン(1行1操作。書き込み停止を起点とする

時刻操作所要時間実施者完了確認の方法この時点で切り戻せるか
T+0旧環境への書き込みを停止
レプリケーション遅延の確認
プロモートここから別手順

セクション7〜10:判定・切り戻し・移行後

  1. Go-Live判定チェックリスト。機能/性能/エラー率/データの整合/切り戻し可能性(残り時間)/運用の準備。各項目に[基準値(第12回から)/実測値/判定]
  2. ロールバック手順書(2本立て)。①トラフィック[手順/所要時間/完了確認]②データ[切り戻せなくなる時刻/逆方向の経路/同期時間の実測値/同期できないデータの一覧
  3. 中止の判断。中止を宣言できる人/連絡経路/中止後の最初の操作/前進復旧を既定とする障害の種類
  4. 移行後。旧環境の停止確認(アクセスログ0件の確認方法)/停止日/削除日/二重管理が残る設定の一覧(DNS、証明書、監視、ファイアウォールの許可設定)

最後の項目を補足します。移行が終わっても、新旧の両方に設定が残る箇所があります。証明書の更新対象、ファイアウォールの許可リスト、監視の対象、DNSのレコード。これらを一覧にして削除日を決めないと、1年後に「これは何のために開いているのか分からない許可設定」として残ります。移行の最後の作業は、旧環境の削除ではなく、旧環境に紐づいた設定の削除です。

次回予告

移行が終わると、クラスタは「作るもの」から「上げ続けるもの」になります。

次回の第14回「K8sライフサイクル・バージョンアップ計画書」では、年3回のマイナーバージョンアップ手順、In-Place と Blue/Green クラスタ移行の選択、K8s APIの非推奨化の影響調査、アドオンの互換性マトリクスを扱います。

接続を明示します。本記事のカットオーバーは一度きり、第14回の更新は年3回繰り返されます。そして本記事で作った切り替え機構は、そのまま第14回のクラスタ移行に使えます。新旧を同じロードバランサの下に並べて重みで移す構成は、移行のためだけの一時的な仕掛けではありません。クラスタを丸ごと入れ替える手段として、年に何度も使うことになります。

前の記事
次の記事