デプロイ後にアプリケーションが健全であるとは、実際にはどのような状態か
デプロイが完了する。パイプラインがグリーンになる。チームのチャットで誰かが当然の疑問を口にする。「アプリ、動いてる?」
プロセス一覧を確認する。アプリケーションプロセスは生きている。ポートは開いている。ホームページはエラーなく表示される。すべて問題なさそうだ。チケットをクローズして、次の作業に移る。
しかし、本当に問うべき質問は、アプリケーションが起動したかどうかではない。本当に問うべき質問は、アプリケーションが実際にそれを使う人々にとって機能しているかどうかである。
起動の罠
動作しているプロセスと健全なアプリケーションは同じではない。この区別は、ほとんどのチームが認識している以上に重要である。
単純なシナリオを考えてみよう。あなたのチームが、入力検証関数の1行のコードを変更する新しいバージョンをデプロイしたとする。アプリケーションは完璧に起動する。ログにエラーはない。ホームページは瞬時に読み込まれる。サーバーの観点からは、すべてが正常である。
しかし、その検証の変更が厳しすぎた。以前は特定のフォーマットでデータを保存できていたユーザーが、拒否されるようになる。彼らは作業を完了できない。アプリケーションは動作しているが、それに依存する人々にとっては壊れている。
サーバーは「健全」と言う。ユーザーは「壊れている」と言う。どちらがより重要か?
健全性はプロセスではなく、ユーザーに関するもの
アプリケーションは技術的には生きているが、機能的には死んでいることがある。これが発生する一般的な3つのパターンを以下に示す。
機能退行。 アプリケーションは動作するが、機能の動作が異なったり、完全に動作しなくなったりする。クラッシュもエラーログもなく、単に不正な動作をするだけだ。チームが気づく前にユーザーが気づく。
パフォーマンス低下。 新しいバージョンによって、アプリケーションがデータベースにクエリを実行する方法が変わる。以前は50ミリ秒で完了していたクエリが、5秒かかるようになる。アプリケーションは決してクラッシュしない。ホームページはまだ読み込まれる。しかし、すべての操作がもっさりと感じられ、ユーザーはワークフローを放棄し始める。
静的なデータ破壊。 アプリケーションは正常に動作するが、生成または表示するデータが間違っている。計算が微妙に変わる。デフォルト値が変わる。ユーザーは誤った情報を目にし、それに基づいて意思決定を行う。アプリケーションはコードに指示された通りに動作しているため、エラーは発生しない。
これら3つのシナリオはすべて同じパターンを共有している。アプリケーションは動作しているが、その目的を果たしていない。
波及効果
健全なアプリケーションは、周囲のシステムにも損害を与えない。これは多くのチームが見落としがちな健全性の側面である。
あなたのチームが、データベースからのデータ取得方法を変更する新しいバージョンをデプロイしたと想像してほしい。アプリケーション自体は正常に動作する。エラーはない。応答時間も良好だ。しかし、新しいアクセスパターンがデータベースサーバーに予期せぬ負荷をかける。データベースが遅くなる。その同じデータベースを共有する他のアプリケーションでレイテンシとタイムアウトが発生し始める。
あなたのアプリケーションは健全である。その周囲の環境は健全ではない。そして最終的に、その環境はあなたのアプリケーションにも影響を及ぼす。
これが、アプリケーションの健全性を単独で評価できない理由である。あるサービスにとっては完璧に機能する変更でも、共有インフラストラクチャを劣化させるのであれば、依然として問題のある変更である。システム全体が安定していなければならない。
アプリケーション健全性の3つの次元
すべてのデプロイ後に、1つではなく3つのことを確認する必要がある。
1. アプリケーションが実行され、アクセス可能であるか。 これは基本的なチェックである。プロセスが生きている。ポートが開いている。ヘルスエンドポイントが応答する。これは必要だが、十分ではない。
以下の図は、これら3つの次元がどのように重なり合って、真に健全なアプリケーションを定義するかを示している。
2. アプリケーションはその機能を正しく実行しているか。 これは機能チェックである。ユーザーはコアワークフローを完了できるか?出力は正しいか?動作は期待通りか?これには「起動するか」を超えたテストが必要である。
3. アプリケーションはその環境に害を及ぼしていないか。 これはシステム全体のチェックである。アプリケーションは共有リソースに過剰な負荷をかけていないか?依存サービスにエラーを引き起こしていないか?他のシステムのパフォーマンスを低下させていないか?
最初の次元だけをチェックしていると、ユーザーが苦情を言い始めるまで問題を見逃すことになる。そしてユーザーが苦情を言う頃には、損害はすでに発生している。
これがデプロイプロセスにとって重要な理由
「健全」が実際に何を意味するかを理解することで、デプロイ後の検証に関する考え方が変わる。
健全性が単にアプリケーションの起動に関するものであれば、単純なヘルスチェックエンドポイントで十分である。しかし、健全性に正しい動作と環境の安定性が含まれるのであれば、より広範な検証戦略が必要になる。
これが、多くのチームが基本的なヘルスチェックを超えて進む理由である。実際のユーザーワークフローをシミュレートする合成モニタリングを追加する。平均レイテンシだけでなく、応答時間のパーセンタイルを追跡する。データベースのクエリパフォーマンスとコネクションプールの使用状況を監視する。ダウンストリームサービスのエラー率を監視する。
これらのプラクティスは、動作しているアプリケーションが必ずしも健全なアプリケーションではないことを、チームが苦い経験から学んだために存在する。
デプロイ後検証のための実践的チェックリスト
次回デプロイするときは、以下のチェックを実行すること。すべてのチェックがすべてのデプロイに適用されるわけではないが、パターンは普遍的である。
- アプリケーションプロセスが実行されており、ヘルスエンドポイントが応答する
- コアユーザーワークフローが正常に完了する(手動または自動チェック)
- 応答時間が期待範囲内であり、劣化していない
- エラー率が安定しているか、デプロイ前よりも低い
- データベースのクエリパフォーマンスが退行していない
- 共有インフラストラクチャ(データベース、キャッシュ、キュー)に負荷の増加が見られない
- 依存サービスが正常なステータスを報告している
- ログボリュームまたはログパターンに予期しない変更がない
このチェックリストは網羅的ではない。あなたのチームは、過去に何が壊れたかに基づいて独自のチェックリストを開発するだろう。しかし、原則は一貫している。アプリケーションがサーバー上で実行されることだけでなく、ユーザーのために機能することを検証することである。
まとめ
健全なアプリケーションとは、周囲のシステムに損害を与えることなく、ユーザーに正しくサービスを提供するものである。動作しているプロセスは単なる出発点に過ぎない。本当の検証は、起動チェックが合格した後に始まる。アプリケーションが起動したかどうかだけをチェックしているのであれば、デプロイが成功したかどうかをチェックしているわけではない。デプロイが終了したかどうかをチェックしているだけである。これらはまったく別のものである。