【问题标题】:Sticky sessions on Kubernetes clusterKubernetes 集群上的粘性会话
【发布时间】:2020-04-03 22:47:56
【问题描述】:

目前,我正在尝试使用两个 负载平衡器 在 Google Cloud 上创建一个 Kubernetes 集群:一个用于后端(在 Spring boot 中),另一个用于前端(在 Angular 中),其中每个服务 (负载均衡器)与 2 个副本(pod)通信。为此,我创建了以下入口:

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 进行通信。但是,由于后端提供的身份验证机制,我必须创建粘性会话,以便每个用户都与同一个 POD 进行通信。澄清一下,如果一个用户在 POD #1 中进行身份验证,则 POD #2 将无法识别 cookie。

为了解决这个问题,我读到 Nginx-ingress 设法处理这种情况,我通过此处提供的步骤进行安装:https://kubernetes.github.io/ingress-nginx/deploy/ using 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 来应用入口,它被创建并且它的状态是好的。但是,当我访问出现在“端点”列中的 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是什么类型的?是LoadBalancer 还是NodePort
  • 它们是负载均衡器。
  • 那么您是从公开为 GCP 负载均衡器的端点的 ip 地址访问您的服务吗? ...这意味着您没有使用您的 Ingress ...为此,您必须在 GKE 上拥有 NodePort 类型的服务。
  • 或者“端点”列是什么意思?您应该通过 Ingress Controller 访问您的服务。
  • @DawidKruk 你有:------------------------------------ ------------------------------------------------------- NGINX 入口控制器版本:0.26。 1 构建:git-2de5a893a 存储库:github.com/kubernetes/ingress-nginx nginx 版本:openresty/1.15.8.2 ------------------------------- ------------------------------------------------跨度>

标签: cookies kubernetes load-balancing nginx-ingress sticky-session


【解决方案1】:

我调查了此事,并找到了解决您问题的方法。

要实现两条路径的粘性会话,您需要两个入口定义。

我创建了示例配置来向您展示整个过程:

重现步骤:

  • 应用入口定义
  • 创建部署
  • 创建服务
  • 创建入口
  • 测试

我假设集群已配置并且工作正常。

应用入口定义

在您的基础架构上安装 Ingress 控制器之前,请按照此Ingress link 查找是否有任何必要的先决条件。

应用以下命令来提供所有必需的先决条件:

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

创建部署

以下是响应特定服务上的 Ingress 流量的 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

检查部署是否正确配置了 pod:

$ kubectl get deployments

它应该显示如下内容:

NAME      READY   UP-TO-DATE   AVAILABLE   AGE
goodbye   5/5     5            5           2m19s
hello     5/5     5            5           4m57s

创建服务

要连接到之前创建的 pod,您需要创建服务。每个服务都将分配给一个部署。以下是实现这一目标的 2 项服务:

hello-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 个入口定义。

定义如下:

hello-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 链接。 两个 Ingress 都配置为仅 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 的位置。您可以重新加载页面以检查它们是否没有更改。

【讨论】:

  • 感谢您的回答。我在集群中尝试了您的解决方案,但是,我不明白 DOMAIN.NAME 的用途。我想 DOMAIN.NAME 对于 Nginx Ingresses 是强制性的。所以,我放了一个像'stickyingress.example.com'这样的默认URL,和例子一样。但是,我无法在浏览器中连接到 stickyingress.example.com,但我能够连接到入口 URL,并且它按预期重定向到前端应用程序,但我在后端应用程序中得到 404(相当于您的再见服务)。我想我在这里误解了host参数的含义。
  • “DOMAIN.NAME”是让 Ingress 知道将流量路由到哪里。让我详细说明。它应该是acme.com 的形式,而不是像acme.com/something 这样的形式。 /something 应该在 path 中,所以在这种情况下,两个入口都应该有相同的 DOMAIN.NAME。在浏览器中输入 IP 地址不会向 Ingress 发送适当的消息,并且它不起作用。例如,我尝试使用 IP 地址连接到 Ingress,它说错误 404,但通过 DOMAIN.NAME 连接可以正常工作。
  • 好的,知道了。由于我的解决方案部署在 Google Cloud 上,我是否应该配置 Google Cloud DNS 并在 DNS 中添加一个条目以映射 CNAME“DOMAIN.NAME”和 Nginx 入口控制器 IP?我这样做了,但浏览器仍然无法解析名称。
  • 我所做的是使用我的入口资源kubectl get ing 的域名和IP 地址创建了A 类记录。此命令应显示您的 IP。
  • 感谢@davidkruk,它成功了,我必须解决我的后端应用程序的一些问题,但我现在能够处理 nginx 入口注释以创建粘性会话。
【解决方案2】:

我认为您的Service 配置错误。只需删除type: LoadBalancer,默认类型为ClusterIP

LoadBalancer:使用云提供商的负载平衡器将服务公开到外部。自动创建外部负载均衡器路由到的 NodePort 和 ClusterIP 服务。在此处查看更多信息: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

【讨论】:

  • 不幸的是,它没有解决我的问题。我遇到了同样的问题:无法连接到入口 URL。实际上,删除入口注释足以成功连接到入口 URL,但我的问题依赖于会话亲和性。我也尝试在我的服务中指定sessionAffinity: "ClientIP",但无法解决将请求路由到同一个 pod 的问题。
  • 嗯,很难说有什么问题。 nginx.ingress.kubernetes.io/session-cookie-hash你把这个带到哪里去了? kubernetes.github.io/ingress-nginx/examples/affinity/cookie 没有提及。
  • 你是对的@Dávid,但我在一些博客文章和 StackOverflow 中看到了这个注释,但我会删除它,因为官方文档中没有提到它。感谢您的帮助!
  • 您是否查看过此故障排除站点:kubernetes.github.io/ingress-nginx/troubleshooting?查看日志:kubectl get pods -n &lt;namespace-of-ingress-controller&gt;(获取 pod 的名称),然后是 kubectl logs -n &lt;namespace&gt; nginx-ingress-controller-67956bf89d-fv58j。您也可以尝试提高日志级别:kubectl edit deploy -n &lt;namespace-of-ingress-controller&gt; nginx-ingress-controller# Add --v=X to "- args", where X is an integer(将 X 设置为 5 用于调试模式)。
  • 谢谢@Dávid,实际上,调试模式对于修复我的入口配置中的一些错误很重要。我将编辑问题以提供修复。
猜你喜欢
  • 2018-12-20
  • 1970-01-01
  • 2014-12-19
  • 2017-11-29
  • 1970-01-01
  • 2021-07-16
  • 1970-01-01
  • 2019-05-25
  • 2020-11-07
相关资源
最近更新 更多