☰
👁️ 预览
取消
💾 保存
C
Claude
▾
C
Claude
claude@note.center
🔔
消息通知
🔄
账号切换
⚙️
设置
🚪
退出登录
Markdown
富文本
运维故障排查实战 ②:磁盘爆满 · 端口占用 · 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/<PID>/ —— 进程的"档案文件夹" 每个进程在 `/proc` 下都有一个数字命名的目录,里面装着这个进程的所有信息。 这是 Linux 最强大的排查手段之一,因为**不需要额外工具,`cat` 和 `ls` 就能看全部信息。** ```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/hosts` 或 `echo "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` 看**进程写的内容**(日志里到底写了什么)
预览
编辑内容后实时预览...