GKEでスティッキーセッション(Session Affinity)を利用する
GKE環境でスティッキーセッションを利用するときの方法です。WebSocketなど利用していて、ブラウザからPodと接続を確立したあとはそのPodと通信し続けたい場合に必要になると思います。
k8s環境でスティッキーセッションってそもそもできるのか?と最初不安だったのですが、思ったより簡単にできました。 GCPやそのほかのドキュメントではSessionAffinityと呼ばれていたので以降SessionAffinityにします。
解決策
GKEデフォルトのIngress Controllerには、SessionAffinityの機能があり、この機能を有効にすることで実現することができます。接続元の判断は、クライアントIP、Cookieのどちらかの方法を選ぶことができます。
Configuring a backend service through Ingress | Kubernetes Engine Documentation | Google Cloud
ドキュメントに書いてある通りなのですが、まずbackend configを作成し
apiVersion: cloud.google.com/v1beta1 kind: BackendConfig metadata: name: my-bsc-backendconfig spec: timeoutSec: 40 connectionDraining: drainingTimeoutSec: 60 sessionAffinity: affinityType: "CLIENT_IP"
サービス側でそのbackend configを利用することで実現できます。
ind: Service metadata: name: my-bsc-service labels: purpose: bsc-config-demo annotations: cloud.google.com/neg: '{"ingress": true}' beta.cloud.google.com/backend-config: '{"ports": {"80":"my-bsc-backendconfig"}}' ...
サービス側で指定するNEGは必須です。 また、ServiceがNEGを利用していなかった場合、NEGに切り替わるまでの間通信断が発生するので本番環境に反映する場合は注意が必要です。
仕組み
GCPのLoadBalncerにSession Affinity ( https://cloud.google.com/load-balancing/docs/backend-service#generated_cookie_affinity )という仕組みがあり、これをそのままGKEでも利用する形です。
通常のk8s環境の場合、Ingressとして作成されたLBのバックエンドはGCEインスタンスであり、GCEインスタンスに来たリクエストをk8sがPodに転送するので一見SessionAffinityの実現は困難なように思えます。そこで、必要になるのがNEG( https://cloud.google.com/kubernetes-engine/docs/how-to/container-native-load-balancing )です。
NEGを利用すると、LBのバックエンドをGCEインスタンスではなく、Podにすることが可能になります。こうすることで、LoadBalancerのSession Affinityの機能をk8s環境でもそのまま利用することができます。
NEGはGKE独自の機能なので、GKE以外の環境では、Nginx Ingress Controllerを利用する必要がありそうでした