问题发现
最近在测试遇到一个问题,容器日志过大导致系统磁盘爆满,造成的影响就是该服务器的一些服务挂掉了。日志过大,解决办法就是删啊,间接定位到容器日志地位发现高达 5G,三下五除二就 rm 了,然而依然显示磁盘空间已满,然而 du -sh 命令也显示文件夹下为空。通过共事百度得悉,容器日志不能间接 rm,要通过 cat /dev/null > {log 文件}形式将日志删除。因为该文件被过程所援用,间接删除并不能擦除磁盘上的文件 block 信息,解决形式就是进行过程。明天就来复现一下并探索一下底层原理。
复现形式
因为容器也属于一种过程,援用文件的形式并没有不同于其余一般过程,所以就应用一个简略的 demo 复现。
首先进入一个目录,以 /tmp/testfile 目录为例, 能够看到我的服务器还有 2G 的残余空间
[centos@guozhao testfile]$ cd /tmp/testfile
[centos@guozhao testfile]$ df -h .
文件系统 容量 已用 可用 已用 % 挂载点
/dev/vda1 50G 49G 2.0G 97% /
而后生成一个随机文件,再来查看残余空间,看到还残余 1014M 空间
[centos@guozhao testfile]$ dd if=/dev/urandom of=/tmp/testfile/delfiletest bs=1M count=1000
记录了 1000+0 的读入
记录了 1000+0 的写出
1048576000 字节 (1.0 GB) 已复制,8.31491 秒,126 MB/ 秒
[centos@guozhao testfile]$
[centos@guozhao testfile]$ df -h .
文件系统 容量 已用 可用 已用 % 挂载点
/dev/vda1 50G 49G 1014M 99% /
启动一个程序,援用该文件,不退出过程
func main() {file, err := os.Open("/tmp/testfile/delfiletest")
defer file.Close()
if err != nil{fmt.Println("open err :",err.Error())
return
}
time.Sleep(100*time.Minute)
}
接下来删除该文件文件,查看磁盘占用状况,看到尽管文件删除,然而磁盘空间并没有开释。
[centos@guozhao testfile]$ rm -rf delfiletest
[centos@guozhao testfile]$ df -h .
文件系统 容量 已用 可用 已用 % 挂载点
/dev/vda1 50G 49G 1015M 99% /
解决办法,找到援用该文件的过程,并进行该过程,能够 Ctrl+ C 进行,也能够 kill 命令进行。
[centos@guozhao testfile]$ lsof|grep deleted|grep delfile
lsof: WARNING: can't stat() proc file system /run/docker/netns/default
Output information may be incomplete.
......
testdelet 29604 29608 centos 3r REG 253,1 1048576000 58925156 /tmp/testfile/delfiletest (deleted)
......
[centos@guozhao testfile]$ kill 29604
进行后再次查看磁盘空间,发现磁盘曾经开释。
[centos@guozhao-170 testfile]$ df -h .
文件系统 容量 已用 可用 已用 % 挂载点
/dev/vda1 50G 49G 2.0G 97% /
重做一次下面的步骤,这次咱们应用正确的形式开释磁盘的空间,能够看到磁盘空间腾出来了。
[centos@guozhao-170 testfile]$ cat /dev/null > /tmp/testfile/delfiletest
[centos@guozhao-170 testfile]$
[centos@guozhao-170 testfile]$ df -h .
文件系统 容量 已用 可用 已用 % 挂载点
/dev/vda1 50G 49G 2.0G 97% /
起因探索
持续深挖一下造成这种后果的起因。
在 Linux 上,每个文件都有一个本人对应的索引节点即 inode,在这个inode 里记录了文件在磁盘的块信息,以及链接数量等信息,一个文件在是否要被真正删除开释空间,取决于两个值,一个是i_count,代表援用计数;一个是i_nlink,代表硬链接数量,只有当两个都为 0,文件才会真正开释。
struct inode{
atomic_t i_count;
unsignet int i_nlink;
......
};
当有过程应用该文件时候,i_count就会加 1,当过程不在援用或过程完结,就会减一。
硬链接也是如此,当为文件创建一个硬链接时,i_nlink就会加一,删除就会减一,当缩小为 0 时候,就会删除文件,开释空间。
在 Linux 中,硬链接指的是文件名与 inode 的链接,通常创立一个文件对应一个硬链接,咱们能够手动通过 ln 命令或者程序触发 link 零碎调用为一个文件创建一个硬链接,相当于两个文件名对应了同一个磁盘文件,两个都删除才会删除磁盘文件(没有过程援用的前提下)。而 Linux 的 rm 命令相当于执行了 unlink 零碎调用,会使得 i_nlink 数量减一。
当然,因为文件并没有被真正删除,所以该文件是能够复原的,只须要找到过程的 pid,并进入 /proc/{pid}/fd 中,找到对应的文件描述符,执行 cp 命令复制即可找回文件。