零停机K8S推出:等到探测器知道后才真正停止pod?



我正在尝试在k8s上实现零停机部署。我的部署有一个副本。pod探测器是这样的:

apiVersion: apps/v1
kind: Deployment
metadata:
name: app
namespace: ${KUBE_NAMESPACE}
spec:
selector:
matchLabels:
app: app
replicas: 1
template:
metadata:
labels:
app: app
spec:
containers:
- name: app-container
imagePullPolicy: IfNotPresent
image: ${DOCKER_IMAGE}:${IMAGE_TAG}
ports:
- containerPort: 80
livenessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 5
periodSeconds: 5
readinessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 5
periodSeconds: 10
terminationGracePeriodSeconds: 130

然而,每次kubectl rollout status返回和报表rollout完成后。我经历了一小段时间的bad gateway

然后我添加了一个测试,我让/healthprestop中返回500,并在实际停止pod之前等待至少20秒。

# If the app test the /tmp/prestop file exists, it will return 500.
lifecycle:
preStop:
exec:
command: ["/bin/bash", "-c", "touch /tmp/prestop && sleep 20"]

然后我发现在k8s停止pod后,流量仍然可以流向旧pod(如果我访问/health,我可以得到500的结果)。

因此,看起来像是负载平衡器决定哪些pod可以仅由探测结果使用。由于探测有一段时间,总是有一个小窗口,pod停止,但负载平衡器仍然不知道,可以将流量引导到它,因此用户体验停机时间。

所以我的问题是:为了实现零停机部署,似乎必须在实际停止pod之前让探测器知道pod正在停止。这样对吗?还是我做错了什么?

在Google搜索并做了一些测试之后。我发现不需要在prestop后手动回复500个探针。

根据文档

在kubelet开始优雅关闭的同时,控制平面从端点(如果启用了,还有EndpointSlice)对象中删除关闭Pod,这些对象代表一个配置了选择器的服务。ReplicaSets和其他工作负载资源不再将关闭Pod视为有效的服务内副本。缓慢关闭的Pod不能继续作为负载均衡器(如服务代理)提供流量,一旦终止宽限期开始,Pod就会从端点列表中删除。

启动关闭后,pod将无法获得流量。但我也发现这个问题说,在开始关闭一个pod到实际从端点删除它之间确实有延迟。

所以不是在prestop中返回500到探针,我只是在prestop中睡眠60秒。同时,让/health检查返回200,并显示节点处于运行或预停止状态。然后我做了一个rollout,得到了以下结果:

b'{"node_id":"a5c387f5df30","node_start_at":1612706851,"status":"running"}' at 1612717529.114602
b'{"node_id":"a5c387f5df30","node_start_at":1612706851,"status":"running"}' at 1612717530.59488
b'{"node_id":"a5c387f5df30","node_start_at":1612706851,"status":"running"}' at 1612717532.094305
b'{"node_id":"a5c387f5df30","node_start_at":1612706851,"status":"running"}' at 1612717533.5859041
b'{"node_id":"a5c387f5df30","node_start_at":1612706851,"status":"running"}' at 1612717535.086944
b'{"node_id":"a5c387f5df30","node_start_at":1612706851,"status":"running"}' at 1612717536.757241
b'{"node_id":"a5c387f5df30","node_start_at":1612706851,"status":"running"}' at 1612717538.57626
b'{"node_id":"a5c387f5df30","node_start_at":1612706851,"status":"prestop"}' at 1612717540.3773062
b'{"node_id":"a5c387f5df30","node_start_at":1612706851,"status":"prestop"}' at 1612717543.2204192
b'{"node_id":"a5c387f5df30","node_start_at":1612706851,"status":"prestop"}' at 1612717544.7196548
b'{"node_id":"a5c387f5df30","node_start_at":1612706851,"status":"prestop"}' at 1612717546.550169
b'{"node_id":"a5c387f5df30","node_start_at":1612706851,"status":"prestop"}' at 1612717548.01408
b'{"node_id":"a5c387f5df30","node_start_at":1612706851,"status":"prestop"}' at 1612717549.471266
b'{"node_id":"17733ca118f4","node_start_at":1612717537,"status":"running"}' at 1612717551.387528
b'{"node_id":"17733ca118f4","node_start_at":1612717537,"status":"running"}' at 1612717553.49984
b'{"node_id":"17733ca118f4","node_start_at":1612717537,"status":"running"}' at 1612717555.404394
b'{"node_id":"17733ca118f4","node_start_at":1612717537,"status":"running"}' at 1612717558.1528351
b'{"node_id":"17733ca118f4","node_start_at":1612717537,"status":"running"}' at 1612717559.64011
b'{"node_id":"17733ca118f4","node_start_at":1612717537,"status":"running"}' at 1612717561.294955
b'{"node_id":"17733ca118f4","node_start_at":1612717537,"status":"running"}' at 1612717563.366436
b'{"node_id":"17733ca118f4","node_start_at":1612717537,"status":"running"}' at 1612717564.972768

调用预停止钩子后,a5c387f5df30节点仍然有流量。大约10秒后,它再也没有收到任何流量。所以它与我在prestop中所做的任何事情都没有关系,它纯粹是一个延迟。

我用fargate在AWS EKS上做了这个测试。我不知道其他k8s平台的情况。

这完全取决于你的应用程序在从kubernetes接收SIGTERM信号时正在做什么。为了优雅地关闭你的应用程序,你应该监听SIGTERM事件并干燥你的所有连接,除此之外,你应该从你的readiness端点开始回复500,这将使kubernetes停止发送你的新请求。

有很多关于这个话题的文章,你可以在谷歌上找到

https://www.driftrock.com/blog/kubernetes-zero-downtime-rolling-updateshttps://learnk8s.io/graceful-shutdown

相关内容

  • 没有找到相关文章

最新更新