被遗忘的是机龄,不是磁盘
我手上最长的一台服务器跑了六年半,中间换过一次硬盘、三次重装系统(都是因为我手贱),真正没有中断的原因是它不在「会被人看见」的地方:没有面板、没有花哨的监控大屏,只有几个每天跑一次的 shell 脚本。相反,出事故的往往是那些「刚上线的漂亮机器」——因为上线时造得精细,上线后就没人再管。
这一篇讲的不是某个具体工具,而是我这几年做个人站和客户小站时总结出的维护习惯:哪些事情必须提前做,哪些事情看起来专业其实没用,以及怎么用最低成本让一台服务器在没人照看的情况下活很多年。全文没有配置模板可以直接抄,但每一条都是踩过坑之后才留下的。
第一天就该定下来的三件事
新机器装好系统之后的头一个小时,决定这台机器后面几年的运维难度。我固定做三件事,顺序不能变。
第一件是时区与时间同步。timedatectl set-timezone Asia/Shanghai,然后确认 systemd-timesyncd 或 chrony 有一个在跑且 timedatectl 里 System clock synchronized: yes。这件事看起来无关紧要,但后面所有日志时间、证书校验、TOTP 二步验证、数据库 binlog 时间都依赖它。改过时区的机器在事后查日志时最痛苦:同一次事故在不同地方显示两个时间。
第二件是建立「谁在哪」的记录。我会在 /etc/motd 里写四行:机器用途、到期日、负责人、还有一句话说明「日志在哪、备份在哪」。这不是形式主义。三个月后你 SSH 上去,motd 会告诉你这台机器到底在干什么,而查文档、翻旧聊天记录的成本高得多。我还会放一个 /root/README.txt,写清部署路径、数据库名、备份位置、容易踩的坑。
第三件是把 SSH 收口。改端口没有安全收益(扫描器会扫全端口),真正有用的是:禁用密码登录、只留密钥、PermitRootLogin prohibit-password、装 fail2ban。要注意改之前先确认新密钥能登录,而且**保留一个已登录的会话不要关**,改完从另一个终端验证,成功了再关旧会话。我因为改 sshd 配置把自己锁在门外过两次,每次都要提工单找服务商进 VNC,代价远大于省下的那几行配置。
备份:不做演练的备份等于没有
备份这个话题我在这个博客里写过太多次,几乎每一篇都有重复的话,因为它是所有运维习惯里最容易被「自我感觉良好」欺骗的一项。crontab 里那行 mysqldump 跑了一个月,文件大小也在增长,看起来一切正常。但真正恢复的时候才会发现:dump 里少了某张表、密码文件早已过期、脚本因为一条错误路径静默失败、备份文件其实是 0 字节的空壳。
我现在只认一个标准:**每月至少完整恢复一次,恢复到一台临时机器上,能打开首页、能登录后台、能看到三年前的老文章。** 不做这一步,「备份」只是磁盘上的几个文件。
为了减少恢复时的意外,我把备份做成三层:
数据库每日 mysqldump --single-transaction --quick,本地保 7 天,异地对象存储保 30 天。文件上传目录每周 rsync 一次。整机配置(/etc、/etc/nginx、cron、证书)每日打包,体积小、恢复最快,出问题第一件事就是把它捞回来。
还有两个小细节能显著减少恢复时的痛苦。一是备份文件名里带 UTC 时间戳和大小,并在日志里打印 md5sum 与行数,事后一眼能看出哪一天的备份可疑。二是备份脚本要显式检查退出码,mysqldump 失败时不要被后面的 gzip 掩盖成「成功」。set -o pipefail 这行加上,能拦下一整类静默失败。
通知链:告警要有,但别把自己吵死
我见过两种极端的服务器主人。一种什么告警都没有,磁盘满了半个月才从读者那里听说网站打不开。另一种配了几十条告警,每天手机上七八条「CPU 使用率超过 80%」,一周之后就全部静音了,等于没有。
我的做法是只保留**会让人立刻动手**的告警,其余降级成日报。目前保留的只有五条:磁盘使用率超过 90%、网站首页 HTTP 状态非 200(每五分钟探测一次)、证书剩余天数小于 14、MySQL 主从延迟超过 300 秒、备份任务当天没有成功记录。这五条任何一条触发,都意味着「今天必须处理」,不会产生噪音。
其余指标,比如 CPU、内存、连接数、QPS,全部进日报,每天早上 9 点一封邮件,扫一眼趋势就行。趋势比瞬时值重要得多:CPU 从 20% 慢慢爬到 60% 是有故事的,一条瞬时告警反而看不出来。
告警的另一个关键是「有恢复通知」。只报故障不报恢复,会让人一直处在「不知道好了没有」的焦虑里,最后干脆不看。我的探针脚本用一个状态文件记住上一次的结果,只在状态变化时发消息:正常转异常发一条,异常转正常再发一条。这样一天最多两封,但每封都有意义。
文档:写给三个月后的自己
个人站长最容易跳过的一步是写文档,理由通常是「就我一个人管,都在脑子里」。这句话在身体健康、项目熟悉的时候成立,但在你半夜被吵醒、或者隔了半年再回到这个项目的时候,脑子的可靠度约等于零。
我的文档不用 Confluence 也不用 Notion,就是一个 git 仓库,里面三样东西:hosts.md 记录每台机器的 IP、用途、系统版本、到期日;runbook.md 记录常见故障的处置步骤(磁盘满、证书失效、数据库连不上、被爬虫打挂);CHANGELOG.md 按日期记录「今天改了什么、为什么改、怎么回滚」。
第三条最有价值。我改配置的习惯是:改之前先 cp 一份带日期的备份,改完在 CHANGELOG 里写一行。看起来啰嗦,但它让「两天前站点还好好的,现在坏了」这类问题有了明确的排查起点,也避免了我把同一个问题修好又改回去。
更省力的做法是用 etckeeper,它把 /etc 变成 git 仓库,每次 apt 操作和手工编辑后自动提交。出了问题 git diff 就能看到配置文件这几天被谁改成了什么样。比起自己写变更记录,我更喜欢这种方式,因为它不依赖自律。
升级与重启:把「以后再说」变成排期
运维里所有的技术债,最终都以「这台机器三年没重启过」的形式体现。内核安全补丁没打、PHP 小版本没跟、证书续期脚本早就悄悄失效、磁盘上堆着十几个废弃的 vhost。unattended-upgrades 能解决一半问题,但它的边界在于:只装安全更新、不自动重启、对某些包的升级保守。
我的做法是固定「每月第二个周末」做一次维护窗口,用一张清单走一遍:
apt list --upgradable 看有哪些包,needrestart -b 看哪些服务因为库升级需要重启,journalctl -p err --since "1 month ago" 扫一遍错误日志,df -h 与 df -i 确认空间,最后看证书剩余天数。整张单子走完大约 40 分钟,一台机器一年 12 次,成本很低,但能避免九成以上的「突然坏掉」。
关于重启这件事,我现在的心态是:**能重启的系统才叫健康系统。**如果一个服务重启需要半小时、需要人工确认十几个状态,那就说明它本身的设计有问题,应该在下一次维护窗口里改造,而不是继续延长 uptime 来安慰自己。一个跑了一千天的 uptime 数字,不代表稳定,只代表没人敢碰它。
最后:接受不完美
写了这么多清单与习惯,其实最想说的是另一句话:个人站长的服务器不需要做到「像大厂一样」。你不需要全链路监控、不需要多可用区、不需要 Kubernetes,一个人维护的站点有一台机器、一份能恢复的备份、五条有效告警,就已经超过绝大多数同类站点了。
真正需要坚持的是「知道自己在做什么」。每一次改动之前想一下会影响到什么、怎么回滚;每一次故障之后写两行复盘记下来;每一个「先这样放着」的决定,都在日历上标记一个复查日期。这套习惯不需要额外预算,也不需要专业工具,只需要在没人监督的时候仍然按流程来。
服务器就像一辆天天开的老车,出问题的从来不是某一次突发的故障,而是那些一直在被忽略的小异响。听见了就去看一眼,比事后大修便宜得多。