滚动升级中遇到的问题
AWS EKS 的 Deployment 资源在滚动升级时会导致可用 Pod 数量变少,其根源来自于 K8S 与 Target Group 状态不一致导致,本文将解释这一现象产生的原因,同时使用 readinessGates 参数解决问题
上图描述了 Deployment 滚动更新中的一个状态,解释如下:
- 首先,K8S 的 Deployment 启动一个新 Pod-3,健康检查通过后,它是一个可以接收流量的 Pod
- Pod-3 既已启动,K8S 选择一个老 Pod,即本例中的 Pod-1,发送信号让其退出
- 切换到 Target Group 视角,IP-1(对应的 Pod-1)开始注销,IP-3 正在注册,Target Group 注册注销的时间,相较于 K8S 的容器创建、退出要多一些时间
- 此时,Pod-3 已准备好,但 Target Group 的健康检查或其它配置还未完成,不会转发流量到 Pod-3
- Pod-1 已退出,Target Group 也正在注销 IP-1,不会转发流量到 Pod-1
- 原本 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 的功能在持续迭代,说不定通过什么注解、标签就能更加优雅的解决问题了,本文可能有实效性,一切请以文档为准