✏️ 编辑

运维故障排查实战 ②:磁盘爆满 · 端口占用 · CPU飙高 · DNS 异常

创建于 2026-07-05 08:36:40 · 更新于 2026-07-05 09:03:25

运维故障排查实战 ②:磁盘爆满 · 端口占用 · CPU飙高 · DNS 异常

标签:运维


开篇说明

本文记录从第③个场景开始的运维实战学习内容,承接"502/504"和"缓存不生效"两篇笔记。


前置概念:进程基础知识

在进入具体场景前,先搞清几个核心概念——这些是排查所有运维问题的基础。

进程的状态(STAT)

ps aux 可以看每个进程的状态(STAT 列):

状态 含义 说明
R Running 正在运行或在运行队列中排队
S Sleeping 休眠等待中——大部分正常进程都是这个状态
D Uninterruptible Sleep 不可中断睡眠——通常是在等磁盘 IO,如果持续不恢复则异常
Z Zombie 僵尸进程——已结束但父进程没回收资源
T Stopped 已暂停——被 STOP 信号或 Ctrl Z 暂停了

进程的信号(Signal)

kill 命令本质是给进程发送一个"信号",让它做出响应:

信号 编号 含义 进程反应
SIGTERM 15 请正常结束 进程收到后可以做清理工作再退出(默认
SIGINT 2 中断 相当于键盘按 Ctrl C
SIGKILL 9 强制杀死 进程无法忽略,直接结束,没机会做清理 ⚠️
SIGSTOP 19 暂停 相当于 Ctrl Z,进程暂停执行
SIGCONT 18 继续 让被暂停的进程继续运行

实际工作中的正确操作顺序:

Bash
kill PID           # SIGTERM - 先好好说"请退出"
# 等几秒...
kill -2 PID        # SIGINT  - 再给个 Ctrl C
# 等几秒...
kill -9 PID        # SIGKILL - 最后手段才强制杀

为什么不能一上来就用 kill -9?
进程被 SIGKILL 时来不及做清理——比如没关闭文件句柄、没写回缓存数据,可能导致数据损坏或下次启动异常。

查看进程信息的三大工具

ps —— 查看进程快照

ps 显示的是执行瞬间的快照,不是实时更新的。

Bash
# 最常用——看所有进程
ps aux

# 只看某个程序
ps aux | grep nginx

# 看特定 PID 的详细信息
ps -p <PID>

# 只显示你关心的列
ps -p <PID> -o pid,ppid,user,%cpu,%mem,stat,start,time,command

# 按内存使用排序(谁吃内存最多)
ps aux --sort=-%mem | head -10

# 按 CPU 使用排序(谁吃 CPU 最多)
ps aux --sort=-%cpu | head -10

# 显示进程树状关系(父→子)
ps auxf

输出字段详解:

字段 含义
USER 进程所有者
PID 进程 ID(唯一编号)
%CPU CPU 使用率百分比
%MEM 内存使用率百分比
STAT 进程状态(见上表)
START 启动时间
TIME 累计占用 CPU 的时间
COMMAND 启动命令(含参数)

lsof —— 查看进程打开的文件

lsof 常用参数组合:

命令 用途
lsof -i -P -n \| grep :8080 查谁占用了 8080 端口
lsof -i -P -n -p <PID> 查某个进程连了哪些网络
lsof \| grep deleted 查"幽灵文件"(删了但空间没释放)
lsof /var/log/nginx/access.log 查谁在写这个日志文件

lsof 输出示例:

COMMAND   PID USER FD   TYPE DEVICE SIZE/OFF    NODE NAME
tail  2050966 root 3r   REG  253,1       14 8389077 /tmp/demo_tail.log

tail —— 查看和跟踪文件尾部内容

Bash
tail /var/log/nginx/access.log          # 看最后 10 行(默认)
tail -n 50 /var/log/nginx/access.log    # 看最后 50 行
tail -f /var/log/nginx/access.log       # 实时跟踪(-f = follow)
tail -f -n 100 /var/log/nginx/access.log # 从最后 100 行开始实时跟踪

重点理解 tail -f

tail -f 启动后,进程持续占用该文件的句柄(file descriptor)——就像一只手一直抓着这个文件不放。即使文件被删除,只要这个进程还在,文件占用的磁盘空间就不会释放(这就是"幽灵文件"的成因)。

三命令对比总结

命令 看什么 何时用 特点
ps 进程快照(瞬间状态) 查进程有没有在跑、状态是什么、CPU/内存占多少 静态的,执行瞬间的信息
lsof 打开的文件/网络 查端口被谁占了、文件被谁打开了 看的是"资源关联关系"
tail 文件尾部内容 看日志、实时跟踪输出 操作的是"文件内容",不是进程本身

三者的关联:

ps 看到进程 PID → 想知道这进程在连啥网络 → lsof -i -P -n -p <PID>
ps 看到进程 PID → 想知道它在写什么文件 → lsof -p <PID>
ps 看到进程 PID → 想跟踪它的实时日志   → tail -f /var/log/xxx

/proc// —— 进程的"档案文件夹"

每个进程在 /proc 下都有一个数字命名的目录,里面装着这个进程的所有信息。

这是 Linux 最强大的排查手段之一,因为不需要额外工具,catls 就能看全部信息。

Bash
# 最常用的几个 /proc 条目:
cat /proc/<PID>/cmdline | tr '\0' ' '    # 完整启动命令(比 ps 更准)
ls -l /proc/<PID>/cwd                    # 工作目录(在哪启动的)
ls -l /proc/<PID>/exe                    # 可执行文件路径(程序本体在哪)
cat /proc/<PID>/environ | tr '\0' '\n'   # 环境变量(包含大量线索)
cat /proc/<PID>/status                   # 进程状态详情
ls -l /proc/<PID>/fd/                    # 打开的所有文件描述符

场景③:磁盘爆满(Disk Full)

排查流程四步走

第一步:看全局——df

Bash
df -h

输出示例:

Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        90G   43G   47G  48% /

重点关注 Use% 列——超过 90% 就要告警了。

第二步:定位一级目录——du

Bash
du -sh /* 2>/dev/null | sort -rh | head -15

| 部分 | 含义 |
| /* | 根目录下所有一级目录 |
| 2>/dev/null | 过滤权限报错 |
| sort -rh | 按大小倒序(最大的排上面) |
| head -15 | 只看前 15 条 |

第三步:逐层深入

Bash
# 看 /var 下谁最大
du -sh /var/* 2>/dev/null | sort -rh | head -10

# 看 /var/log 下谁最大
du -sh /var/log/* 2>/dev/null | sort -rh | head -10

第四步:快速定位所有大文件——find

Bash
find /var -type f -size  100M -exec ls -lh {} \; 2>/dev/null

参数拆解:
| 部分 | 含义 |
| -exec ls -lh {} \; | 对每个找到的文件执行 ls -lh |
| 2>/dev/null | 过滤权限报错 |

-exec 语法:

find ... -exec 要执行的命令 {} \;
  │          │              │  └─ 结束标记(分号转义)
  │          │              └──── 占位符,代表当前找到的文件路径
  │          └─────────────────── 对每个结果执行的操作
  └────────────────────────────── 参数开始

注意:\; 不能写成 ;,否则 bash 会把分号当作自己的语句结束符,导致 find: missing argument to '-exec' 报错。

快速扫全盘大文件的变种:

幽灵文件(删了但没释放空间)

这是磁盘清理最常踩的坑。

现象

Bash
df -h     # 显示磁盘还是满的
du -sh /* # 加起来的空间比 df 显示的小

原因

一个进程正在使用某个文件,你 rm 了它,目录里看不到了,但进程的文件句柄还在,内核不会释放磁盘空间。

Bash
# 查找这些"幽灵文件"
lsof | grep deleted

# 输出示例:
# COMMAND  PID  USER   FD  TYPE  SIZE  NODE NAME
# python3  1234 root   3w  REG  500M  12345 /var/log/app.log (deleted)

正确处理流程

Bash
# ① 找到大文件
find / -type f -size  500M -exec ls -lh {} \; 2>/dev/null

# ② 删掉(但空间可能还没释放)
rm bigfile.log

# ③ df -h 检查,空间没变?
df -h

# ④ lsof | grep deleted 看有没有进程占着
lsof | grep deleted

# ⑤ 重启对应的服务或 kill 进程
systemctl restart xxx
# 或
kill <PID>

# ⑥ 再 df -h 确认空间释放
df -h

安全删除建议

不确定能不能删的文件,先 mv 而不是 rm


场景④:端口被占用(Port Conflict)

现象

启动服务时看到报错:

Error: listen tcp :8080: bind: address already in use

排查流程

第一步:查谁占了端口——ss

Bash
ss -tlnp | grep :8080

第二步:查进程身份——决定能不能杀

方法 1:ps 看基本信息

Bash
ps -p 2053039 -o pid,ppid,user,%cpu,%mem,start,command
看到什么 推断什么
ppid=1 父进程是 init/systemd,可能是孤儿进程被收养
command=python3 -m http.server 8080 手动启动的测试服务
start=15:28:13 刚启动不久

方法 2:/proc 看完整信息

Bash
cat /proc/2053039/cmdline | tr '\0' ' '   # 完整命令
ls -l /proc/2053039/exe                   # 可执行文件路径
ls -l /proc/2053039/cwd                   # 工作目录
cat /proc/2053039/environ | tr '\0' '\n'  # 环境变量
cat /proc/2053039/status                  # 状态详情

方法 3:看进程树

Bash
ps auxf | grep 2053039   # 进程树格式

实际工作中的判断依据:

Bash
# ① 是不是 systemd 管理的服务?
systemctl status <PID>

# ② 是不是重要业务进程?
ps -p <PID> -o command

# ③ 有没有其他依赖?
# 独立进程 → 可杀。有子进程/父进程相关 → 要谨慎

第三步:释放端口——kill

Bash
kill <PID>        # SIGTERM (15) — 请正常退出
kill -2 <PID>     # SIGINT  (2)  — 相当于 Ctrl C
kill -9 <PID>     # SIGKILL (9)  — 强制杀死(最后手段)

判断能不能杀的总结:

场景 结论
手动启动的测试服务,不依赖其他进程 ✅ 安全可杀
systemd 管理的正式服务 systemctl restart xxx 而不是 kill
数据库、消息队列等中间件 谨慎,走正规重启流程
不认识的进程 先查网络连接→确认不是挖矿→再 kill

场景⑤:CPU 飙高(CPU Spike)

排查方法

第一步:top 看全局

Bash
top

top 交互快捷键:

按键 作用
P 按 CPU 使用率排序(大写)
M 按内存使用率排序
c 显示完整命令行
1 看每个 CPU 核分别的使用率
k 杀进程(会提示输入 PID)
q 退出
Bash
# 非交互模式(适合脚本或快速查看)
top -b -n 1 -o %CPU | head -17

# 显示完整命令
top -b -n 1 -o %CPU -c | head -17

第二步:解读 top 输出

第一行:系统概况

top - 15:37:45 up 80 days,  5 users,  load average: 1.00, 0.64, 0.32
  ↑          ↑           ↑                ↑        ↑       ↑
 当前时间    开机天数    在线用户数       1分钟负载 5分钟  15分钟

load average(负载均值)怎么看?

数值代表排队等待 CPU 的任务数量。以 1 核机器为例:

数值 含义
0.5 CPU 有一半空闲 ✅
1.0 CPU 刚好满负荷 ⚠️
2.0 超载一倍,任务在排队 ❌

第三行:CPU 构成(最重要的行)

%Cpu(s): 51.6 us,  9.5 sy,  0.0 ni, 38.1 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
字段 全称 含义 高了说明什么
us user 用户程序占用 应用代码有问题(死循环、慢查询)
sy system 内核/系统调用占用 系统调用太频繁(IO、网络、锁竞争)
ni nice 低优先级程序占用 一般不用管
id idle 空闲比例 这个越低 CPU 越忙
wa iowait 等待 IO ⚠️ 磁盘是瓶颈
hi hard IRQ 硬件中断 硬件问题
si soft IRQ 软件中断 网络软中断过高
st steal 被宿主机偷走(VM) ⚠️ 云服务器超卖

实战口诀: us 高→查应用;sy 高→查内核/系统调用;wa 高→查磁盘;st 高→找云服务商

第三步:确认进程——看进程列表

PID  USER  %CPU  %MEM  COMMAND
2057576 root 100.0  0.2  python3  ← 单个进程占满一个核

第四步:查进程在干什么——strace

Bash
# 统计模式(看系统调用分布),跑几秒按 Ctrl C 停止
strace -p <PID> -c

输出解读:

看到大量 结论
read/write 磁盘 IO 瓶颈
epoll_wait/poll 网络等待
open/close 频繁打开文件
几乎没有系统调用 CPU 在纯计算(死循环、math 运算等)

CPU 处理流程总结

发现 CPU 100%
 │
 ├─ 是已知服务?
 │    ├─ 是 → 看日志 → strace → 重启 / 优化代码
 │    └─ 否 → 查网络连接 → 确认不是挖矿 → kill
 │
 └─ 想快速止血?
      └─ kill <PID> 先恢复,再查日志根因

场景⑥:DNS 异常

Linux 域名解析流程(按顺序)

你输入 www.baidu.com
        │
        ▼
① /etc/hosts  ← 手写的"小电话本"(优先级最高)
   └─ 有记录?→ 直接返回 IP
        │ 没有
        ▼
② DNS 缓存  ← 之前查过的记录会记住
   └─ 有缓存?→ 快速返回
        │ 没有
        ▼
③ /etc/resolv.conf  ← 配置着 DNS 服务器的 IP
   └─ 去问 DNS 服务器
        │
        ▼
   拿到 IP,返回

示例输出:

127.0.0.1   localhost localhost.localdomain
127.0.0.1   VM-0-17-opencloudos

作用:

用途 示例
本地开发测试 127.0.0.1 myapp.test
屏蔽网站 127.0.0.1 ads.example.com
内网互访 192.168.1.10 db-server

⚠️ 常见故障: 有人不小心在 hosts 里写了 127.0.0.1 baidu.com,然后你的应用就永远连不上百度了。

修改 /etc/hosts:
- 直接编辑文本文件(vim /etc/hostsecho "ip domain" >> /etc/hosts
- 修改后立即生效,不需要重启

/etc/resolv.conf —— 配置"去问谁"

Bash
cat /etc/resolv.conf

核心内容只有一行最重要的:

nameserver 183.60.83.19
nameserver 183.60.82.98
内容 含义
nameserver 指令:指明 DNS 服务器的 IP
183.60.83.19 腾讯云内网 DNS 服务器

常见 DNS 服务器:

DNS 服务器 IP 说明
腾讯云内网 DNS 183.60.83.19 腾讯云服务器默认
阿里云内网 DNS 100.100.2.136 阿里云服务器默认
114 DNS 114.114.114.114 国内通用
Google DNS 8.8.8.8 全球通用,国内可能慢
Cloudflare DNS 1.1.1.1 快,注重隐私

nameserver 有数量限制吗?
有,最多 3 个。第 4 个会被忽略。

工作方式: 按顺序一个一个试,第一个超时了才问第二个,不是同时问。

/etc/hosts 和 /etc/resolv.conf 里 IP 的区别

这是初学者最容易混淆的概念:

文件 里面的 IP 是什么 角色 类比
/etc/hosts 目的地 IP "这就是答案" 通讯录里存的号码
/etc/resolv.conf DNS 服务器 IP "帮我去问他" 查号台 114 的号码

dig —— 直接查 DNS 服务器

dig 最大的特点:跳过 /etc/hosts,直接去问 DNS 服务器。

Bash
# 简洁模式(只看 IP 地址)
dig  short baidu.com

# 完整模式(看到所有细节)
dig baidu.com

第二段:问题部分——你问了什么

;; QUESTION SECTION:
;baidu.com.   IN  A
                ↑  ↑
           网络类型 记录类型

A 记录是什么?

DNS 记录类型 含义 示例
A 域名 → IPv4 baidu.com → 110.242.74.102
AAAA 域名 → IPv6 baidu.com → 2400:da00::666
CNAME 别名 www.baidu.com → www.a.shifen.com
MX 邮件交换记录 指定邮件服务器
TXT 文本记录 域名验证等

第三段:答案部分——DNS 回了什么

;; ANSWER SECTION:
baidu.com.  30  IN  A  111.63.65.103
   ↑         ↑   ↑   ↑      ↑
  域名     TTL  类型  IP地址

TTL(Time To Live) = 缓存时间(秒)。

TTL 值 含义 常见场景
30 30 秒后过期 大型网站(百度、Google),方便 IP 快速切换
300 5 分钟 CDN 服务
86400 1 天 小型站点,解析变化少

第四段:统计信息

;; Query time: 1 msec         ← 1 毫秒,非常快
;; SERVER: 183.60.83.19#53   ← 哪个 DNS 回复的,#53 是 DNS 端口
;; WHEN: Sun Jul 05 16:26:41 ← 查询时间
;; MSG SIZE  rcvd: 91        ← 回复包大小(91 字节)

getent hosts —— 系统实际用的解析结果

Bash
getent hosts baidu.com

getent = "get entries",从系统配置的解析库中获取条目。

dig 的核心区别:

dig         → 直接问 DNS 服务器(跳过 /etc/hosts)
getent hosts → 按系统配置的顺序查(/etc/hosts → 缓存 → DNS 服务器)

DNS 异常排查核心方法

两条命令对比法

Bash
# 两条命令同时跑,对比结果:
dig  short baidu.com         # "这是正确答案"
getent hosts baidu.com       # "系统实际用了什么"
dig 结果 getent 结果 结论
正常 IP 正常 IP ✅ DNS 正常
正常 IP 不同 IP ❌ /etc/hosts 被人改了
查不到 查不到 ❌ DNS 服务器或网络问题
查不到 正常 ❌ 罕见,缓存异常

选 DNS 服务器查

Bash
# 用 8.8.8.8 来查(@ 指定 DNS 服务器)
dig @8.8.8.8  short baidu.com

# 用 114.114.114.114 来查
dig @114.114.114.114  short baidu.com
原 DNS 查不到 @8.8.8.8 查得到 结论
原 DNS 服务器挂了,改 resolv.conf
域名本身问题或网络不通

完整排查流程

Bash
# ① 能不能 ping 通域名?
ping baidu.com

# ② ping 不通?查 DNS
dig  short baidu.com

# ③ dig 也查不到?查 DNS 配置
cat /etc/resolv.conf

# ④ 换个 DNS 试试
dig @8.8.8.8  short baidu.com

# ⑤ 对比看是不是 hosts 问题
getent hosts baidu.com

附录:命令速查表

磁盘排查

步骤 命令 作用
全局 df -h 看磁盘使用率
定位 du -sh /* \| sort -rh 找最大的一级目录
钻入 du -sh /var/log/* \| sort -rh 逐层深入
大文件 find / -size 100M -exec ls -lh {} \; 找所有大文件
幽灵文件 lsof \| grep deleted 找删了但没释放的

端口排查

步骤 命令 作用
查占用 ss -tlnp \| grep :8080 谁占的端口
查进程 ps -p <PID> -o pid,ppid,command 是什么进程
释放 kill <PID> 杀掉进程

CPU 排查

步骤 命令 作用
全局 top 看 CPU 排行
细节 cat /proc/<PID>/cmdline 看完整命令
跟踪 strace -p <PID> -c 看系统调用
上下文 cat /proc/<PID>/status \| grep ctxt 看 CPU 争抢程度

DNS 排查

命令 作用
dig short 域名 直接问 DNS
dig 域名 完整 DNS 响应信息
dig @8.8.8.8 short 域名 指定 DNS 服务器来查
getent hosts 域名 系统级解析结果
cat /etc/hosts 看本地电话本
cat /etc/resolv.conf 看 DNS 配置

进程信息三件套对比

命令 全称 看什么 典型用途
ps process status 进程快照(瞬间状态) PID / CPU% / MEM% / 状态
lsof list open files 进程打开的文件和网络 端口占用 / 谁在用文件
tail 文件尾部内容 看日志 / 实时跟踪输出

一句话记:
- ps进程本身(活着没、吃了多少资源)
- lsof进程的资源(占着谁的端口、写着哪个文件)
- tail进程写的内容(日志里到底写了什么)