df -h 看哪个挂载点满了
先看 Use% 和 Avail。注意区分两种情况:真的是某块盘满了;或者 inode 用尽——空间还有但创建不了新文件。
df -hTdf -i # 看 inode 使用率,用尽时同样写不进去容器里看到满,未必是容器的问题——挂载进来的宿主目录满了会直接体现在容器内。
排障的顺序比命令本身重要:先确认现象发生在哪一层,再逐层收窄。这一节按「你看到的现象」组织,每条给出第一步该看什么、判断依据是什么、接下来往哪个方向走。命令都可以直接粘贴。
典型症状是服务突然报 No space left on device、日志写不进去、数据库拒绝写入,但 df 看起来还有空间。先确认到底哪一层满了,再动手删。
df -h 看哪个挂载点满了先看 Use% 和 Avail。注意区分两种情况:真的是某块盘满了;或者 inode 用尽——空间还有但创建不了新文件。
df -hTdf -i # 看 inode 使用率,用尽时同样写不进去容器里看到满,未必是容器的问题——挂载进来的宿主目录满了会直接体现在容器内。
逐层找出大目录从根开始逐层下钻,每层只排前 15 名,很快能定位到具体目录。
du -h --max-depth=1 / 2>/dev/null | sort -rh | head -15du -h --max-depth=1 /var 2>/dev/null | sort -rh | head -15du -ah /var/log | sort -rh | head -20du 遇到权限不足会静默跳过,加 2>/dev/null 让输出干净;必要时用 sudo du。
找出被删除但没释放的文件进程还持有已删除文件的句柄时,df 显示满但 du 找不到对应目录。lsof 的 deleted 标记能抓出来,重启持有它的进程即可释放。
lsof +L1 2>/dev/null | sort -k7 -rn | head -20lsof -nP 2>/dev/null | grep deleted | awk '{print $1, $2, $7, $9}' | sort -k3 -rn | head截断正在写的日志(: > file)通常比 rm 更合适:句柄不失效,空间立即回收。
常见占空间的固定位置依次检查这几个位置,绝大多数「盘满了」都能在这里找到答案。
journalctl --disk-usage
journalctl --vacuum-size=200Mdu -sh /var/lib/docker 2>/dev/null
docker system dfdu -sh /var/cache/apt /var/log 2>/dev/nullfind / -xdev -type f -size +500M -printf '%s\t%p\n' 2>/dev/null | sort -rn | headdocker system prune 会删掉所有未使用镜像和卷,跑之前先确认没有停着待恢复的容器。
systemd 的报错很啰嗦,但定位路径是固定的:systemctl status 看退出码和最后几行日志,journalctl 看完整输出,再核对配置和端口。
systemctl status 看状态与退出码重点看 Active 一行和下面日志的最后几行。failed 后面括号里的退出码很有信息量:203/EXEC 通常是路径或权限问题。
systemctl status nginx --no-pager -lsystemctl is-enabled nginx # 是否开机自启systemctl is-failed nginx203/EXEC 表示可执行文件无法执行:检查 shebang、执行位、以及脚本里 CRLF 换行。
journalctl 看这个服务的完整日志-u 只看某个单元,-n 限制行数,--since 限定时间,-p err 只看错误级别以上。
journalctl -u nginx -n 100 --no-pagerjournalctl -u myapp --since '10 min ago' -p warningjournalctl -u myapp -f # 实时跟踪journalctl -b -1 -u myapp # 上一次启动周期用 -b -1 能回看「重启之前那次」的日志,排查开机就崩的服务非常有用。
先手动跑一遍可执行文件从单元文件里读出 ExecStart 的命令行,手动执行一次,报错会直接打在终端上,比 journal 更直观。
systemctl cat myapp | grep -E '^ExecStart'systemctl show myapp -p ExecStart --no-pagersudo -u www-data /usr/bin/myapp --config /etc/myapp.conf # 换对应用户手动跑手动跑能跑通、systemd 跑不通,九成是环境变量、工作目录或用户不同。
核对配置语法改完配置没 reload 或语法写错,是「重启后起不来」的头号原因。
nginx -tsshd -tsystemd-analyze verify /etc/systemd/system/myapp.servicenginx -t 只校验默认路径的配置;用了自定义 -c 时要带上同样的参数。
最近改过什么按时间列出最近变动的配置和包,能迅速把问题和某次操作对上。
find /etc -type f -mmin -60 -ls 2>/dev/nullrpm -qa --last | head -20 # RHEL 系grep -E ' (install|upgrade) ' /var/log/dpkg.log | tail -20 # Debian 系「昨天还好好的」几乎总能在这里找到那个被改动的文件。
先把「本机能不能连」和「外面能不能连」分开,再看是没人监听、监听到了错误地址、还是中间被拦。
ss 看监听与占用-l 只看监听,-t TCP,-u UDP,-n 不做域名解析,-p 显示进程。要看谁占了某个端口,直接用 grep 过滤。
ss -ltnpss -ltnp | grep ':8080'ss -lunpss -s # 汇总连接数看到 127.0.0.1:8080 说明只监听本机,外部当然连不上——这是最常见的「端口没通」。
判断是没人监听还是被防火墙拦先在本机 curl 一次:连不上说明服务本身有问题;本机能连但外面不能连,再看监听地址和防火墙。
curl -sS -m 3 -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/curl -sS -m 3 -o /dev/null -w '%{http_code}\n' http://192.168.1.10:8080/nc -vz 192.168.1.10 8080连接被拒绝(Connection refused)说明路由可达但没人监听;超时才是防火墙或安全组在丢包。
防火墙规则firewalld、ufw、nftables/iptables 并存时容易互相看不到,逐个确认。
sudo firewall-cmd --list-all # firewalldsudo ufw status verbose # ufwsudo nft list ruleset | head -40 # nftablessudo iptables -S | head -40 # iptables云主机还要单独看安全组——本机防火墙全开也可能被云平台挡在外面。
查看端口属于哪个进程拿到 PID 后可以直接看它的命令行、工作目录和打开的文件。
sudo ss -ltnp | grep ':8080'sudo lsof -nP -iTCP:8080 -sTCP:LISTENsudo ls -l /proc/<PID>/cwd
sudo tr '\0' ' ' < /proc/<PID>/cmdline; echolsof 报的 PID 是命名空间视角的;在容器场景要在宿主机上找 containerd/docker 进程。
先分清是「真的忙」还是「在等」。CPU 高看用户态还是内核态,内存高看是不是缓存,IO 高看是磁盘慢还是程序在狂写。
uptime 与负载三个数字分别是 1、5、15 分钟的平均负载。负载高于 CPU 核数说明有排队。
uptimenproccat /proc/loadavg负载包含等 IO 的进程。磁盘慢的时候负载会很高但 CPU 空闲,别急着加 CPU。
top 交互式定位进 top 后按 P 按 CPU 排序、M 按内存排序、1 展开每核、c 看完整命令行。
top -o %CPUtop -b -n1 | head -20 # 一次性输出,适合脚本ps -eo pid,ppid,user,pcpu,pmem,etime,comm --sort=-pcpu | head -15ps 的 %CPU 是进程整个生命周期的平均值,排查「刚刚飙高」要用 top 或 pidstat。
内存到底被什么占了free 的 available 才是可用内存,buff/cache 是可以回收的。真正要警惕的是 swap 被大量使用。
free -hcat /proc/meminfo | head -5smem -rs rss 2>/dev/null | head # 按实际占用排序ps -eo pid,user,rss,pmem,comm --sort=-rss | head -15OOM 杀进程的记录在 journalctl -k | grep -i oom,或 dmesg | grep -i 'killed process'。
IO 与磁盘延迟iostat 的 %util 接近 100 说明磁盘饱和;await 是平均等待毫秒数,明显偏高就是盘慢。
iostat -xz 1 5vmstat 1 5sudo iotop -oPa 2>/dev/null | head -20cat /proc/pressure/io云盘的突发配额耗尽时会出现「平时很快、突然很慢」,用 fio 或 dd 复测才能确认。
看某个进程具体在做什么strace 跟踪系统调用,perf 采样热点函数,是「CPU 高但看不出为什么」时的下一步。
sudo strace -f -p <PID> -T -tt -e trace=network 2>&1 | head -50sudo perf top -p <PID>sudo cat /proc/<PID>/stackstrace 会显著拖慢被跟踪进程,生产环境只在必要时短时间挂上去。
Linux 的权限判断分好几段:文件模式位、目录的穿越权限、SELinux/AppArmor、以及挂载选项。报错一样,原因完全不同,按顺序排除。
先看这个路径的每一层权限读文件需要路径上每个目录都有 x 位;只给文件 r 而父目录没有 x,照样 Permission denied。
namei -l /var/www/app/config.ymlls -ld /var /var/www /var/www/app /var/www/app/config.ymlid && id -Gnnamei -l 会把路径上每一段的属主和模式位列成一行,是排查这类问题最快的手段。
确认进程实际以哪个用户运行服务不一定以你登录的用户跑。看进程的 real/effective uid 和所属组,再回头核对文件权限。
ps -o user,group,pid,comm -p <PID>sudo cat /proc/<PID>/status | grep -E '^(Uid|Gid|Groups)'systemctl show myapp -p User -p Group --no-pager改了文件属组却没重启服务,进程持有的仍是旧组——这类「改了没用」要先重启再判断。
SELinux / AppArmor 拦截权限看起来都对却仍然拒绝,就要怀疑强制访问控制。
getenforce
sudo ausearch -m avc -ts recent | tail -30sudo aa-status 2>/dev/null | headdmesg | grep -iE 'denied|avc' | tail -20临时用 setenforce 0 验证可以,但别把它当成解决方案;正确做法是修 file context 或加策略。
挂载选项与只读文件系统noexec 会让可执行文件报 Permission denied;只读挂载会让一切写入失败。
mount | grep -E ' /(home|tmp|opt|var) 'findmnt -no OPTIONS /tmpfindmnt -no OPTIONS //tmp 被挂成 noexec 时,脚本里解压出来直接运行就会失败——先 chmod +x 到别的目录或挂载点。
按「网络 → 端口 → 认证 → PAM/Shell」的顺序走,每一步都能用一条命令证伪。
客户端看详细握手过程-vvv 会打印协商、认证、通道建立的每一步,卡在哪一行就是哪一层的问题。
ssh -vvv user@host 2>&1 | tail -40ssh -o ConnectTimeout=5 -o BatchMode=yes user@host true; echo "退出码: $?"nc -vz host 22卡在 kex 是网络或服务问题;到了 Authentication 才失败就是密钥、口令或 PAM 的问题。
服务端看 sshd 日志认证失败的精确原因(密钥不匹配、用户被锁、超出 MaxStartups)都在这里。
journalctl -u ssh -n 80 --no-pagerjournalctl -u sshd -n 80 --no-pagersudo tail -50 /var/log/auth.log # Debian 系sudo tail -50 /var/log/secure # RHEL 系服务名在 Debian 是 ssh、RHEL 是 sshd,journalctl -u 写错会得到空输出,很误导。
密钥权限与 known_hosts私钥权限过宽会被 ssh 直接拒绝;换了主机密钥会有刺眼的告警。
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/authorized_keysssh-keygen -R hostssh-keyscan -t ed25519 host >> ~/.ssh/known_hostsStrictHostKeyChecking=no 只适合临时排查,长期用会失去中间人攻击防护。
登录后立刻断开或卡住能认证成功却拿不到 shell,通常是登录 shell、家目录权限或 PAM 的问题。
ssh -t user@host /bin/bash --noprofile --norc # 绕过启动文件ls -ld /home/user && ls -la /home/user/.sshgrep -E '^\S+:.*(false|nologin)' /etc/passwd.bashrc 里的交互式命令(比如等待输入)会让非交互登录一起卡住,排查时先用 --noprofile --norc。
先知道东西写在哪,再谈过滤。现代发行版里 journald 和传统日志文件是两套并存的体系。
日志的常见位置按来源分:内核在 dmesg,服务在 journald,站点应用多数写自己的文件。
/var/log/syslog # Debian 系总日志
/var/log/messages # RHEL 系总日志
/var/log/auth.log # 认证(RHEL 为 secure)
/var/log/kern.log
/var/log/nginx/{access,error}.log
/var/log/apt/ /var/log/dpkg.logdmesg -T --level=err,warn | tail -30journalctl -b -p err --no-pager | tail -50容器里 /var/log 往往是空的,日志走 docker logs / kubectl logs,而 journald 在宿主机上。
按时间窗口取日志--since 和 --until 支持相对时间和绝对时间,两个配合可以把范围压到几分钟。
journalctl --since '2026-10-19 15:00' --until '2026-10-19 15:30' --no-pagerjournalctl --since '30 min ago' -u nginx -p warninggrep '19/Oct/2026:15:2' /var/log/nginx/access.log | tail时间不写时区时按本机时区解释;跨时区排查要显式带上偏移,比如 2026-10-19T07:00:00Z。
从后往前找报错日志通常很长,先 tail 看尾部,再 grep 收窄关键词,最后用上下文确认。
tail -n 200 /var/log/app.loggrep -niE 'error|exception|timeout|refused' /var/log/app.log | tail -40grep -n -B3 -A15 'Traceback' /var/log/app.log | tail -60-B / -A 给出前后文,比单看错误行更容易判断真实原因。
实时跟踪与合并多文件tail -F 能扛住日志轮转,-f 在多文件会加文件名前缀,排查集群时很方便。
tail -F /var/log/app.logtail -f /var/log/app/app-*.logjournalctl -f -u nginx -u php-fpm-F(大写)等于 --follow=name --retry,文件被 logrotate 换掉后仍能继续跟,-f 会跟丢。
统计而不是逐行读先看分布,再决定要不要读细节。一分钟的错误分布往往直接指向问题。
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | headgrep -c ' 500 ' /var/log/nginx/access.logawk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head先统计再取样:先知道哪类错误最多、集中在哪个时间段,再 tail 那一段原文。