公众号旧文

一个月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 块钱了。

毕竟,服务器可以随时换掉,真正留下来的,是自己对技术更深一层的理解。