在跨机房或高延迟环境下部署,针对 高延迟(Latency) 环境的 Corosync 超时参数优化建议
跨机房或异地部署 PVE 集群时,由于网络延迟(Latency)和抖动(Jitter)的存在,Corosync 默认的“毫秒级”超时参数非常容易导致节点频繁离线、重新加入甚至触发 HA 导致虚拟机非预期重启。
针对这种高延迟环境,建议通过修改 /etc/pve/corosync.conf 来增加容错能力。
1. 核心参数优化建议
在高延迟环境下,最重要的三个参数是 token、token_retransmits_before_loss_const 和 consensus。
| 参数 |
默认值 |
建议值 (异地/跨机房) |
说明 |
| 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),修改时必须非常小心,建议遵循以下流程:
-
备份配置文件:
cp /etc/pve/corosync.conf /etc/pve/corosync.conf.bak
-
修改配置文件:
使用编辑器打开 /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
}
...
-
应用配置:
修改完成后保存。Corosync 会检测到 config_version 的变化并自动应用。如果自动同步失败,可以手动重启服务(这会导致短暂的 quorum 变动,请确保当前没有正在进行的 HA 操作):
systemctl restart corosync
3. QDevice 的特殊考虑
QDevice (qnetd) 默认对延迟的敏感度比 Corosync 本身低,但如果你的节点到 QDevice 的 Ping 值超过 500ms,你可能还需要在 QDevice 的配置中进行微调。
注意: 在异地高延迟环境下,不建议将 HA(高可用)的重启阈值设得太激进。如果网络抖动极其频繁,建议只使用 QDevice 维持票数,而谨慎开启自动 Failover 功。
验证方法
修改后观察 /var/log/syslog 或 journalctl -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. 如何运行
- 将脚本保存为
latency_test.py。
- 赋予执行权限:
chmod +x latency_test.py。
- 运行并指定另一个节点的 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 |
1420 或 1280 |
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 不匹配时自动调小包大小。