第5回のオンライン輪読会に備えて読んだ内容を自分なりにまとめておく。
今回の記事はこちら。
[期待/予想する内容]
この記事の目次は次のとおりである。
- はじめに
- 大きな影響を伴う小さな障害
- ヘルスチェックのトレードオフ
- 正常性を測定する方法
- ヘルスチェックの失敗に対する安全な対応
- 依存関係のヘルスチェックと影響範囲のバランス
- ヘルスチェックで問題が発生した場合に起こること
- まとめ
「大きな影響を伴う小さな障害」……なんだろう、ヘルスチェックにあまり理解がないせいか、内容が予想できない。ヘルスチェックと言えばAWSのEC2 Auto Scaling GroupとかApplication Load Balancerがよろしくやってくれるからなあ。あ、そうか、EC2の物理ホスト障害で仮想マシンが影響をうけるようなやつを、「小さな障害」っていってるのかな。別ホストで仮想マシンを立てれば簡単に回避できるという意味で「小さな」。
「ヘルスチェックのトレードオフ」……ヘルスチェックは単に80番ポートのHTTPステータスをチェックするだけみたいな、ほぼノーコストで実装できる場合もあるけど、アプリケーションのメインのロジックを改修したり、追加でメモリを使用したりと色々コストがかかって、それによりアプリケーション本体の動作に影響があったりする、というような話だろうか。
あとは、ヘルスチェックがNGだった場合のリカバリの実装とかが入ってきて、システム全体が複雑になる問題とか?
「正常性を測定する方法」……そもそも正常性とは何か、どのように測定すれば良いか、はむずかしそう。例えばデータベースのような、アプリケーションの依存サービスも含めて正常性を測定すべきだろうか?HTTPステータスが200を返していたら正常だろうか?アプリケーションにバグがあって正常性をうまく判定できない場合はどうすればいい?
「ヘルスチェックの失敗に対する安全な対応」……失敗したときにどうするのが安全か、これは知りたい。単純にバグのあるアプリケーションバージョンだったとしたら、インスタンスを追加してフリートのキャパシティを維持しても、いずれ同じバグで再度ヘルスチェックに失敗してしまいそう。一個前のアプリケーションバージョンにロールバックすることまで含めて考えた方が良いのだろうか。
「依存関係のヘルスチェックと影響範囲のバランス」……あ、やっぱり依存関係のヘルスチェックもサービス本体のヘルスチェックに含めるのか。バランスは難しそう。データベースならヘルスチェックにモロに影響するだろうけど、サイドキャッシュくらいならサービス本体のヘルスチェックには被害はないかな?
「ヘルスチェックで問題が発生した場合に起こること」……アラートが飛んで、運用チームとかSREチームが動いて、という話だろうか。アプリケーション内部の、システム的な話かな。あるいは、エンドユーザーへの影響まで含めた話?
[記事の要約]
次に、実際に記事を読んだ内容を箇条書きでまとめる。
はじめに
- 予測不可能な障害への対処 -> 信頼性向上
- ハードウェアは最終的には機能しなくなる=ソフトウェアもある時点でクラッシュする可能性
- 障害が発生したサーバーは不均衡な損害をもたらす可能性
- これらに対応するためのヘルスチェック
大きな影響を伴う小さな障害
- 稀に空白のエラーページを表示するバグが発生
- 異常が検知されず、サーバー自動停止もアラームもなし
- エラーページが正常な画面より高速で、高速なサーバーを選択するロードバランサにより影響が拡大
- エラー率のアラームで食い止めることはできるが、ヘルスチェックは影響を最小限にできる
ヘルスチェックのトレードオフ
- ロードバランサが定期的にヘルスチェックし転送先を判断
- ラウンドロビンでなく最小リクエスト方式で転送する場合、障害発生元が「ブラックホール」化する:失敗したリクエストのレスポンス速度を低下させることで保護できる
- キューをポーリングするようなシステムなどは、回避がより難しい
- 障害が発生する理由
- 個々のサーバーの問題:ディスク、クロック、メモリリーク、バグ
- 全体に影響する問題:依存関係の停止、ネットワークの問題
- 重要でないサポートプロセスまでをヘルスチェックして、障害を迅速に軽減するのが良いか、フリート全体での誤検知による障害を防ぐべきか
- 誤検出は慎重に防ぐという重要な課題
正常性を測定する方法
- 特定のサーバーの正確なヘルスチェックもあれば、依存関係まで含めたあいまいなヘルスチェックもある
- 実装しなければならないヘルスチェックもあれば、EC2やELBをセットアップするだけのヘルスチェックもある
- ライブ状態チェック
- ロードバランサや外部エージェントが実行
- アプリ動作の詳細は確認しない(HTTPステータスが200かどうか)
- ローカルヘルスチェック
- ディスクへの読み書きをテストする
- プロキシだけでなくアプリケーションをテストする
- モニタリングプロセスなどをテストする
- 依存関係のヘルスチェック
- 依存関係との通信のための資格情報など不正な設定、古い設定がないか
- ネットワーク疎通性の問題や、ネットワーク接続ソフトウェアのバグ
- 異常検出
- フリート内の他のサーバーと比較して異常がないか検査
- サーバーの高負荷によるクロックの遅れがないか
- 他のサーバーとのコードのバージョンのズレがないか
- 予期しないエラーがないか
- 異常検出が効果的に動作するために
- フリート内のサーバーが同じ仕事を受け持つこと
- フリート内のサーバーの種別(サイズ)が同じであること
- エラーなどを報告するために、例えばロードバランサにエラーを報告させる(サーバー側のモニタリングシステムが壊れても大丈夫)
ヘルスチェックの失敗に対する安全な対応
- サーバーがヘルスチェックに失敗した場合、サービスを停止するか、ロードバランサなど中央システムに決定を委ねる
- フェールオープン
- フリートすべてがヘルスチェック失敗時に、すべてのサーバーへ通信が許可される
- NLB、ALB、Route 53など
- 完全にテストできないのでAmazonでは懐疑的
- フリート全体での予期しないヘルスチェックによるサービス停止を回避するには?
- サーキットブレーカーがあれば良い
- サーキットブレーカーがない場合、ロードバランサやキューポーリングスレッドがヘルスチェックを実行
- かつ、外部モニタリングシステムで依存関係のヘルスチェックと異常検出を実行
- 過度に自動化しすぎると予測できないことが起こる
- 正常性を優先する
依存関係のヘルスチェックと影響範囲のバランス
- 依存関係までヘルスチェックに含めることの功罪
- ソフトな依存関係:たまに呼び出すだけ
- ハードな依存関係:ヘルスチェックで依存関係をテスト、連鎖障害を起こす
- コントロールプレーンのAPIは依存関係は影響を受けても、データプレーンのAPIは影響を受けないようにすべし
- マイクロサービスへの分割にエンドポイント数は基準にならないが、ヘルスチェックの依存関係は基準になりうる
ヘルスチェックで問題が発生した場合に起こること
- ここから実際の例
- デプロイ
- デプロイ時のヘルスチェックがないと不適切なデプロイに気づけない
- そもそもの対策としてテスト環境でのデプロイ、AZごとのデプロイ
- 非同期プロセッサー
- 非同期メッセージ処理の場合、十分なヘルスチェックがない
- 障害の場合、メッセージ処理に失敗するので、処理の遅延が発生
- 処理失敗時のアラートで運用担当が調査できる
- ディスクフル
- 処理本体にもログ書き込みにも失敗:モニタリングシステムに報告できない
- ロードバランサーやキューのメトリクスなど、システム外部のヘルスチェックが重要
- ゾンビ
まとめ
- ソフトウェアもハードウェアも壊れる
- 複数のレイヤのチェックが重要:軽量なライブチェックからメトリクスのパッシブモニタリングまで
- ヘルスチェックにより完全自動化するだけでなく、レート制限やサーキットブレーカーを利用して、最悪の場合に人間が関われるようにすべき
[予想のふりかえりなど]
正常性とかロールバックのあたりはなんとなく考えていた感じの内容が書かれていた。
2020年のはじめにQuarkusのMicroProfile Health実装を知って、こんな簡単に実装できるのか、便利便利と思っていた自分をぶん殴りたい。ソフトな依存関係とハードな依存関係とかあまり区別ついてなかった。今回読んだ記事の内容を正確に実装しようとすると、相当大変な気がする。
そもそもAPIごとに、コントロールプレーンとデータプレーンで障害の影響範囲を切り分けるってことは、内部的にマイクロサービスとしては独立してるってことなんだろうな。そのためのデプロイとか考えるとなかなか地獄な気がする。コントロールプレーンとデータプレーンの関係でいうと、ロールバック可能性を考えつつ新機能をデプロイするとしたら、データプレーンにまず先にデプロイしてから、コントロールプレーンにその機能をオンにするAPIをデプロイするんだろうか。
[まとめ]
- 「The Amazon Builder's Library」の『ヘルスチェックの実装』を読んだ
- 誤検出や依存関係のヘルスチェックをどう扱うか、サービスとしての健全性をどの範囲で担保するか、など難しいテーマだったが、システム分割について良い示唆を与えてくれると感じた
引き続きAmazon Builder's Library読んでいくぞ。