Kubernetes 集群上的粘性会话
Sticky sessions on Kubernetes cluster
目前,我正在尝试在 Google 云上使用两个 负载平衡器 创建一个 Kubernetes 集群:一个用于后端(在 Spring 引导中)另一个用于前端(Angular),其中每个服务(负载均衡器)与 2 个副本(pods)通信。为此,我创建了以下入口:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: sample-ingress
spec:
rules:
- http:
paths:
- path: /rest/v1/*
backend:
serviceName: sample-backend
servicePort: 8082
- path: /*
backend:
serviceName: sample-frontend
servicePort: 80
上面提到的入口可以使前端应用程序与后端应用程序提供的 REST API 通信。但是,由于后端提供的身份验证机制,我必须创建 sticky session,以便每个用户都与同一个 POD 通信。需要澄清的是,如果一个用户在 POD #1 中进行身份验证,则该 cookie 将不会被 POD #2 识别。
为了解决这个问题,我读到 Nginx-ingress 设法处理这种情况,我通过此处提供的步骤安装:https://kubernetes.github.io/ingress-nginx/deploy/ 使用 Helm。
您可以在图表下方找到我正在尝试构建的架构:
有以下服务(我只贴一个服务,另一个类似):
apiVersion: v1
kind: Service
metadata:
name: sample-backend
spec:
selector:
app: sample
tier: backend
ports:
- protocol: TCP
port: 8082
targetPort: 8082
type: LoadBalancer
我声明了以下入口:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: sample-nginx-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/affinity: cookie
nginx.ingress.kubernetes.io/affinity-mode: persistent
nginx.ingress.kubernetes.io/session-cookie-hash: sha1
nginx.ingress.kubernetes.io/session-cookie-name: sample-cookie
spec:
rules:
- http:
paths:
- path: /rest/v1/*
backend:
serviceName: sample-backend
servicePort: 8082
- path: /*
backend:
serviceName: sample-frontend
servicePort: 80
之后,我运行 kubectl apply -f sample-nginx-ingress.yaml
应用入口,它被创建并且状态正常。但是,当我访问 "Endpoints" 列中出现的 URL 时,浏览器无法连接到 URL。
我做错了什么吗?
编辑 1
** 更新的服务和入口配置 **
经过一些帮助,我成功地通过 Ingress Nginx 访问了服务。上面你有配置:
Nginx 入口
路径不应包含“”,这与默认的 Kubernetes 入口不同,默认 Kubernetes 入口必须具有“”来路由我想要的路径。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: sample-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "sample-cookie"
nginx.ingress.kubernetes.io/session-cookie-expires: "172800"
nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"
spec:
rules:
- http:
paths:
- path: /rest/v1/
backend:
serviceName: sample-backend
servicePort: 8082
- path: /
backend:
serviceName: sample-frontend
servicePort: 80
服务
此外,服务不应为 "LoadBalancer" 类型,而应为“ClusterIP”类型,如下所示:
apiVersion: v1
kind: Service
metadata:
name: sample-backend
spec:
selector:
app: sample
tier: backend
ports:
- protocol: TCP
port: 8082
targetPort: 8082
type: ClusterIP
然而,我仍然无法在我的 Kubernetes 集群中实现粘性会话,一旦我仍然收到 403,甚至 cookie 名称也没有被替换,所以我猜注释没有按预期工作。
我认为您的 Service
配置有误。只需删除 type: LoadBalancer
,类型将默认为 ClusterIP
。
LoadBalancer: Exposes the Service externally using a cloud provider’s load balancer. NodePort and ClusterIP Services, to which the external load balancer routes, are automatically created. See more here: https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer.
apiVersion: v1
kind: Service
metadata:
name: sample-backend
spec:
selector:
app: sample
tier: backend
ports:
- protocol: TCP
port: 8082
targetPort: 8082
我调查了此事并找到了解决您的问题的方法。
要为两条路径实现粘性会话,您需要两个入口定义。
我创建了示例配置来向您展示整个过程:
重现步骤:
- 应用入口定义
- 创建部署
- 创建服务
- 创建 Ingress
- 测试
我假设集群已配置并且工作正常。
应用 Ingress 定义
按照此 Ingress link 查找在您的基础架构上安装 Ingress 控制器之前是否需要任何先决条件。
应用以下命令以提供所有强制性先决条件:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml
运行 下面的命令应用通用配置来创建服务:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/cloud-generic.yaml
创建部署
以下是响应特定服务上的入口流量的 2 个示例部署:
hello.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello
spec:
selector:
matchLabels:
app: hello
version: 1.0.0
replicas: 5
template:
metadata:
labels:
app: hello
version: 1.0.0
spec:
containers:
- name: hello
image: "gcr.io/google-samples/hello-app:1.0"
env:
- name: "PORT"
value: "50001"
通过调用命令应用第一个部署配置:
$ kubectl apply -f hello.yaml
goodbye.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: goodbye
spec:
selector:
matchLabels:
app: goodbye
version: 2.0.0
replicas: 5
template:
metadata:
labels:
app: goodbye
version: 2.0.0
spec:
containers:
- name: goodbye
image: "gcr.io/google-samples/hello-app:2.0"
env:
- name: "PORT"
value: "50001"
通过调用命令应用第二个部署配置:
$ kubectl apply -f goodbye.yaml
检查部署配置是否 pods 正确:
$ kubectl get deployments
它应该显示类似的东西:
NAME READY UP-TO-DATE AVAILABLE AGE
goodbye 5/5 5 5 2m19s
hello 5/5 5 5 4m57s
创建服务
要连接到之前创建的 pods,您需要创建服务。每项服务将分配给一个部署。以下是实现此目的的 2 项服务:
你好-service.yaml:
apiVersion: v1
kind: Service
metadata:
name: hello-service
spec:
type: NodePort
selector:
app: hello
version: 1.0.0
ports:
- name: hello-port
protocol: TCP
port: 50001
targetPort: 50001
通过调用命令应用第一个服务配置:
$ kubectl apply -f hello-service.yaml
再见-service.yaml:
apiVersion: v1
kind: Service
metadata:
name: goodbye-service
spec:
type: NodePort
selector:
app: goodbye
version: 2.0.0
ports:
- name: goodbye-port
protocol: TCP
port: 50001
targetPort: 50001
通过调用命令应用第二个服务配置:
$ kubectl apply -f goodbye-service.yaml
请记住,在两种配置中都输入:NodePort
检查服务是否创建成功:
$ kubectl get services
输出应如下所示:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
goodbye-service NodePort 10.0.5.131 <none> 50001:32210/TCP 3s
hello-service NodePort 10.0.8.13 <none> 50001:32118/TCP 8s
创建入口
要实现粘性会话,您需要创建 2 个入口定义。
定义如下:
你好-ingress.yaml:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: hello-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "hello-cookie"
nginx.ingress.kubernetes.io/session-cookie-expires: "172800"
nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/affinity-mode: persistent
nginx.ingress.kubernetes.io/session-cookie-hash: sha1
spec:
rules:
- host: DOMAIN.NAME
http:
paths:
- path: /
backend:
serviceName: hello-service
servicePort: hello-port
再见-ingress.yaml:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: goodbye-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "goodbye-cookie"
nginx.ingress.kubernetes.io/session-cookie-expires: "172800"
nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/affinity-mode: persistent
nginx.ingress.kubernetes.io/session-cookie-hash: sha1
spec:
rules:
- host: DOMAIN.NAME
http:
paths:
- path: /v2/
backend:
serviceName: goodbye-service
servicePort: goodbye-port
请更改两个入口中的 DOMAIN.NAME
以适合您的情况。
我建议看看这个 Ingress Sticky session link。
两个入口都配置为仅 HTTP 流量。
应用它们调用命令:
$ kubectl apply -f hello-ingress.yaml
$ kubectl apply -f goodbye-ingress.yaml
检查是否应用了两种配置:
$ kubectl get ingress
输出应该是这样的:
NAME HOSTS ADDRESS PORTS AGE
goodbye-ingress DOMAIN.NAME IP_ADDRESS 80 26m
hello-ingress DOMAIN.NAME IP_ADDRESS 80 26m
测试
打开浏览器并转到 http://DOMAIN.NAME
输出应该是这样的:
Hello, world!
Version: 1.0.0
Hostname: hello-549db57dfd-4h8fb
Hostname: hello-549db57dfd-4h8fb
是 pod 的名称。刷新几次。
它应该保持不变。
要检查另一条路线是否有效,请转至 http://DOMAIN.NAME/v2/
输出应该是这样的:
Hello, world!
Version: 2.0.0
Hostname: goodbye-7b5798f754-pbkbg
Hostname: goodbye-7b5798f754-pbkbg
是 pod 的名称。刷新几次。
它应该保持不变。
确保 cookie 不会更改打开开发人员工具(可能是 F12)并导航到带有 cookie 的位置。您可以重新加载页面以检查它们是否没有变化。
目前,我正在尝试在 Google 云上使用两个 负载平衡器 创建一个 Kubernetes 集群:一个用于后端(在 Spring 引导中)另一个用于前端(Angular),其中每个服务(负载均衡器)与 2 个副本(pods)通信。为此,我创建了以下入口:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: sample-ingress
spec:
rules:
- http:
paths:
- path: /rest/v1/*
backend:
serviceName: sample-backend
servicePort: 8082
- path: /*
backend:
serviceName: sample-frontend
servicePort: 80
上面提到的入口可以使前端应用程序与后端应用程序提供的 REST API 通信。但是,由于后端提供的身份验证机制,我必须创建 sticky session,以便每个用户都与同一个 POD 通信。需要澄清的是,如果一个用户在 POD #1 中进行身份验证,则该 cookie 将不会被 POD #2 识别。
为了解决这个问题,我读到 Nginx-ingress 设法处理这种情况,我通过此处提供的步骤安装:https://kubernetes.github.io/ingress-nginx/deploy/ 使用 Helm。
您可以在图表下方找到我正在尝试构建的架构:
有以下服务(我只贴一个服务,另一个类似):
apiVersion: v1
kind: Service
metadata:
name: sample-backend
spec:
selector:
app: sample
tier: backend
ports:
- protocol: TCP
port: 8082
targetPort: 8082
type: LoadBalancer
我声明了以下入口:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: sample-nginx-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/affinity: cookie
nginx.ingress.kubernetes.io/affinity-mode: persistent
nginx.ingress.kubernetes.io/session-cookie-hash: sha1
nginx.ingress.kubernetes.io/session-cookie-name: sample-cookie
spec:
rules:
- http:
paths:
- path: /rest/v1/*
backend:
serviceName: sample-backend
servicePort: 8082
- path: /*
backend:
serviceName: sample-frontend
servicePort: 80
之后,我运行 kubectl apply -f sample-nginx-ingress.yaml
应用入口,它被创建并且状态正常。但是,当我访问 "Endpoints" 列中出现的 URL 时,浏览器无法连接到 URL。
我做错了什么吗?
编辑 1
** 更新的服务和入口配置 **
经过一些帮助,我成功地通过 Ingress Nginx 访问了服务。上面你有配置:
Nginx 入口
路径不应包含“”,这与默认的 Kubernetes 入口不同,默认 Kubernetes 入口必须具有“”来路由我想要的路径。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: sample-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "sample-cookie"
nginx.ingress.kubernetes.io/session-cookie-expires: "172800"
nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"
spec:
rules:
- http:
paths:
- path: /rest/v1/
backend:
serviceName: sample-backend
servicePort: 8082
- path: /
backend:
serviceName: sample-frontend
servicePort: 80
服务
此外,服务不应为 "LoadBalancer" 类型,而应为“ClusterIP”类型,如下所示:
apiVersion: v1
kind: Service
metadata:
name: sample-backend
spec:
selector:
app: sample
tier: backend
ports:
- protocol: TCP
port: 8082
targetPort: 8082
type: ClusterIP
然而,我仍然无法在我的 Kubernetes 集群中实现粘性会话,一旦我仍然收到 403,甚至 cookie 名称也没有被替换,所以我猜注释没有按预期工作。
我认为您的 Service
配置有误。只需删除 type: LoadBalancer
,类型将默认为 ClusterIP
。
LoadBalancer: Exposes the Service externally using a cloud provider’s load balancer. NodePort and ClusterIP Services, to which the external load balancer routes, are automatically created. See more here: https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer.
apiVersion: v1
kind: Service
metadata:
name: sample-backend
spec:
selector:
app: sample
tier: backend
ports:
- protocol: TCP
port: 8082
targetPort: 8082
我调查了此事并找到了解决您的问题的方法。
要为两条路径实现粘性会话,您需要两个入口定义。
我创建了示例配置来向您展示整个过程:
重现步骤:
- 应用入口定义
- 创建部署
- 创建服务
- 创建 Ingress
- 测试
我假设集群已配置并且工作正常。
应用 Ingress 定义
按照此 Ingress link 查找在您的基础架构上安装 Ingress 控制器之前是否需要任何先决条件。
应用以下命令以提供所有强制性先决条件:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml
运行 下面的命令应用通用配置来创建服务:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/cloud-generic.yaml
创建部署
以下是响应特定服务上的入口流量的 2 个示例部署:
hello.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello
spec:
selector:
matchLabels:
app: hello
version: 1.0.0
replicas: 5
template:
metadata:
labels:
app: hello
version: 1.0.0
spec:
containers:
- name: hello
image: "gcr.io/google-samples/hello-app:1.0"
env:
- name: "PORT"
value: "50001"
通过调用命令应用第一个部署配置:
$ kubectl apply -f hello.yaml
goodbye.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: goodbye
spec:
selector:
matchLabels:
app: goodbye
version: 2.0.0
replicas: 5
template:
metadata:
labels:
app: goodbye
version: 2.0.0
spec:
containers:
- name: goodbye
image: "gcr.io/google-samples/hello-app:2.0"
env:
- name: "PORT"
value: "50001"
通过调用命令应用第二个部署配置:
$ kubectl apply -f goodbye.yaml
检查部署配置是否 pods 正确:
$ kubectl get deployments
它应该显示类似的东西:
NAME READY UP-TO-DATE AVAILABLE AGE
goodbye 5/5 5 5 2m19s
hello 5/5 5 5 4m57s
创建服务
要连接到之前创建的 pods,您需要创建服务。每项服务将分配给一个部署。以下是实现此目的的 2 项服务:
你好-service.yaml:
apiVersion: v1
kind: Service
metadata:
name: hello-service
spec:
type: NodePort
selector:
app: hello
version: 1.0.0
ports:
- name: hello-port
protocol: TCP
port: 50001
targetPort: 50001
通过调用命令应用第一个服务配置:
$ kubectl apply -f hello-service.yaml
再见-service.yaml:
apiVersion: v1
kind: Service
metadata:
name: goodbye-service
spec:
type: NodePort
selector:
app: goodbye
version: 2.0.0
ports:
- name: goodbye-port
protocol: TCP
port: 50001
targetPort: 50001
通过调用命令应用第二个服务配置:
$ kubectl apply -f goodbye-service.yaml
请记住,在两种配置中都输入:NodePort
检查服务是否创建成功:
$ kubectl get services
输出应如下所示:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
goodbye-service NodePort 10.0.5.131 <none> 50001:32210/TCP 3s
hello-service NodePort 10.0.8.13 <none> 50001:32118/TCP 8s
创建入口
要实现粘性会话,您需要创建 2 个入口定义。
定义如下:
你好-ingress.yaml:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: hello-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "hello-cookie"
nginx.ingress.kubernetes.io/session-cookie-expires: "172800"
nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/affinity-mode: persistent
nginx.ingress.kubernetes.io/session-cookie-hash: sha1
spec:
rules:
- host: DOMAIN.NAME
http:
paths:
- path: /
backend:
serviceName: hello-service
servicePort: hello-port
再见-ingress.yaml:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: goodbye-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "goodbye-cookie"
nginx.ingress.kubernetes.io/session-cookie-expires: "172800"
nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/affinity-mode: persistent
nginx.ingress.kubernetes.io/session-cookie-hash: sha1
spec:
rules:
- host: DOMAIN.NAME
http:
paths:
- path: /v2/
backend:
serviceName: goodbye-service
servicePort: goodbye-port
请更改两个入口中的 DOMAIN.NAME
以适合您的情况。
我建议看看这个 Ingress Sticky session link。
两个入口都配置为仅 HTTP 流量。
应用它们调用命令:
$ kubectl apply -f hello-ingress.yaml
$ kubectl apply -f goodbye-ingress.yaml
检查是否应用了两种配置:
$ kubectl get ingress
输出应该是这样的:
NAME HOSTS ADDRESS PORTS AGE
goodbye-ingress DOMAIN.NAME IP_ADDRESS 80 26m
hello-ingress DOMAIN.NAME IP_ADDRESS 80 26m
测试
打开浏览器并转到 http://DOMAIN.NAME
输出应该是这样的:
Hello, world!
Version: 1.0.0
Hostname: hello-549db57dfd-4h8fb
Hostname: hello-549db57dfd-4h8fb
是 pod 的名称。刷新几次。
它应该保持不变。
要检查另一条路线是否有效,请转至 http://DOMAIN.NAME/v2/
输出应该是这样的:
Hello, world!
Version: 2.0.0
Hostname: goodbye-7b5798f754-pbkbg
Hostname: goodbye-7b5798f754-pbkbg
是 pod 的名称。刷新几次。
它应该保持不变。
确保 cookie 不会更改打开开发人员工具(可能是 F12)并导航到带有 cookie 的位置。您可以重新加载页面以检查它们是否没有变化。