运维的第一性原理:可观测与可回滚

运维日常做的事,抽象到根上其实只有两件:让系统状态看得见,让变更后果退得回。前者靠指标、日志与链路追踪,后者靠版本化、灰度发布与回滚预案。很多人把运维等同于「敲命令」,其实命令只是手段——真正决定故障处理速度的,是你手上有没有一份清晰的观测面板和一份能照做的回滚步骤。

本文按「排查一台正在出问题的 Linux 服务器」这条主线展开:先熟悉常用命令,再定位进程与资源,接着翻日志、查磁盘与文件句柄,最后把它固化成一页排查流程。读完你应该能在没有图形界面的情况下,用几条命令把故障范围收窄到某个进程或某块磁盘。

运维的功夫不在「会多少条命令」,而在「遇到陌生故障时,知道按什么顺序去问问题」。
本文核心知识点
  • 资源四件套ps / top / free / df 覆盖进程、CPU、内存与磁盘四个视角。
  • 进程排查用 pidstat、iostat 把「慢」拆成 CPU、IO、内存三个方向。
  • 日志journalctl 看系统日志,应用日志按时间窗收口,先缩小再细看。
  • 排查流程现象 → 范围 → 指标 → 日志 → 变更 → 结论,六步走完再动手。
Linux 服务器运维示意:服务器机架、终端窗口、齿轮与进程节点组成的蓝色扁平插画
命令是扳手,观测是仪表盘:先看清系统状态,再决定拧哪颗螺丝。

常用命令:按问题归类去记

与其死背长命令,不如按「我要回答什么问题」来归类。下面这张表是值班时最常打开的一组,建议收藏成便签。

要回答的问题命令说明
哪些进程最吃 CPUps -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 按天切割并限制保留份数;临时目录写定时清理脚本。不要等报警了才去删文件——临时抱佛脚删文件,很容易顺手删掉正在被写的日志句柄,空间反而不释放。