高延迟环境下Corosync 超时参数优化建议

在跨机房或高延迟环境下部署,针对 高延迟(Latency) 环境的 Corosync 超时参数优化建议

跨机房或异地部署 PVE 集群时,由于网络延迟(Latency)和抖动(Jitter)的存在,Corosync 默认的“毫秒级”超时参数非常容易导致节点频繁离线、重新加入甚至触发 HA 导致虚拟机非预期重启。

针对这种高延迟环境,建议通过修改 /etc/pve/corosync.conf 来增加容错能力。

1. 核心参数优化建议

在高延迟环境下,最重要的三个参数是 tokentoken_retransmits_before_loss_constconsensus

参数 默认值 建议值 (异地/跨机房) 说明
token 1000 (ms) 3000 - 5000 令牌传递的最大等待时间。如果网络延迟波动大,建议调高。
token_retransmits_before_loss_const 4 10 - 15 判定节点丢失前的重试次数。增加此值可应对短暂的网络抖动。
consensus 1200 1.2 * token 达成共识的超时时间,必须大于 token。建议设为 token 的 1.2 倍。
join 50 100 节点加入集群时的等待时间。

2. 操作步骤

由于 PVE 的 /etc/pve/ 是集群共享的文件系统(pmxcfs),修改时必须非常小心,建议遵循以下流程:

  1. 备份配置文件:

    cp /etc/pve/corosync.conf /etc/pve/corosync.conf.bak
    
  2. 修改配置文件:
    使用编辑器打开 /etc/pve/corosync.conf。你需要手动增加 config_version(版本号加 1),并修改 totem 部分:

    logging {
      debug: off
      to_syslog: yes
    }
    
    totem {
      cluster_name: your-cluster-name
      config_version: 3  # <--- 必须比当前值大
      interface {
        linknumber: 0
      }
      ip_version: ipv4
      secauth: on
      version: 2
    
      # --- 增加/修改以下参数 ---
      token: 5000
      token_retransmits_before_loss_const: 10
      consensus: 6000
      join: 100
    }
    ...
    
  3. 应用配置:
    修改完成后保存。Corosync 会检测到 config_version 的变化并自动应用。如果自动同步失败,可以手动重启服务(这会导致短暂的 quorum 变动,请确保当前没有正在进行的 HA 操作):

    systemctl restart corosync
    

3. QDevice 的特殊考虑

QDevice (qnetd) 默认对延迟的敏感度比 Corosync 本身低,但如果你的节点到 QDevice 的 Ping 值超过 500ms,你可能还需要在 QDevice 的配置中进行微调。

注意: 在异地高延迟环境下,不建议将 HA(高可用)的重启阈值设得太激进。如果网络抖动极其频繁,建议只使用 QDevice 维持票数,而谨慎开启自动 Failover 功。

验证方法

修改后观察 /var/log/syslogjournalctl -u corosync -f

  • 如果看到 [TOTEM ] Token has not been received in X ms 的警告减少,说明调整生效。
  • 使用 pvecm status 检查 Quorum 是否稳定。

在高延迟环境下,单次的 Ping 值(平均值)参考意义有限,真正的威胁在于网络抖动导致的 P99 延迟峰值

下面是一个为您准备的 Python 脚本。它会模拟 Corosync 的心跳频率,连续探测目标节点,并计算出平均值、P95、P99 以及最大延迟。

1. P99 延迟测试脚本 (latency_test.py)

您可以直接在 PVE 节点上运行(PVE 默认自带 Python 3)。

Python

import subprocess
import statistics
import time
import sys

def get_latency(host, count=100):
    print(f"--- 正在测试到 {host} 的延迟,发送 {count} 个包,请稍候... ---")
    latencies = []
    
    for i in range(count):
        try:
            # 模拟心跳频率,每秒发一次或更快
            output = subprocess.check_output(
                ["ping", "-c", "1", "-W", "2", host], 
                stderr=subprocess.STDOUT, 
                universal_newlines=True
            )
            # 提取 time=XX ms
            for line in output.splitlines():
                if "time=" in line:
                    time_val = float(line.split("time=")[1].split(" ")[0])
                    latencies.append(time_val)
        except subprocess.CalledProcessError:
            print(f"警告: 第 {i+1} 个包丢包了")
        
        # 模拟心跳间隔 (Corosync 默认 token 是 1s)
        time.sleep(0.5)

    if not latencies:
        print("错误: 无法获取任何延迟数据,请检查网络连接。")
        return

    latencies.sort()
    avg_v = statistics.mean(latencies)
    p95_v = latencies[int(len(latencies) * 0.95)]
    p99_v = latencies[int(len(latencies) * 0.99)]
    max_v = max(latencies)

    print("\n--- 测试结果 ---")
    print(f"目标主机: {host}")
    print(f"平均延迟: {avg_v:.2f} ms")
    print(f"P95 延迟 : {p95_v:.2f} ms (95% 的包在此延迟内)")
    print(f"P99 延迟 : {p99_v:.2f} ms (极少数尖峰延迟)")
    print(f"最大延迟: {max_v:.2f} ms")
    print("----------------\n")
    
    return p99_v

if __name__ == "__main__":
    target = sys.argv[1] if len(sys.argv) > 1 else "8.8.8.8"
    get_latency(target)

2. 如何运行

  1. 将脚本保存为 latency_test.py
  2. 赋予执行权限:chmod +x latency_test.py
  3. 运行并指定另一个节点的 IP:

Bash

python3 latency_test.py <对端节点IP>

3. 如何根据测试结果计算 token 值?

根据测得的 P99 延迟,建议按照以下公式设置 token 值以确保稳定性:

token = P99 \times 2 + 500

计算逻辑:

  • P99 \times 2:为偶尔出现的极端网络抖动留出两倍的余量。
  • + 500:给系统处理、Corosync 进程调度预留的基础开销。
  • 结果取整:建议设置在 3000ms 到 10000ms 之间。

举例:

如果你测得异地节点间的 P99 延迟是 800ms,那么:

800 \times 2 + 500 = 2100ms

为了更稳妥,建议设置为 3000ms


4. 进阶建议:针对跨机房节点的网络链路

如果您在异地节点间使用公网连接,我建议您检查一下 MTU 值

  • 跨机房的 VPN(如 WireGuard 或 Tailscale)通常会增加额外的包头。
  • 如果 MTU 设置不当导致分包,Corosync 的性能会大幅下降甚至连接断开。
  • 建议将 VPN 网卡的 MTU 设置为 1280 或更低,并确保 PVE 的 Corosync 接口能够匹配。

在异地 PVE 集群(通过 VPN 连接)中,MTU(最大传输单元) 设置不当是导致集群连接不稳定、Web 界面卡顿甚至 QDevice 频繁离线的“隐形杀手”。

由于 VPN 隧道会在原始数据包外包装一层额外的 Header,如果不调整 MTU,数据包就会在路由器或 VPN 网关处被分片(Fragmentation),从而引发 Corosync 心跳丢失。


1. 常见 VPN 的 MTU 推荐值

假设您的物理网卡(以太网)默认 MTU 为 1500

VPN 类型 额外开销 (Header) 推荐 MTU 设置 原因
WireGuard 60 - 80 bytes 14201280 1420 是标准值;1280 是 IPv6/异地跨公网最稳的值。
Tailscale - 1280 Tailscale 内部自动处理,但手动限制 1280 能解决 99% 的兼容性。
ZeroTier 40+ bytes 1280 跨公网节点建议统一降至 1280。
OpenVPN (UDP) ~68 bytes 1300 UDP 封装开销较大。

黄金法则: 在跨地域公网环境下,将 VPN 虚拟网卡的 MTU 统一设置为 1280 是最稳妥的选择,因为这是 IPv6 要求的最小 MTU,几乎能通过所有复杂的公网路由。


2. 如何实测“最完美”的 MTU 值?

不需要猜测,您可以使用 ping 命令通过测试包的大小来探测物理路径允许的最大 MTU(不产生分片):

在 PVE 节点运行以下命令(假设目标是另一个节点的 VPN IP):

# -M do 表示禁止分片,-s 指定包大小
# 注意:1472 (数据) + 8 (ICMP) + 20 (IP Header) = 1500
ping -c 3 -M do -s 1472 <对端VPN_IP>
  • 如果显示 Frag needed and DF set 说明包太大了。请不断减小 -s 后的数值(如 1400, 1350…)。
  • 直到能 Ping 通: 记下该数值 S
  • 最终 MTU = S + 28

3. 配置建议:针对 WireGuard

如果您使用 WireGuard 连接 PVE 节点,请在配置文件的 [Interface] 部分手动指定:

[Interface]
PrivateKey = <YOUR_KEY>
Address = 10.0.0.1/24
MTU = 1280  # <--- 强制设置为 1280
...

修改后重启接口:

wg-quick down wg0
wg-quick up wg0

4. PVE 网络接口同步调整

为了确保 PVE 的 Corosync 流量完全契合 VPN 的 MTU,建议在 /etc/network/interfaces 中也检查一下。

如果您的 Corosync 是运行在 VPN 虚拟网卡上的,通常不需要额外修改 vmbr0 的 MTU,但必须确保 VPN 隧道内传输的包不会超过您刚才设置的 1280


5. 一个关键的内核参数优化

为了进一步缓解由于 MTU 或网络抖动引起的重传问题,建议在所有 PVE 节点执行以下内核优化(特别是针对异地 VPS):

# 开启 TCP 窗口缩放和优化重传机制
cat <<EOF >> /etc/sysctl.conf
net.ipv4.tcp_mtu_probing = 1
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
EOF

sysctl -p
  • tcp_mtu_probing = 1:允许内核自动探测路径上的 MTU,在发生 MTU 不匹配时自动调小包大小。