Linux日常运维与故障排查:从会敲命令到能独立扛事 标题里那个拼写错误我索性不改了反正圈里人一眼就知道说的是 Linux。这一章原本是我给自己整理的学习笔记最早只是一堆命令的罗列后来发现根本没法用——真到了机器上出问题脑子里跳出来的不是命令而是“从哪儿下手”。所以我把这一章整个推翻重写按“遇到事怎么办”的逻辑重新排了一遍。这里聊的内容主要围绕 Linux 的日常使用与运维基础文件系统与权限、进程与服务管理、网络与日志排障这几块。适合刚把常用命令混熟、想往“能独立处理问题”迈一步的人也适合被各种零散笔记绕晕、想找一条主线把知识串起来的朋友。我不会逐条讲命令手册那种东西查文档更准我更想说的是每一步为什么这么设计、哪些坑我实打实踩过、以及怎么把这套东西用在真机器上。1. 这一章到底讲什么从“会敲命令”到“能扛事”我在最早的笔记版本里第一章写的是“Linux 简介”第二章写“常用命令”第三章写“软件安装”。看起来挺规整实际用起来完全是另一回事真遇到一个服务起不来我既想不起它在第二章的哪一页也不知道该看进程还是看日志。知识按“软件分类”归档在故障面前等于零。这一章重写的时候我做的第一件事就是换归档维度从“这是什么东西”换成“这是哪一类问题”。1.1 学习目标的重排把知识按“故障场景”归档我把日常碰到的问题粗分成三类权限类、资源类、连通类。权限类就是“明明文件在那儿就是读不了、写不进、跑不起来”资源类就是“机器没死但慢得像蜗牛”连通类就是“本机看着好好的外面就是连不上”。这三类基本覆盖了我八成以上的实际故障。按这个维度归档后很多零散的知识点就自然归位了目录结构其实服务于权限与路径查找进程与服务服务于资源定位网络与日志服务于连通性判断。这么排还有一个好处就是复习的时候有抓手。我不再问自己“ext4 和 xfs 有什么区别”而是问“磁盘满了怎么最快定位到是谁在占空间”。前者是知识后者是能力。日常真正被考验的永远是后者。顺带一提这种按场景归档的思路不只适用于 Linux任何一套偏操作性的技术栈都可以这么整理效果比按目录抄书好得多。1.2 我给自己定的三条验收标准学完这一章我给自己设了三条验收线这里也分享出来你可以拿它当自测第一给我一台干净的机器我能在不查资料的情况下完成用户创建、目录授权、服务拉起、日志查看这一条链路第二随便丢一个“服务起不来”的场景给我我能在十分钟内把问题缩小到权限、配置、端口、依赖这四个方向之一第三我能说清楚每一条命令的参数为什么这么写而不是靠默认值蒙过去。这三条看起来要求不高实际做起来第二条最容易卡住。原因不是命令不会而是没有形成固定的排查顺序。人在着急的时候容易乱试试一个不行再换一个越试越乱。所以我在下面几节里会尽量把每一步的“意图”写清楚这条命令是用来排除什么的如果输出是某种样子说明问题在哪个方向。有了意图命令才不是孤立的。2. 文件系统与权限先让地基不塌权限问题是我见过最多的“低级但致命”故障。说它低级是因为原理简单说它致命是因为一旦搞错服务直接起不来而且报错往往不在权限本身而是在别的地方绕一圈才暴露出来。这一节我把目录结构、权限数字、特殊权限拆开讲重点放在“为什么这么设计”和“什么时候会踩坑”。2.1 目录结构每个路径都对应一个真实需求很多人背过 FHS 目录规范背完就忘。我后来换了个记法把每个目录和它解决的需求绑在一起。/bin和/sbin是系统启动和单用户模式下就要用的命令所以它们必须能在没有其他分区挂载的情况下访问这也是为什么现在很多发行版把它们合并成指向/usr/bin的软链本质是减少根分区的重复占用。/etc放配置是因为配置需要随系统一起备份和迁移跟二进制分开才方便。/var放会变化的数据日志、缓存、数据库文件都在里面单独分区的理由是防止日志写爆把根分区撑死。/home和/root的区别也挺有意思。/home下是普通用户的家目录通常单独分区方便重装系统时保留数据/root是管理员的家目录故意不放在/home下面就是为了在/home没挂载或者出问题的时候管理员依然能登录并拿到自己的配置和工具。这个设计细节我第一次注意到的时候觉得挺妙——它不是随手定的是考虑过故障场景的。/tmp值得单独说两句。很多老系统会定期清理/tmp现在主流发行版多数用tmpfs挂载内存文件系统重启就没了。这带来的直接后果是你把服务需要的临时文件放/tmp重启后文件消失服务就报“找不到文件”。我吃过一次亏一个脚本把 pid 文件写在/tmp结果机器重启后脚本判断进程是否在跑时永远失败最后改成写到/run才稳定。/run就是给运行时状态准备的pid 文件、socket 文件放这儿才符合规范。2.2 权限数字的算术rwx、umask 与继承权限这块rwx对应 4、2、1 的加法大家都熟但真正容易错的是umask。umask不是“减去”的权限而是“屏蔽”的权限。新建目录的默认权限是 777新建文件默认是 666文件默认不给执行位然后减去umask里设置的位。比如umask是 022目录就是 755文件就是 644。这个计算过程我建议你亲手算几遍别只记结论因为一旦umask设成 027很多人就反应不过来了目录变成 750文件变成 640。umask的坑在于它的作用范围。它只影响当前 shell 及其子进程创建的新文件对已有文件无效对cp也不一定有效——cp默认会尽量保留源文件的权限除非你用-p之外的参数去控制。我见过有人改了umask之后发现cp出来的文件权限还是不对以为是umask没生效其实是cp的行为不同。这种“看起来是权限问题实际是工具行为”的情况特别多排查时一定要先确认到底是哪一步把权限改掉的。还有一个常被忽略的点是目录的执行位。目录的x位不是“能执行”而是“能进入”。一个目录如果没有x位哪怕你有r位能列出目录内容也无法cd进去更无法访问里面的文件。反过来只有x没有r你可以进入并访问已知路径的文件但没法列目录。这个组合在实际授权里很有用比如给某个用户访问特定文件的权限但不想让他看到目录里还有什么别的东西。2.3 特殊权限与 ACL三个必须用对的场景特殊权限里SUID、SGID、Sticky三个最容易混。SUID用在可执行文件上作用是“以文件属主的身份运行”最典型的就是passwd普通用户改自己的密码需要写/etc/shadow靠的就是passwd的SUID位。SGID用在目录上作用是“新建文件继承目录的属组”团队协作目录几乎必用不然每个人创建的文件属组都是自己的主组别人想改就得靠chmod ow那等于把门全开了。Sticky位用在目录上作用是“只有文件属主或 root 能删除文件”最典型的是/tmp。你可以去看看/tmp的权限通常是1777那个开头的 1 就是Sticky位。如果没有它任何用户都能删掉别人在/tmp里的文件这在多用户环境下是灾难。ACL 是在传统三组权限之外的补充。什么时候必须用当你要给一个用户单独的权限又不想把他塞进属组也不想改文件属主的时候。比如某个日志文件要给一个监控账号读但这个账号不属于任何业务组用setfacl -m u:monitor:r-- file.log一行就搞定。这里提醒一句ACL 不是所有文件系统都默认开启挂载参数里需要有acl较新的发行版基本默认支持但老环境最好先用getfacl确认一下别写完发现没生效还找不到原因。3. 进程与服务看清楚谁在跑、谁在拖后腿进程这块我最初的理解停留在“ps aux看到一堆东西”。真正让我开窍的是理解了进程之间的父子关系和状态字段一个进程卡住往往不是它自己的问题而是它在等一个永远等不到的资源或者它父进程已经没了变成孤儿。这一节我想把“看进程”这件事讲清楚同时把服务管理的新旧两套体系拉出来对比一下。3.1 进程状态与父子关系一张表看懂 ps 输出ps输出里最值得盯的是STAT这一列。R是运行或可运行S是可中断睡眠通常是等 IO 或等事件D是不可中断睡眠通常是在等磁盘 IO这个状态最麻烦因为它连kill -9都不一定响应。Z是僵尸进程已经结束但父进程还没回收它的退出状态。看到Z不要慌僵尸本身不占 CPU占的只是一个进程表项真正要处理的是它的父进程。我遇到过一次D状态的进程现象是kill不掉机器负载很高但 CPU 使用率不高。当时第一反应是进程有问题后来用iostat一看磁盘的%util已经接近 100await很高问题在存储层不在进程本身。这件事让我记住一个原则D状态优先怀疑 IOR状态多且 CPU 高才怀疑计算S状态多一般是正常的不用管。父子关系可以用pstree -p看也可以ps -ef看PPID列。这个信息在排查“进程莫名其妙重启”时特别有用如果一个进程反复出现又消失先看它是不是被某个守护进程拉起来的比如 systemd、supervisor 或者某个脚本里的while true循环。找到父进程才知道该去改哪儿的配置。3.2 systemd 与 init 脚本的取舍服务管理这块老环境可能还在用 SysV init 脚本新环境基本都是 systemd。我不打算站队说哪个好只说实际使用中的差异。SysV 脚本本质是一个 shell 脚本启动逻辑写在里面灵活但每家的写法都不一样排查时得读脚本。systemd 用 unit 文件描述服务字段固定systemctl status的输出格式也统一出问题时能快速看到最近的日志片段这一点在应急时很省时间。写 unit 文件有几个参数值得注意。Type决定 systemd 怎么判断服务启动成功simple是 fork 出去就算启动forking是等父进程退出notify是等应用主动发通知。选错了会出现“服务明明在跑systemctl 却显示 failed”或者反过来的情况。Restart控制退出后是否重启RestartSec控制重启间隔这两个配好了能省不少手动拉起的工作但间隔别设太短不然服务本身有问题时会疯狂重启日志刷屏还拖慢机器。ExecStart里不要写相对路径也不要用 shell 的通配符systemd 不经过 shell 解析。我见过有人在ExecStart里写*想匹配文件结果服务起不来还找不到原因。需要 shell 特性的时候老老实实写ExecStart/bin/bash -c ...虽然不优雅但能跑。3.3 资源排查CPU、内存、IO 的分线处理资源排查我的习惯是先看整体再看个体。整体用top或htop重点看三个数负载、CPU 使用率、内存可用量。负载高但 CPU 不高多半是 IO 或进程在等锁CPU 高要看是用户态还是内核态用户态高是应用在算内核态高可能是系统调用频繁或者中断多内存要看available而不是freefree那一列基本没参考价值缓存也算可用内存。定位到具体进程后pidstat比top好用因为它能按进程持续采样看出趋势而不是某一瞬间。内存方面pmap可以看进程的内存映射排查内存泄漏时有用但更常用的是看/proc/pid/status里的VmRSS和VmSwap。如果VmSwap很大说明这个进程被换出得厉害性能肯定受影响。IO 方面iostat -x 1看%util和awaitiotop看是哪个进程在读写。这里分享一个我自己的顺序先uptime看负载趋势再top按 CPU 排序看热点再iostat看是不是 IO 瓶颈最后才针对具体进程用strace或perf深挖。顺序反了容易陷进细节花很多时间在一个其实不是瓶颈的进程上。这个顺序不是标准答案但它能保证每一步都在缩小范围而不是扩大范围。4. 网络与日志把问题缩到一个点网络问题最怕一上来就抓包。抓包不是不能用而是成本高、信息量大没方向的时候抓了也看不懂。我的做法是先分层把问题卡在某一层再决定要不要上重武器。日志这块也是一样关键是知道该看哪个日志、用什么过滤而不是把所有日志翻一遍。4.1 分层排障从网卡到端口到应用我的分层顺序是网卡有没有起来、IP 和路由对不对、端口有没有监听、防火墙有没有拦、应用本身有没有报错。对应的命令依次是ip addr、ip route、ss -lntp、防火墙规则查看、应用日志。这个顺序的好处是每一步的结论都很明确不会出现“好像都正常”的模糊状态。举一个实际场景。有一次一台机器上的服务本机curl能通外面连不上。按顺序走ip addr正常ip route正常ss -lntp显示服务监听在127.0.0.1:8080问题直接定位——服务只监听了回环地址外部当然连不上。这种情况改配置文件里的监听地址就行根本不用抓包。如果当时一上来就tcpdump看到一堆包但不知道问题在绑定地址上可能还要绕很久。再举一个容易误判的ss显示端口在监听防火墙也放行了但外部还是连不上。这时候要看云环境的安全组或者上游的负载均衡配置这些不在本机层面本机怎么查都是正常的。所以分层排查要包括“本机之外”的那一层不要默认问题一定在机器上。4.2 日志抓取journalctl 加文件日志的组合拳systemd 环境下journalctl是第一入口。journalctl -u 服务名 -n 100看最近一百行-f实时跟踪--since 10 min ago限定时间范围。时间范围这个参数特别好用故障发生后再回头看能直接把噪音滤掉。-p err只看错误级别适合快速扫一遍有没有明显报错。但journalctl不是万能的。很多应用有自己的日志文件写在/var/log下面或者应用目录里这些journalctl看不到。所以实际排查时我是两条线并行先journalctl -u看服务管理层面的信息比如启动失败、退出码、重启记录再看应用日志看业务层面的报错。两边的时间戳对一下往往能拼出完整的故事。日志轮转也值得提一句。logrotate配置不当会导致日志要么不切分把磁盘撑满要么切分后应用还往旧文件句柄写新文件一直是空的。判断方法很简单lsof | grep deleted看有没有已删除但还被占用的文件如果有且大小很大就是句柄没释放。解决办法通常是让应用重新打开日志文件或者用copytruncate模式轮转代价是切分瞬间可能丢少量日志。4.3 定时任务与自启动那些“重启就失效”的坑定时任务这块crontab的坑集中在环境和路径上。cron执行时的环境变量跟登录 shell 完全不一样PATH通常只有很短的一截所以脚本里用相对路径或者依赖用户环境变量的命令经常失败。我的习惯是脚本里一律写绝对路径需要环境变量就在脚本开头自己export不指望继承。还有一个经典问题手动执行脚本没问题cron执行就失败。九成是环境差异剩下的一成是权限或者工作目录。排查方法是在crontab里把输出重定向到文件比如 /tmp/cron.log 21跑一次看日志里报什么。这个笨办法比猜有效得多。开机自启动这块现在推荐用 systemd 的enable而不是往/etc/rc.local里塞命令。rc.local在新系统上默认可能不存在就算存在执行时机和依赖关系也不好控制。用 unit 文件可以声明After和Requires明确依赖顺序出问题时systemctl status也能看到状态。这一点在有多项服务互相依赖的环境里特别重要顺序错了就是间歇性启动失败很难查。5. 常见问题与排查技巧实录这一节是我这些年攒下来的问题清单按“现象、可能原因、怎么查”整理成表方便你直接对照。表后面再补几条我觉得最有价值的实操心得都是踩过之后才记住的。5.1 高频故障速查表现象常见原因排查入口服务启动失败无明确报错unit 文件Type配错、依赖未就绪systemctl status、journalctl -u文件存在但读不了目录缺x位、ACL 未生效、SELinux 拦截namei -l 路径、getfacl、审计日志磁盘满了但找不到大文件已删除文件仍被进程占用lsof | grep deleted、du -sh逐层定位机器负载高但 CPU 不高IO 等待、D状态进程iostat -x 1、ps看STAT本机能通外部不通监听地址、防火墙、上游安全组ss -lntp、规则检查定时任务不执行环境变量、路径、权限重定向输出到日志文件重启后配置丢失配置写在临时目录、未持久化检查/tmp、/run的使用日志文件不更新轮转后句柄未释放lsof查占用、调整轮转模式这张表我自己是打印出来贴在工位上的后来发现最有用的不是表本身而是“看到现象先对表”这个动作。人在故障现场容易慌慌就容易乱试对一下表能把思路拉回正轨。5.2 几条踩出来的实操心得第一条改配置之前先备份而且要备份到一个不会被自己误删的地方。我吃过一次亏改sshd配置改错重启服务直接把自己锁在外面最后只能走控制台救回来。从那以后我改这类关键配置都是先cp一份带时间戳的备份改完先sshd -t测试语法确认没问题再重载绝不直接重启。第二条rm -rf之前先ls一遍。这个习惯听起来很笨但真能救命。尤其是脚本里带变量的路径变量为空的时候rm -rf $DIR/*会变成rm -rf /*后果不用多说。稳妥的写法是脚本开头加set -u变量未定义直接报错退出再配合路径判空检查。第三条遇到“时好时坏”的问题优先怀疑并发、资源竞争和超时而不是代码逻辑。逻辑错误通常是稳定复现的间歇性故障多半跟时序有关。这时候日志里的时间戳就特别关键把两次成功和失败的时间点拉出来对比往往能看出差异在哪儿——比如是不是都发生在整点、是不是都在某个批量任务跑的时候。第四条不要迷信“重启解决一切”。重启确实能让服务恢复但它把现场也一起清掉了下次再出同样的问题你还是不知道原因。我的做法是先尽量保留现场该导的日志、该存的进程状态先存下来再重启恢复业务。业务恢复和根因排查是两件事顺序别搞反但也不能只做前者。最后分享一个小技巧关于查命令来自哪个包。有时候一台机器上某个命令不存在你也不知道该装哪个包用yum provides或者apt-file search能反查。如果是已经装了的命令想看它属于哪个包rpm -qf或dpkg -S直接给答案。这个技巧在处理“这个环境跟那个环境不一样”的问题时特别省事比一个个试装快得多。