我试图确定为什么在 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
。
问题:
- 是什么消耗了这个内存?我对 Docker 容器或文件系统有什么误解
overlay2
,导致我认为向工作层添加文件和目录不应该消耗内存? - 有没有办法通过使用 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 -o
:https://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
。建议很简单,不要在运行时修改容器。