2026年Linux运维实战:磁盘扩容、systemd服务与故障排查笔记 2026年开年第一周我把手头几台服务器重新梳理了一遍从系统初始化到服务部署再到问题排查踩了不少坑也沉淀了一些新经验。这篇笔记算是2026年的第一篇Linux实操记录内容比较杂涵盖了环境配置、磁盘管理、systemd服务、网络排查和几个经典故障的处理过程适合做运维的朋友和自学Linux的开发者参考。我用的环境是某发行版的长期支持版本内核版本5.15所有操作都是在虚拟机集群和一台物理服务器上完成的没有涉及生产环境的核心业务所以有些命令可以放心折腾。下面直接进正题按我实际操作的顺序来记录。1. 环境准备与系统基础配置1.1 系统安装后的基础安全配置新装好的Linux系统第一件事不是装软件而是先把基础安全项处理掉。我这批机器是某云平台交付的镜像版系统初始状态比较干净但默认开了密码登录而且root密码强度一般。第一步先更新软件源缓存把系统补丁打上这里有两点值得注意一是更新前先确认当前磁盘空间是否充足至少留出3GB左右的余量否则更新到一半磁盘满了会很被动二是有些发行版默认的源在国内网络环境下速度很慢建议先换成合适的镜像源再执行更新。实际操作中我用sed把源文件里的默认地址替换成了某镜像站地址然后执行apt update apt upgrade -y耗时大约8分钟。第二步是加固SSH配置。编辑/etc/ssh/sshd_config重点改这几项PermitRootLogin no PasswordAuthentication no PubkeyAuthentication yes X11Forwarding no MaxAuthTries 3这里说明一下PermitRootLogin改成no会禁止root直接登录对于需要sudo权限的日常操作没有影响但能挡住大量暴力破解尝试。注意修改完不要立刻重启sshd建议新开一个终端窗口测试密钥登录是否正常确认无误后再执行systemctl restart sshd否则万一密钥配置有问题你会把自己锁在门外。第三步是配置防火墙和fail2ban这部分我放到后面网络章节细说但基础规则在初始化阶段就要定好基调只开放必要的端口其他一律拒绝。1.2 Shell环境与用户习惯定制我日常登录服务器最常用的shell是bash虽然zsh更炫酷但在服务器上我倾向保持最小依赖所以只做必要的定制。在~/.bashrc里我维护了一段常用的别名和函数alias llls -alh --colorauto alias dfdf -h alias dudu -sh * alias grepgrep --colorauto alias netstatss -tunlp function mkcd() { mkdir -p $1 cd $1 || return } function extract() { if [ -f $1 ]; then case $1 in *.tar.gz|*.tgz) tar xzvf $1 ;; *.tar.bz2) tar xjvf $1 ;; *.zip) unzip $1 ;; *.7z) 7z x $1 ;; *) echo 不支持的文件格式 ;; esac else echo 文件不存在 fi }这里有个小技巧netstat命令在很多新版本系统上已经被ss替代我直接用alias把习惯命令映射过去以后就不会因为命令不存在而懵掉。还有一个在笔记里反复用到的技巧就是给命令输出增加时间戳。比如要看日志文件的实时变化用tail -f配合date命令tail -f /var/log/syslog | while read line; do echo $(date %Y-%m-%d %H:%M:%S) $line; done虽然有点土但在排查间歇性问题时非常有用能看到每条日志对应的精确时间点比裸的tail -f好使多了。2. 磁盘与文件系统管理实战2.1 磁盘空间排查与大文件定位这次要处理的一台业务服务器报警磁盘占用率超过90%登录后我第一反应是查df -h发现根分区确实快满了但奇怪的是du -sh *在看根目录各子目录时总占用又对不上这是新手常见的一个困惑点。排查思路应该这样先用df确认文件系统级别的占用再用du逐目录排查两者的差异往往是因为有已经被删除但仍然被进程占用的文件或者挂载点把某个目录挡住了。我这里就遇到了前者用lsof | grep deleted找到了几个被删掉但仍然被Java进程占用的日志文件lsof L1 | grep deleted这个命令列出所有被删除但仍被进程持有句柄的文件输出里能看到进程PID和文件大小。解决方法是重启对应服务或者用truncate -s 0 /proc/PID/fd/文件描述符直接清空文件内容强烈建议用truncate优雅处理不用重启服务。接下来定位大文件我用的是这个组合find / -xdev -type f -size 500M -exec ls -lh {} \; 2/dev/null | sort -k5 -rh参数含义解释一下-xdev表示不跨文件系统避免挂载点目录的重复扫描同时防止扫描到大数据盘把耗时拉长-type f只查普通文件-size 500M过滤大于500MB的文件。2/dev/null是过滤掉无权限访问目录的报错否则输出会全是Permission denied关键结果被淹没。查下来的结果让我意外最大的文件是一个MySQL的binlog日志已经积累到4GB。这其实是个需要定期处理的隐患后面会专门说。2.2 LVM扩容与分区调整记录磁盘清理完后我发现业务数据增长太快光靠删日志解决不了根本问题于是规划给数据分区做LVM扩容。这是Linux运维里非常基础但极其重要的操作。先看现状pvdisplay vgdisplay lvdisplay确认物理卷、卷组、逻辑卷的对应关系。我的场景是卷组vgdata下面有一个逻辑卷lvdata挂在/data现在要把新加的2TB磁盘加入卷组并扩展逻辑卷。新磁盘在系统里是/dev/sdb先创建物理卷pvcreate /dev/sdb vgextend vgdata /dev/sdb lvextend -L 1.9T /dev/vgdata/lvdata注意lvextend时不要加满留出约2%的余量防止文件系统元数据写入时空间不够。扩展逻辑卷后文件系统也要同步扩展这一步最容易出错。如果文件系统是ext4用resize2fs如果是xfs用xfs_growfs。区别在于ext4可以在挂载状态下在线扩容xfs也支持在线扩容但xfs不支持缩容这是规划分区时就要想清楚的。我这次的分区是xfs执行了xfs_growfs /data执行完用df -h确认容量已经扩展成功。有一个很关键的操作禁忌xfs分区千万别尝试用resize2fs去扩容会直接报错提示文件系统类型不匹配这时只要换成xfs_growfs就对了不是分区有问题是工具用错了。MySQL binlog的问题也顺手处理了在/etc/mysql/mysql.conf.d/mysqld.cnf里加了expire_logs_days 7 max_binlog_size 500M同时手动清理了旧的binlogPURGE BINARY LOGS BEFORE NOW() - INTERVAL 3 DAY;这个操作有留存要求但清理前一定要确认没有从库还在读取这些日志否则从库会断掉这个坑我踩过后面问题章节会细说。3. 服务部署与进程管理3.1 用systemd管理自定义服务现在部署服务基本都是systemd风格比之前的SysV init脚本清晰多了。我这次部署了一个模拟项目X的Java应用手动启动用了nohup java -jar app.jar 虽然能跑但缺少守护能力进程崩了没人拉起来开机也不会自启。我把它改造成了systemd服务。在/etc/systemd/system/mock-project.service里写[Unit] DescriptionMock Project Service Afternetwork.target mysql.service Wantsmysql.service [Service] Typesimple Userdaemon Groupdaemon WorkingDirectory/opt/mock ExecStart/usr/bin/java -Xms512m -Xmx1g -jar /opt/mock/app.jar Restartalways RestartSec10 StandardOutputappend:/var/log/mock/out.log StandardErrorappend:/var/log/mock/error.log [Install] WantedBymulti-user.target几个关键点说明Afternetwork.target mysql.service表示在网络就绪且mysql服务启动后再启动这个服务。Wantsmysql.service是弱依赖mysql如果起不来这个服务也会尝试启动但不阻塞相比Requires不会导致级联失败。Restartalways适合Web服务这类应该常驻的进程。配合RestartSec10是让崩溃后的重启有个10秒间隔避免不断快速重启把日志刷爆或者CPU打满。Typesimple是最常用的类型它表示ExecStart启动的进程就是主进程。如果你的应用是先启动一个脚本脚本再拉起主进程那最好用Typeforking并配合PIDFile指定PID文件否则systemd认为进程退出后就会尝试重启。启用开机自启systemctl daemon-reload systemctl enable --now mock-project systemctl status mock-project这里有个实用的排查技巧systemctl status如果显示Active: active (running)但你的业务端口就是不通大部分时候是服务进程起来的不是你想的那个或者启动时加载了错误的配置。直接看systemctl status下面的日志片段往往不够用journalctl -u mock-project -f看实时日志才是正解。3.2 进程资源占用排查服务上线后某次观察发现CPU占用经常冲到300%以上用top看了下找到对应的Java进程PID发现确实有两个线程在疯狂占用CPU。我抓了一个线程转储jstack PID /tmp/threaddump.txt然后从top -H里找到CPU占用高的线程ID把线程ID从十进制转十六进制printf %x\n 12345再到threaddump.txt里搜索对应的十六进制线程号就能定位到具体代码位置。这套排查组合是Java应用出问题时的标准动作虽然不是Linux本身的问题但排查过程完全是在Linux命令行上完成的所以也放进笔记里。如果是纯系统层面的CPU异常我会先用mpstat确认是不是单核打满或者整体都高再用perf top看内核热点的分布。perf命令需要root权限有些系统还要先安装linux-perf包用sudo perf top能实时看到最耗CPU的函数名对定位驱动问题或内核bug非常有帮助。内存方面free -h只能看个大概详细的分配情况在/proc/meminfo里。我在排查一个疑似内存泄漏的问题时习惯做这样一组采集for i in {1..60}; do date %H:%M:%S /tmp/mem.log grep -E ^(MemFree|Cached|Dirty|Writeback) /proc/meminfo /tmp/mem.log sleep 60 done采集60次每60秒一次持续一小时。然后配合业务流量曲线看趋势比只看某个时刻的快照要靠谱得多。Dirty和Writeback这两个值加起来如果一直在上涨且居高不下说明写盘速度跟不上内存脏页的生成速度常见的原因是磁盘IO能力不足或者是swap配置有问题数据迟迟没有落盘。4. 网络排查与防火墙配置4.1 端口占用与连通性排查这次排查一个服务连不上数据库的问题应用日志里报Connection refused我第一步是用ss -tunlp看数据库端口是否在监听ss -tunlp | grep 3306如果端口没监听说明MySQL没起来如果监听地址是127.0.0.1:3306而应用在另一台机器上那必然连不上需要把绑定地址改成0.0.0.0或者内网IP。我这次的问题就是MySQL默认只绑定了本地地址修改/etc/mysql/mysql.conf.d/mysqld.cnf里的bind-address后解决了。还有一种相当隐蔽的情况ss -tunlp显示监听正常应用所在的机器也能telnet IP 3306看到连接成功但应用仍然报超时这通常是防火墙禁了端口但拒绝方式是丢包而不是拒绝握手因为连接直接卡死客户端等超时了才报错。排查链路的时候我用三层确认法ping 目标IP telnet 目标IP 目标端口 nc -vz 目标IP 目标端口如果ping通了但telnet不通先查本机和目标机上防火墙的INPUT链。在目标机上用iptables -L -n或者firewall-cmd --list-all查看当前规则看是不是默认策略就是DROP且没加放行规则。这里有个让新手很懵的点有些云主机控制台的安全组也在挡流量即使系统里防火墙全开了也没用。排查时要把云平台安全组和系统内防火墙这两层分开验证先用tcpdump在目标机看有没有流量到达tcpdump -i eth0 port 3306如果有流量到达但连接还是建立不了基本可以确认是系统防火墙或安全组丢包如果压根没有流量到达可能是目标机的网络配置或路由问题。tcpdump是我排查网络问题的神器参数不多但很管用。4.2 firewalld规则配置笔记现在大多数发行版默认的防火墙管理工具是firewalld虽然底层还是iptables但管理逻辑差别很大。我用firewalld做了一次端口最小化开放。先看默认区域firewall-cmd --get-default-zone firewall-cmd --list-all默认区域通常叫public在这个区域下开放端口firewall-cmd --permanent --zonepublic --add-port80/tcp firewall-cmd --permanent --zonepublic --add-port443/tcp firewall-cmd --reload这里我踩过一个不算坑的坑firewall-cmd --add-port如果你不加--permanent它只对当前运行态生效重启后规则就没了。这跟iptables的临时规则逻辑刚好相反iptables默认是临时的保存才永久firewalld默认是永久的不写永久就是临时的很容易搞混。另一个细节firewalld的富规则可以做得比较精细比如限制某个IP限速访问或者只允许某个IP段访问某端口firewall-cmd --permanent --zonepublic --add-rich-rulerule familyipv4 source address192.168.1.0/24 port port3306 protocoltcp accept这样只有内网网段能访问数据库端口外网直接丢弃。需要说明的是firewalld和直接操作iptables不推荐混用。如果你在firewalld里改了规则发现不生效先检查是不是有别的工具也在操作iptables规则最常见的是docker和kube-proxy这类容器网络组件它们的规则在docker0和KUBE-*链里用iptables -L -n能看到一大片规则别误删。5. 常见问题与排查技巧实录5.1 文件句柄耗尽故障春节前某天业务运维反馈应用报大量socket: too many open files我第一反应是文件句柄被耗尽了但奇怪的是普通进程怎么会耗尽系统限额。先用ulimit -n看当前会话的软限制和硬限制显示是1024和65535这个1024是典型的默认软限制。给某个用户调大限制要改/etc/security/limits.confdaemon soft nofile 65535 daemon hard nofile 65535但其实影响一个进程实际文件句柄上限的还有一个隐藏因素systemd服务本身会重新设置限制。在systemd unit文件里如果没写LimitNOFILE服务进程拿到的限制跟普通的shell登录会话不一样默认可能只有1024。后来我确认模拟项目X的服务就是这种情况在service文件里加[Service] LimitNOFILE65535 LimitNPROC65535然后systemctl daemon-reload systemctl restart mock-project再看/proc/PID/limits已经变成65535了。排查这类问题有个特别有用的命令ls /proc/PID/fd | wc -l能实时查看一个进程打开的fd数量配合/proc/PID/limits就能判断这个进程是系统限制不够还是程序自身泄漏。我后来从业务侧确认程序确实存在连接池未回收的情况但系统限制128个连接左右就把进程逼到报错属于双重问题。限制调大解决的是系统瓶颈业务代码的泄漏也要修否则只是把爆发时间延后。另外如果遇到Too many open files始终没有改善能够重启业务的情况下先确认是不是root用户启动的服务root用户的软限制默认可能本来就很高但systemd的unit里写的是LimitNOFILE1024这时即使你改了limits.conf也没有任何作用因为unit文件的优先级更高。5.2 服务器时间同步异常元旦后的监控告警里有一台机器的时间偏差超过了5分钟导致日志时间轴和另外几台机器对不上。查时间同步状态用的是timedatectl status chronyc sources -v发现时钟源的连接状态是^*表示已同步但本地时间还是偏了。再深入查发现是硬件时钟和系统时钟差异较大。用hwclock -r hwclock --systohc把系统时间写回硬件时钟解决。这台机器的配置其实是/etc/chrony/chrony.conf里的pool地址指向了一个已经失效的NTP服务器而备用源又没配置好导致chronyd只能从本地猜测时间。建议配置两个不同地域的时间源并且定期校验chronyc tracking看Leap status显示NormalStratum层级在3以内就说明状态健康。我还有一个笨但很有用的土办法在监控脚本里加一条检查时间偏差超过30秒就告警。因为很多协议故障比如某些严格的握手校验和数据库主从切换时间偏差都是内部诱发因素能在早期发现省的后期排查成本高得吓人。5.3 磁盘清理的误删教训最后记录一个我自己犯过的错误避免大家重蹈覆辙。某次磁盘满了我在清理/tmp目录时用了rm -rf /tmp/*结果把正在运行的一个应用输出的临时文件删了个干净那个应用直接就卡死了。虽然这些文件的确是临时文件但进程还开着句柄在写直接删除以后进程后续写入到哪里都成了未知数读旧数据也找不到输出错乱。正确的清理姿势是先确认哪些进程在用这些文件lsof L1 | grep /tmp确认没有进程引用后再删除或者用find指定文件的最后修改时间只删超过一定时长的旧文件find /tmp -type f -mtime 3 -delete这样能很大程度避免误删正在使用的文件。另一个磁盘清理的误区是对日志文件使用了rm而不是truncate。如果你直接删除一个正在被Java进程写写的日志文件磁盘空间可能不会马上释放因为句柄还开着文件数据仍然被进程占用。这时候要么重启进程要么用truncate -s 0 日志文件清空内容。很多运维直接把rm干掉的文件配合日志轮转配置再生成一个同名文件但旧句柄占用的空间仍然留在磁盘上想释放必须让进程重新open文件。所以对于一直在增长的日志文件脚本里优先用truncate才是干净的做法。6. 这次操作的一些体会年前年后这一轮操作下来我最大的感受是Linux排查问题的本质就一句话确认现象缩小范围找到根因。前面这些内容里反复出现的ss、lsof、tcpdump、systemctl status本质上都是为了帮你定位当前系统到底处于什么状态。对于系统运维这事有几个习惯已成为我的个人操作标准第一改动前先备份配置文件哪怕只是加一行注释也要cp一份第二生产环境操作之前先确认当前目录有些灾难性操作比如rm -rf前面加个pwd往往能救你一命第三任何服务参数调整之后用systemctl daemon-reload让配置生效然后立刻看status确认别等到事情变了再回查。还有一个小技巧顺手分享在写完一些关键配置以后我会把当时的完整记录保存在自己的笔记库里包括当时的环境、问题、排查命令和解决方法下次遇到类似问题直接搜索旧笔记效率比重新翻论坛快太多了。这篇Linux笔记也是属于我自己记录体系中的一部分对于刚开始接触运维的朋友们希望它对你的实际工作与自学能有点作用。