Webサービスの正常性監視とヘルスチェックエンドポイント

Webサービスの正常性監視について悩んだ。

「入門 監視」のアプリケーション監視の章に書かれているヘルスチェックエンドポイントパターンは良い考えだと思って取り入れた。

ヘルスチェックエンドポイントパターンはWebAPIやWebサイトなら https://..../health のようなエンドポイントを用意しておき、リクエストを受けたらサービスの動作に必要な操作が行えるかチェックして正常/異常で応答するもの。何らかのメトリックを返しても良い。

ただ、以下のあたりに悩んだので、考えを書き出しておきたい。

  • サービスの動作に必要な操作とはどこまでやるのか?更新系は?
  • Healthチェックエンドポイントは誰のためにつかうべきか?
  • 無駄に多く叩かれるのはよいのか?
  • 用途でヘルスチェックエンドポイント分ける?
  • そこまでやる価値はあるのか?

色々書きちらかしたが、自分の中で少し答えを作れた。まとめると以下。

  • 疎通レベルは最悪でもやる
    • healthチェックエンドポイントを用意できないならindex.htmlに対してGETでもいい
  • 裏のサービスの疎通確認はやりたければやるぐらいでいいのかもしれない
    • デプロイ後のサービス間の疎通確認に使うことを考えているならやってもよいかも
    • 大規模でないと恩恵はすくない気はする
  • 更新系の操作をヘルスチェックに含めるか「悩む」ぐらいならやらなくてよい
    • レートリミットの心配事や実装コストもかかる割に恩恵が少ない

サービスの動作に必要な操作とは何か?どこまでやるのか?更新系は?

tl;dr: 私の中では最低限、ネットワーク的に疎通がとれる点で良いと思っている。

サービスの動作に必要な操作とは何か?

  • TCPコネクションが行えるか
  • DBにSELECT 1;を打てるか
  • キャッシュサーバからPUT/GETできるか
  • メールが送れるか
  • オブジェクトストレージから情報を取得できるか

色々なシステムやサービスと連携するほどたくさん出てくるはず。サービスを動かすには読み取りのような状態が変わらない操作だけのときもあれば、取り消せない不可逆な操作も必要になる。

そう、参照はできるが更新ができない、みたいなことはよくある。

  • disk fullになると書き込みはできないが読み取りはできる。(DBならSELECTはできるがINSERT/UPDATE/DELETEができない)
  • コネクションは確立できるけど、処理してくれない(最近はあまり遭遇しない)

もしそういったのが致命的なら不可逆な操作も観点に含めても良いかもしれない。 しかし、 Healthチェックエンドポイントが重厚になってしまうし、疎通が取れる以上のことは相手のサービスの責任に踏み込み過ぎているとも思う。

Healthチェックエンドポイントは誰のためにつかうべきか?

システムが正常に動作していることを「監視」したいのか「判断」したいのか、がある気がしている。

監視はユーザのアクセスが無いタイミングでも定期的にアクセスしたりして、サービスが使える状態であることを確認する。そのメトリックが可用性の指標とかになる。

判断はロードバランサやCDNからの疎通に使い、サービスが動いてないならアクセスを振り分けてほしくないと判断したり、デプロイがうまくいったかどうかの判断に使いたい。

「サービスが動いてないならアクセスを振り分けてほしくない。」はだいぶ我儘なことがある。サービスとしては部分的に使えなくても動作する場面はしばしばあり、それはもったいないのではと思う時はある。例えば、たまたまレートに達してしまって使えなくなる時もある。メール送信はしばしば流量制限を行わないと一時的に止めてくることもある。(それは利用に際する設計上の問題なので流量制限しなさい、というのがやるべき対応だけど、それを上に納得できるよう説明して予算付けて工数をもらうというのは面倒であったり、ほぼ動いてるならいいじゃんという妥協があったり

だから単純にOK/NGで切り分けてもよいのか?という気持ちもある。つまり判断に使うのはよくないんじゃないか、と思う。ならば監視と判断で分けてしまうのはどうか。

また、暖機運転が終わるまではアクセスしてほしくないみたいなことはある。ディスクキャッシュを利かせるための小細工や、キャッシュストレージも遅いからとオンメモリにキャッシュを載せきるために前処理をぐるぐる動かしたいときがあり、そういう時は動いてるけどアクセスを振ってほしくない事もあるだろう。でも、そこまでに嫌ならHTTP listenerを立ち上げなければいいだけでもある。だからヘルスチェックエンドポイントでやることではないかもしれない。

あと、デプロイしてみたけどなんか動きが怪しいので原因を切り分けたいときに、サービス異常がないかどうかを区別するときの「判断」として使いたくなるかもしれない。これは個人的には良かった。デプロイ後に異常が起きて切り戻しをしたことがあったが、再現せず1日溶かした結果、裏のサービスがたまたま調子悪かったらしい、ということがあり、それで裏のサービスの動作の判断をヘルスチェックエンドポイントにやらせるようにしたら、デプロイ後は勝手にチェックされるようになる。

無駄に多く叩かれるのはよいのか?

参照系の操作は頻繁に行われるが、更新系や不可逆な操作はあまり行われない傾向にある。

前者は見合う価値があると思うが、後者はどうなのか。

10秒に1回とすると、1日で8640回になる。 ここから何倍かかかる。LBの後ろにあるインスタンスの数、CDN経由だと各CDN PoPの数、東と西の2か所で冗長構成とってたら2倍・・・。とか。 AzureもApplication Insight(Azure monitor?)がWebテストを提供しており、これがもっともユーザの操作に近い。このサービスは世界各地に存在しており、複数個所から同じエンドポイントを叩いてテストする。Webテスト自体も冗長化している側面もある。それでお金も件数に比例する。阿漕な商売だ。

最近のサービスは良くも悪くも従量課金であり、そのコストが気になるかもしれない。

言葉だと伝わりにくい気がするのである構成図をもってきた。

1つのLBに2つのVMがぶら下がっている、ひと昔前にあった無難な構成を、東西で分けてCDNを挟んで分散しているもの。

ユーザはCDNを経由してサービスを利用するので、Application InsightはCDNに対してリクエストを行い疎通確認する。 DBの矢印はレプリケーション的なことをしている矢印なのでそこは無視してほしい。

ここでサービスの正常性の意味で役に立つのはVMから出ている赤い矢印の部分で、サービスの疎通で役に立つのが黒い矢印だと思う。

コンポーネント(CDN,LB)は外向きの矢印が正常であるかどうかだけを見たいが、コンポーネントの疎通のために後ろにいるモノにリクエストが流れてしまうし、それには偏りが生まれる。(4つのサービス(VM)に対して3件のリクエストだと到達しないサービスも出てくるかもしれない) なので、LBは個別に疎通確認をしたくなる。LBも不安定なサービスに割り振らないために定期的にアクセスする仕組みを持つ。そうなるとApplicationInsightsとは別にLBからのリクエストも増える。

しかし、末端にいるサービス(VM)単位で見ればこんな短期間にヘルスチェックエンドポイントを叩かれてもそうそう結果は変化しないはずである。むしろレートに引っかかってしまわないか心配になる。なのでレスポンスを10秒キャッシュしたくなるような気がするのだが、事例はあまりなさそうだった。

とはいえ、「ユーザが使えてるならいいでしょ」と思わなくもない。たまたま疎通チェックが通っていないサービスに割り振られたときに運悪く死ぬ事象にぶつかるかもしれない。そうなればサービスはアラートをあげるだろう。それで気づけるならもうそれでいいのではないか。

用途でヘルスチェックエンドポイント分ける?

うんうんと色々考えた結果、サービスの監視の観点が違うのに同じエンドポイントでやるのが良くなさそうな気がしている。誰のためのヘルスチェックエンドポイントか、を考えて分けてしまうのがいいのではないか。サービスとの疎通のためのカジュアルなヘルスチェックエンドポイントと、サービスの正常性を判断するヘビーなヘルスチェックエンドポイントを用意したほうがよいのかもしれない。

ASP.NETのhealthcheckミドルウェアはここを配慮してかタグ付けできるようになっている。まぁAzure Functionとかではミドルウェアが使えないので色々処理かかないといけないんですが。

docs.microsoft.com

そこまで色々やる価値はあるのか?

じゃあそこまで色々やる価値はあるのか?というとそんなにないのかもしれない。コードで書けることは何でもできてしまうので色々できてしまうが、価値を生みにくい気がする。ユーザ数が多いとかで毎秒数百リクエスト捌いているなら10秒に1回正常性確認を行うのは誤差レベルである一方で、それだけ使われているなら正常性確認しなくても異常アラートは出てくるのではという気もする。

何にしても、1個用意してSaaSなり外部サービスから叩く(エンドツーエンドな)疎通を定期的に流すのはやったほうがよいと思う。

「どうせユーザがアクセスしてくるんだし、異常があったらアラートが上がるのだから、そもそも要らなくないか?」とはならなくて、ネットワークレベルで疎通が取れなくなった時に全く検知できないから。(ユーザからメールや電話で「繋がらないんですけど」と問い合わせが来て初めて検知できてヨシとするなら別)

まとめ

負担にならない程度にやりましょう。無駄に悩んでも考えすぎても評価されませんからね。