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