优雅退出的时序到底是什么?preStop 睡一会儿真的能让负载均衡有时间摘除吗?宽限期是怎么被 preStop 和应用退出分掉的?这篇用节点上的 /proc/uptime 时间戳和 EndpointSlice 的状态变化,把删除一个 Pod 的每一步都对到同一条时间轴上。
preStop 睡 5 秒、应用收到 SIGTERM 后再睡 1 秒才退出:kubectl delete 从发出到 Pod 消失用了 7.2 秒
容器日志 start T0 -2.83s
容器日志 prestop-start T0 +0.13s
容器日志 prestop-end T0 +5.13s
容器日志 sigterm T0 +5.15s
容器日志 exit T0 +6.15s
EndpointSlice 里端点的 (ready, serving, terminating) = ((True, True, False),) T0 -0.64s
EndpointSlice 里端点的 (ready, serving, terminating) = ((False, True, True),) T0 +0.15s
EndpointSlice 里端点的 (ready, serving, terminating) = (没有端点) T0 +6.32s
事件 Killing:Stopping container app以 kubectl delete 发出的时刻为 T0:+0.13 秒 preStop 开始,+5.13 秒结束,SIGTERM 在 +5.15 秒才到,+6.15 秒应用退出,kubectl delete 一共等了 7.2 秒。EndpointSlice 里这个端点在 +0.15 秒就变成 (ready=false, serving=true, terminating=true):从被删除那一刻起 Service 就不再把新流量发给它,不用等 preStop。常见的 preStop: sleep 5 就是利用这段时间,让负载均衡、kube-proxy 有机会把它摘掉,再收到 SIGTERM。
preStop 是个空命令(true):kubectl delete 从发出到 Pod 消失用了 1.5 秒
容器日志 start T0 -2.85s
容器日志 sigterm T0 +0.07s
容器日志 exit T0 +1.08s
EndpointSlice 里端点的 (ready, serving, terminating) = ((True, True, False),) T0 -0.62s
EndpointSlice 里端点的 (ready, serving, terminating) = ((False, True, True),) T0 +0.08s
EndpointSlice 里端点的 (ready, serving, terminating) = (没有端点) T0 +1.18s
事件 Killing:Stopping container apppreStop 是空命令时 SIGTERM 在 +0.07 秒就到,端点在 +0.08 秒变成 terminating,两件事几乎同时发生——如果负载均衡摘除得慢,就有新请求被送到一个已经开始关闭的进程。这是为什么要用 preStop 睡一会儿。
terminationGracePeriodSeconds=6,preStop 睡 10 秒,应用收到 SIGTERM 后要 5 秒才肯退出:kubectl delete 从发出到 Pod 消失用了 8.5 秒
容器日志 start T0 -2.86s
容器日志 prestop-start T0 +0.07s
容器日志 sigterm T0 +6.07s
EndpointSlice 里端点的 (ready, serving, terminating) = ((True, True, False),) T0 -0.61s
EndpointSlice 里端点的 (ready, serving, terminating) = ((False, True, True),) T0 +0.07s
EndpointSlice 里端点的 (ready, serving, terminating) = (没有端点) T0 +8.28s
事件 Killing:Stopping container app
事件 FailedPreStopHook:PreStopHook failedpreStop 睡 10 秒但 terminationGracePeriodSeconds 只有 6 秒:+6.07 秒宽限期用完,钩子被截断(事件 FailedPreStopHook),SIGTERM 这时才送达;应用要 5 秒才肯退出,来不及了,Pod 在 +8.5 秒消失——SIGTERM 之后又被额外给了约 2 秒才被 SIGKILL,日志里没有应用的 exit。所以 preStop 的时间要计入宽限期,宽限期要设得比 preStop 加应用退出时间更大。
preStop 执行 exit 1:kubectl delete 从发出到 Pod 消失用了 1.6 秒
容器日志 start T0 -2.97s
容器日志 prestop-fail T0 +0.11s
容器日志 sigterm T0 +0.12s
容器日志 exit T0 +1.12s
EndpointSlice 里端点的 (ready, serving, terminating) = ((True, True, False),) T0 -0.71s
EndpointSlice 里端点的 (ready, serving, terminating) = ((False, True, True),) T0 +0.13s
EndpointSlice 里端点的 (ready, serving, terminating) = (没有端点) T0 +1.28s
事件 Killing:Stopping container app
事件 FailedPreStopHook:PreStopHook failed钩子 exit 1:事件 FailedPreStopHook: PreStopHook failed,但终止照常进行——SIGTERM 紧接着在 +0.12 秒到,应用 +1.12 秒退出。preStop 不能当作“一定会执行完的清理步骤”;重要的清理要写在应用自己处理 SIGTERM 的代码里。