OpenVPN 本地绑定在启动时失败:等待还是重试
当本地地址尚未就绪时,OpenVPN 无法绑定。比较有上限的等待与重启策略,并看看为什么 network-online.target 并不是保证。
对于开机时无法启动的 OpenVPN 服务器,常见的修复有两种:等待地址出现,或者让服务不断重试直到绑定成功。两者针对同一个症状,但失败时的表现不同。等待把一项明确的前提写进逻辑,前提缺失时就大声失败;重试则把前提藏起来,一直试下去。哪一种适合你的 unit,取决于那个地址是经过设计的依赖,还是时序上的偶然。
两者都胜过固定睡眠,因为固定睡眠既不等待任何条件,也不报告自己在等什么。
local 选项究竟要求什么
local 指定守护进程绑定套接字所用的地址,而绑定到一个尚不存在的地址会立刻失败,并报出关于所请求地址的错误。没有这个选项时,守护进程监听所有地址,这正是同一份配置能在接口尚未就绪的机器上工作的原因:它监听的是所有地址,而不是你指定的那一个。这一差别就是故障的全部,而且在重启之前一直不可见。
要求并不是接口在某种笼统意义上已经就绪。地址必须在绑定的那一刻就已分配给正确的接口,而且必须是底层接口,而不是同一守护进程稍后才会创建的隧道。登录几分钟后才做的检查,对启动顺序没有任何证明力。
等待地址与重试绑定
等待意味着用一个 ExecStartPre 反复轮询直到地址出现,尝试次数有上限,超时后以非零状态退出。重试则是让主进程失败,再请求服务管理器把它重新启动,例如 Restart=on-failure 配合 RestartSec=5。前者把缺失的前提报告为失败的 unit,后者则把它报告为一连串最终会停止的重启;这在日志里更吵,但能扛住因你无法预料的原因而迟到的地址。
若地址是经过设计的依赖,优先等待;若地址通常会出现只是时机不定,优先重试。把两者结合起来是合理的:有上限的等待覆盖常见情况,重启策略覆盖例外。两者都不该无限运行,因为无限重试的 unit 是在掩盖错误配置,而不是把它报告出来。
为什么 network-online.target 还不够
network-online.target 只承诺当前网络实现所理解的在线状态,此外别无其他。在 systemd-networkd 下,由 systemd-networkd-wait-online 决定该目标何时达成,而它默认的在线判定可能在你那个具体地址出现之前就已满足。在 NetworkManager 下,wait-online 服务按自己的规则执行。排在目标之后是可靠的基础,但并不保证某一个地址、某次 DNS 应答或某个远端对端已经就绪。
同样的推理也排除了那些常见替代方案。桌面自启动项等待的是用户会话,而服务器上这个会话可能永远不打开;延迟执行的 cron 命令只是增加了流逝的时间,却没有指明它指望出现的那个资源。两者都可能数月看似成功,然后在唯一真正重要的一次启动上失败。
冷启动之后再检查绑定
用真正的冷启动来验证,而不是重启服务,因为重启会继承启动流程必须创建的地址。启动之后,ss -lntp 或 ss -lunp 会显示每个监听者实际绑定的本地地址,journalctl -b -u openvpn-server@server 则显示第一次尝试是否失败。把绑定的地址与配置中的地址对照,然后在网络重新连接后再重复一次对照。
如果配置从来不需要固定地址,删掉 local 是合理的简化,但请把它当作安全决策,而不是排障习惯。
放宽监听范围会改变谁能访问它,所以这项改动要配合本该限制它的防火墙策略。一个在每个接口上都能可靠启动的服务并没有解决原来的问题;它只是把问题挪到了网络边界。