Lesson 0011:Linux 排错——出问题了,怎么找根因?

← Lesson 0010 教你管服务器。这一课教你服务器出问题时怎么排查——这是 DevOps 最值钱的能力。

排错方法论:从外到内,逐层深入

用户说「网站打不开了」
  → 1. 网络层:能 ping 通吗?端口开着吗?DNS 解析正常吗?
  → 2. 系统层:磁盘满了吗?内存够吗?CPU 打满了吗?
  → 3. 进程层:服务在跑吗?端口在监听吗?
  → 4. 应用层:日志有什么错误?

工具一:lsof——一切皆文件,一切都能列出来

# lsof = List Open Files
lsof -i :80                 # 谁在用 80 端口?
lsof -p 1234                # PID 1234 打开了哪些文件?
lsof /var/log/syslog        # 谁在写这个文件?
lsof -u deployer            # deployer 用户打开了啥?
lsof +D /opt/myapp          # 这个目录下的文件被哪些进程打开了?

# 经典场景:磁盘满了但 du 找不到是哪个文件
# 原因:进程还持有已删除文件的句柄
lsof | grep deleted         # 找到这些「幽灵文件」
# 重启那个进程就释放了

工具二:strace——看进程在做什么系统调用

# 追踪一个运行中的进程
sudo strace -p 1234 -f -e trace=network   # 只看网络相关调用

# 启动时追踪
strace -o /tmp/trace.log node server.js   # 输出到文件

# 常用过滤
strace -e trace=file node server.js       # 只看文件操作
strace -e trace=open,read,write -p 1234   # 只看 open/read/write
strace -c -p 1234                          # 统计,Ctrl+C 后看汇总

# 经典场景:程序报 "No such file",但你看文件确实在
# → strace 会告诉你它实际在找哪个路径(可能是相对路径搞错了)
strace 有性能开销——每个系统调用都要拦截。生产环境慎用,先在测试环境复现。如果必须在线排查,用 -e trace=... 缩小范围。

工具三:tcpdump——抓包看网络

# 抓 80 端口的流量
sudo tcpdump -i any port 80 -A    # -A = ASCII 输出(能看 HTTP 内容)

# 只看特定主机
sudo tcpdump -i eth0 host 10.0.0.5

# 保存到文件
sudo tcpdump -i any -w capture.pcap
# 之后用 Wireshark 打开分析

# 经典场景:怀疑 DNS 有问题
sudo tcpdump -i any port 53
# 看看 DNS 请求发出去了吗?响应回来了吗?

工具四:性能分析套件

# CPU
top                         # 实时看谁在用 CPU
htop                        # 更好看
mpstat -P ALL 1             # 每个核心的 CPU 使用率

# 内存
free -h                     # 内存概览
cat /proc/meminfo            # 详细内存信息
# OOM Killer 日志
dmesg | grep -i "out of memory"
grep -i "out of memory" /var/log/syslog

# 磁盘 I/O
iostat -x 1                 # 磁盘读写速率和等待时间
iotop                       # 哪个进程在狂读写磁盘

# 综合
vmstat 1                    # CPU + 内存 + I/O 一览
sar -n DEV 1                # 网络接口流量(需 sysstat 包)

经典故障场景

场景 1:「磁盘满了」

df -h                       # 确认哪个分区满了
du -sh /* | sort -rh | head -10  # 哪个目录最大
# 如果 du 加起来远小于 df 显示的使用量
lsof | grep deleted         # 找被删除但未释放的文件
# → 重启持有文件句柄的进程

场景 2:「内存不足」

free -h                     # 看整体
ps aux --sort=-%mem | head -10  # 谁在吃内存
dmesg | tail -50            # 有没有 OOM killer 日志
# 如果 free 显示 available 很少但 cached 很大
# → 这是正常的!Linux 用空闲内存做缓存,需要时会释放

场景 3:「服务端口被占」

# Cannot bind to :3000
ss -tlnp | grep 3000        # 谁在监听 3000?
sudo lsof -i :3000          # 更详细的信息
# 如果是僵尸进程:ps aux | grep PID → kill

场景 4:「进程莫名死掉」

# 检查退出码
echo $?                     # 上一个命令的退出码

# systemd 服务的退出信息
systemctl status myapp
journalctl -u myapp -n 50 --no-pager

# 检查限制
ulimit -a                   # 当前 shell 的限制
cat /proc/PID/limits        # 某个进程的限制
# 常见:open files 限制太小 → 改 /etc/security/limits.conf

场景 5:「网站响应慢」

# 用 curl 测量各阶段耗时
curl -w "@curl-format.txt" -o /dev/null -s https://example.com

# curl-format.txt 内容:
# time_namelookup:  %{time_namelookup}\n    # DNS 解析
# time_connect:     %{time_connect}\n       # TCP 连接
# time_appconnect:  %{time_appconnect}\n    # SSL/TLS 握手
# time_starttransfer: %{time_starttransfer}\n  # 第一个字节
# time_total:       %{time_total}\n         # 总耗时

# 如果 time_namelookup 高 → DNS 慢
# 如果 time_connect 高 → 网络延迟
# 如果 time_starttransfer 高 → 应用处理慢

动手练习

用 Docker 创建一个「故障容器」来练习排查:

docker run -d --name troubleshoot ubuntu:22.04 sleep infinity

# 进去制造并排查问题
docker exec -it troubleshoot bash

# 1. 装一些工具
apt update && apt install -y procps lsof net-tools iproute2 curl

# 2. 练习 lsof:写一个脚本,用 lsof 找出哪个进程在写 /tmp/test.log
# 3. 练习 ss:启动一个 nc 监听 9999 端口,用 ss 找到它
# 4. 练习 curl 计时:curl -w "...各阶段耗时..." 访问 github.com

docker rm -f troubleshoot

小测验

Q1:磁盘满了,但 du 加起来远小于 df 显示,最可能的原因?
Q2:想知道哪个进程在监听 3000 端口,最佳命令是?
Q3:curl 测量中发现 time_namelookup 很高,说明什么问题?

下一步

Lesson 0012:网络协议基础 →——TCP 三次握手为什么是三次?DNS 解析到底走了几步?TLS 证书链怎么验证?面试必问。

← Lesson 0010 · Lesson 0012 → · 术语表