运维的第一性原理:可观测与可回滚
运维日常做的事,抽象到根上其实只有两件:让系统状态看得见,让变更后果退得回。前者靠指标、日志与链路追踪,后者靠版本化、灰度发布与回滚预案。很多人把运维等同于「敲命令」,其实命令只是手段——真正决定故障处理速度的,是你手上有没有一份清晰的观测面板和一份能照做的回滚步骤。
本文按「排查一台正在出问题的 Linux 服务器」这条主线展开:先熟悉常用命令,再定位进程与资源,接着翻日志、查磁盘与文件句柄,最后把它固化成一页排查流程。读完你应该能在没有图形界面的情况下,用几条命令把故障范围收窄到某个进程或某块磁盘。
运维的功夫不在「会多少条命令」,而在「遇到陌生故障时,知道按什么顺序去问问题」。
- 资源四件套ps / top / free / df 覆盖进程、CPU、内存与磁盘四个视角。
- 进程排查用 pidstat、iostat 把「慢」拆成 CPU、IO、内存三个方向。
- 日志journalctl 看系统日志,应用日志按时间窗收口,先缩小再细看。
- 排查流程现象 → 范围 → 指标 → 日志 → 变更 → 结论,六步走完再动手。
常用命令:按问题归类去记
与其死背长命令,不如按「我要回答什么问题」来归类。下面这张表是值班时最常打开的一组,建议收藏成便签。
| 要回答的问题 | 命令 | 说明 |
|---|---|---|
| 哪些进程最吃 CPU | ps -eo pid,%cpu,%mem,cmd --sort=-%cpu | head | 排序取头部,快速锁定热点 |
| 内存还剩多少 | free -h | 重点看 available 而非 free |
| 磁盘满没满 | df -h | 看挂载点使用率,别只看根分区 |
| 某目录谁占空间 | du -sh * | sort -h | 逐层下钻直到找到大文件 |
| 端口被谁占用 | ss -lntp | grep :8080 | 比 netstat 更快,显示 pid |
| 服务通不通 | curl -I http://127.0.0.1:8080 | 先看本机,再排除网络层 |
几个高频的组合命令,可以放在 shell 别名里备用:
# 查看 CPU、内存占用最高的前 10 个进程
ps -eo pid,ppid,%cpu,%mem,cmd --sort=-%cpu | head -n 11
# 按线程查看某个进程谁在吃 CPU(-H 展开线程)
top -H -p 12345
# 查看端口监听与被占用情况
ss -lntp | grep -E ':80|:443|:8080'
# 网络连通与延迟
curl -o /dev/null -s -w '%{http_code} %{time_total}s\n' http://127.0.0.1:8080/healthz
记命令的关键是记参数语义而不是记整句:-h 多为 human readable,-t 常表示 TCP,-p 表示显示进程,-n 表示不解析名字。理解这些共性,遇到没见过的命令也能猜个七七八八。
进程与资源:谁吃掉了 CPU 和内存
「服务器变慢了」这句话信息量为零,第一步是把它拆成可测量的方向。资源无非四类:CPU、内存、磁盘 IO、网络。用下面的命令做一次快照,基本能判断方向。
- CPU 高:
top的 %us 高说明应用在算,%sy 高说明系统调用多,%wa 高其实往往是 IO 等待。 - 内存紧:
free -h的 available 持续走低,配合dmesg | grep -i oom看有没有进程被 OOM 杀掉。 - IO 慢:
iostat -x 1看 %util 接近 100 且 await 明显偏高。 - 进程维度:
pidstat -u -r -d 1按进程输出 CPU、内存、IO 三个指标。
# 每 1 秒刷新一次,观察全局
iostat -x 1
# 按进程维度采样,-u CPU / -r 内存 / -d 磁盘
pidstat -u -r -d 1 5
# 查看被内核 OOM killer 杀掉的进程
dmesg | grep -i -E 'oom|killed process'
# 追查某个进程打开的句柄与内存映射
lsof -p 12345 | head
cat /proc/12345/status | grep -E 'VmRSS|Threads'
一个容易被忽略的坑是线程数:进程看起来 CPU 不高、内存不大,但线程数上千,上下文切换(vmstat 的 cs 列)飙升,同样会拖慢整机。看到 cs 每秒几万次,就该怀疑是不是有连接池、定时器或日志框架在疯狂创建线程。
日志排查:从 journalctl 到聚合
日志排查的核心动作是收口:先把无穷无尽的输出缩小到一个时间窗、一个服务、一个关键词,再逐行读。systemd 时代的系统日志统一走 journalctl,应用日志则各写各家。
# 看某个服务最近的日志并持续跟踪
journalctl -u myapp.service -f
# 只看出错级别,并限定时间窗
journalctl -p err --since '2026-10-14 09:00' --until '2026-10-14 10:00'
# 应用日志按时间窗与关键词收口
grep -n 'ERROR' app.log | awk '$0 >= "2026-10-14 09:00"'
# 统计某段时间内各类错误的出现次数
grep -oE 'ERROR [A-Z_]+' app.log | sort | uniq -c | sort -rn | head
单机日志 grep 能解决小规模问题,但集群里日志散落在几十台机器上,就必须上聚合。经验做法是:应用只写 stdout,由采集组件(如 Filebeat、Fluent Bit)统一投递到 Elasticsearch,再用 Kibana 检索。这样做的最大好处不是「能看」,而是能按请求 ID 跨服务串联——一次报错能立刻看到它在上游、下游的表现。
写日志本身也有讲究:结构化(JSON)、带上 traceId、区分级别、避免把大对象整体打印。日志写得乱,聚合系统再强也救不回来。
磁盘与文件句柄:半夜报警的元凶
凌晨三点的告警,十有八九是两件事:磁盘满,或者句柄耗尽。它们的共同点是——平时完全正常,一旦到阈值就整机不可用。
- inode 满:
df -i显示 IUse% 100%,即便还有空间也写不进文件,常见于大量小文件(缓存、会话文件)。 - 已删除但被占用的文件:删了大文件空间不释放,是因为还有进程持有句柄,用
lsof | grep deleted找出元凶。 - 句柄上限:
ulimit -n与内核fs.file-max决定单进程与全机能开多少文件,连接数一高就报「Too many open files」。
# 查看 inode 使用率
df -i
# 找出被进程占用、已删除但空间未释放的文件
lsof | grep deleted | sort -k7 -nr | head
# 查看与调整单进程文件句柄上限
ulimit -n
ulimit -n 65535
# 统计当前系统打开的句柄总数
cat /proc/sys/fs/file-nr
治理思路是「监控 + 轮转 + 清理」:给磁盘和 inode 都配 80% 的告警线;日志用 logrotate 按天切割并限制保留份数;临时目录写定时清理脚本。不要等报警了才去删文件——临时抱佛脚删文件,很容易顺手删掉正在被写的日志句柄,空间反而不释放。
权限与安全加固
运维手里的权限本身就是最大的风险面。加固的原则是最小权限 + 可审计:日常操作不用 root,高危操作留痕,远程登录只走密钥。
| 加固项 | 做法 | 收益 |
|---|---|---|
| 登录方式 | 禁用密码登录,只允许密钥,修改默认 SSH 端口 | 大幅降低暴力破解成功率 |
| 账号权限 | 日常账号 sudo 精细化,禁止直接用 root 登录 | 误操作与提权风险可控 |
| 文件权限 | 敏感配置 600,目录 750,属主属组最小化 | 防止越权读取密钥 |
| 防火墙 | 只放行必要端口,默认拒绝入站 | 收敛暴露面 |
| 审计 | 开启 auditd / 集中收集登录与提权日志 | 事后可追溯 |
# 修改 SSH 配置:禁用密码、禁止 root 直登
sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
sudo systemctl reload sshd
# 给敏感文件收紧权限
chmod 600 /etc/app/secret.env
chown app:app /etc/app/secret.env
# 查看当前登录与提权记录
last -n 20
sudo grep 'sudo' /var/log/auth.log | tail
加固不是一次性动作,而要定期复核:账号是否还有效、密钥是否该轮换、开放端口是否还必要。把复核写进季度清单,比事后补救便宜得多。
一套可复制的故障排查流程
把前面几节的命令串成一条可复制的排查动线,新人照着走也能在十分钟内把故障范围缩到最小:
- 确认现象与范围:是单机还是整个集群?是全部请求还是某个接口?先固定影响面,避免上来就重启。
- 看资源快照:
top、free -h、df -h、iostat -x 1各看一眼,判断瓶颈在 CPU、内存、磁盘还是网络。 - 锁定进程:用
pidstat找到热点进程,再看它在做什么——日志、句柄还是连接。 - 查日志:按时间窗收口,优先看 ERROR 级别与最近的变更记录。
- 对照变更:最近有没有发布、改配置、动网络?绝大多数故障与近期变更相关。
- 止损与复盘:先回滚或降级恢复服务,再从容定位根因,最后补齐监控与预案。
流程里最容易被跳过、却最关键的是第 5 步对照变更。多数线上故障不是「系统自己坏了」,而是某次变更引入了未预料的影响。把发布记录与故障时间线并排放在一起,往往一眼就能看出关联。
好的排查流程不是让你更快地敲命令,而是让你在慌乱时依然知道下一步该看哪里。把它写进值班手册,比背下一百条命令更有用。