1. 先聊聊这篇要解决什么问题
系列写到第七篇,前面几篇把 cd、ls、cp、mv、grep 这些高频基础命令都过了一遍。这篇我不想再罗列命令参数了,换个思路——以“线上排障”和“日常运维”的真实场景为线索,把一组命令串起来讲,重点说清楚它们各自适合在什么情况用、怎么组合使用、有哪些容易被忽略的细节。
有朋友可能觉得,Linux 命令哪有什么好讲的,背下来不就行了。我刚开始工作也是这么想的,直到后来在业务环境里排查问题,发现很多命令我“背过”但根本没想到用它,或者用了但用错了参数,导致看了一下午日志都没定位到问题。所以这篇整理了我在真实环境中用得最多的几组命令,覆盖用户与权限、系统状态排查、磁盘与文件系统、网络定位、日志追踪这几块。不管是刚接触 Linux 的新人,还是已经会用基础命令但想进一步体系化提升的运维或后端同学,这篇都值得花十分钟过一遍。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从建号到授权:用户与权限类命令的实际协作方式
2.1 新建用户不是只有 useradd 这一个动作
很多教程讲到创建用户,就是 useradd 加一个用户名就完了。但真正落到生产环境,这个操作通常要配合好几个命令一起完成。
以创建一个部署服务用的用户 deploy 为例,我通常这样操作:
bash复制# 创建用户并指定 home 目录、Shell 和备注信息
useradd -m -d /home/deploy -s /bin/bash -c "Deploy Service Account" deploy
# 设置密码(交互式输入)
passwd deploy
# 或者通过管道批量设置初始密码
echo "YourInitialPassword" | passwd --stdin deploy
这里面几个参数解释一下:
-m:自动创建 home 目录。如果不加,/home/deploy不会自动生成,后面很多依赖家目录的程序会莫名报错。-d:指定家目录路径,默认是/home/用户名,但也可以自定义到别的地方,比如数据盘挂载点。-s:登录 Shell。常见选择是/bin/bash。但如果创建的是纯服务账号,不需要交互登录,我习惯设成/sbin/nologin,这样该用户无法直接 ssh 登录,安全得多。
这里有个我踩过的坑:早期做自动化部署脚本时,用 chpasswd 批量重置密码,命令写成了 echo "user:pass" | chpasswd,但是 chpasswd 不是所有发行版都默认支持从标准输入读这种方式。有的发行版需要先确认 PAM 配置是否正确,否则命令看起来成功了,但实际上密码没改掉,导致后续自动化流程登录失败。所以用这类批量改密命令前,建议先跑一遍验证一下确认是否真的生效。
2.2 usermod 与 passwd 的常见组合用法
用户创建不是一次性的事,后面业务调整经常要改用户属性。usermod 就是干这个的。
几个高频场景:
bash复制# 把用户加入 sudo 组(Debian/Ubuntu 系)
usermod -aG sudo deploy
# 把用户加入 docker 组,免 sudo 执行 docker 命令
usermod -aG docker deploy
# 修改用户的登录 Shell
usermod -s /bin/bash deploy
# 锁定用户,禁止登录但又不想删除账号
usermod -L deploy
# 解锁用户
usermod -U deploy
这里要特别提醒:usermod -aG 中的 -a 是 append 的意思,不加 -a 的话,用户会被从原有附属组中移除,只保留你指定的组。我有次想把一个用户加入某个项目组,结果忘了加 -a,用户直接从 docker 组里被踢出去了,导致 CI 构建机上的容器命令全部权限拒绝,排查了大半天。这个教训后来我每次写命令都会检查一眼。
密码策略方面,生产环境密码一般不会直接设成永久有效,通常配合策略来控制:
bash复制# 查看用户的密码有效期信息
chage -l deploy
# 设置密码 90 天必须更换,到期前 7 天开始警告
chage -M 90 -W 7 deploy
# 设置账号在指定日期过期(格式 YYYY-MM-DD),适合临时账号
chage -E 2025-12-31 deploy
2.3 权限位、属主属组和 setuid/setgid 的现场经验
权限模型是 Linux 系统的基石,chmod 和 chown 是使用频率最高的两个命令。但我工作中发现,很多人对它们的理解停留在“753 是什么”的层面,一旦遇到“目录为什么删不了文件”“为什么文件能读不能执行”“为什么用户明明在组里却报无权限”这类问题就抓瞎。
先说基本操作:
bash复制# 修改文件权限为 rwxr-xr-x
chmod 755 script.sh
# 给属主加执行权限,不给其他用户改权限
chmod u+x script.sh
# 递归修改目录权限(注意:这会覆盖目录下所有文件)
chmod -R 750 /data/project
# 修改属主和属组
chown deploy:deploy /data/project/app.jar
递归 chmod -R 是个危险操作。如果目录里既有普通文件又有脚本,一个 755 会把文件全部变成可执行,虽然多数场景影响不大,但有时会造成安全隐患。更精细的做法是用 find 做筛选:
bash复制# 目录统一 755,普通文件统一 644,脚本统一 755
find /data/project -type d -exec chmod 755 {} \;
find /data/project -type f -name "*.conf" -exec chmod 644 {} \;
find /data/project -type f -name "*.sh" -exec chmod 755 {} \;
关于 setuid/setgid,这个知识点面试常考、工作中也真的会用到。比如 passwd 命令本身有 setuid 位,所以普通用户才能修改 /etc/shadow 文件:
bash复制# 查看 setuid 文件(全盘扫描比较慢,可以限定目录范围)
find /usr/bin -perm -4000 -type f -ls
还有 /tmp 目录的 sticky bit(粘滞位),权限表示为 drwxrwxrwt,意思是所有的 users 都能在里面创建文件,但是只有文件属主和 root 能删除自己的文件。Java 应用在 /tmp 下生成了临时文件,如果其他用户想清理,就会报 “Operation not permitted”,这就是 sticky bit 在起作用。
2.4 sudoers 配置:如何安全地授“部分 root 权限”
生产环境直接给开发同学 root 密码的做法越来越少见,更常见的是通过 sudo 按需授权。visudo 命令编辑 /etc/sudoers,这个文件语法非常敏感,格式错了会导致 sudo 全部失效,所以必须用 visudo 而不能直接 vi,它会做语法检查。
几个常用的配置手法:
bash复制# 在 /etc/sudoers.d/ 下新建一个独立文件,比直接改主文件更安全
# 文件内容如下:
deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx
这个配置的意思是:允许 deploy 用户在所有主机上、以所有用户身份、免密码执行 systemctl restart nginx 这一条命令。这种白名单式授权,既满足了重启服务需求,又没把完整的 root 交出去。
我负责的服务器上,以前把 NOPASSWD: ALL 配给了好几个开发账号,后来审计时发现某台机器被拖库,对方拿到低权限账号后直接 sudo 提权,把整个服务配置翻了个底朝天。从此以后我立了一个规矩:能精确到命令就精确到命令,能用 NOPASSWD 的场景尽量缩减,不能图省事。如果确需要完整 root 权限,至少开启 sudo 的日志记录:
bash复制# 在 /etc/sudoers 中启用日志
Defaults logfile=/var/log/sudo.log
Defaults log_input, log_output
2.5 用户相关排障:getent、id、groups 的用处
用户权限问题排查时,有几个命令非常高效:
bash复制# 查看用户所有属组(包括从配置文件中读取的)
id deploy
groups deploy
# 从系统数据库查询用户信息,可能包含 NIS/LDAP 等远端数据源
getent passwd deploy
getent group docker
# 查看某用户最近登录历史
last -n 10 deploy
id 和 groups 对于判断“用户是否在某个组里”非常直接。有时候用户明明执行了 usermod -aG docker deploy,但是新开的 shell 里执行 docker ps 还是报权限不足。原因是用户组变更不会立刻反映到已经建立的会话里,要么重新登录,要么执行 newgrp docker 让当前会话重新读取组信息。这事我见过太多人卡住。
3. 机器卡了怎么查:进程、内存与负载排查命令链
3.1 top 的显示信息怎么看才不慌
服务器卡顿是最常见的告警场景。拿到告警第一件事,大多数人是 top,但 top 出来后看清楚哪些字段,很多人其实没概念。
看 top 的输出,我一般按这个顺序:
- 第一行 load average 后面的三个数,分别代表 1 分钟、5 分钟、15 分钟的负载均值。如果 1 分钟数值显著高于 15 分钟,说明负载正在上升,是即时性问题;如果三个数都很高,说明问题持续了一段时间。
%Cpu(s)这一行,留意us(用户态)和sy(系统态)。如果sy占比很高,通常说明系统调用密集、内核层有瓶颈,比如频繁的上下文切换、网络中断风暴。KiB Mem这行,注意available而不是free。free很小但available充足,意味着内存并没有真正耗尽,buff/cache 还可以回收。- 下方进程列表,按
P键按 CPU 排序,按M键按内存排序,按E键切换内存显示单位。
top 里我对程序员的建议是:不要只看 CPU 占用最高那个进程,还要看它有没有大量的“D”状态(不可中断睡眠)。如果进程大量处于 D 状态,通常是磁盘 IO 扛不住了,这时候 CPU 负载可能虚高但实际计算能力没跑满。
3.2 ps 查询进程:常用参数组合与典型场景
ps 的命令行参数特别多,但实际高频场景就几个。我的习惯是把 ps -ef 和 ps aux 混着用,但因为它们输出格式不同,后来干脆统一用了 ps -ef 加自定义格式:
bash复制# 查找特定进程
ps -ef | grep java
# 完整展示 CPU、内存、进程开始时间,方便定位老进程
ps -eo pid,ppid,user,%cpu,%mem,stat,lstart,cmd --sort=-%cpu | head -20
# 查看某个进程的线程数(配合 top -H 使用)
ps -o nlwp,pid,cmd -p <PID>
# 查看进程启动的完整命令参数(注意 /proc 方式)
cat /proc/<PID>/cmdline | tr '\0' ' '
定位 Java 应用时,我常配合 jstack、jstat 一起看,但 ps 本身提供的信息往往已经能缩小范围。比如用 ps -eo 看到某个进程的 %mem 异常增长,结合启动时间判断是不是近期发布的新版本引入了内存泄漏。
3.3 free、vmstat、iostat:内存和 IO 瓶颈的分工
free 命令很简单,但输出里的 buff/cache 和 available 常常被误读。早期我刚学会看 free 的时候,看到 buff/cache 占用十几个 G,立刻就觉得内存不够,急忙调 JVM 参数。后来才明白,buff/cache 是 Linux 用空闲内存做的磁盘缓存,当业务申请内存时会自动释放,所以 available 才是真正“还能用”的维度。
bash复制# 动态观察内存变化,每 2 秒刷新一次
free -h -s 2
# 查看系统内存详细统计(每个 NUMA 节点的内存情况)
numastat
vmstat 是个被很多人忽略的好工具,它用来观察系统整体状态非常直观:
bash复制# 每 2 秒输出一次,连续输出 5 次
vmstat 2 5
输出里 r 列表示运行队列中的进程数,b 列表示不可中断睡眠的进程数,si 和 so 表示从交换分区换入换出的内存量。如果 si、so 持续非零,说明物理内存已经吃紧,系统在频繁换页,性能会急剧下降。
IO 瓶颈用 iostat 更精确:
bash复制# 查看每个磁盘的 IO 情况,包含 %util、svctm、await 等指标
iostat -x 2 5
%util 接近 100% 表示设备已经满负荷。但注意,SSD 和机械盘对 %util 的接受程度差异很大。新上的 NVMe 盘 %util 到 90% 业务可能还很流畅,而机械盘 60% 就可能已经严重卡顿了,结合 await(IO 请求平均等待时间)一起判断更靠谱。
3.4 定位“谁在偷 CPU”:pidstat 与 top -H 的配合
系统整体负载高,但 top 里所有进程 CPU 占用看起来都不高,这种情况多见于多核服务器,某个进程的某个线程把单个核打满了,但按进程汇总后 CPU 占比被平均稀释了。
定位方式:
bash复制# 按进程维度查看 CPU 使用率,每 1 秒刷新
pidstat -u 1 5
# 按线程维度查看 CPU 使用率
pidstat -t -p <PID> 1 5
# 或者用 top 进入后按 H 键切换到线程视图
top -H -p <PID>
找到具体线程 ID 后,如果是 Java 应用,把它转成十六进制再去 jstack 里搜:
bash复制# 得到线程号 12345,转换成十六进制
printf "%x\n" 12345
然后 jstack <PID> | grep -A 30 "0x3039" 就能看到对应线程的调用栈。这种方式排查“诡异 CPU 高但找不到进程”的问题非常好用。
4. 磁盘满不等于空间满:inode 与文件系统排查的几个隐蔽坑
4.1 df 的两种视图:空间占用与 inode 占用
磁盘告警是运维日常,但这里有个常见误区:df -h 显示空间还有剩余,可应用却报“No space left on device”。如果是这种情况,大概率是 inode 耗尽了。
bash复制# 查看文件系统磁盘空间占用
df -h
# 查看文件系统 inode 占用(重点看 IUse% 列)
df -i
inode 是文件系统用来保存文件元数据的数据结构。每个文件、目录都要占用一个 inode。如果小文件特别多,即使数据块还有大量空间,inode 也可能先用完。我遇到过 rsyslog 服务因 /var/log 下海量小日志文件导致 / 分区 inode 耗尽,系统出现各种诡异“只读”现象。
检查某个目录下的文件数量:
bash复制# 统计当前目录下文件数量(递归)
find . -type f | wc -l
# 配合 du 查看占用空间最大的前 10 个目录
du -x -h --max-depth=1 / | sort -hr | head -10
4.2 lsof +L1:找“已删除但仍被占用”的文件
另一个常见坑是:df -h 显示磁盘满了,但 du -sh * 看了一圈,每个目录空间都不大,怎么加都对不上。这种时候通常是文件已经被删除,但仍有进程持有它的文件句柄,占用的空间没有真正释放。
bash复制# 找出所有已删除但仍被进程打开的文件
lsof +L1
看到输出后,记下 PID,确认进程是什么,然后看能不能重启该进程释放句柄。比如我遇到过 logrotate 没有正确通知 Nginx 重新打开日志文件,旧的 access.log 被删除后,Nginx 还握着旧句柄,导致 /var/log 分区空间被“幽灵文件”占满。处理方式是:
bash复制# 查看 Nginx master 进程 PID
cat /var/run/nginx.pid
# 向 master 发送 USR1 信号触发热重开日志
kill -USR1 $(cat /var/run/nginx.pid)
4.3 目录挂载、软硬链接和 mv 行为导致的“假象”
磁盘排查还经常遇到目录挂载导致的困惑。比如 /data 是一个独立分区,你往 /data/project/log 写日志,但 df -h / 显示满了、df -h /data 显示正常,业务却报磁盘写不进去。这通常是日志目录被误放到了 / 分区,而应用配置里写的是绝对路径。
排查目录实际落在哪个分区,用两个命令:
bash复制# 查看当前目录所在文件系统
df -h .
# 查看挂载点的详细信息
findmnt /data
软链接和硬链接的区别,在这个场景里特别重要:
bash复制# 创建软链接(可以跨文件系统,原文件删除后链接失效)
ln -s /data/real_dir /root/link_dir
# 创建硬链接(不能跨文件系统,原文件删除后链接仍指向文件数据,只是链接计数减一)
ln /data/real_file /root/link_file
硬链接在备份场景有特殊价值。比如某些程序写日志时会做 rename 归档,如果你提前建了硬链接,即使原路径被 rename,硬链接指向的仍然是原始文件数据。不过硬链接不能用于目录、不能跨文件系统,所以实际使用要分清场景。
4.4 rm 命令的安全替代:trash-cli 与 find 定期清理
rm -rf 是 Linux 最危险命令之一,我见过不止一次有人把构建目录路径写错,结果一条命令把整个项目源码删了。如果团队里新人多,我建议在开发机上引入 trash-cli,让 rm 变成移动到回收站而不是直接物理删除:
bash复制# 安装 trash-cli(Debian/Ubuntu 系)
apt install trash-cli
# 用 trash-put 替代 rm 删除文件
trash-put old-file.txt
# 查看回收站
trash-list
# 恢复文件
trash-restore
# 清空回收站(等价于不可恢复删除)
trash-empty
而在生产服务器上清理日志,不要直接用 rm,更推荐用 find 按时间自动清理:
bash复制# 删除 7 天前的 .log 文件
find /var/log/app -name "*.log" -mtime +7 -delete
# 删除文件前先列出确认一下(强烈建议先执行这一步)
find /var/log/app -name "*.log" -mtime +7 -ls
我的习惯是:清理敏感操作前,永远先跑一遍不带 -delete 的 find,把匹配到的文件列表看一遍再执行真正的删除。这多花十秒,但能避免错删线上数据。
5. 没有界面的排障:查看端口、连接与路由的一套思路
5.1 用 ss 而不是 netstat:查看端口监听状态
遇到“端口起不来”“端口被占用”“监听了但外网连不上”这类问题,首选命令是 ss,新一代系统里 netstat 可能都没装,但 ss 是 iproute2 包自带的:
bash复制# 查看所有监听端口
ss -tlnp
# 查看某个端口的连接情况
ss -tan | grep :8080
# 查看所有 TCP 连接状态统计(看 SYN_SENT、TIME_WAIT 数量)
ss -s
ss -tlnp 输出里的 State 列,监听状态是 LISTEN,已建立的连接是 ESTAB。如果端口没出现在监听列表里,说明服务可能没起来;如果监听在 127.0.0.1 而不是 0.0.0.0 或具体内网 IP,则外部无法访问。
这个我印象很深:有一次部署一个内部工具,服务启动日志一切正常,但同事反映网页打不开。我用 ss -tlnp 一看,进程监听的是 127.0.0.1:8080,而服务器内网 IP 是 10.0.x.x,外部流量根本到不了。原因就是服务配置里绑定了 localhost。这种问题不熟悉 ss 的话,可能要折腾很久才能定位。
TIME_WAIT 状态连接过多也是常见问题。短连接请求量大的服务,ss -s 里 TIME_WAIT 可能上万,本身不算故障,但会占用端口资源。如果系统层面 net.ipv4.ip_local_port_range 范围又比较小,就容易出现“Cannot assign requested address”错误。
5.2 curl 的五种使用姿势:接口排查实用技巧
排查 HTTP 接口问题时,curl 是最趁手的工具。但很多人只会 curl http://xxx,其实它的调试能力远不止于此:
bash复制# 只查看响应头,不下载正文
curl -I http://example.com
# 完整显示请求和响应的头信息与正文
curl -v http://example.com/api/health
# 指定超时时间(5 秒连接超时,10 秒最大传输时间),防止卡死
curl --connect-timeout 5 --max-time 10 http://example.com
# 携带请求头
curl -H "Content-Type: application/json" -H "Authorization: Bearer token" http://example.com/api
# 发送 JSON POST 请求
curl -X POST http://example.com/api/user -d '{"name":"test"}'
-v 输出里值得关注的是 Connected to 表示 TCP 连接成功,HTTP/1.1 200 OK 之前的 > 开头行是请求头,< 开头行是响应头。如果 Connected to 之前卡了很久,通常是 DNS 解析慢;如果连接成功但迟迟收不到响应,一般是服务端处理慢或防火墙拦截了响应。
5.3 你连不上服务,先分清楚是 DNS、TCP 还是防火墙问题
网络排查有个基本思路:从协议栈底层往上层一层层排除。
第一步,ping 网关或目标 IP,确认链路通不通。但不建议把 ping 作为最终依据,因为很多云环境默认禁 ICMP,ping 不通不代表端口不通。
第二步,telnet 或 nc 测试端口:
bash复制# 测试目标端口是否开放(直到出现 Connected 或超时)
telnet 192.168.1.100 3306
# 或者用 nc 测试,-v 显示详细信息,-w 3 指定超时 3 秒
nc -vz -w 3 192.168.1.100 3306
第三步,如果端口通但业务异常,用 curl 继续测 HTTP。如果不通,看防火墙:
bash复制# 查看 firewalld 规则(CentOS/RHEL 系)
firewall-cmd --list-all
# 查看 iptables 规则
iptables -L -n -v
# 查看 ufw 状态(Debian/Ubuntu 系)
ufw status verbose
还有个容易被忽略的点:云厂商安全组。很多人在服务器本地开了端口、防火墙也放行了,但外面还是连不上,最后发现是云控制台安全组没放行对应端口。这类问题在云化环境排障中占比极高。
5.4 路由和 DNS:traceroute 与 dig 的正确用法
如果网络延迟高、丢包严重,用 traceroute 定位是哪个节点出了问题:
bash复制# 安装并测试到目标地址的路由路径(多数发行版需要安装 inetutils-traceroute 或 traceroute 包)
traceroute -n 8.8.8.8
# 不分片探测,防止路径上 MTU 问题导致的丢包(很实用)
traceroute -F -n 目标IP
输出中每一行代表一跳,* * * 表示该跳没有响应(可能是设备屏蔽了探测包,不一定代表故障),但如果连续多跳出现高延迟,基本可以定位到网络瓶颈。
DNS 排查用 dig 比 nslookup 信息更全:
bash复制# 查询 A 记录,并显示查询耗时
dig example.com +noall +answer +stats
# 指定 DNS 服务器查询
dig @8.8.8.8 example.com
# 反向查询 IP 对应的域名
dig -x 8.8.8.8
如果 dig 能正常解析,但应用报域名解析失败,很可能是应用自己的 resolver 配置问题(比如 Java 的 InetAddress 缓存或者 nsswitch.conf 配置),这点和系统层面 DNS 正常与否是两回事。
6. 查问题最后都靠它:日志滚动与动态追踪的命令组合
6.1 journalctl:systemd 时代看日志的正确姿势
现在主流发行版都用 systemd,日志查看方式也变了。journalctl 是我推荐新手优先掌握的日志命令:
bash复制# 查看某个服务的全部日志
journalctl -u nginx
# 实时跟踪日志输出,类似 tail -f
journalctl -u nginx -f
# 查看最近 1 小时的日志
journalctl -u nginx --since "1 hour ago"
# 查看指定时间段的日志
journalctl -u nginx --since "2025-01-01 08:00:00" --until "2025-01-01 12:00:00"
# 按优先级过滤(err=0..6,0 是紧急,3 是错误,4 是警告)
journalctl -u nginx -p err -n 50
# 查看上一次启动到现在的日志(排查开机问题,比 last 更直观)
journalctl -b
journalctl 的一个优势是日志是有结构的,可以用 -o json-pretty 输出 JSON 格式,配合脚本做分析很方便。缺点是在高并发服务上日志量非常大,如果磁盘空间紧张,要留意 journal 文件大小。默认情况下 journal 可能占满 /var/log/journal,长时间不清理会影响系统盘空间。我一般会设置按大小和保留时间双限制:
bash复制# 查看当前 journal 占用
journalctl --disk-usage
# 限制 journal 最多保留 500MB(这行命令会立即生效)
journalctl --vacuum-size=500M
# 限制只保留最近 7 天日志(同样立即生效)
journalctl --vacuum-time=7d
如果想永久限制,编辑 /etc/systemd/journald.conf,设置 SystemMaxUse=500M,然后 systemctl restart systemd-journald。现在很多服务器空间也不宽裕,这个细节很实用。
6.2 tail、grep 与 less:日志文件处理的黄金组合
传统日志文件场景(/var/log/nginx/access.log、/var/log/app/application.log)依然是主流。查看日志最常用的方法,组合起来效率极高:
bash复制# 实时跟踪最后 200 行
tail -200f /var/log/app/application.log
# 文件较大时,不要直接 vim,用 less 更流畅,且可以搜索
less /var/log/app/application.log
# 在 less 中按 / 搜索关键字,按 n 跳转到下一个匹配,按 G 跳到文件末尾
# 按关键字过滤并显示行号
grep -n "ERROR" /var/log/app/application.log
# 查看匹配行及后面 5 行(A=after),和前面 5 行(B=before)
grep -n -A 5 -B 5 "OutOfMemoryError" /var/log/app/application.log
tail -f 有个限制是只能跟踪到当前文件尾,如果日志发生轮转(access.log 变成 access.log.1),tail -f 会继续读旧文件。这时候用 tail -F(大写 F),它会根据文件名重新打开文件,自动跟进轮转。日常排查我几乎只用 tail -F。
6.3 dmesg:系统内核日志里藏着 OOM 和硬件问题
很多棘手问题在应用日志里看不到,却在系统内核日志里有线索。dmesg 命令专门看内核环形缓冲区消息:
bash复制# 查看最近的内核日志,分页查看方便定位
dmesg -T | less
# 查看 OOM Killer 相关记录(区分大小写)
dmesg -T | grep -i "out of memory"
# 查看硬盘错误(定位磁盘损坏)
dmesg -T | grep -i "error\|i/o error"
# 实时跟踪内核日志
dmesg -T -w
-T 参数把时间戳转换成可读时间格式,这个很重要,否则默认输出的是系统启动以来的秒数。之前排查过一次突发的 Java 进程被杀死,应用日志和系统服务日志都没有异常记录,最终在 dmesg 里看到 OOM Killer 记录:某个进程占用了大量内存,触发内核回收机制把它杀了。如果只看应用日志,这个问题会非常难定位。
6.4 strace:追查程序行为异常的“终极工具”
当所有常规手段都用完、仍然查不到程序为何行为怪异时,strace 往往能给出答案。它跟踪进程发起的系统调用和收到的信号:
bash复制# 跟踪一个正在运行的进程的所有系统调用
strace -p <PID>
# 跟踪并显示耗时超过 1 秒的系统调用(定位卡顿很有用)
strace -p <PID> -f -T -e trace=all -o /tmp/strace.log
grep -E "<[0-9]+\.[0-9]+>" /tmp/strace.log | sort -t'<' -k2 -rn | head -20
# 跟踪文件操作(查看程序在读取哪些文件、有没有权限问题)
strace -e trace=file -p <PID>
# 程序启动时跟踪,输出到文件
strace -f -o /tmp/app_strace.log ./your_app
strace 的输出信息量很大且难读,但几个关键信号很直观:ENOENT 表示文件不存在,EACCES 表示权限不足,ETIMEDOUT 表示超时。如果应用启动时报“成功”但实际没干活,跟踪一下启动时打开的文件就够了。
以前排查过一个奇怪问题:某个服务进程莫名其妙地不执行定时任务了,配置和代码看着都没问题。用 strace 跟踪后发现它尝试读取一个配置文件时路径拼错了,open() 返回 ENOENT,代码里没有对这种情况做校验,于是静默跳过了任务。如果没有 strace,这种逻辑层面的隐蔽问题很难凭空猜出来。
6.5 日志轮转 logrotate:别等日志占满磁盘才想起来
日志不清理,迟早要出事。Linux 下 logrotate 是处理日志轮转的标准工具,系统自带的 rsyslog、Nginx、Apache 等都有对应的 logrotate 配置。
一个典型的 Nginx 日志轮转配置在 /etc/logrotate.d/nginx:
nginx复制/var/log/nginx/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
create 0640 nginx adm
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
endscript
}
解释一下各个指令的意义:
daily:每天轮转一次,也可以改weekly或size +100M(达到指定大小才轮转)。rotate 14:保留最近 14 份轮转后的日志。compress:轮转后压缩成.gz(gzip 压缩),省空间。delaycompress:延迟压缩一天,方便刚轮转完时还能直接查看日志。missingok:日志文件不存在时不报错。notifempty:空日志文件不轮转。create 0640 nginx adm:轮转后新创建日志文件,指定权限和属主属组。postrotate脚本:在轮转完成后向 Nginx 发送USR1信号,让 Nginx 重新打开日志文件句柄,避免继续往旧文件里写。
这段配置我用了很久,因为很多应用日志是需要轮转的,尤其是 Java 应用自己可能还有一套 logback/log4j 的滚动方案,两者要协调好,否则可能冲突成双份轮转或者空间没释放。每接触一个新服务,我第一件事就是检查它的日志目录和 logrotate 配置是否匹配。
7. 建号、部署、查故障的小团队实用流程
文章最后分享一个我实际工作中沉淀下来的小流程,把前面几组命令串成一个可复用的操作序列。小团队在部署一个全新服务时,按照这个流程走,大部分问题能在 10 分钟内定位。
第一步,建号并锁定权限:
bash复制useradd -m -d /home/app -s /bin/bash app
echo "RandomStrongPass_2025" | passwd --stdin app
# 如果服务不需要登录
usermod -s /sbin/nologin app
第二步,准备目录并检查挂载:
bash复制mkdir -p /data/app/{logs,conf,bin}
df -h /data/app
chown -R app:app /data/app
第三步,启动服务后立刻检查状态:
bash复制ss -tlnp | grep <服务端口>
ps -ef | grep <服务名>
journalctl -u <服务名> -n 50 --no-pager
第四步,验证外部访问是否正常(在另一台机器执行):
bash复制curl -v http://<服务器IP>:<端口>/health
如果最后一步失败了,按之前的排查链路走:先 ping IP,再 nc -vz 测端口,再 ss 看本地监听,再查防火墙和安全组。这套流程走完,八成情况都能锁定问题所在。
我自己的习惯是,所有服务器上都会预先安装 lsof、strace、tcpdump、iotop 这类排查工具。很多 CentOS 精简安装镜像上这些命令未必自带,等到出故障时再装既慢又可能临时找不到源。趁早装好,有备无患。
还有一个小技巧:把常用排查命令写成 shell 函数放到 .bashrc 里,能节省大量敲命令的时间。比如:
bash复制# 查看端口占用详情
ports() { ss -tlnp | grep -E "LISTEN|$1"; }
# 查看进程启动时间和完整命令
pinfo() { ps -eo pid,lstart,cmd | grep -E "PID|$1" | grep -v grep; }
# 查看最近 100 行错误日志(自动适配常见日志路径)
elog() { tail -100 /var/log/$1*.log | grep -iE "error|exception|fail"; }
这些函数看着不起眼,但每天排查问题时能省下不少重复劳动。
Linux 命令说到底是工具,工具的价值在于解决问题,而不在于背得全。真正关键的是在排查问题时能想到用哪个工具、怎么组合起来定位根因。这篇挑选的每一组命令,都是我在实际环境中反复用过、验证过有效的方法,希望能给正在学习 Linux 的朋友提供一些不一样的思路。你踩过的那些命令坑,大概率也值得记录下来,下次遇到类似问题就能少走很多弯路。
