关于 Istio 的 Ambient 模式,一个常见的问题是它如何在升级或重启期间处理流量。
在 Sidecar 模型中,代理和应用程序是 1:1 的关系,这种情况下无需担心此类问题——代理在应用程序停止时会关闭,并在应用程序启动时加载最新版本。
然而,Ambient 模式中,每个节点都有一个专用代理(“Ztunnel”),这意味着我们可能需要在应用程序运行时对其进行升级。
Ztunnel 遵循标准的滚动更新流程。也就是说,当我们需要引入新版本时,会按照以下步骤进行:
对于 Ztunnel,有两种类型的流量需要考虑:
第一种是当同时运行多个实例时,新建连接会发生什么。在非常短的时间内,两个实例都会接受连接(利用 SO_REUSEPORT
),这些连接由内核随机分配。然而,一旦新实例完全准备就绪,就会通知旧实例停止接受新连接,这样新实例可以开始处理所有新建连接。
第二种是对于我们正在关闭的实例上的现有连接会发生什么。虽然应用协议通常具有通知对端停止使用的机制,例如 HTTP/1.1 可以发送 Connection: close
标头,HTTP/2 可以发送 GOAWAY
,但 Ztunnel 在 L4 层工作,TCP 本身并没有这样的机制。为此,Ztunnel 使用了一种宽限期机制。只要仍有活动连接,旧实例就会继续运行并服务这些连接(但正如前面提到的,不再处理任何新连接)。最终,如果超出了可配置的宽限期,任何剩余的连接都会被发送 RST
(连接复位)。
结合起来,从“蓝色版本”升级到“绿色版本”的生命周期如下图所示:
第一条时间轴显示哪个实例正在积极接受连接。可以看到,这个状态从“蓝色”切换到“绿色”,其中有一个短暂的时间段内两个实例都在接受连接。
第二和第三条时间轴分别显示旧实例和新实例的状态。旧实例在新实例准备就绪后开始其宽限期,最终强制终止所有剩余连接。
对大多数人来说,重要的不是内部运作的细节,而是他们的应用程序是否会受到影响。简短的答案是:这取决于情况。
如果你的应用程序存在比宽限期更长的连接,这些连接可能会被重置。具体影响取决于你的应用程序——有些能够更优雅地处理这种情况,而有些则不行。
需要特别注意的是,在任何时候,新建连接都不会失败。因此,如果你的应用程序在连接终止后尝试重新建立新连接(这是正确的做法!),那么这个过程始终可以成功。
如果你的应用程序无法很好地处理连接重置,有两种主要的方法可以缓解问题:
第一种方法是确保 Ztunnel 的宽限期比你的最大连接时长更长。许多使用长连接的场景(例如连接池)可以配置连接的最大时长,并在达到最大时长后重新建立连接。如果需要,可以将此值设置得更短。此外,Ztunnel 的宽限期可以通过配置其 terminationGracePeriodSeconds
设置来调整——这个值可以设置得相当高,甚至是数小时。
另一种更具侵入性但非常安全的选项是,确保升级过程中节点上没有运行应用程序。这可以通过给节点打上隔离标记来实现。在大多数情况下,这种方法可能过于繁琐,但如果你将 TCP 连接视为“宠物”(需要精心维护),这种方式可能是值得的。
你可能会好奇,为什么节点隔离/关闭可以 100% 避免连接终止,而 Ztunnel 升级却不能?问题在于,Ztunnel 升级时无法通知应用程序优雅地关闭或终止连接。然而,当应用程序关闭时,它会收到
SIGTERM
信号,可以利用该信号进行优雅的关闭。当然,你的应用程序必须正确处理该信号才能获得任何好处!
最后更新于 2025/01/08