「故事会」一次处理磁盘空间不足的记录

2022-03-02, 星期三, 22:05

赛博驱鬼故事会DevOpsTroubleshooting培训

2022-03-02 Update

在 Linux 文件系统中,rm /home/john/example.log 并不会立即删除这个文件。文件系统将等待这个文件的连接数降为 1,确保没有任何进程打开这个文件,才会执行删除动作释放空间。因此删除程序的日志文件很可能不会立即产生效果。

要找出哪个进程在使用文件,可使用 lsof(LiSt Open Files)。

When +L is specified without a following number, all link counts will be listed. When -L is specified (the default), no link counts will be listed.

When +L is followed by a number, only files having a link count less than that number will be listed. (No number may follow -L.) A specification of the form +L1 will select open files that have been unlinked. A specification of the form +aL1 <file_system> will select unlinked open files on the specified file system.

# 创建一个文件并使用 tail 保持对文件的访问
echo 'hello world' >> example.log
tail -f example.log

# 删除文件
rm -f example.log

# 使用 lsof 查找已删除但仍有进程读写的文件
lsof +L1

# 新一些的 lsof 版本会为删除的文件添加 `deleted` 修饰,可以使用 grep 进一步过滤结果
lsof +L1 | grep deleted

输出摘要为下(略去无关内容)

COMMAND  PID    USER  FD  TYPE DEVICE  SIZE/OFF  NLINK  NODE    NAME
tail     34958  john  3r  REG  253,0   12        0      579054  /home/john/example.log (deleted)

可以看到 ID 为 4344 的进程正在占用该文件,使用 kill -2 终止该进程即可。


在一次项目更新操作中,备份原 jar 包的脚本返回 cp: error writing 'xxx.bak': No space left on device,磁盘空间已耗尽。

查看磁盘剩余空间,/dev/vda1 只有 60GB。磁盘 /vda 应该是 ECS 的系统盘,此时已达到 100% 占用率。

$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        59G   59G     0 100% /
devtmpfs        3.9G     0  3.9G   0% /dev
tmpfs           3.9G     0  3.9G   0% /dev/shm
tmpfs           3.9G  400K  3.9G   1% /run
tmpfs           3.9G     0  3.9G   0% /sys/fs/cgroup
tmpfs           783M     0  783M   0% /run/user/0

查看其他磁盘,有一个 536.9GB 的磁盘未被使用过,应该是 ECS 的数据盘。

$ lsblk -f
NAME   FSTYPE LABEL UUID                                 MOUNTPOINT
vda
`-vda1 ext3         07151862-c2b9-45dc-bf7a-af8d2a6fa6c1 /
vdb

$ fdisk -l
Disk /dev/vda: 64.4 GB, 64424509440 bytes, 125829120 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk label type: dos
Disk identifier: 0x0000efd2

   Device Boot      Start         End      Blocks   Id  System
/dev/vda1   *        2048   125827071    62912512   83  Linux

Disk /dev/vdb: 536.9 GB, 536870912000 bytes, 1048576000 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes

使用 fdisk 为未使用的磁盘分区 fdisk /dev/vdb 将在磁盘 vdb 上创建分区 /dev/vdb1

为了和系统盘使用的文件系统保持一致,数据盘也使用 mkfs -t ext3 /dev/vdb1 格式化为 ext3 格式。此过程中需要交互式地选择一些参数,具体内容需要参考 mkfs 的相关资料。

通过 mount /dev/vdb1 /opt 挂载新分区到 /opt。再次查询可用空间,发现增加了 467GB。

$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        59G   59G     0 100% /
devtmpfs        3.9G     0  3.9G   0% /dev
tmpfs           3.9G     0  3.9G   0% /dev/shm
tmpfs           3.9G  376K  3.9G   1% /run
tmpfs           3.9G     0  3.9G   0% /sys/fs/cgroup
tmpfs           783M     0  783M   0% /run/user/0
/dev/vdb1       493G   70M  467G   1% /opt

编辑 /etc/fstab 添加一条记录以使系统重新启动时自动挂载设备,格式为 UUID=[UUID] [mount-point] [fstype] defaults 1 2。磁盘的 UUID、挂载点、文件系统类型均可通过 lsblk -f 查询,其他参数的配置参考 fstab 文档。本例中使用如下配置:

UUID=21b989f2-f5a9-42a8-96d4-89d35a8fcb2e /opt ext3 defaults 1 2

接下来清理系统盘,根据对业务的了解,估计是系统运行中产生的日志文件和中间文件耗尽了磁盘空间。项目的可执行文件位于 /usr/local/ 目录,从该目录开始查找。

du 命令可以输出指定目录下文件夹的大小。可以看到 /usr/local/picture 目录占用了 56GB 空间。

$ du -h --max-depth=1 /usr/local | sort -rh
57G     /usr/local
56G     /usr/local/picture
149M    /usr/local/backups
82M     /usr/local/test
25M     /usr/local/nginx
4.0K    /usr/local/lib64
4.0K    /usr/local/etc
4.0K    /usr/local/sbin
4.0K    /usr/local/src
4.0K    /usr/local/bin
4.0K    /usr/local/include

尝试把 /usr/local/picture/ 移动到 /opt/smart-claims/picture/。由于这是两个磁盘间的复制操作,整个过程耗时将长达数小时,建议在 tmux 创建的 session 中执行,防止堡垒机长时间未检测到用户操作断开连接。

目标机器没有安装 tmux,且磁盘已经没有空闲空间可以安装软件包了,故使用 nohup mv /usr/local/picture /opt/smart-claims/ & 命令在后台运行作业。可用通过 jobs 等作业相关命令查看执行情况。

通过查证源代码确定了应用程序使用绝对路径保存文件,使用 ln -s /opt/smart-claims/picture /usr/local/picture 在原目录位置创建指向新路径的软链接。

ll /usr/local/picture 输出 /usr/local/picture -> /opt/smart-claims/picture,此时访问 /usrlocal/picture/README 实际上是在访问位于 /opt/smart-claims/picture/ 的 README 文件,因此程序的执行基本不会受到影响。

查看可用磁盘空间,问题得到了解决。

$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        59G  3.5G   53G   7% /
devtmpfs        3.9G     0  3.9G   0% /dev
tmpfs           3.9G     0  3.9G   0% /dev/shm
tmpfs           3.9G  348K  3.9G   1% /run
tmpfs           3.9G     0  3.9G   0% /sys/fs/cgroup
/dev/vdb1       493G   54G  414G   1% /opt
tmpfs           783M     0  783M   0% /run/user/0