概要
ここ最近関わっているプロジェクトで、
クラスタを安全にBlue/Greenで更新するためにマルチクラスタ構成にして冗長化する案件を推進していました。
クラスタの安全な更新とクラスタのHealth Check、Fail Overは切っても切れない関係にあります。
そこで得られた、どのような目的で、どのような構成でヘルスチェックするべきか、という1意見を記述してみます。
想定の構成
-
GCPでGCLBの下にメインでルーティングするためのGKEクラスタと、レプリカクラスタを配備
-
Istioが入っており、LBはまずIstio Ingress Gatewayにルーティングし、Ingress Gatewayでクラスタ内の各サービスにルーティング
-
(これによってGCLBからのトラフィックに対してもIstioのproxyルールを適用させる)
なぜクラスタを冗長化するのか?
クラスタのアップデート、Istioのバージョンアップデート時にダウンタイム無しで安全に更新するために冗長化しています。
InPlaceでUpdateするのではなく、
一度レプリカクラスタをLBから切り離して更新し、もう一度LBに紐づけ、
しばらく様子を見て問題なさそうならメインのクラスタも更新する、という方針をとっています。
またシステムのユーザへの影響を最小にするためにも、クラスタに問題があったときにまだ更新していないクラスタにちゃんとFail Overさせたいので、
ヘルスチェックの定義がとても重要になってきます。
ヘルスチェックを定義する上での課題
本来クラスタ自体の更新といえども、どのサービスにどんな影響を与えるかは各サービス依存です。
例えば、クラスタの中にはバージョン依存のリソースを使っているサービスもあり、アップデートすると一部壊れてしまうかもしれません。
よって、
- 各サービスごとに、問題があれば別クラスタにFail Overする
- すべてのサービスが正常に稼働していなければ別クラスタにFail Overする
といったような方針で安全を担保することが考えられます。
しかし1は現状構成が複雑になりがちであり、
2に関していえば特定のサービスだけの障害がクラスタ全体に波及してしまうため論外です。
(たとえばあるサービスに障害が出たときに、クラスタ全体でFail Overされてしまい、仮にFail Over先もヘルスチェックに通っていなかったときにLBからみてルーティング先がなくなる)
とはいえ、クラスタ更新起因でサービスに障害が発生したときに検知してFail Overする必要あります...
ではどうすればいいでしょうか?
採用した方針
ニーズに応じてヘルスチェックの内容を変えることにしました。
本来サービスの可用性は、CanaryやBlue/Greeなどを駆使してサービスのレイヤーで担保するべきであり、サービスすべての状態を検知する必要があるのはクラスタの更新中の間だけです。
よって、
-
ヘルスチェック用のサービスを実装
- 全サービスのヘルスチェックを行い、すべて健康ならOK、そうじゃないならNGを返すエンドポイントを提供 ※1
- 何もせず200を返すエンドポイントを提供
-
クラスタ更新中はGCLBのヘルスチェックは、Istio IngressGatewayを通して1のエンドポイントを見に行く
-
更新が落ち着いたらFeature Toggleなどで、GCLBが見るヘルスチェックエンドポイントを2に切り替える ※2
という方針を採用しました。
これにより、クラスタ更新中はかなりシビアに全サービスをチェックし、
それ以外のときは、Istioの疎通だけ死んでいないことだけ担保して、サービスの可用性担保はサービスのレイヤに任せる、
といった方針を実現しています。
補足
- ※1 実施自体は裏で非同期で行い、エンドポイントに問い合わせが来たら最新のスナップショットから結果を返す
- ※2 Istio IngressGatewayを通すことでクラスタ全体でIstio起因で疎通が死んでいないことを担保する
まとめ
今回、普段はクラスタはIstioの疎通まで担保できてたら健康とし、サービスごとの可用性はサービスに任せるという思想で、
見るべきヘルスチェックエンドポイントを切り替えるという方針を取りました。
アーキテクチャの良し悪しは運用してはじめて明らかになるので、しばらく運用して所感を別の機会に振り返りたいと思います。