公众号旧文
一个月1块钱的服务器,我却花了几小时去修
我有一台美国的 VPS,一个月只要 1 块钱人民币。
配置很普通,内存不到 512MB,硬盘只有 10GB,而且只有 IPv6,没有公网 IPv4。
说实话,这样一台机器,即使直接扔掉,也不会有什么经济损失。
但今天,我却为了修复它的网络问题,花费了大量时间,还消耗了不少 AI Token。
折腾结束后,我都有点怀疑,这件事到底值不值得。
一、从服务器突然断网开始
最开始,我发现服务器无法通过 SSH 连接,Tailscale 也访问不到。
好在服务商提供了 VNC 控制台,还能直接进入系统。
登录以后,尝试 Ping Tailscale 内网地址,系统返回:
ping: connect: Network is unreachable
这不是普通的网络超时,而是系统根本找不到可用的路由。
继续执行:
ip -br addr
结果发现,物理网卡 eth0 竟然处于 DOWN 状态。
这意味着操作系统启动以后,网卡根本没有正常配置起来。
我先手动启用网卡:
ip link set eth0 up
网卡虽然变成 UP,却只有一个 fe80:: 开头的链路本地 IPv6 地址,依然无法访问公网。
问题显然没有那么简单。
二、手动恢复网络,只是第一步
继续检查发现,这台服务器使用 cloud-init 生成网络配置,再交给 Debian 的 ifupdown 管理。
它的公网 IPv6 地址和网关并不位于同一个 /64 子网,需要额外处理路由。
更关键的是,之前的配置里加入了一条 pre-up 命令,试图在网卡启动前就添加网关路由。
启动日志中清楚地写着:
Error: Nexthop device is not up.
ifup: failed to bring up eth0
也就是说,系统还没把网卡启用,就试图通过这块网卡建立路由。
最终,网络初始化失败,连带导致公网 IPv6 和 Tailscale 全部失去连接。
通过 VNC,我手动配置 IPv6 地址和路由,终于重新 Ping 通了 Cloudflare 的公共 DNS。
四次请求全部成功,平均延迟约 22 毫秒。
但我知道,现在能联网,不代表问题已经修好。
因为手动执行的命令只改变当前运行状态,下一次重启,故障仍然可能出现。
三、真正麻烦的是开机启动
为了防止下一次重启又失联,我让 Codex 帮忙审计网络初始化过程。
这次没有急着修改配置,而是要求它先调查根因,准备修复方案,生成差异和回滚脚本,再逐步实施。
最后确认,之前加入的 pre-up 命令执行时机错误。
修复方案是让 ifupdown 按照正常流程启动网卡,再通过标准的 if-up.d 钩子补齐必要配置,同时恢复 cloud-init 的正常管理。
修复完成后,我们进行了一次受控重启。
结果很好。
eth0 自动启用,两个公网 IPv6 地址全部恢复,默认路由正常,SSH 和 Tailscale 也恢复了连接。
原先失败的 networking.service,终于变成了正常的 active (exited)。
到这里,最初的网络故障其实已经解决。
但在检查系统日志时,我们又发现了新的问题。
四、为了修好一个问题,又发现了一堆旧问题
systemd-sysctl 启动失败。
继续排查发现,服务器里竟然保留着 2022 年的旧网络调优配置。
其中一个参数:
net.core.netdev_budget_usecs = 4096
但当前内核允许的最低值已经是 8000。
还有一个 TCP 时间戳参数写错了名字,以及几个 conntrack 参数在启动时由于模块尚未加载而无法应用。
这些问题未必直接导致断网,却使系统启动流程出现错误。
于是又开始一轮审计。
旧参数该删除还是修改?内核默认值是否更合理?conntrack 模块什么时候加载?systemd-sysctl 和 udev 应该如何配合?
每个问题看起来都不大,但真正想把它处理干净,就需要不断核实。
尤其是 conntrack。
它负责跟踪网络连接状态,对防火墙、NAT 等功能很重要。
我最终把配置整理进独立文件:
/etc/sysctl.d/90-local-conntrack.conf
再利用 systemd-sysctl 和 udev 配合应用参数。
原先散落在旧配置中的重复赋值,则逐步移除。
整个过程没有直接清空连接表,也没有随意重载内核模块。
五、最费时间的不是修改,而是验证
有意思的是,实际修改的配置并不多。
真正消耗时间的是证明它们可靠。
比如,udev 规则究竟有没有被触发?
我们先在隔离环境验证规则匹配,再验证命令执行,最后才在生产环境进行一次限定范围的事件测试。
测试中确实捕获到了目标 systemd-sysctl 进程,现有 SSH 连接也没有中断。
但由于没有捕获到直接的内核写入证据,我们仍然没有把它描述为所有场景都已经验证成功。
类似的谨慎贯穿整个过程。
每次修改之前备份,每次修改之后比较参数、路由、配置哈希和网络状态。
能静态验证的,就不急着重启。
必须重启时,则提前准备好 VNC 救援。
最终,在最后一次重启验收中,三个 conntrack 参数保持为:
262144 / 262144 / 36000
systemd-sysctl 正常启动,两个公网 IPv6 地址都能访问外网,SSH、DNS 和 Tailscale 也全部正常。
这次迁移终于可以结案了。
六、但我真的需要为一块钱的机器做到这一步吗?
如果从经济账来看,当然不值。
这台 VPS 一个月只要 1 块钱,一年也不过 12 块钱。
我为它花费的时间,以及调用 AI 所消耗的 Token 成本,早就超过了服务器本身的价值。
如果它只是一台随时可以替换的测试机器,直接重新部署系统,可能会更省事。
甚至直接不要了,也没什么损失。
但我又觉得,这几个小时并不完全是浪费。
因为我真正学到的,不是如何修复某台特定服务器,而是理解了一套 Linux 网络启动机制。
以前遇到网络故障,我可能只是想着重启服务,或者重新安装系统。
这次却真正接触了 cloud-init、ifupdown、systemd-sysctl、udev 和内核模块之间的关系。
也进一步体会到,一个参数最终显示正确,并不能证明它是通过预期的机制正确生效的。
这恰恰是系统维护中很容易忽略的问题。
AI 的确可以帮我们写命令、分析日志、准备补丁,甚至协助设计验证流程。
但如果不给它明确的权限边界,它也可能做出错误的修改。
这次最初导致断网的 pre-up 配置,就是一个教训。
所以后来,我逐渐形成了一套做法:先只读审计,再准备候选方案,明确授权范围,实施最小改动,最后通过实际启动完成验收。
这套方法将来完全可以用在其他服务器上。
七、便宜的服务器,未必只能提供便宜的价值
后来,我想了想,如果今天处理的不是一台月租 1 块钱的 VPS,而是一台承载重要业务的生产服务器呢?
同样的故障,处理思路仍然适用。
只不过,那个时候如果缺乏经验,代价可能就不是几个小时和一些 Token 了。
从这个角度看,这台便宜的服务器反而成了一个很好的实验环境。
可以观察真实故障,可以研究系统行为,也可以在风险相对可控的情况下积累运维经验。
当然,这不意味着每个小问题都值得无限深入。
有时候及时止损,比追求完美更重要。
现在服务器网络已经恢复,配置迁移也通过了开机验收。
虽然仍有一些不影响当前使用的遗留警告,我决定暂时不再继续折腾。
技术上的认真应该有边界,学习上的收获也不必用机器本身的价格来衡量。
一台月租 1 块钱的服务器,确实不值得我一直投入几个小时。
但如果这些时间最终变成了可以迁移到其他机器的经验,又能够写成这样一篇文章,那它的价值就不止 1 块钱了。
毕竟,服务器可以随时换掉,真正留下来的,是自己对技术更深一层的理解。