Kubernetes集群上的粘性会话

29

目前,我正在尝试在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中进行身份验证,则cookie将不被POD#2识别。

为了解决这个问题,我阅读到Nginx-ingress可以处理这种情况,并且我通过此处提供的步骤:https://kubernetes.github.io/ingress-nginx/deploy/使用Helm进行了安装。

您可以在下面找到我要构建的架构图:

enter image description here

使用以下服务(我只会贴出其中一个服务,另一个服务类似):

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来应用ingress,它被创建且状态为OK。但是,当我访问“Endpoints”列中出现的URL时,浏览器无法连接到该URL。

我做错了什么吗?

编辑1

**已更新服务和ingress配置**

在得到一些帮助后,我成功地通过Ingress Nginx访问了服务。这里是配置:

Nginx Ingress

路径不应包含“”,与默认的Kubernetes ingress不同,后者必须具有“”以路由我想要的路径。

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 - Jonas
它们是负载均衡器。 - migueltaoliveira
或者你说的“Endpoint”列是什么意思?你应该通过你的Ingress控制器访问你的服务。 - Jonas
请问您能否发布您的“Service” Yaml文件? - Dávid Molnár
1
@DawidKruk 这是您需要的内容:
NGINX Ingress 控制器 发布版本: 0.26.1 构建版本: git-2de5a893a 代码仓库: https://github.com/kubernetes/ingress-nginx nginx 版本: openresty/1.15.8.2
- migueltaoliveira
显示剩余8条评论
2个回答

30

我已经调查了此事,并找到了解决方案。

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

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

重现步骤:

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

我假设集群已经配好且正常运行。

应用入口定义

请遵循此链接以查找是否需要安装入口控制器的任何必需先决条件。

应用以下命令以提供所有强制性先决条件:

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

创建部署

以下是两个示例部署,用于响应特定服务的入口流量:

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,您需要创建服务。每个服务将分配给一个部署。以下是两个完成此操作的服务:

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

goodbye-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

创建Ingresses

为了实现粘性会话,您需要创建两个Ingress定义。

下面提供了这些定义:

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

goodbye-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
请在两个Ingress中将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的位置。您可以重新加载页面以检查它们是否未更改。

Cookies


谢谢您的回答。我在我的集群中尝试了您的解决方案,但是我不理解DOMAIN.NAME的目的。我认为Nginx Ingresses需要DOMAIN.NAME。因此,我像示例一样设置了一个默认URL,例如'stickyingress.example.com'。然而,我无法在浏览器中连接到stickyingress.example.com,但我可以连接到Ingress URL,并且它按预期重定向到前端应用程序,但是在后端应用程序(相当于您的goodbye服务)中我收到404错误。我认为我误解了这里主机参数的含义。 - migueltaoliveira
1
'DOMAIN.NAME' 是为了让Ingress知道如何路由流量。让我详细说明一下。它应该是形式为 acme.com 而不是像 acme.com/something 这样的形式。/something 应该在 path 中。因此,在这种情况下,两个Ingress应该具有相同的 DOMAIN.NAME。在浏览器中输入IP地址将无法向Ingress发送适当的消息,因此它将无法正常工作。例如,我尝试使用IP地址连接到Ingress,但它显示错误404,但通过 DOMAIN.NAME 连接可以正常工作。 - Dawid Kruk
我所做的是创建了一个类型为A的记录,其中包含我的域名和入口资源的IP地址 kubectl get ing。这个命令应该会显示你的IP。 - Dawid Kruk
@migueltaoliveira,请告诉我你是否成功了。 - Dawid Kruk
1
谢谢@davidkruk,它起作用了,我必须修复一些后端应用程序的问题,但现在我能够处理nginx ingress注释以创建粘性会话。 - migueltaoliveira
显示剩余6条评论

0

我认为你的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 的问题。 - migueltaoliveira
2
嗯,很难说出问题出在哪里。nginx.ingress.kubernetes.io/session-cookie-hash,你从哪里得到的信息?https://kubernetes.github.io/ingress-nginx/examples/affinity/cookie/ 上没有提到它。 - Dávid Molnár
你是对的 @Dávid,但我在一些博客文章和StackOverflow的答案中看到了这个注解,不过我会将其删除,因为它在官方文档中没有提到。谢谢你的帮助! - migueltaoliveira
1
你有查看过这个故障排除站点吗:https://kubernetes.github.io/ingress-nginx/troubleshooting/? 查看日志:kubectl get pods -n <namespace-of-ingress-controller>(获取 Pod 的名称),然后 kubectl logs -n <namespace> nginx-ingress-controller-67956bf89d-fv58j。你也可以尝试增加日志级别:kubectl edit deploy -n <namespace-of-ingress-controller> nginx-ingress-controller,并在 "- args" 中添加 "--v=X"(X 为整数)(调试模式设置 X 为 5)。 - Dávid Molnár
1
谢谢@Dávid,实际上,调试模式对于修复我的入口配置中的一些错误非常重要。我将编辑问题以提供修复方案。 - migueltaoliveira

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接