磁盘空间和 inode 似乎会消耗 Docker 容器中的 RAM

磁盘空间和 inode 似乎会消耗 Docker 容器中的 RAM

我试图确定为什么在 Docker 化环境中运行的 Web 服务器消耗的内存比我预期的要多。在调查这个问题时,我发现以下行为对我来说没有意义:

  • 创建一个 Docker 容器,docker run -d --name test ubuntu:22.04 tail -f /dev/null
  • 检查docker stats,内存使用情况报告为400KiB / 62.68GiB
  • 检查docker top test,只有一个进程在运行 ( tail -f /dev/null)
  • 现在在运行的容器中创建大量的文件和目录,例如docker exec -it test sh -c 'apt update && apt install -y git && git clone https://git.savannah.gnu.org/git/emacs.git'
  • 再次检查docker stats,内存使用情况现在报告为494.9MiB / 62.68GiB
  • 检查docker top test并确认仍然只有一个进程在运行 ( tail -f /dev/null)

那么我的 Docker 容器中这近 500MB 的内存是用来做什么的呢?我的印象是内存被进程消耗了,但容器中只有一个进程,它不能负责。

内存使用量是来自文件系统吗?主机系统正在运行 btrfs。我使用 Docker 的默认存储驱动程序overlay2,以下是有关覆盖文件系统的信息:

% docker inspect test | jq '.[].GraphDriver.Data.MergedDir' -r
/var/lib/docker/overlay2/559d41c89d074c45a1ae89109ebec145e8fd4d929151819f08aaae26f73f7bda/merged

% mount | grep overlay
overlay on /var/lib/docker/overlay2/559d41c89d074c45a1ae89109ebec145e8fd4d929151819f08aaae26f73f7bda/merged type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/JADZ5UYJDWI5EDAAGALCRWUM3B:/var/lib/docker/overlay2/l/JK3H4JAYNUAUSN2W3V2DQ6TB6C,upperdir=/var/lib/docker/overlay2/559d41c89d074c45a1ae89109ebec145e8fd4d929151819f08aaae26f73f7bda/diff,workdir=/var/lib/docker/overlay2/559d41c89d074c45a1ae89109ebec145e8fd4d929151819f08aaae26f73f7bda/work)

% sudo ls -lAd /var/lib/docker/overlay2/l/JADZ5UYJDWI5EDAAGALCRWUM3B/emacs                                           
ls: cannot access '/var/lib/docker/overlay2/l/JADZ5UYJDWI5EDAAGALCRWUM3B/emacs': No such file or directory

% sudo ls -lAd /var/lib/docker/overlay2/l/JK3H4JAYNUAUSN2W3V2DQ6TB6C/emacs
ls: cannot access '/var/lib/docker/overlay2/l/JK3H4JAYNUAUSN2W3V2DQ6TB6C/emacs': No such file or directory

% sudo ls -lAd /var/lib/docker/overlay2/559d41c89d074c45a1ae89109ebec145e8fd4d929151819f08aaae26f73f7bda/diff/emacs
drwxr-xr-x 1 root root 602 Feb 25 16:08 /var/lib/docker/overlay2/559d41c89d074c45a1ae89109ebec145e8fd4d929151819f08aaae26f73f7bda/diff/emacs

% sudo ls -lAd /var/lib/docker/overlay2/559d41c89d074c45a1ae89109ebec145e8fd4d929151819f08aaae26f73f7bda/merged/emacs
drwxr-xr-x 1 root root 602 Feb 25 16:08 /var/lib/docker/overlay2/559d41c89d074c45a1ae89109ebec145e8fd4d929151819f08aaae26f73f7bda/merged/emacs

由于我的基本文件系统是 btrfs,因此我希望克隆的 Git 存储库将驻留在 btrfs 的磁盘上(在 下/var/lib/docker/overlay2/559d41c89d074c45a1ae89109ebec145e8fd4d929151819f08aaae26f73f7bda/diff),该存储库不在任何特殊卷挂载内,因此不会消耗内存。

如果我删除添加的文件和目录,例如使用docker exec test rm -rf emacs,那么内存使用量会立即下降到68.16 MiB / 62.68GiB

问题:

  1. 是什么消耗了这个内存?我对 Docker 容器或文件系统有什么误解overlay2,导致我认为向工作层添加文件和目录不应该消耗内存?
  2. 有没有办法通过使用 Docker 守护程序或容器化工作负载更改配置来减少 Docker 容器中除进程 RAM 之外的其他来源消耗的内存量?

我在 [docker] 标签下查看了 UNIX Stack Exchange 上的现有问题,但找不到任何有助于我理解的问题。我还读过关于overlayfs 的官方 Docker 文档并简要回顾了Linux 内核中 overlayfs 的文档,但没有找到任何可以解释内存使用情况的信息。

我的最终目标是减少生产中运行的空闲 Docker 化工作负载的资源消耗,因为我是根据 CPU 和内存使用情况计费的。根据生产指标,我观察到我的工作负载在空闲时没有释放尽可能多的内存,即使使用该内存的进程已被终止。

上述测试是在具有 Docker CE 23.0.1 的 Pop!_OS 22.04 和使用 btrfs 的 Linux 内核 6.0.12 上执行的。

我的 btrfs 的系统参数

/etc/fstab显示了文件系统的挂载选项 - 基本上,所有内容的默认值:

UUID=ee43bc7e-1d9d-4300-a46a-5d2d772a1e0f / btrfs defaults,subvol=@ 0 0
UUID=ee43bc7e-1d9d-4300-a46a-5d2d772a1e0f /.snapshots btrfs defaults,subvol=@snapshots 0 0
UUID=127d5704-324b-4a50-97bf-9f5f4646ddd5 /home/raxod502 btrfs defaults,subvol=@ 0 0
UUID=127d5704-324b-4a50-97bf-9f5f4646ddd5 /home/raxod502/.snapshots btrfs defaults,subvol=@snapshots 0 0

我使用自动拍摄快照鲷鱼,我的保留政策总共保留了大约 100 个:

% sudo snapper -c system list | wc -l
41
% sudo snapper -c home list | wc -l  
44

我从未明确使用过引用链接,也没有据我所知自动创建它们的软件。如上所述,我最先进的 btrfs 用法是 snapper。

这是输出sudo slaptop -ohttps://gist.github.com/raxod502/843a6580dc6fa0f9949d2f4dc03b5c23

很高兴提供任何可能有帮助的进一步系统配置详细信息。

ext4 的比较

我在另一个 Linux 系统上的 ext4 上运行了相同的测试,得到了大致相同的结果。克隆之前的内存使用量为328KiB / 969.4MiB.克隆后的内存使用量为233.5MiB / 969.4MiB.删除克隆存储库后的内存使用率为16.84MiB / 969.4MiB

答案1

这似乎与文件系统无关,而是与文件缓存有关。为了重现这个确切的情况,我首先将 Emacs 存储库克隆到/usr/src/emacs.

$ docker run --mount type=bind,source=/usr/src/emacs/.git,destination=/mnt/emacs.git,ro -d --rm --name test debian tail -f /dev/null
$ docker exec -it test sh -c 'apt update && apt install -y git && git clone /mnt/emacs.git'
$ docker stats --no-stream test
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O    PIDS
c468a203b9d2   test      0.00%     46.91MiB / 15.52GiB   0.30%     30.5MB / 667kB   0B / 786MB   1

就我而言,我没有获得约 500MiB 内存使用量,我猜测这与文件系统有关,但重点是最终的内存使用量与从网络克隆时的内存使用量大致相同。接下来,我删除了生成的/emacs目录,内存使用量下降了。

$ docker exec -it test rm -rf /emacs
$ docker stats --no-stream test
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O    PIDS
c468a203b9d2   test      0.00%     15.84MiB / 15.52GiB   0.10%     30.5MB / 667kB   0B / 786MB   1

为了测试这是否与文件系统相关,我跳过了克隆部分,并尝试从位于同一安装中的另一个目录中mv访问该目录。emacs假设/usr/src/emacs和容器的overlay上层目录在同一个块设备挂载中。

$ docker run -d --rm --name test debian tail -f /dev/null
$ TEST_UPPER=$(docker container inspect --format '{{.GraphDriver.Data.UpperDir}}' test)
$ mv -v /usr/src/emacs "$TEST_UPPER/emacs"
renamed '/usr/src/emacs' -> '/var/lib/docker/overlay2/dc228a74029510c61ce454549248132bec806686c4da9eac8909d89595ff5a32/diff/emacs'
$ docker stats --no-stream test
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT   MEM %     NET I/O         BLOCK I/O   PIDS
e700998acb86   test      0.00%     400KiB / 15.52GiB   0.00%     3.79kB / 866B   0B / 0B     1

再次,但docker cp-ing 到根目录中的容器目录:

$ docker run -d --rm --name test debian tail -f /dev/null
$ docker exec -it test mkdir /emacs
$ tar -C /usr/src/emacs -c . | docker cp - test:/emacs
$ docker exec -i ls -la /emacs | wc -l
42
$ docker stats --no-stream test
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT   MEM %     NET I/O         BLOCK I/O   PIDS
4ea520193998   test      0.00%     444KiB / 15.52GiB   0.00%     3.79kB / 866B   0B / 0B     1

据此,似乎所有内存使用量都是apt和使用的缓存内存git。建议很简单,不要在运行时修改容器。

相关内容