日志与排查思路
Linux 运维基础 · 第十五章 目标:建立系统化排查思维,知道出问题后从哪开始、往哪查
1. 概述#
这是本教程的终章。前面十四章讲了工具和操作,这一章把它们串起来:出现故障后,你的排查路径是什么?
核心就一句话:日志确认识现象 → 进程确认服务状态 → 网络确认连通性 → 磁盘确认空间 → 内核确认硬件。把这条链路刻在脑子里,大部分故障都能自己搞定。
2. 日志体系全景#
2.1 /var/log 目录速查#
bash
ls -la /var/log/| 日志文件 | 内容 | 何时看 |
|---|---|---|
/var/log/syslog | 系统主日志(Debian/Ubuntu) | 任何不知来源的问题 |
/var/log/messages | 系统主日志(RHEL/Rocky/Alma) | 同上 |
/var/log/auth.log | 认证日志:登录/sudo/SSH(Debian/Ubuntu) | SSH 连不上、权限异常 |
/var/log/secure | 认证日志(RHEL/Rocky/Alma,对应 auth.log) | 同上 |
/var/log/kern.log | 内核日志(Debian 系;RHEL 走 messages/journald) | 硬件错误、驱动问题 |
/var/log/dpkg.log | 包管理日志(Debian/Ubuntu) | 装了/卸了什么包 |
/var/log/yum.log | 包管理日志(旧 CentOS/yum;RHEL 8+ 已改用 dnf,日志在 /var/log/dnf.log、dnf.rpm.log) | 同上 |
/var/log/nginx/ | Nginx 访问和错误日志 | 网站状态码异常 |
/var/log/apt/ | APT 详细日志 | 包管理故障详解 |
/var/log/boot.log | 启动过程日志 | 服务开机自启失败 |
/var/log/faillog | 登录失败记录 | faillog 命令查看 |
两套日志系统并存,别只盯一边 现代发行版上 journald(二进制,
journalctl看) 和 rsyslog(纯文本,/var/log/*) 通常同时在跑:journald 先统一收集所有日志,再转发给 rsyslog 落成/var/log/syslog(或messages)。
- journald 的优势:结构化、能按服务(
-u)/优先级(-p)/时间(--since)/启动次数(-b)过滤。- 纯文本日志的优势:
grep/awk顺手、远程集中收集、第三方工具兼容。- 坑:有些精简容器/镜像只装了 journald,
/var/log/syslog根本不存在—— 这时别去tail /var/log/syslog,直接journalctl。反过来,某些应用只写自己的/var/log/xxx/,不进 journald,得去对应目录翻。- journald 默认可能不持久化(重启即丢),排障前确认已开持久化(见第 13 章 4.4)。
2.2 日志轮替(logrotate)#
bash
# 日志文件不会无限增长,logrotate 负责旋转
cat /etc/logrotate.conf # 全局配置
ls /etc/logrotate.d/ # 各服务的旋转策略
# 手动触发
logrotate -f /etc/logrotate.conf
logrotate -f /etc/logrotate.d/nginx
# 典型配置
# /var/log/myapp/*.log {
# daily # 每天旋转
# rotate 7 # 保留 7 份
# compress # 压缩旧日志
# missingok # 文件不存在不报错
# notifempty # 空文件不旋转
# copytruncate # 复制后截断(不中断服务)
# }3. dmesg — 内核日志#
bash
dmesg -T # 【首选】-T 把"开机秒数"转成可读的墙上时间,否则时间戳没法和日志对齐
dmesg -T | tail -50 # 最近 50 条
dmesg -T | grep -i error # 找错误
dmesg -T | grep -i fail # 找失败
# 默认不带 -T 时,时间戳形如 [12345.678],是【开机后经过的秒数】,不是日期——排查时务必加 -T
# 按级别过滤
dmesg -T --level=err # 只看错误
dmesg -T --level=err,warn # 错误 + 警告
# 实时查看
dmesg -Tw # -w 持续跟踪(类似 tail -f),配 -T 更易读
# 注意:部分系统限制非 root 读 dmesg(内核参数 kernel.dmesg_restrict=1),加 sudo什么情况用 dmesg#
- 磁盘 I/O 错误
- 内存错误(ECC 故障)
- 驱动加载失败
- OOM(Out of Memory)事件
- 硬件热插拔事件
4. journalctl 进阶#
bash
# 按时间:只看今天的
journalctl --since today
# 全局搜索关键词
journalctl --grep="OOM"
journalctl --grep="killed process"
# 只看内核日志
journalctl -k
journalctl -k --since today
# 只看错误级别
journalctl -p err --since today
# 看某个服务的详细日志
journalctl -u nginx -n 100 --no-pager -l
# 看从某个时间点开始的
journalctl --since "2026-07-01 09:00:00" --until "2026-07-01 10:00:00"
# 导出日志
journalctl --since today > /tmp/today_logs.txt5. 排查思路总纲#
5.1 四步排查法#
text
第 1 步:日志 (Logs)
↓
第 2 步:进程 (Process)
↓
第 3 步:网络 (Network)
↓
第 4 步:磁盘 (Disk)
↓
第 5 步:内核/硬件 (Kernel/Hardware)从外到内、从现象到根因。
5.2 每一步查什么#
| 步骤 | 查什么 | 命令 |
|---|---|---|
| 日志 | 确认故障现象和时间点 | systemctl status / journalctl -u / tail / grep |
| 进程 | 服务在不在跑?CPU/内存正常吗? | ps aux / top / systemctl status |
| 网络 | 端口在监听吗?能连上吗? | ss -tlnp / nc / curl / ping |
| 磁盘 | 空间满了吗?inode 满了吗? | df -h / df -i / du -sh |
| 内核/硬件 | 硬件报错?内存故障? | dmesg / journalctl -k / free -h |
6. 实战场景#
实战 1:服务启动失败#
用户反馈:"网站打不开了"
bash
# 第 1 步:日志 — 确认现象
systemctl status nginx --no-pager -l
# Active: failed (Result: exit-code)
# nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
# ^ 一看就明白:80 端口被占了
# 第 2 步:进程 — 确认谁占的端口
ss -tlnp | grep :80
# LISTEN 0 128 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=5678,fd=6))
# 老的 nginx 进程没死透
# 解决
kill 5678 # 杀掉旧进程
systemctl start nginx # 重启
systemctl status nginx # 确认 OK
# 如果日志没有明显报错,继续深入:
journalctl -u nginx -n 100 --no-pager
# 找启动前后的日志
# 还不行?手动跑看报错
sudo -u www-data nginx -t # 测试配置
sudo nginx # 前台启动看直接报错实战 2:磁盘写满#
监控告警:磁盘使用率 > 95%
bash
# 第 1 步:确认告警
df -h
# /dev/sda1 50G 48G 0 100% / ← 真满了
# 第 2 步:找大户
du -sh /* 2>/dev/null | sort -rh | head -10
# 24G /var
# 12G /home
# 8.5G /usr
# 第 3 步:深入 /var
du -h --max-depth=1 /var/ | sort -rh | head
# 22G /var/log
du -h --max-depth=1 /var/log/ | sort -rh | head
# 18G /var/log/journal ← journald 日志太大了
# 第 4 步:清理
du -sh /var/log/journal/
journalctl --disk-usage
journalctl --vacuum-size=500M
df -h /
# 恢复了
# 如果 du 加起来不大,df 却满了 → 幽灵文件(进程占着已删除的文件)
lsof +L1 | awk '{print $1, $7}' | sort -nrk2 | head
# 找到后重启对应进程
# 如果 df -h 显示还有空间却报 No space left on device → inode 耗尽
df -i # 看 IUse%,到 100% 就是 inode 满了(小文件太多)
# 定位:for d in /var/*; do echo "$(find "$d" 2>/dev/null|wc -l) $d"; done | sort -rn | head实战 3:SSH 连不上#
场景:
ssh user@server卡住或提示 Connection refused
bash
# 事前检查(如果能通过云控制台/串口登录)
# 第 1 步:防火墙
# 检查是不是防火墙挡住了
iptables -L -n # 或 firewall-cmd --list-all
# 确认 22 端口是否开放
# 第 2 步:sshd 在跑吗
systemctl status sshd # 或 ssh
# Active: inactive (dead) ← 没跑!
# 第 3 步:看认证日志
tail -50 /var/log/auth.log # 或 /var/log/secure
# 有无登录失败记录?
# 有无 "Connection closed" 的记录?
# 第 4 步:端口在监听吗
ss -tlnp | grep :22
# 没输出 → sshd 没起来
# 启动
systemctl start sshd
systemctl enable sshd # 设为开机自启
# 第 5 步:从外部测
# 在本地机器上:
ssh -v user@server # -v 看详细连接过程
nc -zv server_ip 22 # 端口是否可达实战 4:OOM(内存溢出)#
进程莫名挂了,日志里没有异常
bash
# 第 1 步:看内核日志(加 -T 才知道是【什么时候】被杀的)
dmesg -T | grep -i "killed process"
# [Wed Jul 1 02:14:07 2026] Out of memory: Killed process 8901 (java) total-vm:4194304kB
# 关键信息:oom-kill 会打印是谁触发的、被杀进程的 RSS、以及 total_vm,据此定位内存大户
# 第 2 步:看 journald
journalctl --grep="killed process" --since today
# 第 3 步:确认当前内存
free -h
# Mem: 7.8G 7.5G 156M 1.2G 100M 1.0G
# 可用内存极低
# 第 4 步:找吃内存大户
ps aux --sort=-%mem | head -10
# 或 top 按 M 排序
# 临时缓解:加 swap
dd if=/dev/zero of=/swapfile bs=1G count=4
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
# 长期修复:增加物理内存 / 优化应用内存使用 / 加内存限制7. 排障工具速查表#
| 故障类型 | 第一步命令 | 关键工具 |
|---|---|---|
| 服务起不来 | systemctl status <svc> + journalctl -u <svc> -n 50 | journalctl, systemctl |
| 磁盘满 | df -h + du -sh /* | sort -rh | df, du, lsof |
| 内存高 | free -h + ps aux --sort=-%mem | free, ps, top, dmesg |
| CPU 高 | top → top -H -p <pid> | top, ps |
| 网络不通 | ping → nc -zv → curl -v | ping, nc, curl, ss, traceroute |
| DNS 异常 | dig +short <domain> + cat /etc/resolv.conf | dig, nslookup |
| SSH 连不上 | nc -zv host 22 + 控制台看 auth.log | nc, journalctl, ss |
| 服务响应慢 | curl -o /dev/null -w 看各阶段耗时 | curl, strace, tcpdump |
8. 运维应急流程图#
text
用户报故障
↓
【1. 日志】— 确认现象和时间点
├── systemctl status <服务>
├── journalctl -u <服务> -n 50
├── tail -100 /var/log/<服务>/error.log
└── dmesg | tail
↓
【2. 进程】— 确认服务状态
├── 在运行吗?→ systemctl status / ps aux
├── CPU/内存正常吗?→ top / free -h
└── 重启有效吗?→ systemctl restart
↓
【3. 网络】— 确认连通性
├── 端口监听?→ ss -tlnp | grep <port>
├── 能连上?→ nc -zv <host> <port>
├── DNS 解析?→ dig +short <domain>
└── 防火墙通?→ iptables -L / firewall-cmd --list-all
↓
【4. 磁盘】— 确认存储
├── 空间满?→ df -h / df -i
├── 谁占的多?→ du -sh /* | sort -rh
└── 幽灵文件?→ lsof | grep deleted
↓
【5. 内核/硬件】— 底层排查
├── 内核报错?→ dmesg | grep -i error
├── OOM?→ dmesg | grep -i killed
└── 硬件故障?→ dmesg -T | tail
↓
解决 / 升级求助9. 好习惯#
- 操作前先备份:改配置前
cp config.conf config.conf.bak.$(date +%Y%m%d) - 一步一步改:别同时改多个配置项,改完一项测一项
- 记录操作:在 tmux 里操作,自动留下完整记录,方便回看和交接
- 先查后改:不确认根因不动手,尤其是
rm -rf和kill -9 - 永远有回滚方案:改之前想好"怎么改回去"
- 结构化排障:不要东一榔头西一棒子,按四步法走
10. 小结#
| 知识点 | 一句话记忆 |
|---|---|
| 日志体系 | Debian 系 syslog/auth.log,RHEL 系 messages/secure;服务日志 journalctl -u |
| journald vs rsyslog | 二者并存,journctl 看结构化、/var/log/* 供 grep;容器里可能只有 journald |
| 四步排查 | 日志→进程→网络→磁盘→内核,从现象到根因 |
| 服务失败 | systemctl status + journalctl -u -n 50 三步以内定位 |
| 磁盘满 | df -h 看块空间、df -i 看 inode,du -sh /* 找大户,lsof | grep deleted 找幽灵 |
| SSH 不通 | nc -zv host 22 测端口,控制台看 auth.log/secure |
| OOM | dmesg -T | grep killed 确认,free -h 看现状,加 swap 应急 |
| dmesg | 一律加 -T,否则时间戳是开机秒数不是日期 |
上一章:十四、Shell 脚本
全教程目录: