一个人维护四页运维文档:清单与变更记录

一、我花了两年,才发现自己的待办清单在骗我

我独自维护两个站点、四台机器,还有一堆零散的脚本和域名。最开始我用的是那种「想到什么就记一条」的清单,每天醒来第一件事是打开它,从上往下挑几件看起来最急的做完,然后划掉。两年后回头看,这套方法有一个致命问题:清单上的内容几乎从来只增不减,而那些真正重要但「今天不做也死不了」的事,永远排在最后,永远轮不到。

比如证书到期前更新,我每次都是在告警响了之后才动手;比如每季度该做一次恢复演练,我连续六个季度都写在清单上、连续六个季度都没做;比如备份脚本的日志我会在三个月后某次排查故障时才翻,然后发现它已经失败了二十多天。

真正让我下决心改的是一次很普通的连环事故:某个周一早上站点打不开,原因是一条半年前就该清理的旧日志把磁盘写满了——它在我清单上躺了整整半年,被我划掉又加回来过三次。这件事之后我停掉了所有待办工具,重新设计了一套只有一个人用、只有四页纸的运维文档。这篇文章写的就是那套东西。

二、先承认一件事:一个人的运维,瓶颈不在时间,在「记得」

团队运维有一整套东西可以依赖:值班表、交接文档、变更审批、同事之间的互相提醒。一个人做站的时候这些全都没有,你唯一能依赖的就是自己的记忆力和习惯。而这两样东西恰恰是最不可靠的。

我数过自己一个月里「因为忘了」而出问题的次数:忘记 reload 配置导致新规则不生效、忘记清理缓存导致改了文章前台不更新、忘记更新一条 DNS 记录导致迁移拖了三天、忘记给某个 cron 加日志导致它静默失败两个月没人知道。这些都不是技术难题,全是「记得与不记得」的问题。

所以一个人运维的文档体系,目标不是「记录所有知识」,而是把「需要记得」这件事从脑子里搬到纸上,并且搬到正确的位置。我把需要记住的东西分成四类,对应四页纸:

  • 第一页:这一台机器上到底跑着什么。 服务清单,防止「不知道有这个进程」。
  • 第二页:出事的时候先做什么。 应急手册,防止「慌乱中做错动作」。
  • 第三页:每月该看什么。 例行检查表,防止「无人值守的地方悄悄腐烂」。
  • 第四页:改了东西之后怎么回退。 变更记录,防止「三天后出问题但想不起改了啥」。

这四页的共同点是:每一页都必须能在不看代码、不登录服务器的情况下读懂,因为真正的使用场景往往是凌晨三点、脑子不清楚、手里只有手机。

三、第一页:服务清单,从「你以为在跑的」变成「实际在跑的」

第一页的第一件事不是写,而是查。我见过太多人(包括两年前的自己)以为自己清楚机器上有什么,直到某次迁移时发现一堆不认识的服务在跑,其中两个还在监听公网端口。

用四条命令就能把事实拉出来:

# 1) 所有正在监听的端口及其进程
ss -lntup

# 2) 所有启用的 systemd 服务(排除官方模板与生成器)
systemctl list-unit-files --type=service --state=enabled

# 3) 所有用户的定时任务
for u in $(cut -d: -f1 /etc/passwd); do crontab -l -u "$u" 2>/dev/null | sed "s/^/[$u] /"; done
ls -1 /etc/cron.d/ 2>/dev/null

# 4) 所有 systemd timer
systemctl list-timers --all --no-pager

把四条的输出抄进第一页,每条占一行,写四列:服务名 / 监听端口 / 谁在启动它(systemd unit 或 crontab 或 docker)/ 出问题时的第一反应。第四列是最有价值的,也是绝大多数人不会写的那一列。举个例子:

nginx        :80 :443    systemd: nginx.service        先 nginx -t 再看 error.log 尾部
php-fpm      :9000       systemd: php7.4-fpm.service   先看 /var/log/php7.4-fpm.log 的 pool 报错
mysqld       :3306(内网) systemd: mysql.service        先看 innodb 报错与磁盘剩余空间
redis        :6379(内网) systemd: redis-server.service 先 redis-cli ping 再 info memory
backup.sh    —           cron: 03:20 每日             看 /var/log/backup.log 最后 30 行

写完这一页你会发现两件事。第一,一定有几个你不认识或者早就该关掉的东西——我这次查出三个两年前测试留下的小服务,其中一个还在跑旧版本的 PHP。第二,「出问题时的第一反应」这一列写不出来的,往往就是你最不熟悉、也最容易在半夜出事时耽误时间的地方。

四、第二页:应急手册,写「症状」而不是写「原因」

第二页是最容易写歪的一页。绝大多数人的应急文档是按原因组织的:「如果是磁盘满怎么办」「如果是数据库挂了怎么办」。但真实的故障现场,你面对的不是原因,而是症状:站点打不开、页面白屏、后台登录不进去、某个接口返回 502、收到一条莫名其妙的告警。

所以我的第二页是按症状排的,每一条只有三个部分:三十秒能做的判断、最可能的两三个原因、以及「不确定时千万不要做的事」。举两条真实的:

症状:站点打不开,浏览器超时。三十秒判断:本机 curl -I http://127.0.0.1/ 有没有响应。有响应 → 问题在 Nginx 之外(DNS、CDN、防火墙),别动 Nginx 配置;没响应 → 问题在本机,看 ss -lntp 端口在不在、df -h 磁盘满没满、dmesg -T | tail -20 有没有 OOM。不确定时不要做的事:不要重启整个机器,重启会清掉现场,而且很多问题重启后自动恢复、下次再发生时你依然不知道为什么。

症状:后台能登录,但保存文章报 500。三十秒判断:看 Nginx error.log 尾部有没有 PHP 的 Fatal error。有 → 按报错的文件行号找;没有 → 看 PHP-FPM 的 log 有没有「pool 满了」或「请求超时」。最可能的三个原因:磁盘满导致写不进去、某个 mkdir 的目录权限变了、数据库连接数打满。不确定时不要做的事:不要改数据库密码或清空缓存目录,这两个动作看起来「试试看」,但会把「能查清的问题」变成「查不清的问题」。

这一页我只保留了七条症状,因为超过十条我自己也记不住、也翻不到。七条覆盖了我这两年里 95% 的实际故障。写得多的价值远小于写得准——一条能立刻用上的症状描述,胜过二十条正确的废话。

还有一个细节:每条的结尾写一句「如果二十分钟内搞不定,先做的止损动作」。比如「先把 CDN 切到维护页,避免用户看到 502」。一个人运维最怕的是「忙着查原因,忘了用户还看着白屏」,止损动作应该写在查原因之前。

五、第三页:例行检查表,给「没人看的地方」安排一个看的人

第三页解决的是最隐蔽的一类问题:不是某个东西坏了,而是某个东西早就坏了但没人知道。备份脚本失败二十天、证书过期前一天才被发现、某个 cron 因为路径变了静默失败两个月,都属于这一类。它们的共同点是「没有任何人会主动去看」。

我的做法是把检查表分成日、周、月三档,每一档的项目控制在五分钟能做完的范围内。之所以要压到五分钟,是因为只要一项超过五分钟,我第二天就会跳过它。

每天(两分钟,早上看一次):磁盘剩余、备份日志最后一行、站点首页 HTTP 码、证书剩余天数。前三个可以直接拼成一条命令:

echo "disk: $(df -h / | awk 'NR==2{print $5" used, "$4" left"}')"
echo "backup: $(tail -n 3 /var/log/backup.log | tr ' ' ' ')"
echo "site: $(curl -s -o /dev/null -w %{http_code} -m 10 https://www.example.com/)"
echo "cert: $(echo | openssl s_client -servername www.example.com -connect www.example.com:443 2>/dev/null | openssl x509 -noout -enddate)"

每周(十分钟):看一眼 Nginx 日志里的 5xx 总数与 404 集中的路径、确认 cron 是否有未执行的、看 MySQL 的慢查询数量、检查是否有异常的登录尝试。这一档的意义是发现「趋势」,而不是发现「故障」——比如 404 突然集中在某个目录,通常是某次改版漏了重定向。

每月(三十分钟):恢复演练(从备份里真的恢复一个目录)、依赖与系统更新评估、清理旧日志与临时文件、检查一次账号与密钥(有没有多余的用户、有没有该删的 SSH 公钥)。这一档最容易被跳过,但它恰恰是防止「半年后才发现问题」的唯一手段。

这套表我贴在笔记本第一页,用一张 5 列的小表格手写打勾。不用任何工具,因为工具会让我多一个「打开、加载、录入」的成本,而这个成本足以让我放弃。

六、第四页:变更记录,写给三天后的自己看

第四页是我认为收益最大、也最少人做的一页。它不记录「我做了什么」,而记录「如果出问题,怎么退回去」。

每次改动之前,我在这一页写四行:改了什么、为什么改、怎么验证改成功了、怎么回退。四行里最后一行最重要,也最容易被跳过。举几个真实条目:

2026-10-18  nginx 加 client_max_body_size 64m
            原因: 后台上传大于 1m 的图报 413
            验证: 上传一张 5m 图成功
            回退: cp /etc/nginx/nginx.conf.bak_20261018 /etc/nginx/nginx.conf && nginx -s reload

2026-10-22  MySQL 加索引 idx_status_created (status, created)
            原因: 后台列表页 2.8s,ORDER BY created 全表扫
            验证: EXPLAIN 显示 Using index,列表页 0.3s
            回退: ALTER TABLE posts DROP INDEX idx_status_created
                 (⚠ 大表删除索引会锁,先低峰执行)

这一页的价值在三天到三个月后才体现。我遇到过一次:改完 Nginx 配置三天后,某个上传接口莫名其妙报 413,我第一反应是「代码出问题了」,翻代码翻了一小时,最后翻到变更记录里一行字,五秒钟解决。如果没有这一页,那天晚上大概率会演变成「重装服务」和「回滚整个发布」。

关于回退,还有两个原则值得单独写在这里。一是回退动作必须提前验证过——「把文件备份成 .bak」不等于「能回退」,因为备份文件可能改完之后才生成,或者备份的是改坏之前又改了一轮的那一版。真正可靠的做法是改之前先复制一份带日期的备份、改完立刻 nginx -t,并在验证通过后才删旧备份。二是回退成本要事先算,比如数据库加索引回退是「删索引」,而删索引在大表上可能锁表,这行字必须写在记录里,否则半夜回退时才会发现。

七、四页纸怎么「活」起来:让它自动出现在你面前

写得再好的文档,如果只存在硬盘某个角落,一周后就会被忘记。我用了三个很土的办法让它「活」起来。

第一,把它放在你必然会看到的地方。我的四页 Markdown 放在一个私有 Git 仓库里,同时打印两份:一份贴在机房的柜门上,一份夹在笔记本里。手机上通过一个自建的小页面快速查看(一个静态 HTML 就够了,不需要任何后台)。

第二,让例行检查有「提醒」的入口。我没有做复杂的提醒系统,而是把「每天两分钟」那条命令写成了一个脚本,放在早上固定时间跑一次,把结果发到自己的即时通讯里。这不算告警(我明确不设阈值),只是「每天看一眼」这个动作的载体。看到「disk: 88% used」时我自己会判断要不要处理。

第三,每次出了事故,回头改这四页。这是最关键的一环。我给自己定的规矩是:任何一次故障处理完之后,必须回答「这次事故是不是某一页没写清楚导致的」,如果是,当天就把那一页改掉。两年下来四页纸改了十几次——第二页从最初的三条症状长到七条,第一页从一屏变成两屏,第三页的每日检查从五项砍到四项(有一项统计下来从来没起过作用)。这套文档的价值不来自它写得全,而来自它一直在被真实故障修正。

八、几个我踩过、也劝你别踩的坑

不要一开始就上工具。我试过三个待办应用、两个知识库、一个自建 wiki。全部活不过三个月。原因很简单:这些工具的「录入成本」高于「记在纸上」,而单人运维的文档更新是发生在「刚处理完故障、脑子还热」的那十五分钟里,那十五分钟你只想快点结束、去喝口水,不会想打开某个 App 去填表单。

不要写「原则」和「理念」。我的第一版文档里写了「一切变更必须可回退」「所有服务必须有监控」这种话,后来全部删掉。因为它们无法执行、无法验证、也无法在凌晨三点帮到我。能帮到我的只有具体的东西:这个端口谁在听、这条命令怎么敲、这个文件备份在哪。

不要把文档写成「教程」。教程是给别人看的,讲原理、讲为什么;运维文档是给自己看的,只讲「做什么」。我一开始照着博客的写法写应急手册,写了三页纸还没写到具体动作,最后全部重写成短句和命令清单。

不要让文档和现实脱节。每季度我会做一件事:拿第一页的服务清单,逐条到机器上核对一遍。两年里每次核对都能发现至少一条漂移——某个服务已经停了但清单里还写着、某个端口变了但没记录、某个 cron 被注释掉了但没人删。文档一旦和现实脱节,就彻底失去价值,而且比没有文档更危险,因为它会让你自信地做出错误判断。

九、小结

一个人做站,你既是运维也是开发还是客服,不可能把每件事都记住。四页纸的作用不是「记录知识」,而是把「需要记住」这件事从脑子里搬到纸上,并且搬到你会看到的位置:一页服务清单让你知道自己跑了什么,一页应急手册让你在慌的时候不做错事,一页检查表让没人看的地方有人看,一页变更记录让三天后的你能退回三天前。这套东西全部的成本不到一个下午,改到稳定用了一年,但它替我挡掉的麻烦远比一个下午多。

Last modification:November 7th, 2026 at 10:26 pm

Leave a Comment