几条用血换来的运维铁律

管道把文件写成 0 字节、远程重启服务干掉自己的 SSH、geoip 数据 grep 不到明文——四件真发生过的事,和它们的解法。

自己维护几台机器,出故障的时候最多,涨记性的时候也最多。下面四条规矩,每一条背后都有一次真实的翻车——不是从文档里抄来的,是修完之后,被写进脚本注释里的那种。

一、管道会骗过你自己的眼睛

最阴的一次:要把一段配置推到远程机器上,用了最顺手的写法——

ssh node 'cat > /etc/foo.conf' < foo.conf

命令返回 0,没有任何报错。半小时后服务起不来,登进去一看,/etc/foo.conf 是 0 字节。

根因在 SSH 的标准输入上。某些会话——尤其是由网络层直接代理的连接(比如 Tailscale SSH)——并不保证把本地重定向的字节流原样送到远端命令的 stdin。你以为传的是文件,实际传的是沉默。要命的不是它会失败,而是它失败得毫无痕迹:退出码 0,日志干净,只有目标文件是空的。

凡是「静默成功」的操作,都要有一条对得上的凭证。

现在的规矩:跨机传文件只走 scp,传完在两边各跑一次 sha256sum,把数字对齐。多花三秒,换掉一整类问题。

二、别在自己脚下抽地毯

节点上的网络服务要重启,最方便的是走内网地址。但如果你正是通过这条内网链路连过去的,重启服务的瞬间,你自己的 SSH 会话就是第一个被切断的东西;而远端那条脚本,会在 SIGHUP 里被砍成半成品——改了一半的配置、重启了一半的服务。

规矩:重启网络服务时走公网地址,绕开自己那条命脉。只有内网可达的机器,就用 nohup 把脚本扔到后台跑,然后轮询日志,而不是守在前台等它回话。

让操作的生命周期,不要依赖那条正被你操作掉的东西。

三、长驻进程只在启动时读一次文件

证书、配置、数据文件——这类外部依赖,很多守护进程只在启动那一刻读进内存,之后再也不回头看。于是「新证书已经覆盖上去了」和「服务正在用新证书」是两件事,中间隔着一次重启。

判断方法很简单:问自己一句「这个进程是什么时候读这个文件的」。答不上来,就当它只读一次。换完文件后重启,再用工具本身确认生效——例如用 openssl x509 -noout -dates 看服务真正加载的那份证书,而不是只看文件的时间戳。

四、grep 查不到,不代表文件坏了

还有一次更大的事故,起因是怀疑一份数据文件损坏:grep 那个二进制文件,明文一个都搜不到,于是认定它坏了,换了文件、改了引用、重启——整个服务链全挂。

后来才知道,那份文件本来就是压缩格式,明文当然搜不到。真正的校验方式,是让程序自己去解析它:几乎所有服务都提供 dry-run 或配置校验参数(形如 xxx run -test -config …)。它说 OK,文件就是好的;它报错,才轮到怀疑。

对自己看不懂的二进制格式,别用文本工具下结论。

为什么写这些

这四条都不高级,甚至有点笨。但自托管的成本从来不在「搭起来」,而在「半年后还认得它」。每踩一次坑,最后都沉淀成脚本里的一行注释、一句断言、一个校验步骤——机器不会记得你的教训,文件才会。

顺便一提,这套站点的部署也遵守同样的纪律:打包、传输、两边核对摘要、解包成新目录、原子替换、保留一份可回滚的旧目录。整个流程朴素到可以背下来,但它从来没有给我制造过「半夜回滚」的惊喜。


江上风大,先系好缆绳。

← 回到首页