サーバー間ヘルスチェック

概要

サーバー間ヘルスチェックをどのように設計するか悩んだのでまとめます。

要件

サーバーAはサーバーBのrest apiに差分データを随時postするという設計から以下の要件を出ます.

  • サーバーBはサーバーAからの更新が途絶した場合に警告を出す
    - サーバーBはサーバーAからの更新途絶を検出する
     (更新頻度は一定でないため,サーバーAのダウンと識別する能力が必要)
    - xxx
  • サーバーAはサーバーBへのpostに失敗した場合に警告を出す
    - サーバーAは失敗したpostを一定期間保持し,リトライする.
    - サーバーAは失敗したpostを一定期間後に破棄する.
    - サーバーAは破棄したpost内容と対処の通知を行う
    - xxx

一方通行のデータ授受のため,上記のような要件が想定されます.

今回の記事は主に太字で示した部分,即ちヘルスチェックをどう設計するかというのが題材です.

設計

アクティブヘルスチェック / パッシブヘルスチェック

大別するとアクティブヘルスチェックとパッシブヘルスチェックの2通りが存在します.(*1

アクティブヘルスチェックとは定期的に対象機器に対して,いずれかのレイヤー通信で応答確認を行うことで,対象機器の生存を確認します.

パッシブヘルスチェックとは対象機器への必要な通信時に,通信結果から対象機器の生存を確認します.

今回はデータ受領側がデータ送信側のヘルスチェックを行いたいので,パッシブヘルスチェックは取れません.(データ送信側がダウンしている場合にはそもそも通信が行われないため).そのためアクティブヘルスチェックを実施するしかありません.

ヘルスチェック手法

ヘルスチェックはロードバランサーで最も一般的に使用されている技術です。ヘルスチェックをどのレイヤーで実施するかも決定する必要があります.

L3チェック
ICMP echoリクエストを送信し、echoリプライが返ってくるかどうかを確認します。

L4チェック
TCPのハンドシェークを行い、サーバの動作を確認します。

L7チェック
HTTP等のアプリケーション レイヤで擬似的なリクエストを出し、サーバのレスポンスを確認する。

https://www.f5.com/ja\_jp/services/resources/glossary/health-check

L3チェックでは機器自体が生存しているが,アプリケーションプロセスがダウンしている場合にも応答を返してしまうため,本目的には沿いません.機器の電源が入っているか等のより低レベルでのヘルスチェックを実現する際に採用されます.

L4チェック/L7チェックのどちらを採用するかはアプリケーションの設計に依存します.
- HTTPを採用しているか
- L4チェックがTCPハンドシェイクのみか特定応答の返答も可能か(TCPハンドシェイクのみの場合は,接続確認のみでプロセスが正常動作しているか不明.偶々TCP接続部だけ正常動作している場合があるかもしれない)
- L4チェックにおけるTCPポートの確保は可能か
等かと思います.

基本的にはアプリケーションプロセスレベルのヘルスチェックを実施できるのであれば,機能要件としてはL4/L7のどちらでも構わないと思います.非機能要件としては
- HTTPログにヘルスチェックが含まれるべきか否か
- ヘルスチェックも暗号化や機器認証を行いたいか(HTTPとして設計しておいた方がHTTPS等への拡張が容易)
- 実装工数
等が考慮事項として挙げられます.

今回はTCP層でアプリケーションプロセスレベルのヘルスチェックを実施する設計を採用したと仮定します.(サーバーAのHTTPサーバー機能には機器認証が必須なため,プロトタイプとして試行するには手間がかかるため見送ったというのが実情です.通常は大人しくHTTPで実現するのがいいと思います.)

余談

ちなみにSpring Bootを使用している場合はspring-boot-actuatorなるライブラリが存在し,実装工数を大幅に下げることが可能です.

L4ヘルスチェックの設計悩み

L4ヘルスチェックでも以下の3通りぐらいは選択肢としてあると思います

TCP接続を保持し続けて,その接続が切れたらダウンと判断する
TCP接続を保持し続け,そこで定期的にステータス通知する
TCP接続を毎回張り直し,定期的にステータス通知する

① TCP接続を保持し続けて,その接続が切れたらダウンと判断する

ヘルスチェック用のTCP接続が途絶したとしても,本機能のプロセスは正常に動作している可能性があります.誤診する可能性があるため不採用

② TCP接続を保持し続け,そこで定期的にステータス通知する

TCP接続を保持し続けるためにはTCP規格のkeepAlive(*3), OS(*4),ファイアーウォール等の接続ヘルスチェックによる途絶が発生しないように,
- ステータス更新間隔をTCP keepAlive / OS / Firewallの閾値以下に設定する.
ことが必要です.

常にリソースは消費されますが,接続のオーバーヘッドがなくなるので,選択肢の一つと言えます

③ TCP接続を毎回張り直し,定期的にステータス通知する

TCP接続に使用するポートを常に確保するように設計する必要があります.また,毎回接続するオーバーヘッドが発生します.一方で,接続形式がHTTPと同様なので,HTTPへの移行を意識する場合,②より③の方が良いと考えられます.

②,③のどちらを採用するかはオーバーヘッドと将来的な移植性を考慮すればいいと思われます.

まとめ

一旦以上のようにまとめましたが,他にも考慮すべき事項は山のようにあると思われます.また課題にぶつかったら随時まとめていきます.

Reference

*1

https://docs.aws.amazon.com/ja\_jp/elasticloadbalancing/latest/network/target-group-health-checks.html

*2

https://www.f5.com/ja\_jp/services/resources/glossary/health-check

*3

https://milestone-of-se.nesuke.com/nw-basic/as-nw-engineer/keepalive-tcp-http/

*4

https://access.redhat.com/ja/solutions/2379841