你在运行中的容器里编辑 WireGuard 配置,它工作正常,而下一次镜像更新会把它抹掉。配置并没有写错。这个文件位于容器的可写层,而该层只为这一个容器实例存在。真正要比较的不是用哪个工具,而是你的部署声明了哪些输入。凡是手工改过的东西,都是部署并不知道的输入。

两种状态,其中一种是一次性的

容器有一个用于运行时改动的可写层,容器被替换时这层会被丢弃。它不是配置仓库,也不是备份。镜像加上已声明的输入,才是运行内容的完整定义。

问一问:哪些 VPN 文件是声明过的输入,哪些只是意外:

docker diff wg-vpn
docker inspect --format '{{json .Mounts}}' wg-vpn

diff 会列出可写层里改了什么,那正是将要消失的东西。挂载列表则显示什么会留下。把两者一比,就能在丢失之前得到答案。

绑定挂载、命名卷还是机密:各自保护什么

三者都能存放 VPN 配置,区别在于谁有权限读取,以及替换时会发生什么。

MechanismSurvives recreationWho can read it
Writable layerNoAnyone with container access, until it is gone
Bind mountYes, while the host path existsWhatever the host permissions allow
Named volumeYes, until the volume is removedManaged by the runtime
SecretYes, as a declared inputThe container process, by design

绑定挂载会映射主机路径,因此文件的保护程度取决于该路径,可见范围取决于主机的权限。命名卷由运行时管理,容器删除后仍然存在,因此对于部署所拥有的状态,它是更安全的默认选择。机密机制用于你不希望放进镜像或环境变量的材料。

不要把每个环境变量和每个主机文件都当成同等强度的保护,它们并不是。

在容器内部编辑就是陷阱

陷阱不在工具,而在改动方向。在运行中的容器里做的改动对部署不可见,于是下一次重建会悄悄把它回滚,症状看起来像镜像里的回归。若必须在那里改动以排查问题,请立即复制出来并放进已声明的输入。然后再重建,证明部署能复现它。

配置可能包含私钥和账户材料,因此备份该文件属于机密处理工作,而不是文件拷贝。把属主和读取权限限制在容器的实际进程上,而不是为了消掉权限错误就放宽主机目录。

这种放宽正是容器里的便利变成主机暴露的方式。

有意识地重建,然后核对四件事

记录镜像版本、所需设备和能力、网络接入以及持久输入。设备与能力清单对 VPN 容器尤其重要,因为隧道需要网络设备以及配置它的权限。

确认哪些生成的运行时状态应当重建而不是保存。隧道首次启动时生成的密钥属于状态,保留还是重新生成,会影响每一个知道旧公钥的对端。

受控重建之后,核对身份、路由、DNS 以及必需的失败阻断。一个没有报错就启动的进程,并不能证明这四项中的任何一项。要在隧道运行期间检查一次,停止之后再检查一次,因为失败阻断只在隧道消失时才显现。

在容器之外保留恢复副本

在容器之外保留一份独立的恢复副本,并按照文档流程测试恢复。未经测试的 VPN 配置备份只是一种希望,尤其是当恢复路径涉及容器进程所需的权限时。

如果持有 VPN 的容器被替换,要在运行时支持的生命周期内协调共享其网络栈的应用。

这种协调就是重启与中断的区别。部署应当说明配置从哪里来,而卷文档正是核对运行时真正保留了什么的地方。

相关文章