概要
最近のGKEはContainer Nativeなロードバランシングを推奨しています。
これは、Alias IP, NEGという仕組みを使って、GCPのロードバランサーがPodのIPに直接ルーティングすることができます。
しかし、適切にPodを設定していない場合、クラスタのメンテナンスなどでノードからPodがevictされたときにダウンタイムが発生してしまいます。
この記事ではContainer Native LoadBalancerの仕組みと、Podの適切な設定について説明していきます。
Container Nativeなロードバランシングの仕組み
Container Native LoadBalancingに記載してある通り、
引用: Container Native LoadBalancing
GKEのMasterノードにNEG ControllerというCustom Controllerがいて、特定のAnnotationがついたServiceが登録されたときに、GCPにNEGリソースを作成し、Serviceに紐づくPodをNEGにAttachするという仕組みのようです。
また、zonal network endpoint groupという名前の通り、各ゾーンごとにNEGはつくられ、Podは自分が存在するゾーンのNEGに所属する形になります。
Podの退避時に纏る注意点
GKEではクラスタを自動アップグレードしてくれるので、ノードがローリングアップデートされます。するとそのタイミングでスケジュールされていたPodは一旦吐き出されて別のノードに再作成されるため、ライフサイクルに注意しないとダウンタイムが発生してしまいます。
Podが退避されるときのライフサイクル
Podが退避され、一旦NEGから外れてルーティングされなくなる時、下記のようなフローになります。
- PodがTerminating状態へ
- 下記が同時に走る
- ServiceのEndpointから退避されたPodが外れる
- PodのpreStop + SIGTERM処理が走る
- ServiceからPodが外れたことを検知してNEG ControllerがNEGからPodを外す
- GCLBが退避されたPodにルーティングされなくなる
つまり、ここで意識しないといけないポイントは2点です。
- NEGから外れる前にPodが停止しないようにする
- NEGから外れても処理中のリクエストだけは処理完了させる
具体的な対応
1に関しては、PodにpreStopを適切に実装しましょう。
基本的なServiceによるルーティングのときと一緒ですね。
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 20"]
2に関しては、GCLBのBackendの設定にコネクションドレインを設定しておきましょう。
また、1は2のドレインタイムより長く設定しておかないとドレインが最悪終わる前にPodが死んでしまうことになるので気をつけましょう。
Ingressを使用している場合は、CRDで設定することもできます。
BackendConfig パラメータによる Ingress 機能の構成
確認方法
locustやApache Benchなどを使って一定負荷をかけつつ、drainコマンドを使ってノードからPodを退避させてもダウンタイム無しで安全に移動ができてることがわかるかと思います。
サンプル
kubectl drain gke-service-1-service-1-nodes-320d8165-7p7p --ignore-daemonsets --delete-local-data
まとめ
Container Load Balancingはとても強力な仕組みですが、
基本的なServiceのルーティング、LBの設定の考え方を踏襲し、忠実に設定してあげる必要があります。