マイクロサービスではアプリケーションはステートレスであるのが望ましいですが、既存アプリケーションをなるべくリファクタリングせずKubernetesで動かしたい場合など、Kubernetes環境でもSession Stickyを使いたい場合はあると思います。
MinikubeやIBM Cloud Privateなど、Ingress ControllerとしてNGINX Ingress Controllerを使用している環境では、NGINX Ingress ControllerのSession Sticky機能を使って、Session Sticky(Session Affinity)が行えます。セッションを使うWebアプリケーションをMinikubeにデプロイし、Session Stickyができることを確認してみます。
MinikubeでのIngressの利用
MinikubeでIngressのaddonが有効になっているかを確認し、有効でない場合は有効にします。
minikube addons list
minikube addons enable ingress
セッションを使うアプリケーションの準備
セッションを使うWebアプリケーションと、そのアプリケーションをデプロイ済みのLibertyコンテナを準備します。今回はアプリがデプロイ済みのsotoiwa540/sample:1.0
というLibertyイメージを使用します。
アプリケーションの作成方法は以下の記事が参考になります。
アプリケーションのデプロイ
sotoiwa540/sample:1.0
のLibertyコンテナのPodをデプロイするマニフェストを作成します。
apiVersion: apps/v1
kind: Deployment
metadata:
name: liberty
spec:
selector:
matchLabels:
app: liberty
replicas: 3
template:
metadata:
labels:
app: liberty
spec:
containers:
- name: liberty
image: sotoiwa540/sample:1.0
imagePullPolicy: Always
ports:
- containerPort: 9080
マニフェストを適用します。
kubectl apply -f liberty-deployment.yaml
確認します。
$ kubectl get po
NAME READY STATUS RESTARTS AGE
liberty-f68456cbc-dzrzq 1/1 Running 0 64s
liberty-f68456cbc-gbgjm 1/1 Running 0 64s
liberty-f68456cbc-m5fbr 1/1 Running 0 64s
$
Serviceの作成
Serviceのマニフェストを作成します。
apiVersion: v1
kind: Service
metadata:
name: liberty
spec:
type: ClusterIP
selector:
app: liberty
ports:
- protocol: TCP
port: 9080
マニフェストを適用します。
kubectl apply -f liberty-service.yaml
確認します。
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4m10s
liberty ClusterIP 10.102.34.78 <none> 9080/TCP 68s
$
Ingressの作成
Ingressのマニフェストを作成します。
MinikubeではなくIBM Cloud Privateの場合は、 Nginx Ingress Controllerの設定として--annotations-prefix=ingress.kubernetes.io
設定されているため、アノテーションのキー名はnginx.ingress.kubernetes.io/hogehoge
ではなくingress.kubernetes.io/hogehoge
であることに注意。
(参考)
Enable Ingress Controller to use a new annotation prefix
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: liberty
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: sample.example.com
http:
paths:
- path: /app1
backend:
serviceName: liberty
servicePort: 9080
マニフェストを適用します。
kubectl apply -f liberty-ingress.yaml
$ kubectl get ing
NAME HOSTS ADDRESS PORTS AGE
liberty sample.example.com 10.0.2.15 80 7s
$
Minikubeの場合はMinikubeのIPを確認します。
$ minikube ip
192.168.99.113
$
/etc/hosts
に192.168.99.113 sample.example.com
を追記します。
sudo vi /etc/hosts
動作確認(Stickyなし)
ブラウザを開き、以下のURLでIngress経由でアプリケーションにアクセスします。
incrementボタンを押すことでセッションのカウンターを増やすことができます。ボタンを連打し、セッションが維持されていないので、カウントが増えていかないことを確認します。
curlでアクセスしてレスポンスヘッダを確認します。JSESSIONID
のみがSet-Cookieされています。
$ curl -v -c cookie.dat http://sample.example.com/app1/sample/session?increment=increment 2>&1 | grep -i "set-cookie:"
< Set-Cookie: JSESSIONID=0000w76PPbo4dXi5lAo03nzhSLM:e5aed9a9-19cb-41df-b170-6d6aa0791700; Path=/; HttpOnly
$
cookie.dat
にJSESSIIONID
が保存されていることが確認できます。
$ cat cookie.dat
# Netscape HTTP Cookie File
# https://curl.haxx.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.
#HttpOnly_sample.example.com FALSE / FALSE 0 JSESSIONID 0000w76PPbo4dXi5lAo03nzhSLM:e5aed9a9-19cb-41df-b170-6d6aa0791700
$
Session Stickyの有効化
Ingressを一度削除します。
kubectl delete -f liberty-ingress.yaml
以下のドキュメントを参考に、IngressでのActive Cookie Session Affinityを有効にするためのアノテーションを追加します。Active Cookieなので、IngressがCookieを付加するようになるはずです。
liberty-ingress-sticky.yaml
を作成します。
Kubernetes環境がMinikubeではなくIBM Cloud Privateの場合はnginx.ingress.kubernetes.io/hogehoge
ではなくingress.kubernetes.io/hogehoge
であることに注意。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: liberty
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "route"
nginx.ingress.kubernetes.io/session-cookie-hash: "sha1"
spec:
rules:
- host: sample.example.com
http:
paths:
- path: /app1
backend:
serviceName: liberty
servicePort: 9080
マニフェストを適用します。
kubectl apply -f liberty-ingress-sticky.yaml
動作確認(Stickyあり)
アプリケーションにアクセスします。
incrementボタンを連打します。今度はセッションが維持されているのでカウンターが増えていくことが確認できます。
curlでアクセスしてレスポンスヘッダを確認します。JSESSIONID
に加えてroute
がSet-Cookieされています。
$ curl -v -c cookie.dat http://sample.example.com/app1/sample/session?increment=increment 2>&1 | grep -i "set-cookie:"
< Set-Cookie: route=62d26518514d05ee42de6a48017d492202d11606; Domain=sample.example.com; Path=/app1; HttpOnly
< Set-Cookie: JSESSIONID=00003DHvBFe2s00bLutVDflq3It:e5aed9a9-19cb-41df-b170-6d6aa0791700; Path=/; HttpOnly
$
cookie.datを確認すると、JSESSIONID
に加えて、Ingressがセットしたroute
というCookieが確認できます。
$ cat cookie.dat
# Netscape HTTP Cookie File
# https://curl.haxx.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.
#HttpOnly_.sample.example.com TRUE /app1 FALSE 0 route 872ea48729dcfeef62390b62963ddcc20e5bbf9b
#HttpOnly_sample.example.com FALSE / FALSE 0 JSESSIONID 00008gyAfvIxqgTLiaTHXqMXRDQ:e5aed9a9-19cb-41df-b170-6d6aa0791700
Ingress ControllerのHA構成
Ingress ControllerがHA構成の場合、最初にCookieを発行したIngress Controllerとは違うIngress Controllerにリクエストが割り振られたとしたら、ちゃんとStickyが効くのかを確認します。
MinikubeではIngress ControllerはHA構成になっていないので、ここでは、簡易的に検証するため、単にIngress ControllerのPodを再起動することによりIngress Controllerが保持している情報をクリアしてみて、そのあともStickyが有効になるのかを確認します。
Podを削除して再作成させます。
$ kubectl get po -n kube-system | grep ingress
nginx-ingress-controller-6958898f8f-2tc59 1/1 Running 0 2m29s
$ kubectl delete po -n kube-system nginx-ingress-controller-6958898f8f-2tc59
pod "nginx-ingress-controller-6958898f8f-2tc59" deleted
$ kubectl get po -n kube-system | grep ingress
nginx-ingress-controller-6958898f8f-4bbvv 0/1 Running 0 13s
$
先ほどのブラウザでアクセスし、セッションが維持されることを確認します。
何度か再作成を繰り返してみても、セッションは維持されます。ですので、Ingress ControllerがHA構成の場合、その前段にいる負荷分散装置からIngress Controllerに対しては、アフィニティーを効かせなくても大丈夫と思われます。
(補足)
IBM Cloud Private v3.1.0に付属のバージョンのNGINX Ingress ControllerではCookieの値は単に<PodのIP>:<ポート>
を指定のアルゴリズム(sha1)でハッシュしただけのものなので、echo -n '172.17.0.7:9080' | shasum -a 1
のようにしたものと一致したのですが、この変更で変わったっぽいです。他にもアフィニティー関連の修正は多そうなので、バージョンによる挙動の違いには注意した方がよさそうです。
環境 | イメージ |
---|---|
minikube v0.32.0 | quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.21.0 |
IBM Cloud Private v3.1.0 | mycluster.icp:8500/ibmcom/nginx-ingress-controller-amd64:0.16.2 |