ZTunnel 更新过程

ZTunnel 在 OSI 模型的第 4 层(L4)运行:它代理 TCP 字节流,无法查看其上层的应用协议。因此,已建立的 TCP 连接无法从一个 ZTunnel 进程移交给替代进程。由于 ZTunnel 以每个节点一个的 DaemonSet 形式运行,替换它至少会一次影响一个节点上的所有网格流量。了解这种滚动更新行为,有助于你为依赖长连接的工作负载规划更新。

滚动更新阶段

默认情况下,ZTunnel DaemonSet 使用 RollingUpdate 策略,并一次处理一个节点。在每个节点上,替代过程会经历以下阶段:

  • 启动 — 新的 ZTunnel pod 启动,旧 pod 继续提供流量服务。
  • 就绪 — 新的 ZTunnel 在节点上的每个 pod 中建立监听器并报告就绪。两个实例会短暂并行运行;由于 ZTunnel 使用 SO_REUSEPORT,在此期间任一实例都可能接受新连接。
  • 排空 — Kubernetes 向旧 ZTunnel 发送 SIGTERM,旧 ZTunnel 随即关闭监听器并开始排空。从此时起,只有新的 ZTunnel 接受连接;节点在任何时刻都不会处于没有 ZTunnel 监听的状态。
  • 连接处理 — 旧 ZTunnel 继续为其已经持有的连接提供服务。
  • 终止terminationGracePeriodSeconds 定义的排空时间结束后,旧 ZTunnel 会强制关闭仍处于打开状态的连接。

任何持续时间超过排空时间的连接都会被重置。下面两节介绍如何延长该时间,或完全避免强制重置。

配置优雅的连接终止

最简单的缓解措施是将 terminationGracePeriodSeconds 调高到足够让应用的连接在排空阶段自然完成。要选择合适的值,需要了解网格中工作负载的连接生命周期。请注意,DaemonSet 一次处理一个节点,因此过大的值会延长整个集群更新的持续时间——应尽量设置一个平衡的值。

ZTunnel custom resource(CR)中设置该值:

apiVersion: sailoperator.io/v1
kind: ZTunnel
metadata:
  name: default
spec:
  version: v1.30.4
  namespace: ztunnel
  values:
    ztunnel:
      terminationGracePeriodSeconds: 300
  1. 此示例中为五分钟——请根据需要保护的最长连接生命周期调整该值。
NOTE

实现重试逻辑或使用较短 keepalive 超时的应用,比持有非常长的空闲 TCP 连接的应用更能从 ZTunnel 重启中平稳恢复。

通过排空节点安全地更新 ZTunnel

由于 TCP 连接无法在 ZTunnel 进程之间传输,将应用迁移到新 ZTunnel 的唯一可靠方式是优雅地重启应用本身。排空节点可以受控地完成这一过程:应用按照自身的终止宽限期关闭,空节点上的 ZTunnel 在没有任何流量风险的情况下完成替换,应用恢复后通过新的 ZTunnel 重新连接。

操作步骤

  1. 将 ZTunnel DaemonSet 切换为 OnDelete 更新策略,以便仅在删除旧 pod 后创建新 pod:

    apiVersion: sailoperator.io/v1
    kind: ZTunnel
    metadata:
      name: default
    spec:
      version: v1.30.4
      namespace: ztunnel
      values:
        ztunnel:
          updateStrategy:
            type: OnDelete
    1. 使用 OnDelete 时,更新 ZTunnel 资源本身不会替换任何正在运行的 pod;只有删除某个节点上的 ZTunnel pod 时,该节点才会更新。
  2. ZTunnel CR 的 spec.version 字段设置为目标版本。

  3. 排空一个节点。所有应用会迁移到其他节点,并在各自的 terminationGracePeriodSeconds 约束下优雅地关闭长连接。

  4. 删除已排空节点上的旧 ZTunnel pod,并等待新 pod 启动。由于节点上已没有工作负载,此替换不会给流量带来风险。

  5. 再次将节点标记为可调度。重新调度回该节点的工作负载会自动使用新的 ZTunnel。

  6. 对集群中的每个剩余节点重复步骤 3 至 5。

其他资源