Linux 磁盘管理与 LVM 实战:从日志轮转到启动恢复
接上一期《Linux 服务管理实战——systemd 日志与时间同步》,我们讲了日志"怎么看、怎么收"。
这一期往存储底层延伸:日志轮转(logrotate)是怎么把日志管住的、Linux 开机到底经历了什么、以及最硬核的部分——磁盘 / 分区 / LVM / 文件系统 / 挂载点这一整条链路,外加加盘、扩容、持久挂载,和一旦配错导致起不来的启动恢复。全程命令可复制,建议边看边敲。
文章目录
- Linux 磁盘管理与 LVM 实战:从日志轮转到启动恢复
- 一、Logrotate 日志轮转的原理
- 1.1 它到底怎么工作的
- 1.2 配置文件在哪
- 1.3 最常用的指令
- 1.4 自己写一个轮转配置(演示)
- 1.5 先"演习"再"真干"
- 二、Linux 启动链(开机到底发生了什么)
- 2.1 BIOS 与 UEFI 的区别(影响分区表)
- 2.2 查看当前启动模式
- 2.3 GRUB 与 systemd 常用命令
- 2.4 拓展实战:忘记 root 密码?用救援模式进系统
- 三、目录背后的完整映射(存储栈)
- 四、添加磁盘后的三类证据互相验证:lsblk / blkid / findmnt
- 4.1 三连击演示
- 4.2 怎么"互相验证"
- 五、磁盘分区:GPT vs MBR 与实操步骤
- 5.1 选哪个
- 5.2 用 parted 建 GPT 分区(推荐)
- 5.3 查看分区结果
- 六、格式化、临时挂载与卸载
- 6.1 格式化(建立文件系统)
- 6.2 临时挂载(重启失效)
- 6.3 卸载
- 七、持久挂载与 /etc/fstab 详解
- 7.1 fstab 每行 6 个字段
- 7.2 实操:把 /data 持久化
- 八、LVM 数据卷管理:创建与扩容完整步骤
- 8.1 概念对应
- 8.2 场景 A:从零新建 LVM 并挂载
- 8.3 场景 B:扩容(把磁盘"撑大")—— 重点
- 8.4 逐步检查
- 总结
一、Logrotate 日志轮转的原理
系统跑久了,日志会无限变大。logrotate就是专门干"日志瘦身"的定时工具——它不是守护进程,而是被cron / anacron每天定时调用一次,按规则对日志做轮转(rotate)、压缩、删除、重命名。
1.1 它到底怎么工作的
每天 (cron.daily) 触发 └── /usr/sbin/logrotate /etc/logrotate.conf ├── 读主配置 /etc/logrotate.conf ├── 逐个读 /etc/logrotate.d/* 里的应用规则 └── 对每条规则: ① 判断是否满足轮转条件(时间/大小) ② 把 access.log → access.log.1(旧的顺延 .2 .3 ...) ③ 可选:压缩成 .gz ④ 可选:通知服务重新打开日志文件(postrotate) ⑤ 超过保留份数的老日志直接删关键点:轮转后原文件名被让出来,应用必须重新打开文件句柄,否则会一直往已轮转走的旧文件里写。这就是postrotate里常见systemctl reload xxx或kill -HUP的原因。
1.2 配置文件在哪
- 主配置:
/etc/logrotate.conf(全局默认) - 应用配置:
/etc/logrotate.d/目录(每个服务一个文件,优先级更高)
1.3 最常用的指令
| 指令 | 含义 |
|---|---|
daily/weekly/monthly | 轮转周期 |
rotate N | 保留 N 份旧日志 |
size 100M | 日志超过 100M 才轮转(和周期"或"关系) |
compress/nocompress | 是否压缩旧日志 |
delaycompress | 延迟一轮再压缩(配合正在写的日志更稳) |
missingok | 日志不存在也不报错 |
notifempty | 空日志不轮转 |
copytruncate | 拷贝后清空原文件(进程不重开文件也能用,但可能丢几字节) |
create 0640 user group | 轮转后新建日志并指定权限属主 |
dateext | 用日期命名(access.log-20261012)而非序号 |
postrotate ... endscript | 轮转后执行脚本(如 reload 服务) |
sharedscripts | 多个日志只执行一次 postrotate |
1.4 自己写一个轮转配置(演示)
假设有个应用日志/var/log/myapp.log,要求:每天轮转、保留 7 份、压缩、空文件不转、轮转后让应用重开日志。
sudovim/etc/logrotate.d/myapp写入:
/var/log/myapp.log { daily rotate 7 missingok notifempty compress delaycompress create 0640 root root dateext sharedscripts postrotate # 假设 myapp 支持 HUP 信号重开日志 /bin/kill -HUP $(cat /var/run/myapp.pid 2>/dev/null) 2>/dev/null || true endscript }1.5 先"演习"再"真干"
# -d 调试模式:只模拟,不真改文件,用来检查配置写对没有sudologrotate-d/etc/logrotate.d/myapp# -vf 强制执行一次(v=verbose 看过程)sudologrotate-vf/etc/logrotate.d/myapp# 看结果ls-lh/var/log/myapp.log*# 预期:myapp.log myapp.log-20261012.gz排错:如果
logrotate -d报 “error: xxx” 一般是括号/路径写错;配完一定要-d跑一遍再等它自动执行。
二、Linux 启动链(开机到底发生了什么)
理解启动链,才能在"起不来"的时候知道卡在哪一环。主流发行版(CentOS 7+/Ubuntu)都是UEFI/BIOS → GRUB2 → 内核 → initramfs → systemd。
[加电] │ ▼ ① BIOS / UEFI (POST 自检,找启动设备) │ BIOS:读磁盘第 1 个扇区(MBR, 512B)里的引导程序 │ UEFI:读 ESP 分区里的 \EFI\BOOT\bootx64.efi ▼ ② GRUB2 引导加载程序 │ 显示启动菜单,加载内核与 initramfs 到内存 │ 配置:/boot/grub2/grub.cfg(不要手写,用命令生成) ▼ ③ 内核 (vmlinuz) 解压并启动,挂载 initramfs │ initramfs:临时根文件系统,内含驱动(尤其磁盘/raid/lvm 驱动) │ 作用:先让内核能认出真正的根设备 ▼ ④ 切换根 (switch_root) 到真实根文件系统 / ▼ ⑤ systemd 作为 PID 1 启动 │ 根据默认 target 拉起所有服务 ▼ ⑥ 到达默认 target multi-user.target → 命令行(相当于 runlevel 3) graphical.target → 图形界面(相当于 runlevel 5)2.1 BIOS 与 UEFI 的区别(影响分区表)
| BIOS + MBR | UEFI + GPT | |
|---|---|---|
| 启动方式 | 读 MBR 引导扇区 | 读 ESP 分区 EFI 文件 |
| 磁盘容量 | ≤ 2TB | 无限制 |
| 分区数 | 最多 4 主分区 | 128+ |
| 安全性 | 弱 | 支持 Secure Boot |
顺带一查当前机器用的哪种固件:
ls /sys/firmware/efi—— 有内容就是 UEFI,报错/为空就是传统 BIOS。
2.2 查看当前启动模式
# 查看默认启动到哪个模式:multi-user.target=命令行 / graphical.target=图形界面sudosystemctl get-default2.3 GRUB 与 systemd 常用命令
# 重新生成 GRUB 配置(改了内核/磁盘后必跑)# Ubuntu/Debian:sudoupdate-grub# 临时切换到多用户(命令行)sudosystemctl set-default multi-user.target2.4 拓展实战:忘记 root 密码?用救援模式进系统
讲完启动链,顺手解决一个最常被问到的真问题:密码忘了、进不去系统怎么办。核心思路就一句——在 GRUB 里打断内核启动,抢在 systemd 之前拿到 root 权限改密码。
操作(systemd 系发行版通用):
# ① 开机到 GRUB 菜单,光标停在内核上,按 e 编辑启动项# ② 找到以 linux(或 linux16)开头的行,在行尾追加:rd.break# ③ 按 Ctrl+X 启动,系统会停在 initramfs 紧急 shell# ④ 此时真实根以只读方式挂在 /sysroot,先重新挂载可写mount-oremount,rw /sysroot# ⑤ 切换根到真实系统chroot/sysroot# ⑥ 改密码passwdroot# ⑦ 开了 SELinux 的机器(CentOS/RHEL)必须打这个标记,否则策略不刷新会仍登不进touch/.autorelabel# ⑧ 退出并重启exitreboot图形化如下:
要点:
rd.break是在内核启动早期打断,所以能绕过登录先拿 root;/.autorelabel在 SELinux 开启的机器上不能省。这是"救援模式"最典型的一种用法——后面第九章还会讲 GRUB 损坏、fstab 写错等更多自救场景。
启动这关打通了:系统怎么起、起不来怎么救,心里都有数了。但光会"起得来"还不够——数据放哪、盘怎么加、满了怎么扩,才是日常运维真正的大头。下面进入存储世界:先看清一条目录背后到底链着多少层,再动手加盘、分区、上 LVM。
三、目录背后的完整映射(存储栈)
你cd /data/app看到的路径,背后是一条多层映射链。搞清楚它,磁盘问题就不再是黑盒:
物理磁盘 (Disk) │ /dev/sdb ▼ 分区 (Partition) │ /dev/sdb1 ← 分区表(GPT/MBR)把磁盘切成几块 ▼ [可选] 物理卷 PV → 卷组 VG → 逻辑卷 LV (LVM 层) │ /dev/vg_data/lv_data ← 把多块/多个分区"拼"成灵活的逻辑盘 ▼ 文件系统 (File System) │ mkfs.xfs / mkfs.ext4 ← 在块设备上建立目录树的索引结构 ▼ 挂载点 (Mount Point) │ mount /dev/... /data ← 把文件系统"挂"到目录树的某个节点 ▼ 应用看到的路径 /data/app/config.yaml ← 应用只认目录,不关心底下是块磁盘还是 LVM一句话:应用看到的是"挂载点之后的路径",底层是磁盘还是 LVM 对它透明。出问题时要从下往上逐层验证。
四、添加磁盘后的三类证据互相验证:lsblk / blkid / findmnt
新加一块盘,光fdisk -l看一眼不够。三个命令各看一个维度,互相印证才放心:
| 命令 | 看什么 | 好比 |
|---|---|---|
lsblk | 块设备的层级树(盘→分区→LV)和挂哪了 | 家族族谱 |
blkid | 每个设备的身份证(UUID、文件系统类型、PARTUUID) | 身份证 |
findmnt | 当前挂载关系(哪个设备挂在哪个目录) | 户口本 |
4.1 三连击演示
# ① 层级:看到 sdb 下面有 sdb1,以及它挂到 /datalsblk# NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT# sda 8:0 0 40G 0 disk# ├─sda1 8:1 0 1G 0 part /boot# └─sda2 8:2 0 39G 0 part# └─cl-root 253:0 0 37G 0 lvm /# sdb 8:16 0 100G 0 disk ← 新盘# └─sdb1 8:17 0 100G 0 part /data ← 已挂到 /data# ② 身份证:拿到 sdb1 的 UUID(持久挂载就靠它)blkid /dev/sdb1# /dev/sdb1: UUID="a1b2c3d4-..." TYPE="xfs" PARTUUID="..."# ③ 户口本:确认 /data 确实由该 UUID 的设备挂载findmnt /data# TARGET SOURCE FSTYPE OPTIONS# /data /dev/sdb1[xfs] xfs rw,relatime,...4.2 怎么"互相验证"
排查思路(闭环):
lsblk看到sdb1挂在/data;blkid /dev/sdb1拿到它的UUID=a1b2...;findmnt /data里 SOURCE 应显示同一设备、FSTYPE是 xfs;- 三者对得上 → 磁盘、分区、文件系统、挂载点全链路健康。
常见坑:
lsblk显示挂载了,但findmnt没有 → 多半是只写了 fstab 没mount -a,或挂载点目录被覆盖。
五、磁盘分区:GPT vs MBR 与实操步骤
5.1 选哪个
- GPT:现代默认,支持 >2TB、128+ 分区、有备份分区表,UEFI 必选。用
parted/gdisk。 - MBR:老机器 / 传统 BIOS,≤2TB、最多 4 主分区,用
fdisk。
5.2 用 parted 建 GPT 分区(推荐)
假设新盘是/dev/sdb,整盘划一个分区:
sudoparted/dev/sdb# (parted) mklabel gpt # 建 GPT 标签(会清掉旧表!)# (parted) mkpart primary xfs 1MiB 100% # 类型名只是标签,真正文件系统在 mkfs 定# (parted) align-check optimal 1 # 确认对齐(显示 1 aligned 即 OK)# (parted) print# (parted) quit不想进交互模式,一条命令也行:
sudoparted-s/dev/sdb mklabel gptsudoparted-s/dev/sdb mkpart primary xfs 1MiB100%5.3 查看分区结果
lsblk /dev/sdb六、格式化、临时挂载与卸载
6.1 格式化(建立文件系统)
# XFS(CentOS/RHEL 默认,适合大文件/大容量)sudomkfs.xfs /dev/sdb1# EXT4(Ubuntu 默认,通用稳定)sudomkfs.ext4 /dev/sdb1已格式化的盘想强制重格:
mkfs.xfs -f /dev/sdb1/mkfs.ext4 -F /dev/sdb1。
6.2 临时挂载(重启失效)
sudomkdir-p/datasudomount/dev/sdb1 /data# 验证findmnt /datadf-h/data6.3 卸载
# 按挂载点或设备都行sudoumount/data# 或sudoumount/dev/sdb1卸载报target is busy?说明有进程正在用这个目录:
# 看谁在占用lsof+D /data# 或fuser-mv/data# 退出相关进程后再 umount;紧急情况可懒卸载(等进程关了再真卸)sudoumount-l/data七、持久挂载与 /etc/fstab 详解
临时挂载重启就没了。要让它开机自动挂,得写/etc/fstab。
7.1 fstab 每行 6 个字段
# <设备> <挂载点> <类型> <选项> <dump> <pass> UUID=a1b2c3d4-... /data xfs defaults 0 0| 字段 | 说明 |
|---|---|
| 设备 | 用UUID(最稳,blkid 获取)。也可用 /dev/sdb1,但盘序变了会挂错 |
| 挂载点 | 必须已存在的空目录 |
| 类型 | xfs / ext4 / swap 等 |
| 选项 | defaults= rw,suid,dev,exec,auto,user,async 的集合 |
| dump | 0=不备份(基本都写 0) |
| pass | 开机 fsck 顺序:根=1,其他=2,不检查=0 |
常用选项补充:
noatime:不更新访问时间,提升性能nofail:盘不存在也不阻断启动(移动硬盘推荐)defaults,noatime,nofail
7.2 实操:把 /data 持久化
# ① 拿 UUIDsudoblkid /dev/sdb1# 复制 UUID="a1b2c3d4-..." 里的那串# ② 编辑 fstab(务必先备份!)sudocp/etc/fstab /etc/fstab.baksudovim/etc/fstab# 追加一行:# UUID=a1b2c3d4-xxxx-xxxx /data xfs defaults,noatime 0 0# ③ 关键:用 mount -a 测试,不报错才说明写对了sudomount-aecho$?# 返回 0 = OKfindmnt /data# 确认已挂上⚠️fstab 写错是启动失败的头号原因!改完务必
mount -a验证,且一定先备份。
八、LVM 数据卷管理:创建与扩容完整步骤
LVM(逻辑卷管理)把"物理磁盘"抽象成"可随意伸缩的逻辑盘",加盘/扩容不用停机重分区。
8.1 概念对应
PV 物理卷 = 一块被 LVM 接管的磁盘/分区 (pvcreate) VG 卷组 = 多个 PV 凑成的存储池 (vgcreate / vgextend) LV 逻辑卷 = 从 VG 里切出来的"逻辑磁盘" (lvcreate / lvextend)8.2 场景 A:从零新建 LVM 并挂载
# 假设新盘 /dev/sdc# ① 分区(LVM 建议整盘一个分区,类型 linux-lvm=0x8e 或留空)sudoparted-s/dev/sdc mklabel gptsudoparted-s/dev/sdc mkpart primary 1MiB100%# ② 创建 PVsudopvcreate /dev/sdc1sudopvs# 查看 PV 列表# ③ 创建 VGsudovgcreate vg_data /dev/sdc1sudovgs# 查看 VG# ④ 创建 LV(5G 示例)sudolvcreate-L5G-nlv_data vg_datasudolvs# 查看 LV# ⑤ 格式化 + 挂载sudomkfs.ext4 /dev/vg_data/lv_datasudomkdir-p/datasudomount/dev/vg_data/lv_data /data# ⑥ 持久化(拿 LV 的 UUID 写 fstab,同上节)sudoblkid /dev/vg_data/lv_data8.3 场景 B:扩容(把磁盘"撑大")—— 重点
真实运维里最常见:业务涨了,/data 满了,要扩。
方式一:VG 里还有空闲空间
# 直接扩 LV + 扩文件系统一步到位(-r 自动 resize)sudolvextend-L+5G-r/dev/vg_data/lv_data# 没加 -r 的话,要手动扩文件系统:xfs:sudoxfs_growfs /data ext4:sudoresize2fs /dev/vg_data/lv_datadf-h/data# 确认容量已变大方式二:VG 满了,先加新物理盘
# 新盘 /dev/sddsudopvcreate /dev/sdd1sudovgextend vg_data /dev/sdd1# 把新 PV 并入卷组vgs# 看 VG 的 VSize/VFree 变大了# 再扩 LV(自动扩文件系统)sudolvextend-L+10G-r/dev/vg_data/lv_datadf-h/data方式三:虚拟机"在线扩容"已有虚拟磁盘
# 先在虚拟化平台把磁盘调大,系统里让内核重读分区表echo1|sudotee/sys/class/block/sdc/device/rescan# 用 growpart 把分区扩到整盘(CentOS: cloud-utils-growpart)sudogrowpart /dev/sdc1# 之后 pvresize 让 PV 认到新大小sudopvresize /dev/sdc1# 再走 lvextend -r 扩 LV口诀:加盘 → pvcreate → vgextend → lvextend -r → 看 df。XFS 只能扩不能缩,ext4 可扩可缩(缩要先 umount 且风险高,谨慎)。
8.4 逐步检查
lsblk-fpvscan vgs lvscan findmnt--target....findmnt--target....df-hT....总结
这一期我们把存储链路打通了:
- 日志轮转:logrotate 靠 cron 触发,按规则轮转+压缩,
postrotate让应用重开日志;改完用-d演习。 - 启动链:UEFI/BIOS → GRUB2 → 内核+initramfs → systemd → target,配错卡哪环心里有数。
- 存储栈:disk → partition →(LVM: PV/VG/LV)→ 文件系统 → 挂载点 → 应用路径,应用只认挂载点之后。
- 三证验证:
lsblk(层级) +blkid(UUID) +findmnt(挂载) 互相印证。 - 分区:GPT(>2TB/UEFI)用 parted,MBR 用 fdisk。
- 挂载:临时
mount、持久写fstab(用 UUID,改前备份、mount -a验证)。 - LVM 扩容:加盘 →
pvcreate→vgextend→lvextend -r→df确认。
本文命令在 CentOS 7+/Rocky 9 与 Ubuntu 22.04 验证思路一致,个别包名(如
resize2fsvsxfs_growfs)按文件系统区分。动手前务必对生产数据备份。