☰
2026年Linux运维实战:磁盘扩容、systemd服务与故障排查笔记
2026/10/21 4:43:53 网站建设 项目流程

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 ll='ls -alh --color=auto' alias df='df -h' alias du='du -sh *' alias grep='grep --color=auto' alias netstat='ss -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

同时手动清理了旧的binlog:

PURGE 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] Description=Mock Project Service After=network.target mysql.service Wants=mysql.service [Service] Type=simple User=daemon Group=daemon WorkingDirectory=/opt/mock ExecStart=/usr/bin/java -Xms512m -Xmx1g -jar /opt/mock/app.jar Restart=always RestartSec=10 StandardOutput=append:/var/log/mock/out.log StandardError=append:/var/log/mock/error.log [Install] WantedBy=multi-user.target

几个关键点说明:

After=network.target mysql.service表示在网络就绪且mysql服务启动后再启动这个服务。Wants=mysql.service是弱依赖,mysql如果起不来,这个服务也会尝试启动,但不阻塞,相比Requires不会导致级联失败。

Restart=always适合Web服务这类应该常驻的进程。配合RestartSec=10是让崩溃后的重启有个10秒间隔,避免不断快速重启把日志刷爆或者CPU打满。

Type=simple是最常用的类型,它表示ExecStart启动的进程就是主进程。如果你的应用是先启动一个脚本,脚本再拉起主进程,那最好用Type=forking并配合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 --zone=public --add-port=80/tcp firewall-cmd --permanent --zone=public --add-port=443/tcp firewall-cmd --reload

这里我踩过一个不算坑的坑:firewall-cmd --add-port如果你不加--permanent,它只对当前运行态生效,重启后规则就没了。这跟iptables的临时规则逻辑刚好相反,iptables默认是临时的,保存才永久;firewalld默认是永久的,不写永久就是临时的,很容易搞混。

另一个细节:firewalld的富规则可以做得比较精细,比如限制某个IP限速访问,或者只允许某个IP段访问某端口:

firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port port="3306" protocol="tcp" 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.conf:

daemon soft nofile 65535 daemon hard nofile 65535

但其实影响一个进程实际文件句柄上限的,还有一个隐藏因素:systemd服务本身会重新设置限制。在systemd unit文件里,如果没写LimitNOFILE,服务进程拿到的限制跟普通的shell登录会话不一样,默认可能只有1024。后来我确认模拟项目X的服务就是这种情况,在service文件里加:

[Service] LimitNOFILE=65535 LimitNPROC=65535

然后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里写的是LimitNOFILE=1024,这时即使你改了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显示Normal,Stratum层级在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笔记也是属于我自己记录体系中的一部分,对于刚开始接触运维的朋友们,希望它对你的实际工作与自学能有点作用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询