故障切换测试:已建立的 TCP 流能否存活?
备用出口可以接受每一个新连接,却仍会切断正在进行的传输。两种结果都要测,并把切回的情形写进计划。
「备份能用」这句话里藏着两种结果。新请求成功,这是恢复;已在进行的传输继续,这是连续性。这是两个不同的承诺,而多数故障切换设计只兑现前者,后者则悄然超时。选机制之前先确定你需要哪一种,并把测试写成能区分两者的样子。
两种看起来像同一个成功的结果
设想一个位于 10.90.0.42 的客户端,连接到 198.51.100.20:443。主出口把它转换成一组公网地址和端口,备用出口转换成另一组。客户端的内网地址从不改变,所以人们以为连接会存活。
服务器看不到内网地址。它看到的是转换后的端点,而切换之后这个端点变了。
于是旧流超时,而到同一服务的新连接立刻可用。两个观察同时成立。
出口变化时服务器看到什么
TCP 连接由四元组标识,改变源地址或端口,在接收方主机看来就是另一条连接。来自新源、属于服务器不认识的连接的报文会被丢弃,或收到重置。VPN 客户端的地址里没有任何东西能在这次变化中保住关联。
有状态故障切换正是为此存在,它在转发设备之间交换转换状态与连接状态。这是这对设备的特性,不是隧道的特性,必须设计和测试,不能假定。
在向任何人承诺连续性之前,先确认这对设备是否真的保留状态。下面的命令显示两侧的当前状态。
conntrack -L -d 198.51.100.20 2>/dev/null | head
ss -tin dst 198.51.100.20
tcpdump -ni any host 198.51.100.20 -c 10为什么同一个公网地址还不够
两条路径保持同一个公网地址是好起点,因为服务器看到的源端点相同。但它本身并不充分。转发设备仍然需要转换状态、防火墙状态和可用的回程路径,而这些必须存在于切换后承载流量的那台设备上。
RFC 3022 描述了地址转换,并把转换设备之间的状态交换视为备用设计的一部分。浮动地址或复制的配置文件只搬配置,不搬状态。
所以只通过备用做 ping 的测试证明力很弱。ping 没有可失去的连接状态。
确定需求的强度
如果用户可以在故障后重连,设计就是恢复型,验收测试是一条新请求。如果文件传输必须存活,设计就需要状态同步和细致得多的测试。如果远程会话必须存活,需求更强,可能需要应用层机制而不是网络层机制。
把需求写成带可度量结果的句子,例如上传无需从头重来即可完成,或运维人员在一分钟内重连。然后选择能满足它的最便宜机制,而这往往就是恢复型设计。
明确说出主路径恢复时会发生什么,因为自动切回可能切断刚刚在备用上恢复的连接。这个情形属于测试计划,而不是脚注。
检查旧流与新请求
先通过主路径启动一个无害的长时传输,再向同一服务发一条独立的新请求。记录应用上下文、观察到的出口地址和时间。用获批的方式切断主路径,并在通过备用发送新请求的同时继续观察原传输。
报告三个值:旧流是继续、被重置还是超时;新操作第一次成功的时间;以及切回是否打断过什么。这三行就是验收记录。
ss -tin dst 198.51.100.20
curl -sS -o /dev/null -w '%{http_code} %{time_total}\n' https://198.51.100.20/
tcpdump -ni any port 443 -c 20如果负载会写数据,超时后不要盲目重试不确定的操作。写入可能在应答丢失之前就已经落地。向应用询问结果,或只在操作幂等的地方重试。然后把验收声明写成三段:新操作在出口策略下恢复,已有流具有已记录的中断行为,应用安全地继续运行。这个声明可测试,也诚实地说明了设计并不承诺什么。