Kubernetes 实践:AWS EKS 中的 readinessGates 参数

Published: 2024-08-16

Tags: k8s EKS

本文总阅读量

滚动升级中遇到的问题

AWS EKS 的 Deployment 资源在滚动升级时会导致可用 Pod 数量变少,其根源来自于 K8S 与 Target Group 状态不一致导致,本文将解释这一现象产生的原因,同时使用 readinessGates 参数解决问题

上图描述了 Deployment 滚动更新中的一个状态,解释如下:

  1. 首先,K8S 的 Deployment 启动一个新 Pod-3,健康检查通过后,它是一个可以接收流量的 Pod
  2. Pod-3 既已启动,K8S 选择一个老 Pod,即本例中的 Pod-1,发送信号让其退出
  3. 切换到 Target Group 视角,IP-1(对应的 Pod-1)开始注销,IP-3 正在注册,Target Group 注册注销的时间,相较于 K8S 的容器创建、退出要多一些时间
  4. 此时,Pod-3 已准备好,但 Target Group 的健康检查或其它配置还未完成,不会转发流量到 Pod-3
  5. Pod-1 已退出,Target Group 也正在注销 IP-1,不会转发流量到 Pod-1
  6. 原本 3 个 Pod 支撑的服务,现在仅有 IP-2 / Pod-2 这个链路的服务是正常的,显然这有问题

使用 readinessGates 解决问题

仅使用 readinessProbe 配置,其检查通过,K8S 即认为服务就绪,可供外部访问,readinessGates 的作用是在 readinessProbe 的基础上等待 readinessGates 的状态也可用时,才认为服务准备妥当,等待的过程中不会退出上例中的 Pod-1,即可解决 Pod 数量变少,服务可用性不够的问题

在 AWS EKS 中,readinessProbe 的工作原理是怎样的?

AWS 提供有 aws-load-balancer-controller 插件,它会从 Target Group 获取 Pod 注册状态,在 Target Group 中的 Pod 注册完成后设置 readinessGates 为通过状态,之后 K8S 才会退出旧 Pod,这是 AWS EKS 和 Target Group 联动的具体实现(推荐方式)

接下来使用测试 Nginx Demo 服务进行验证

配置 Namespace 支持 pod-readiness-gate-inject

要启用这一功能,需要在 namespace 添加 elbv2.k8s.aws/pod-readiness-gate-inject=enabled 标签

$ kubectl create namespace readiness
namespace/readiness created

$ kubectl label namespace readiness elbv2.k8s.aws/pod-readiness-gate-inject=enabled
namespace/readiness labeled

$ kubectl describe namespace readiness
Name:         readiness
Labels:       elbv2.k8s.aws/pod-readiness-gate-inject=enabled
Annotations:  <none>
Status:       Active

创建 Service 资源

apiVersion: v1
kind: Service
metadata:
  name: nginx-demo-service
  namespace: readiness
spec:
  ports:
    - name: http
      port: 80
      protocol: TCP
      targetPort: 80
  type: ClusterIP
  selector:
    app: nginx

创建 TargetGroupBinding 资源

TargetGroupBinding 资源也是 AWS EKS 专属,用于将 Service 和 Target Group 进行绑定,也是 EKS 和 ALB 一起使用时,相较于 Ingress 来说更加推荐的配置方式

apiVersion: elbv2.k8s.aws/v1beta1
kind: TargetGroupBinding
metadata:
  name: nginx-demo-tgb
  namespace: readiness
spec:
  serviceRef:
    name: nginx-demo-service
    port: 80
  targetGroupARN: <your-target-group-arn>
  targetType: ip

备注:TargetGroup 先手动创建,将资源 arn 进行替换

创建 Deployment 资源

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-demo-deployment
  namespace: readiness
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
        readinessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 10
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 3
      readinessGates:
      - conditionType: target-health.elbv2.k8s.aws/nginx-demo-tgb

这里的 target-health.elbv2.k8s.aws/nginx-demo-tgb 值,target-health.elbv2.k8s.aws 是固定前缀,后者 nginx-demo-tgb 是上边儿创建的 TargetGroupBinding 资源名称

滚动更新测试

当滚动更新时,通过 Rancher 面板可以看到提示

The status of pod readiness gate "target-health.elbv2.k8s.aws/nginx-demo-tgb" is not "True", but False

面板没有展示 Readiness Gates 的值,使用 kubectl 命令查看 Pod 状态

在等待中,最后一列是 “0/1”,当 Target Group 注册完成后,状态变为 “1/1”

滚动更新后,此时 Target Group 面板几个老 Pod 的信息还在,显示为 “deregistration” 正在注销

引申:值得一提的 Pod 注销等待

了解了 readinessGates 联动可能会自然的想到,当 Pod 在 Target Group 注销的时候有没有什么优雅的方式,等待 Target Group 注销完成,Pod 再退出?

目前来说是没有的。

实际上,以图一为例,Target Group IP-1 在注销的前期,还是会有少量流量进到 Pod-1,如果 Pod-1 退出的太快,那么这些请求就会 5XX 错误,当下的解决办法是等待 30 秒 Pod 再退出

lifecycle:
  preStop:
    exec:
      command: ["sleep", "30"]

其它补充

查询集群中已安装 aws-load-balancer-controller 的版本

$ kubectl describe deployment aws-load-balancer-controller -n kube-system | grep Image

最后,AWS EKS 的功能在持续迭代,说不定通过什么注解、标签就能更加优雅的解决问题了,本文可能有实效性,一切请以文档为准

参考