
书接上回。前一篇讲完了 Linux 第 11 讲上半部分里关于日常命令与文件操作的内容这篇继续往下走聊几个我在实际干活里觉得更吃经验的部分权限位的冷门用法、磁盘与文件系统在真实故障下的处理、网络问题的定位顺序以及用 systemd 管服务时那些文档里一笔带过但坑死人的细节。这一篇不是命令大全更像是我自己踩过坑之后整理的一份操作笔记。如果你已经熟悉 ls、cd、grep 这些基础操作想往系统管理深处再走一步这篇应该能帮上忙。1. 权限体系里最容易被忽略的三个特殊位1.1 SUID、SGID、Sticky Bit 到底解决了什么问题很多人在学权限的时候注意力都放在 rwx 这三组权限上觉得把 owner、group、other 搞明白就完事了。但真正到了生产环境特殊权限位才是最容易让人挠头的地方。先说 SUIDSet User ID。它最经典的体现就是/usr/bin/passwd这个命令。普通用户执行 passwd 修改自己的密码时需要去写/etc/shadow这个文件而 shadow 文件默认只有 root 才能读写。如果没有任何特殊机制普通用户这辈子都没法改密码。SUID 的作用是当用户执行某个带有 SUID 位的程序时这个进程的有效用户 ID 会临时变成文件属主的 ID而不是执行者自己的 ID。所以 passwd 文件属主是 root普通用户执行它时进程就有了 root 身份去操作 shadow任务完成后再退出。SGID 的逻辑类似只不过作用对象从用户变成了用户组。它有两种常见场景如果 SGID 设置在文件上执行者会临时获得文件属组的权限更常见的是设置在目录上比如某个团队协作目录属组设置为 devgroup并打上 SGID那么任何人在这个目录下新建文件文件所属组都会自动继承目录的属组而不是创建者的默认组。这样团队内部共享文件时就省去了频繁 chgrp 的麻烦。Sticky Bit粘滞位就更好理解了。典型场景是/tmp目录任何人都有写权限但我创建的文件只有我能删你创建的文件我动不了。如果不设粘滞位在一个 everyone 可写的目录里任何有写权限的人都能删掉别人的文件这在多用户环境下就是个灾难。1.2 排查真实环境里的特殊位问题有一回我在处理一个应用报错时发现服务启动后生成了文件属主变成了 root导致应用进程无法写入自己的日志目录。查了一圈发现运维同事为了省事把整个应用目录直接配上了递归 SUID新创建的子目录和文件全带上了继承的权限而这个应用本身需要以普通用户身份写日志结果就被卡住了。那个排查过程让我记住一件事看到文件权限时第一反应不只要看 rwx还要看权限位里有没有出现s和t。ls -l显示-rwsr-xr-x这种形式说明 SUID 被设置了目录权限是drwxrwxrwt说明 Sticky Bit 生效。s出现在 user 的 x 位就是 SUID出现在 group 的 x 位就是 SGID如果原来是 x 位会显示小写 s如果原来没有 x 位则显示大写 S实际意思是设置了特殊位但没有执行权限这种一般属于配置不合理。排查特殊位用find也很快find /usr -perm -4000 -type f 2/dev/null find / -perm -2000 -type d 2/dev/null一个系统的安全基线里对 SUID 文件通常是有严格清单的哪个文件该有、哪个不该有心里得有数。如果发现陌生路径下有 SUID 文件基本可以怀疑是有问题的程序被丢进来了需要立刻确认来源。1.3 umask 对新建文件权限的影响比你想的大权限位的另一块隐藏区域是 umask。很多人都知道执行umask能看到一个数字比如 022但真正理解它对文件创建影响的人不多。默认情况下普通文件的最大权限是 666目录是 777然后用 umask 值去掉对应位。umask 022 意味着去掉 group 和 other 的写权限所以新建文件变成 644目录变成 755。这里有个特别容易误判的地方很多人以为 umask 0022 就是新建文件权限为 755这是错的新建的是目录才是 755普通文件只能是 644。因为普通文件不默认加执行权限。在设置服务运行目录时umask 直接决定了日志文件是否会被其他用户读取。如果服务涉及敏感数据建议在服务启动脚本里显式设置umask 077这样新生成的日志文件就只有属主能读写避免因为默认 umask 过宽导致的信息泄露。还有一个细节修改 umask 只影响当前 shell 及后续派生的进程想让某个服务固定使用某个 umask最好是写在它的启动脚本或 systemd unit 文件里而不是指望登录 shell 的配置。2. 文件系统扩容与回收LVM 场景下的完整操作链2.1 为什么推荐你装系统时就规划好 LVM我见过不少服务器装系统时图省事直接给根分区划了一个固定大小等跑了一年发现数据盘满了而根分区旁边还躺着一大块空闲空间没被分配。这时候如果当初没用 LVM扩根分区的操作会非常痛苦要么用分区工具在线调整风险很高要么停机用救援模式操作业务就得中断。所以只要不是特殊需求装系统时我都会建议把主要分区放进 LVM 里。LVM 的架构一句话概括就是把物理分区或整块磁盘标记为物理卷PV再把这些 PV 合入一个卷组VG最后在 VG 里按需切割出逻辑卷LV来格式化使用。文件系统看到的只是一个 block 设备底层切多大、怎么分布都是 LVM 的事。这个中间层就是灵活性所在。查看当前 LVM 布局的三条命令很常用pvs # 查看物理卷 vgs # 查看卷组重点看剩余空间 lvs # 查看逻辑卷重点看谁快满了如果vgs显示 VFree 那一列还有很多剩余空间说明卷组还没分完扩容只需要把空闲空间划给对应的逻辑卷即可如果 VFree 是 0那就得先加物理盘再 pvcreate、vgextend把新空间并入卷组。这两种情况的操作流程完全不同建议操作前先vgs看清楚再动手。2.2 在线扩容一个逻辑卷的完整过程这里模拟一个最常见的场景/data挂载在逻辑卷/dev/vgdata/lvdata上使用 ext4 文件系统现在空间不够了卷组里还有空闲。先扩展逻辑卷把卷组剩余空间全部给/datalvextend -l 100%FREE /dev/vgdata/lvdata-l 100%FREE表示把卷组所有剩余空间都划入这个逻辑卷。如果只想扩大 100G用-L 100G也行。注意这只是把逻辑卷本身扩大了文件系统并没有感知到。分水岭在这里ext4 和 xfs 的在线扩容命令不同。ext4 用resize2fs /dev/vgdata/lvdataxfs 用xfs_growfs /mount/pointxfs 的扩容参数是挂载点不是块设备路径这个细节杀了很多人。我见过有人对着 xfs 文件系统执行resize2fs结果直接报错提示无法识别文件系统这才反应过来没看当初格式化用的什么类型。建议操作前先看一眼/etc/fstab或者df -T确认文件系统类型再选命令。扩完后用df -h验证一下容量逻辑卷和文件系统两边都更新了才算彻底完成。整个过程在线进行业务不需要停。2.3 磁盘满排查df 显示的 100% 不一定是真的没空间有段时间我接到一个告警说一个应用服务器的根分区使用率到了 100%服务马上就要写不进日志了。我登上去先df -h确实显示/已用 100%。但当我执行du -sh / 2/dev/null想找大目录时发现所有目录加起来远不到总容量。这说明什么问题经典的场景是文件被删除但进程仍然持有 fd。也就是说某个进程打开了日志文件日志被rm了但进程没关、fd 没释放磁盘空间实际上还被占着。这时候df依然满但du找不到这个文件因为它已经不在目录树里了。排查方法是用lsof找出被删除但仍被占用的文件lsof L1 | grep deletedL1表示显示 link count 小于 1 的文件也就是已经被删除但仍打开的文件。找到进程后重启该服务或者向进程发送恰当的信号让它重新打开文件句柄空间才会真正释放。生产环境里处理这个问题时一定要先确认这是哪个业务的进程不能随手 kill最好先和业务方沟通找一个可以重启的窗口。还有一个容易被忽略的满盘原因inode 耗尽。df -h显示空间还有剩余但df -i显示 inode 使用率 100%。inode 是文件系统里记录文件元数据的结构每个文件或目录都要消耗一个 inode。当某个目录下堆积了大量小文件时比如缓存目录、消息队列临时文件inode 会被快速消耗完这时即使磁盘有空间系统也无法再创建任何新文件。排查方向是找出大目录里的小文件数量find /data -type f | wc -l find /var/spool -type f | wc -l定位到堆积目录后和业务方确认哪些文件可以清理清理后 inode 数会明显下降。3. 网络排查不是背命令一条异常链路的问题定位顺序3.1 从底层到顶层逐层确认而不是乱猜很多新手遇到网络问题第一反应是让我 ping 一下ping 不通就慌了到处试命令。这样做效率很低因为 ping 只告诉你 ICMP 通不通而 ICMP 通不代表服务通ICMP 不通也不代表应用一定不可用。正确的思路应该是分层排查按物理链路、IP 层、传输层、应用层的顺序一层一层往上走。第一步是确认本机网卡和 IP 配置。用ip addr看看网卡是否 up、有没有拿到合法 IP。以前很多教程喜欢用ifconfig现在很多发行版默认已经不装 net-tools 了建议直接养成用ip命令的习惯。确认完 IP 后看路由ip route确认默认网关是否正确。网关错了后面再怎么 ping 都是白费。第二步是确认链路是否通。ping网关地址能通说明本机到网关这一段链路没问题ping不通就要查网线、交换机端口、网卡协商速率。这里有个容易混淆的概念能 ping 通 IP 但解析不了域名那是 DNS 的问题别在链路层反复折腾。第三步是确认目标端口是否可达。ping 网关通、本机 IP 没问题但应用就是连不上这时候要考虑远程服务的端口处于什么状态用ss比netstat要更贴近现代发行版的现状ss -tlnp-t表示 TCP-l表示监听状态-n表示不反解域名显示数字地址-p显示进程信息。我要看某个端口有没有进程在监听这一条命令是最直接的。3.2 服务通但响应异常一条完整的问题定位链分享一个实际经历。某天同事反馈应用访问数据库服务时经常超时但数据库所在的服务器 ping 得通端口也能连上就是请求一多就卡住。当时我们按顺序做了一串排查。先看连接状态统计。用ss -s能快速看到当前系统的 TCP 连接概况里面会列出 ESTAB、TIME_WAIT、CLOSE_WAIT 等状态的数量。如果发现 TIME_WAIT 数量巨大且集中在某几个连接上就有可能是连接反复重建而不是长连接复用。再看服务端进程的日志应用日志和数据库日志都翻一遍。定位到某个时间段出现大量连接失败后用tcpdump在数据库服务器抓包tcpdump -i eth0 -nn port 3306 -w /tmp/mysql_debug.pcap抓一段时间后把 pcap 文件拉回本地用分析工具打开看 TCP 握手是否完成、是否有大量重传、RST 包是从哪边发出来的。重传多说明网络链路丢包RST 多说明对端进程主动拒绝或异常断开。排查的最终结论是应用侧连接池配置不当和网络本身关系不大。但如果没有那一条层层递进的排查链路就可能陷入网络到底通没通的死循环。我的体会是网络排查不是靠某一个神级命令解决一切而是靠一个清晰的层次模型来缩小范围每一步用最合适的工具取证据。3.3 DNS 故障的典型假象还有一个高发场景是 DNS 问题而且往往伪装成网络断连的样子。应用报错说连接失败工程师第一反应是 ping 外部域名发现 ping 不通立刻联想到防火墙策略或链路故障。但实际上如果 ping 的是域名这个操作本身就要先做 DNS 解析。正确的验证方法是先解析再连通getent hosts api.example.org如果 getent 返回了解析结果但ping仍然不通链路问题概率更大如果 getent 直接卡住或者返回空问题就可能出在 DNS 解析上。查/etc/resolv.conf确认 nameserver 配置然后用nslookup或dig直接向具体 DNS 服务器查询nslookup api.example.org 223.5.5.5如果指定 DNS 服务器能解析但默认配置解析不了说明/etc/resolv.conf里的 DNS 配置有问题。还有一个容易被忽略的点/etc/nsswitch.conf里定义的解析顺序hosts 条目如果是files dns那系统会先查/etc/hosts再走 DNS。有些时候/etc/hosts里残留了老的解析记录导致域名一直指向一个已经不存在的旧 IP这种问题查半天网络其实都白查了。4. systemd 管理服务时的日志与依赖处理4.1 服务起不来的第一现场具体报错信息用 systemd 管服务最常见的操作就是systemctl status看服务状态。但状态提示往往很笼统比如显示failed却看不到具体原因。这时候真正的第一现场在两个地方服务进程的输出和 journald 日志。先看状态详情systemctl status myservice这条命令会主动显示最近几条日志但信息不够完整。直接用 journalctl 拉完整日志journalctl -u myservice -n 100 --no-pager-u指定 unit 名-n 100看最后 100 行--no-pager防止日志太长进入交互模式反而看不到内容。如果日志里直接报缺少某个文件、某个配置文件格式错误问题就好办了。但如果是Main process exited, codeexited, status1/FAILURE这么一行笼统描述得继续往下挖。对于 shell 脚本类的服务可以手动执行脚本看输出对于二进制程序可以用systemctl start myservice后再立刻查状态和日志。记住一个原则排障时先看日志其次是看进程输出最后才考虑是不是依赖问题。不要先改配置重试这样可能掩盖原始报错。4.2 unit 文件里的两个关键依赖陷阱写 systemd unit 文件时有两个坑我以前都踩过。第一个坑是After和Requires的关系。新手容易以为写了Afternetwork.target就能保证网络可用其实这是个误解。After只控制启动顺序要求这个网络服务先启动但 network.target 在很多系统里只代表网络管理服务的入口不保证网络配置已经完成。真正要等网络就绪可能需要依赖network-online.target并且要确保 NetworkManager 或 systemd-networkd 对应的在线等待服务是启用的。否则你的应用可能启动了但网络还没配置好一上来连接就全失败。第二个坑是重启次数限制。服务因为某种原因反复崩溃systemd 默认有启动频率限制短时间多次失败后会进入start-limit-hit状态这时候你手动systemctl start也会被拒绝。日志里会看到明确提示。遇到这种情况需要重置启动限制systemctl reset-failed myservice然后再启动。但这个操作治标不治本真正要做的是搞清楚服务为什么反复退出。这就像家里跳闸你把闸推上去容易但如果不查是哪个电器短路推上去还会再跳。自定义 unit 文件写好之后记得先让 systemd 重新加载:systemctl daemon-reload改了 unit 文件不 reload 就直接 restartsystemd 用的还是旧配置这种情况非常普遍。4.3 日志占用磁盘空间的治理journald 日志默认是持久化到/var/log/journal/的它最大的优点是把服务的 stdout、stderr、内核消息统一收集进来排查问题不用到处翻文件。但它也有个副作用日志不会自动缩减时间久了会占用可观磁盘空间。查看当前日志占用journalctl --disk-usage如果发现占用过高最直接的操作是清理旧日志journalctl --vacuum-time7d这会只保留最近 7 天的日志。或者按大小清理journalctl --vacuum-size200M但清理只是事后补救。我习惯在配置层面提前限制 journald 的容量占用修改/etc/systemd/journald.conf里的SystemMaxUse比如设置成 500MSystemMaxUse500M保存后重启 systemd-journald 生效systemctl restart systemd-journald这样做的好处是日志量再大也不会撑爆根分区。对于日志需要长期留存的场景则需要单独配置日志转发到远程日志服务器或定期归档到独立存储而不是指望着 journald 无限保存。4.4 自定义 service 的一个标准模板最后给一个我常用的、比较稳妥的自定义 service unit 模板。假设我要把一个可执行文件/opt/myapp/myapp注册成服务配置如下[Unit] DescriptionMy Custom Application Afternetwork-online.target Wantsnetwork-online.target [Service] Typesimple Usermyapp Groupmyapp WorkingDirectory/opt/myapp ExecStart/opt/myapp/myapp Restartalways RestartSec5 LimitNOFILE65535 [Install] WantedBymulti-user.target几个字段的解释Typesimple表示 ExecStart 启动的进程就是主进程systemd 不再额外 fork。多数 Web 服务和后台程序用这个最合适。User和Group指定运行身份避免用 root 跑业务进程这个习惯能减少权限扩大带来的安全风险。Restartalways只要进程意外退出就自动拉起。对于不支持自守护的服务这比在程序里写死循环可靠得多。LimitNOFILE65535调高文件描述符上限很多高并发服务默认的 1024 限制会不够用提前放开省得服务在高峰期报 too many open files。写完把文件放到/etc/systemd/system/myapp.service执行systemctl daemon-reload然后systemctl enable --now myapp服务就起来了开机也会自动启动。根据我个人的使用经验一个小规模集群里把服务全部交给 systemd 托管后排障路径统一了无论哪个服务出问题都是同样的status加journalctl组合拳少去了猜测进程怎么起、日志在哪个文件这种无谓的消耗。这也是为什么我建议大家不要在 systemd 管理时再自己在脚本里写nohup和后台符号交给 systemd 作为主进程管理退出码、重启策略、日志收集才能完整覆盖到。这篇差不多就到这里。最后再分享一个小技巧每次看完 journald 日志或者改完 systemd 配置我都会固定在终端里敲一遍systemctl daemon-reload不管当时改没改配置——养成顺手 reload 的习惯能避免掉一半改了配置不生效的诡异问题。