言林博客
博客
AWS 负载均衡部署零停机

AWS 负载均衡部署零停机

汤如林
发布于 2023-03-05 11:13
云计算
言林

目前我们的应用服务全部使用AWS EKS的 Kubernetes 平台做的容器部署,同时使用aws Application Load Balancer 做服务的公网流量入口。开始每次部署的时候,都会出现服务停止的现象。停止时间从几秒钟到几分钟时间。

我们做了一系列的测试,跟脚本的验证。

这是我们的部署方案之一:

  • service jiameng-api-dev 运行在 aws EKS
  • service jiameng-api-dev 有两个Pod
  • Aws Load Lalancer 用来作为 Kubernetes ingress 服务
  • kubctl apply 用来在aws ECR中image镜像发生变更时,强制Pod重新构建容器

为了保证 aws 零部署停机时间,我们启用了 alb pod_readiness_gate

当 alb pod_readiness_gate 未启用时,目标组中可能没有健康目标。 目标可以处于耗尽或初始状态,但没有健康状态。

启用 alb pod_readiness_gate 后,可以保证始终至少有一个健康目标可用于目标组。 但是,这只是降低了 5xx 错误的几率,并没有 100% 消除错误。

我们写一个脚本来测试它。 请参阅 aws_alb_test.sh。 它使用 describe-target-health 在向 Go 应用程序发送请求之前获取目标健康状态。

我们还启用了 alb 访问日志来跟踪哪些目标正在为我们的测试请求服务。

下面是测试步骤:

  • 在一个终端中运行 shell 脚本 ./aws_alb_test.sh 以将请求发送到负载均衡器
  • 在 k8s_deployment.yaml 中更改日期值 date: "<DATE>" 和其他测试更改以强制在下一个 kubectl apply 命令中重建 pod
  • 在单独的终端中运行 kubectl apply -f k8s_deployment.yaml 以重建 pod
  • 观察shell脚本终端结果搜索5xx错误

5xx 错误结果

理想情况下,我们可以期望在启用 alb pod_readiness_gate 的情况下出现零停机时间。 但实际上,我们仍然可以从 shell 脚本终端观察到 5xx 错误。

上面的示例显示同时存在两个耗尽(Terminating)目标和两个健康目标。 看起来两个耗尽目标之一仍在接收流量,否则我们应该从其他两个健康目标中获得 200。

为了再次确认,我们找到了上述请求的负载均衡器访问日志记录。 敏感数据已替换为 xxxxxxxx。

我们可以看到负载均衡器已将请求路由到这个耗尽目标 10.0.2.61:1325。target_status_code 为-,elb_status_code 为504。表示负载均衡器与此目10.0.2.61 之间的连接已关闭,然后负载均衡器返回504 给客户端。 这些字段的解释可以在问日志中找到。 这个连接关闭是有道理的,因为这个目标 10.0.2.61 的 pod jiameng-api-dev-7cf849584f-kh7vk 处于终止状态,这个 pod 可以很快退出。

5xx错误原因

负载均衡器已将流量路由到耗尽状态目标,但负载均衡器与耗尽目标之间的连接因目标的 pod 终止而关闭。

5xx错误解决方法

为解决此问题,我们希望使耗尽目标始终可用。 换句话说,如果一个目标处于 draining 状态,则其关联的 pod 不应退出。 通过这种方式,我们希望负载均衡器和 draining 目标之间的连接不会关闭,除非达到目标的连接空闲超时。

最终我们发现这几个参数值比较有用

总而言之,我们希望 Pod 的生存时间长于其相关目标,即对于这三个值,terminationGracePeriodSeconds > preStop > Deregistration delay。

最终我们实现了部署零停机的效果。更多详情,参阅言林在github上的英文分享 https://github.com/yanlin-group/cicd。