从根目录到生产环境磁盘治理
一、学习目标
学完本篇,你应当能够:
- 理解 Linux“一切皆文件”的设计思想,以及 FHS 目录规范的作用
- 深入理解
/etc、/var 两个生产环境核心目录 - 掌握 Docker 占满
/var/lib/docker 的原因、排查流程与治理方法 - 理解“磁盘已满但找不到大文件”等典型故障背后的原理
- 建立按用途、生命周期和变更频率组织数据的目录设计思维
二、Linux 目录体系的核心设计思想
2.1 一切皆文件
Linux 经常被概括为:
一切皆文件。
这句话并不意味着 Linux 中的所有对象都是真正存储在磁盘上的普通文件,而是说 Linux 尽量使用统一的文件接口来描述和操作不同类型的资源。
例如:
| |
|---|
| |
| |
| /dev/sda1 |
| /dev/tty |
| /proc/<PID>/ |
| /proc/sys/ |
| /sys/ |
| |
| |
应用程序通常通过统一的系统调用访问这些资源:
open()read()write()close()ioctl()
这种统一抽象带来了一个重要优势:应用程序不需要针对每一种硬件或内核对象设计完全不同的访问方式。
例如,读取 CPU 信息本质上可以表现为读取文件:
cat /proc/cpuinfo
查看内存状态:
cat /proc/meminfo
向终端输出内容:
echo"hello" > /dev/tty
因此,“一切皆文件”的核心不是“所有内容都存储在磁盘上”,而是:
Linux 尽可能使用统一的文件接口抽象系统资源。
2.2 Linux 只有一棵目录树
Windows 通常使用多个盘符组织文件:
C:\D:\E:\
Linux 不使用盘符作为顶层入口,而是将所有文件系统挂载到同一棵目录树中。
整棵目录树的起点是:
/
无论底层有多少块磁盘、分区、逻辑卷、网络存储或临时文件系统,最终都会挂载到某个目录上。
例如:
/dev/nvme0n1p2 → //dev/sdb1 → /data/dev/sdc1 → /var/lib/dockertmpfs → /runproc → /procsysfs → /sys
用户看到的是统一目录树:
/├── data├── home├── proc├── run├── sys└── var
但这些目录背后可能来自完全不同的存储设备。
这也是理解 Linux 文件系统的关键:
目录路径是逻辑入口,挂载点决定数据实际落在哪个文件系统中。
可以使用以下命令确认目录所属的文件系统:
findmnt /var/lib/dockerdf -hT /var/lib/docker
2.3 FHS:文件系统层级标准
Linux 发行版通常参考 FHS,即 Filesystem Hierarchy Standard,中文常译为“文件系统层级标准”。
FHS 的作用是约定常见目录的用途,例如:
有了相对统一的目录规范,管理员进入一台陌生 Linux 服务器时,仍然能够快速定位常见文件。
例如:
系统配置→ /etc系统日志→ /var/log用户目录→ /home临时文件→ /tmp设备文件→ /dev进程信息→ /proc第三方软件→ /opt本地安装软件→ /usr/local
不同发行版仍然可能存在差异,但整体设计逻辑基本一致。
三、Linux 目录体系的三项设计原则
3.1 静态数据与动态数据分离
系统程序、共享库等内容通常不会频繁变化,而日志、缓存、数据库和队列文件会持续写入。
Linux 会尽量将它们分开:
/usr →相对稳定的程序和共享资源/etc →系统及服务配置/var →持续变化的数据
这种拆分有利于:
3.2 系统数据与用户数据分离
系统组件与普通用户数据也需要隔离:
/etc →系统配置/usr →系统共享程序/home →普通用户数据/root → root 用户数据
普通用户通常只对自己的家目录拥有完整写权限,从而降低误操作和越权修改系统文件的风险。
3.3 物理存储与虚拟接口分离
Linux 中并不是所有目录都对应真实磁盘数据。
例如:
/proc →内核和进程信息/sys →设备、驱动和内核对象/dev →设备节点/run →本次启动期间的运行时状态
这些目录中的大量内容由内核在运行时动态生成。
因此,看到一个“文件”并不代表它一定占用真实磁盘空间。
四、根目录下的核心目录总览
一个典型 Linux 根目录大致如下:
/├── bin # 基础用户命令,现代系统通常链接到 /usr/bin├── boot # 内核、initramfs、GRUB 等启动文件├── dev # 设备节点├── etc # 系统和服务配置├── home # 普通用户家目录├── lib # 基础共享库,现代系统通常链接到 /usr/lib├── lib64 # 64 位共享库或兼容链接├── media # U 盘、光盘等可移动介质挂载点├── mnt # 临时手动挂载目录├── opt # 独立安装的第三方软件├── proc # 进程与内核虚拟文件系统├── root # root 用户家目录├── run # 本次启动期间的运行时数据├── sbin # 系统管理命令,现代系统通常链接到 /usr/sbin├── srv # 服务对外提供的数据├── sys # 设备与内核对象虚拟文件系统├── tmp # 通用临时文件├── usr # 应用程序、共享库和静态资源└── var # 日志、数据库、缓存等动态数据
在现代 Ubuntu、Debian、CentOS、Rocky Linux 等系统中,通常已经采用 usr-merge 设计。
因此可能看到:
ls -ld /bin /sbin /lib
输出类似:
/bin -> usr/bin/sbin -> usr/sbin/lib -> usr/lib
这意味着 /bin、/sbin、/lib 仍然保留原有路径,但实际内容已经统一存放到 /usr 下。
五、按功能划分 Linux 核心目录
为了更容易理解,可以将 Linux 根目录划分为六个逻辑区域。
| | |
|---|
| /boot | |
| /usr | |
| /etc | |
| /var | |
| /home | |
| /proc | |
需要注意,/tmp 和 /var/tmp 虽然都用于临时文件,但生命周期有所区别:
/tmp →短生命周期临时文件,可能在重启或定期任务中被清理/var/tmp →期望在重启后继续保留的临时文件
不能简单认为所有 Linux 系统都会在每次重启时清空 /tmp。具体行为取决于发行版和 systemd-tmpfiles 等清理策略。
六、重点目录深度解析
6.1 /boot:系统启动入口
/boot 保存系统启动所需文件,常见内容包括:
/boot/├── grub/├── vmlinuz-*├── initrd.img-*├── config-*└── System.map-*
主要文件作用如下:
| |
|---|
vmlinuz-* | |
initrd.img-* | |
grub/ | |
config-* | |
System.map-* | |
/boot 空间不足时,系统更新可能失败。
Ubuntu 中可以查看已安装内核:
dpkg -l 'linux-image*' | grep '^ii'
查看当前正在运行的内核:
uname -r
删除旧内核时必须保留当前内核,不能直接手工删除正在使用的内核文件。
6.2 /dev:设备文件目录
/dev 中保存设备节点,它们是用户空间访问硬件设备的接口。
常见设备文件包括:
/dev/sda # 第一块 SATA/SCSI 磁盘/dev/sda1 # 第一块磁盘的第一个分区/dev/nvme0n1 # 第一块 NVMe 磁盘/dev/nvme0n1p1 # NVMe 磁盘第一个分区/dev/null # 丢弃所有写入数据/dev/zero # 持续产生零字节/dev/random # 随机数设备/dev/tty # 当前终端/dev/pts/0 # 伪终端
例如,将输出丢弃:
command > /dev/null 2>&1
生成一个 100MB 的测试文件:
ddif=/dev/zero of=test.img bs=1M count=100
设备文件本身不是硬盘中的真实设备数据,而是内核提供的访问入口。
6.3 /home 与 /root:用户数据目录
普通用户的家目录通常位于:
/home/用户名
例如:
/home/alice/home/developer
root 用户的家目录是:
/root
而不是:
/home/root
用户家目录中经常存在隐藏配置文件:
.bashrc.profile.ssh/.config/.local/.cache/
以 . 开头的文件默认不会被普通 ls 命令显示,可以使用:
ls -la
在生产环境中,/home 经常被单独划分分区,以避免用户文件持续增长拖垮根分区。
6.4 /usr:系统程序与共享资源区
/usr 是 Linux 中体积最大的目录之一,通常包含大部分用户空间程序和共享资源。
典型结构:
/usr/├── bin # 普通命令├── sbin # 系统管理命令├── lib # 共享库├── lib64 # 64 位共享库├── local # 本地手工安装的软件├── share # 文档、图标、语言文件等架构无关资源└── src # 源代码
需要特别纠正一个常见误解:
/usr 不是“用户家目录”的缩写,也不是专门存放用户个人文件的目录。
在现代 Linux 中,/usr 主要存放系统安装的软件、命令、共享库和静态资源。
软件来源通常可以这样理解:
/usr →由系统包管理器维护的软件/usr/local →管理员手工安装的本地软件/opt →独立封装的第三方软件
例如:
/usr/bin/java/usr/bin/python3/usr/local/bin/custom-tool/opt/application/
6.5 /opt:独立第三方软件目录
/opt 常用于安装不完全遵循发行版目录结构的第三方软件。
例如:
/opt/google/chrome//opt/idea//opt/company-app/
相比将文件散落到 /usr/bin、/usr/lib 和 /etc,使用 /opt 可以让软件更容易整体安装、升级或删除。
企业内部软件也可以按照公司和应用名称组织:
/opt/company/project-a//opt/company/project-b/
6.6 /run:本次启动期间的运行状态
/run 用于保存当前系统启动周期内的运行时数据,一般由 tmpfs 提供,位于内存中。
常见内容包括:
/run/├── lock/├── log/├── systemd/├── user/└── *.pid
它可能保存:
系统重启后,/run 中的数据通常会消失。
查看其文件系统类型:
findmnt /run
6.7 /tmp:临时文件目录
/tmp 允许应用程序和用户写入临时数据。
典型权限为:
ls -ld /tmp
输出通常类似:
drwxrwxrwt
末尾的 t 表示 Sticky Bit。
它的作用是:
/tmp 适合保存短生命周期数据,不适合保存重要业务文件。
不能依赖“重启一定清空 /tmp”。不同发行版的清理周期可能不同,可通过以下配置查看:
systemd-tmpfiles --cat-config
6.8 /mnt 与 /media:挂载入口
这两个目录都可以用于挂载文件系统,但用途有所区别。
/mnt →管理员手动、临时挂载/media →桌面系统自动挂载 U 盘、光盘等移动设备
例如,临时挂载一块磁盘:
mount /dev/sdb1 /mnt
卸载:
umount /mnt
挂载前应先确认设备与文件系统信息:
lsblk -f
6.9 /srv:服务对外提供的数据
/srv 用于存放服务对外提供的数据,例如:
/srv/www//srv/ftp//srv/git/
但实际生产环境中,不同团队可能使用:
/data/app/apps/www/var/www
因此,/srv 虽然符合标准设计,但并非所有项目都会采用。
七、生产核心目录:/etc
7.1 /etc 的定位
/etc 是系统级配置中心,存放操作系统和服务的全局配置。
典型文件包括:
/etc/├── apt/├── cron.d/├── docker/├── fstab├── group├── hosts├── mysql/├── nginx/├── passwd├── ssh/├── sudoers├── systemd/└── sysctl.conf
常见配置如下:
| |
|---|
/etc/hosts | |
/etc/fstab | |
/etc/passwd | |
/etc/group | |
/etc/ssh/sshd_config | |
/etc/nginx/nginx.conf | |
/etc/mysql/ | |
/etc/docker/daemon.json | |
/etc/systemd/system/ | |
/etc/sysctl.conf | |
7.2 /etc 中不只是普通文本文件
大多数 /etc 配置确实是文本文件,但不能绝对认为其中所有文件都可以随意手工编辑。
部分文件可能:
例如,修改 sudo 配置应使用:
visudo
修改 GRUB 默认参数后,需要重新生成配置:
update-grub
修改 systemd 单元文件后,需要执行:
systemctl daemon-reload
修改 Nginx 配置后,应先验证:
nginx -t
再平滑重载:
systemctl reload nginx
因此,不能笼统认为“修改 /etc 后直接重载服务即可”,需要根据具体软件判断。
7.3 .d 片段目录
现代 Linux 软件经常使用 .d 目录拆分配置。
例如:
/etc/nginx/conf.d//etc/mysql/conf.d//etc/systemd/system/docker.service.d//etc/sudoers.d//etc/cron.d/
这种设计可以:
但需要注意文件加载顺序和命名规则。
例如,可以使用数字前缀控制顺序:
10-base.conf20-performance.conf90-custom.conf
7.4 修改配置的标准流程
生产环境中修改配置,建议遵循以下步骤:
确认当前状态↓备份原配置↓修改配置↓执行语法检查↓重载或重启服务↓验证运行状态↓检查日志
以 Nginx 为例:
cp -a /etc/nginx/nginx.conf \ /etc/nginx/nginx.conf.bak.$(date +%F-%H%M%S)vim /etc/nginx/nginx.confnginx -tsystemctl reload nginxsystemctl status nginx --no-pagerjournalctl -u nginx -n 100 --no-pager
7.5 备份 /etc
可以使用以下命令备份:
tar -czpf /root/etc-backup-$(date +%F-%H%M%S).tar.gz /etc
由于 /etc 是绝对路径,tar 可能提示:
Removing leading `/' from member names
这是正常行为。归档内部通常保存为:
etc/...
查看备份内容:
tar -tzf /root/etc-backup-*.tar.gz | head
不要在未检查内容的情况下直接将整个 /etc 覆盖恢复到另一台不同版本的系统中。
更安全的迁移方法是:
八、生产核心目录:/var
8.1 /var 的定位
var 来自 variable,表示可变数据。
与相对稳定的程序文件相比,/var 中的数据会随着系统运行持续变化。
典型结构:
/var/├── cache├── lib├── local├── lock├── log├── mail├── spool├── tmp└── www
核心子目录如下:
| |
|---|
/var/log | |
/var/lib | |
/var/cache | |
/var/spool | |
/var/tmp | |
/var/www | |
/var/mail | |
8.2 /var/lib:服务持久化状态目录
/var/lib 存储应用程序运行期间需要长期保留的状态数据。
例如:
/var/lib/mysql/var/lib/postgresql/var/lib/docker/var/lib/containerd/var/lib/redis/var/lib/systemd
这些目录中的数据通常不能随意删除。
例如:
/var/lib/mysql → MySQL 数据文件/var/lib/docker → Docker 镜像、容器和卷/var/lib/redis → Redis 持久化文件
删除这些目录可能导致:
8.3 /var/log:故障排查的第一现场
常见日志包括:
/var/log/syslog/var/log/auth.log/var/log/kern.log/var/log/nginx//var/log/mysql//var/log/apt/
但现代 Linux 中,一部分日志可能主要存储在 systemd Journal 中,不一定都以普通文本文件存在于 /var/log。
查看系统日志:
journalctl
查看指定服务:
journalctl -u docker
查看最近 100 行:
journalctl -u docker -n 100 --no-pager
查看本次启动日志:
journalctl -b
查看 Journal 磁盘占用:
journalctl --disk-usage
清理较旧日志:
journalctl --vacuum-time=14d
或者限制总大小:
journalctl --vacuum-size=1G
8.4 /var/cache:可以重建的缓存
/var/cache 中的数据通常可以通过软件重新下载或重新生成。
例如:
/var/cache/apt/var/cache/man
Ubuntu 清理 APT 缓存:
apt clean
查看占用:
du -sh /var/cache/*
虽然缓存通常可以清理,但仍应优先使用软件自身提供的清理命令,不要直接粗暴删除整个目录。
8.5 为什么 /var 容易爆满
常见原因包括:
因此,排查磁盘问题不能只查看文件容量,还要同时检查:
df -hdf -i
其中:
df -h →检查磁盘空间df -i →检查 inode 使用率
九、目录、挂载点与磁盘空间的关系
9.1 目录不等于分区
/var 默认只是根目录下的一个普通目录。
只有当管理员将单独文件系统挂载到 /var 时,它才对应独立分区。
查看挂载关系:
findmnt
查看指定目录:
findmnt /varfindmnt /var/lib/docker
假设输出为:
TARGET SOURCE FSTYPE/ /dev/sda2 ext4/var/lib/docker /dev/sdb1 xfs
这意味着:
因此,判断一个目录实际使用哪块磁盘,不能只看路径,必须查看挂载关系。
9.2 df 与 du 的区别
这是 Linux 磁盘排查中非常重要的一组命令。
df 查看文件系统整体使用情况:
df -hT
du 统计目录树中可见文件的容量:
du -sh /var
二者统计角度不同:
df →文件系统视角du →目录中文件视角
如果出现:
df 显示磁盘已满du 却找不到对应的大文件
常见原因包括:
9.3 已删除文件仍占用磁盘
Linux 删除一个文件时,本质上只是删除目录项。
如果某个进程仍然打开该文件,文件的数据块不会立即释放。
典型场景:
排查命令:
lsof +L1
或者:
lsof | grep '(deleted)'
解决方法通常是:
例如:
systemctl restart application
不要为了释放一个日志文件,直接重启整台服务器。
十、生产实战:Docker 占满 /var/lib/docker
10.1 先确认 Docker 的真实数据目录
Docker 默认数据目录通常是:
/var/lib/docker
但它可以通过 data-root 修改,也可能因 rootless Docker、Snap 安装或其他运行模式而不同。
查看 Docker 实际根目录:
docker info --format '{{.DockerRootDir}}'
也可以执行:
docker info | grep "Docker Root Dir"
假设输出:
Docker Root Dir: /var/lib/docker
后续再针对该目录排查。
不要在未确认的情况下默认所有 Docker 数据都位于 /var/lib/docker。
10.2 Docker 数据目录的主要组成
常见目录如下:
/var/lib/docker/├── buildkit/├── containers/├── image/├── network/├── overlay2/├── plugins/├── runtimes/├── swarm/├── tmp/└── volumes/
主要占用来源如下:
| | |
|---|
overlay2/ | | |
containers/ | | |
volumes/ | | |
image/ | | |
buildkit/ | | |
tmp/ | | |
需要特别注意:
不要直接手工删除 overlay2、image、containers 或 volumes 目录中的未知文件。
Docker 内部存在镜像层、容器、快照和元数据之间的引用关系。手工删除可能导致 Docker 元数据损坏。
10.3 Docker 磁盘膨胀的四类来源
第一类:容器日志无限增长
Docker 使用 json-file 日志驱动时,容器写向标准输出和标准错误的内容会保存到类似路径:
/var/lib/docker/containers/<容器ID>/<容器ID>-json.log
如果没有配置日志轮转,日志可能持续增长到数十 GB,甚至数百 GB。
高风险行为包括:
while (true) { log.info("processing...");}
以及应用频繁输出:
第二类:镜像和构建缓存堆积
CI/CD 或频繁重新构建镜像时,可能产生:
查看镜像:
docker image ls
查看全部镜像,包括中间镜像:
docker image ls -a
查看构建缓存:
docker builder du
第三类:数据卷持续增长
Docker 卷可能保存:
查看数据卷:
docker volume ls
查看某个卷:
docker volume inspect 卷名
匿名卷尤其容易被忽视。删除容器时,如果没有明确删除关联卷,卷可能继续保留。
第四类:容器可写层被当作数据盘
有些应用没有挂载数据卷,而是直接将数据写入容器内部,例如:
/app/logs/app/uploads/tmp/export
这些内容会进入容器可写层,最终体现在 overlay2 占用中。
查看容器可写层大小:
docker ps -a --size
正确做法是将持久化数据挂载到卷或宿主机目录。
十一、Docker 磁盘排查标准流程
11.1 第一步:确认文件系统是否真的已满
df -hT
重点查看:
同时检查 inode:
df -i
如果磁盘空间充足但 inode 使用率达到 100%,通常说明存在海量小文件。
11.2 第二步:确认 Docker 数据目录
docker info --format '{{.DockerRootDir}}'
记录实际目录,例如:
DOCKER_ROOT=$(docker info --format '{{.DockerRootDir}}')echo"$DOCKER_ROOT"
11.3 第三步:查看 Docker 官方统计
docker system df
查看更详细统计:
docker system df -v
该命令会统计:
11.4 第四步:定位宿主机目录占用
du -xhd1 "$DOCKER_ROOT" 2>/dev/null | sort -h
参数含义:
-x →不跨越其他文件系统-h →以易读格式显示-d1 →只统计第一层目录
继续深入某个目录:
du -xhd1 "$DOCKER_ROOT/containers" 2>/dev/null | sort -h
du -xhd1 "$DOCKER_ROOT/volumes" 2>/dev/null | sort -h
11.5 第五步:排查大型容器日志
find "$DOCKER_ROOT/containers" \ -type f \ -name '*-json.log' \ -size +1G \ -printf'%s %p\n' \ | sort -nr \ | numfmt --field=1 --to=iec
也可以简单查看:
find "$DOCKER_ROOT/containers" \ -type f \ -name '*-json.log' \ -size +1G \ -ls
将日志路径映射回容器:
docker inspect \ --format '{{.Name}} {{.LogPath}}' \ $(docker ps -aq)
11.6 第六步:排查已删除但仍占用的文件
lsof +L1
如果发现 Docker 或应用进程仍持有大型已删除日志,应优先处理对应服务,而不是继续删除其他文件。
十二、Docker 磁盘问题的分层解决方案
12.1 第一层:紧急止血
清空超大容器日志
在确认日志可以舍弃后,可以使用截断方式:
truncate -s 0 /var/lib/docker/containers/容器ID/容器ID-json.log
或者:
: > /var/lib/docker/containers/容器ID/容器ID-json.log
相较于直接 rm,截断文件可以保留原 inode,避免 Docker 进程继续持有已删除文件。
但这只是紧急措施,之后必须配置日志轮转。
清理停止的容器
先查看:
docker ps -a --filter status=exited
再清理:
docker container prune
清理悬空镜像
docker image prune
该命令主要清理没有标签且未被容器引用的镜像。
清理所有未使用镜像
docker image prune -a
这会删除未被任何容器使用的镜像。生产环境执行前必须确认后续是否需要快速回滚旧镜像。
清理无用构建缓存
docker builder prune
更彻底地清理:
docker builder prune -a
清理未使用数据卷
先查看:
docker volume ls -f dangling=true
再执行:
docker volume prune
数据卷可能包含业务数据,必须在确认无容器使用、无备份需求后清理。
综合清理
docker system prune
更激进的清理:
docker system prune -a
连未使用卷一起清理:
docker system prune -a --volumes
最后一条风险最高,可能删除未被当前容器引用但仍有业务价值的数据卷,生产环境不应直接执行。
12.2 第二层:限制 Docker 日志增长
编辑:
/etc/docker/daemon.json
配置:
{"log-driver":"json-file","log-opts":{"max-size":"100m","max-file":"3"}}
检查 JSON 格式:
python3 -m json.tool /etc/docker/daemon.json
重启 Docker:
systemctl restart docker
注意:
- 修改
daemon.json 后不需要执行 systemctl daemon-reload daemon-reload
Docker Compose 也可以针对单个服务配置:
services:app:image:example/app:latestlogging:driver:json-fileoptions:max-size:"100m"max-file:"3"
修改后重新创建容器:
docker compose up -d --force-recreate
12.3 使用 local 日志驱动
对于不依赖直接解析 JSON 日志文件的场景,也可以使用 Docker 的 local 日志驱动:
{"log-driver":"local"}
local 驱动通常具有更好的磁盘使用控制和日志轮转能力。
但切换前应确认:
12.4 第三层:将持久化数据移出容器可写层
错误方式:
容器内部:/app/uploads/app/logs/app/data
如果没有挂载,这些数据会写入 overlay2。
推荐方式:
services:app:volumes:-/data/app/uploads:/app/uploads-/data/app/logs:/app/logs
数据库可以使用命名卷:
services:mysql:volumes:-mysql-data:/var/lib/mysqlvolumes:mysql-data:
也可以使用宿主机目录:
services:mysql:volumes:-/data/mysql:/var/lib/mysql
两者各有特点:
12.5 第四层:将 Docker 数据目录放到独立磁盘
方案一:独立挂载 /var/lib/docker
这种方式对 Docker 配置改动较少:
/dev/sdb1 → /var/lib/docker
大致迁移流程如下:
systemctl stop dockerrsync -aHAXx --numeric-ids \ /var/lib/docker/ \ /mnt/new-docker/mv /var/lib/docker /var/lib/docker.bakmkdir -p /var/lib/dockermount /dev/sdb1 /var/lib/docker
配置 /etc/fstab 后再启动:
systemctl start docker
迁移前必须:
方案二:修改 Docker data-root
在 /etc/docker/daemon.json 中配置:
{"data-root":"/data/docker","log-driver":"json-file","log-opts":{"max-size":"100m","max-file":"3"}}
之后停止 Docker,迁移数据:
systemctl stop dockermkdir -p /data/dockerrsync -aHAXx --numeric-ids \ /var/lib/docker/ \ /data/docker/
启动并验证:
systemctl start dockerdocker info --format '{{.DockerRootDir}}'docker ps
不要在 Docker 运行过程中直接复制整个数据目录,否则可能得到不一致的数据副本。
12.6 第五层:建立监控和预警
仅依赖定时清理并不可靠,更合理的做法是配置磁盘告警。
建议至少监控:
文件系统使用率inode 使用率Docker Root Dir 容量单个容器日志大小Docker 卷增长速度数据库目录增长速度Journal 日志占用
建议设置分级阈值:
70% →提醒关注80% →发出告警90% →紧急处理95% →可能影响业务写入
可以使用以下工具:
简单检查脚本示例:
#!/usr/bin/env bashset -euo pipefailTHRESHOLD=80df -P | awk -v threshold="$THRESHOLD"'NR > 1 { usage = $5 gsub("%", "", usage) if (usage >= threshold) { printf "WARNING: %s usage is %s%%, mounted on %s\n", $1, usage, $6 }}'
十三、定时清理是否可靠
可以通过定时任务清理无用镜像,例如:
30 3 * * * /usr/bin/docker image prune -f --filter "until=168h" >> /var/log/docker-prune.log 2>&1
这里的 168h 表示 7 天。
相比每天清理 24 小时前的镜像,保留 7 天通常能够提供一定回滚窗口。
但自动清理存在风险:
因此,更合理的策略是:
日志轮转 +容量监控 +镜像保留策略 +人工审核式清理 +独立磁盘隔离
而不是单纯依赖 docker system prune -a -f。
十四、Linux 磁盘爆满通用排查流程
当系统提示:
No space left on device
可以按以下顺序排查。
14.1 查看空间和 inode
df -hTdf -i
14.2 确认目标目录所在文件系统
findmnt /varfindmnt /var/lib/docker
14.3 从根目录逐层定位
du -xhd1 / 2>/dev/null | sort -h
如果 /var 较大:
du -xhd1 /var 2>/dev/null | sort -h
如果 /var/lib 较大:
du -xhd1 /var/lib 2>/dev/null | sort -h
14.4 查找大文件
查找大于 1GB 的文件:
find /var -xdev -type f -size +1G -printf'%s %p\n' \ | sort -nr \ | numfmt --field=1 --to=iec
14.5 排查已删除文件
lsof +L1
14.6 排查 systemd Journal
journalctl --disk-usage
14.7 排查 Docker
docker system df -v
14.8 排查数据库
MySQL 常见增长来源:
数据文件binlog慢查询日志普通查询日志错误日志临时文件备份文件
例如检查 binlog:
SHOWBINARY LOGS;
查看过期配置:
SHOW VARIABLES LIKE'expire_logs_days';SHOW VARIABLES LIKE'binlog_expire_logs_seconds';
执行日志清理前必须确认复制、备份和时间点恢复需求。
十五、目录权限与安全边界
Linux 目录设计不仅是为了分类,也用于建立权限边界。
例如:
/etc →通常只有 root 可以修改/home →用户管理自己的数据/tmp →所有人可写,但有 Sticky Bit/root →仅 root 可访问/var/log →通常由 root 或特定服务用户管理
查看权限:
ls -ld /etc /home /root /tmp /var/log
不要通过以下方式“解决权限问题”:
chmod -R 777 /var/lib/dockerchmod -R 777 /var/lib/mysqlchmod -R 777 /etc
这会破坏系统安全边界,甚至导致服务拒绝启动。
正确做法是先确认:
namei -l /目标路径
然后根据服务账户设置:
chownchmodsetfacl
例如:
chown -R mysql:mysql /data/mysqlchmod 750 /data/mysql
十六、目录设计思想在软件架构中的迁移
Linux 目录体系的本质是:
按用途、生命周期、变更频率和安全边界组织资源。
这种思想可以直接迁移到软件系统设计中。
16.1 配置与代码分离
Linux:
/usr/bin/app →程序/etc/app/app.conf →配置
应用系统:
JAR 或镜像→程序代码Nacos、Apollo →动态配置环境变量→部署配置Secret →敏感配置
配置与代码分离后,可以在不重新编译程序的情况下切换环境和调整参数。
16.2 日志与业务数据分离
Linux:
/var/log →日志/var/lib →状态和业务数据
应用架构:
日志系统→ Elasticsearch、Loki关系数据→ MySQL、PostgreSQL对象数据→ MinIO、OSS缓存数据→ Redis
不同类型的数据应使用不同的存储系统,而不是全部写入同一个目录或数据库。
16.3 临时数据与持久数据分离
Linux:
/tmp →短生命周期/var/tmp →跨重启临时数据/var/lib →持久化状态
应用架构:
本地缓存→可丢失Redis 缓存→可重建数据库→必须持久化对象存储→长期文件消息队列→有限生命周期
只有明确数据生命周期,才能制定正确的清理、备份和容灾策略。
16.4 高增长目录独立隔离
Linux 中通常将以下目录独立挂载:
/home/var/var/log/var/lib/docker/data
软件架构中同样应将高增长组件隔离:
应用程序磁盘数据库磁盘日志磁盘上传文件磁盘备份磁盘
这样即使日志磁盘写满,也不会直接导致数据库或系统根分区不可用。
十七、核心目录速查表
| | | |
|---|
/boot | | | |
/dev | | | |
/etc | | | |
/home | | | |
/root | | | |
/usr | | | |
/usr/local | | | |
/opt | | | |
/run | | | |
/tmp | | | |
/var | | | |
/var/log | | | |
/var/lib | | | |
/var/lib/docker | | | |
/proc | | | |
/sys | | | |
/mnt | | | |
十八、本篇关键命令汇总
查看目录与文件系统关系
findmntfindmnt /var/lib/dockerlsblk -fdf -hTdf -i
查看目录占用
du -xhd1 /var 2>/dev/null | sort -hdu -sh /var/log
查找大文件
find /var -xdev -type f -size +1G -ls
查找已删除但仍占用空间的文件
lsof +L1
查看 systemd 日志占用
journalctl --disk-usage
查看 Docker 占用
docker info --format '{{.DockerRootDir}}'docker system dfdocker system df -vdocker ps -a --sizedocker builder du
查看目录权限
ls -ld /etc /home /root /tmp /var/lognamei -l /目标路径
十九、总结
Linux 目录体系不是一组需要机械背诵的路径,而是一套围绕资源类型、数据生命周期和安全边界建立的系统设计。
本篇需要掌握以下核心结论:
- Linux 使用唯一的根目录
/ 组织所有文件系统,不使用 Windows 式盘符作为顶层入口 - “一切皆文件”强调的是统一接口抽象,并不意味着所有文件都真实占用磁盘
- 目录是逻辑路径,挂载点决定数据实际位于哪个文件系统
/etc 是系统和服务配置中心,修改配置必须遵循“备份、验证、重载、检查日志”的流程/var 保存持续变化的数据,是生产环境最容易发生磁盘故障的区域/var/lib 中通常保存重要应用状态,不能像缓存目录一样随意删除- Docker 磁盘膨胀主要来自容器日志、镜像层、构建缓存、数据卷和容器可写层
- Docker 治理不能只依靠
prune,还需要日志轮转、数据外置、独立磁盘和容量监控 df 与 du 统计角度不同,磁盘已满但找不到大文件时,应排查已删除但仍被进程占用的文件