ライブアプリケーションを更新するときに実際に何が起きているのか

フォームの入力中だった。ページが固まる。次に真っ白な画面。そしてエラーメッセージ。リロードすると、入力したデータは消えている。どこかで、誰かがあなたが使っていたアプリケーションの新しいバージョンをデプロイしたのだ。

このシナリオは、大小さまざまな企業で毎日何千回も発生している。デプロイした側は、おそらくルーチン作業だと思っていただろう。いくつかのバグ修正、あるいは新機能。しかし、ユーザー側から見れば、体験は完全に壊れていた。なぜそうなるのかを理解することが、ユーザーとチームの両方を守るデプロイ戦略を選ぶための第一歩である。

決してなくならない4つの問題

アプリケーションのバージョンを別のものに置き換えるたびに、4つの根本的な問題が表面化する。テストの網羅性、コードの品質、リリースに対する自信の度合いは関係ない。

以下の図は、単一のデプロイが4つのコア問題とその下流への影響にどのように分岐するかを示している。

flowchart TD A[新しいバージョンをデプロイ] --> B[ダウンタイム] A --> C[新しいバージョンのエラー] A --> D[データ非互換性] A --> E[ロールバックの罠] B --> F[収益の損失] B --> G[ユーザーの不満] C --> H[負荷によるクラッシュ] C --> I[本番環境のバグ] D --> J[データ破損] D --> K[古いコードが新しいデータを読めない] E --> L[データ損失] E --> M[手動による整合性確認]

ダウンタイム

最も明白な問題だ。古いバージョンを停止し、新しいバージョンを起動する。その間、何も動作していない。5人しか使わない社内ツールであれば、30秒のダウンタイムは許容できるかもしれない。しかし、毎分数千のリクエストを処理するチェックアウトページでは、3秒のダウンタイムでも取引の損失、ユーザーの不満、そして実際の収益への影響を意味する。

問題はダウンタイムが存在するかどうかではない。問題は、ユーザーが離れたりサービスへの信頼を失ったりする前に、どれだけのダウンタイムを許容できるかである。

新しいバージョンのエラー

新しいバージョンはテスト済みだ。ステージング環境もパスした。しかし、本番環境はステージング環境ではない。新しいコードには、実際のトラフィックパターンでのみ現れるバグがあるかもしれない。予想以上にメモリを消費し、負荷でクラッシュするかもしれない。テストフィクスチャでは決して捕捉できなかった方法で、本番データと相互作用するかもしれない。

すぐに表面化するエラーもあれば、現れるまでに数時間かかるエラーもある。その頃には、損害はシステム全体に波及している。

データ非互換性

これは静かな殺し屋だ。アプリケーションが単独で動作することはほとんどない。データベース、キャッシュ、メッセージキュー、その他のサービスに接続している。新しいバージョンは、わずかに異なるフォーマットでデータを書き込んだり、カラムを追加したり、フィールドの解釈方法を変更したりするかもしれない。

新しいバージョンが新しいフォーマットでデータを書き込んでいる間に、古いバージョンがまだ古いデータを読み取っていると、破損が発生する。ロールバックが必要になった場合、新しいフォーマットのデータは古いコードでは読み取れない可能性がある。データの問題は、早期発見が最も難しく、後からの修正が最も高くつく。

ロールバックの罠

ロールバックは単純に聞こえる。新しいバージョンが壊れているので、古いバージョンに戻す。しかし、新しいバージョンが実行されていた間に、ユーザーは新しいレコードを作成し、トランザクションを完了し、状態を変更している。古いバージョンを復元したとき、そのデータはどうなるのか?

削除するのか?古いコードが読めないフォーマットのままにするのか?元のフォーマットに変換するのか?どの選択肢にも結果が伴う。クリーンなロールバックは稀だ。ほとんどのロールバックには、ある程度のデータ損失、手動による整合性確認、またはある程度の不整合期間が伴う。

なぜこれらの問題は避けられないのか

これら4つの問題を排除することはできない。すべてのデプロイがそれらを引き起こす。制御できるのは、それらがユーザーに与える影響の大きさと、問題が発生したときにどれだけ迅速に回復できるかである。

ここでデプロイ戦略が重要になる。デプロイ戦略とは、どのボタンを押すか、どのツールを設定するかではない。スピード、安全性、複雑さの間で意図的なトレードオフを行うことである。戦略が異なれば、4つの問題への対処方法も異なる。

優れたデプロイ戦略が実際に行うこと

デプロイ戦略は、3つの実用的な問いに答えるものである。

  • 古いバージョンを早急に削除せずに、新しいバージョンを利用可能にするにはどうすればよいか?
  • 新しいバージョンが壊れていた場合、影響範囲をどのように限定するか?
  • データを失ったりシステムを破損したりせずに、動作状態に戻るにはどうすればよいか?

これらの答えによって、ユーザーが更新に気付くかどうか、オンコールのエンジニアが午前2時に呼び出されるかどうか、チームが1日に複数回デプロイできるか月に1回しかできないかが決まる。

簡単な現実確認

ローリングアップデート、ブルーグリーンデプロイメント、カナリアリリースといった具体的な戦略に飛び込む前に、現在ほとんどのチームが実際にどのようにデプロイしているかを見てみよう。最も一般的なパターンは、今でも最も単純なもの、すなわち古いバージョンを停止し、新しいバージョンを起動し、うまくいくことを願う、である。これは低トラフィックの社内ツールでは機能する。人々が依存するものでは失敗する。

このパターンから脱却したチームは、必ずしもより優れたツールやより多くのエンジニアを擁しているわけではない。彼らは、自社の特定のアプリケーションにとって4つの問題のうちどれが最も重要かをより明確に理解している。決済システムはデータ整合性を最も重視する。コンテンツサイトは稼働時間を最も重視する。モバイルアプリはロールバック能力を最も重視する。なぜなら、ユーザーにアップデートを強制できないからだ。

戦略を選ぶ前の実用的チェックリスト

デプロイ戦略を選ぶ前に、これらの質問に正直に答えよう。その答えが、どのトレードオフを行うべきかを教えてくれる。

  • ユーザーはアプリケーションを放棄せずに、何秒のダウンタイムに耐えられるか?
  • 新しいバージョンが異なるフォーマットでレコードを書き込んだ場合、データはどうなるか?
  • デプロイから数秒以内にエラーを検出できるか、それとも検出に数時間かかるか?
  • データが破損した場合、バックアップからサービスを完全に復元するのにどのくらい時間がかかるか?
  • 手動によるデータクリーンアップなしでロールバックできるか、それともロールバックのたびにDBAの介入が必要か?
  • 新しいバージョンが即座にクラッシュした場合、何人のユーザーが影響を受けるか?

まとめ

ライブアプリケーションの更新は、ファイルコピー操作ではない。古いコード、新しいコード、ライブデータ、アクティブユーザーの間の調整問題である。ダウンタイム、エラー、データ非互換性、ロールバックの複雑さという4つの問題は常に存在する。ツールやプロセスでそれらを除去することはできない。優れたデプロイ戦略は、これらの問題が存在しないふりをしない。アプリケーションとユーザーが実際に必要とするものに基づいて、どの問題を最小化し、どの問題を受け入れるかを選択する。問題を理解することから始めよう。戦略は後からついてくる。