在 Ravelin,我们已经迁移到 Kubernetes(在 GKE 上)。这是非常成功的。我们听到了 pod 中断预算,我们的 statefulsets 非常庄严,滚动节点替换运行顺利。
最后一个难题是将我们的 API 层从旧的 VM 移动到我们的 kubernetes 集群中。为此,我们需要设置一个 Ingress,以便可以从外部访问 API。
起初,这似乎是直截了当的。我们只需定义入口控制器,修改 terraform 以获得一些 IP 地址,而 Google 会处理几乎所有其他事情。这一切都像魔术一样工作。伟大的!
但是我们开始注意到我们的集成测试偶尔会收到 502 错误。然后开始了一段旅程,我将通过直接切入最终结论来为您省去阅读的痛苦。

优雅关机。

每个人都在谈论优雅关机。但是你真的不应该在 Kubernetes 中这样做。或者至少不是你在母亲膝下学到的优雅关机。在 Kubernetes 的世界中,这种程度的优雅对于危险的程度来说是不必要的。

好地方

以下是每个人都认为从服务或负载均衡器中删除 pod 在 Kubernetes 中工作的方式。

  1. 复制控制器决定删除一个 pod。
  2. pod 的端点将从服务或负载均衡器中删除。新的流量不再流向 pod。
  3. 调用 pod 的 pre-stop hook,或者 pod 收到 SIGTERM。
  4. 吊舱“优雅地关闭”。它停止侦听新连接。
  5. 当所有现有连接最终变为空闲或终止时,优雅关闭完成,并且 pod 退出。

不幸的是,这不是它的工作方式。

真实的故事

许多文档都暗示这不是它的工作方式,但并没有说明它。这个过程中的大问题是步骤 2 不会在步骤 3 之前发生。它们同时发生。使用普通服务删除端点是如此之快,您不太可能注意到问题。但是入口的反应通常要慢很多,因此问题变得非常明显。pod 可能会在端点的更改在入口处执行之前的很长一段时间内收到 SIGTERM。
这导致“优雅地关闭”实际上不是 pod 应该做的。它将接收新的连接,并且必须继续处理它们,否则客户端将收到 500 个错误,并且无缝部署和扩展的整个精彩故事将开始分崩离析。
这是真正发生的事情。

  1. 复制控制器决定删除一个 pod。
  2. pod 的端点将从服务或负载均衡器中删除。对于入口,这可能需要一些时间,并且新的流量将继续发送到 pod。
  3. 调用 pod 的 pre-stop hook,或者 pod 收到 SIGTERM。
  4. pod 应该在很大程度上忽略这一点,继续运行,并继续提供新的连接。如果可以的话,它可以向客户暗示他们应该搬到别处去。如果它使用 HTTP,它可能希望在响应的标头中设置“连接”:“关闭”。
  5. 仅当 Pod 的终止宽限期到期并被 SIGKILL 杀死时,Pod 才会退出。
  6. 确保此宽限期比重新编程负载均衡器所需的时间长。

如果它是第 3 方代码并且您无法更改其行为,那么您可以做的最好的事情是添加一个停止前的生命周期挂钩,该挂钩会在宽限期内休眠,这样 pod 将继续服务,就好像什么都没发生一样。

参考:https://philpearl.github.io/post/k8s_ingress/
参考: https://i4t.com/4424.html