我不能 100% 确定这是一个 U&L 问题还是一个所以问题。总的来说,我将其发布在 U&L 上,因为它与操作系统相关。
背景
据我所知,Linux 将通过内存映射为写入时复制的方式加载共享库(.so 文件)。这样做的优点之一是,共享同一个大型库的多个进程将共享该库的大部分内容的相同物理 RAM。
Docker 不一定会发生这种情况,因为进程在基于“映像”的自己的“容器”中运行,并且每个映像都包含它自己的共享库副本。这是故意的。它允许程序附带自己的依赖项(库),这些依赖项可能与系统上已安装的库有很大不同。
因此,在 docker 主机上本机运行的程序不会与在 docker 容器内运行的程序共享相同的库内存,因为 docker 容器中的程序已映射到库的不同副本。
Docker 层解释
Docker 镜像是分层创建的。每一层都会添加到下一层,有时会覆盖现有文件。并非每个层中的每个文件都会更改。
Docker 允许您通过向旧映像添加新层来创建新映像。发生这种情况时,您最终会得到多个共享相同图层的图像。这些图像共享某些相同文件的相同副本。
Docker 至少将各层分开前运行。例如:当从 Docker Hub 拉取镜像时,Docker 通过获取每个镜像的组成层来获取镜像。它只获取它还没有的图层。
我不知道什么
创建或运行容器时,Docker 必须将各层组装成单个一致的文件系统。我不知道它是如何做到这一点的。它可以:
- 将文件复制到一处
- 在一处创建硬链接
- 使用覆盖文件系统
根据它的作用,源自同一层的文件可能是相同的副本,也可能是文件系统上完全相同的文件。
这最终将影响文件由多个进程进行内存映射时发生的情况。
我真正想发现什么?
我想知道从两个不同的映像运行两个容器是否会为源自单层的单个共享库共享相同的 RAM。
答案1
至少在某些配置中,容器可以共享不同镜像中同一层文件的内存映射。
这是一个实验来证明这一点。我使用两个不同的图像,一个基于另一个:
$ docker history 5f35156022ae
IMAGE CREATED CREATED BY SIZE COMMENT
5f35156022ae 7 weeks ago COPY scripts/shared/ . # buildkit 1.05MB buildkit.dockerfile.v0
<missing> 7 weeks ago WORKDIR /opt/shipyard/scripts 0B buildkit.dockerfile.v0
...
$ docker history 569bf4207a08
IMAGE CREATED CREATED BY SIZE COMMENT
569bf4207a08 7 weeks ago /bin/sh -c #(nop) CMD ["sh"] 0B
ed9510deb54e 7 weeks ago /bin/sh -c #(nop) ENTRYPOINT ["/opt/shipyar… 0B
c3e0351f0dd2 7 weeks ago /bin/sh -c #(nop) WORKDIR /go/src/github.com… 0B
a476f9f2b118 7 weeks ago /bin/sh -c #(nop) ENV DAPPER_OUTPUT=/go/src… 0B
29a76c4ff3e7 7 weeks ago /bin/sh -c #(nop) ENV DAPPER_ENV=QUAY_USERN… 0B
2f4a590d61ef 7 weeks ago /bin/sh -c #(nop) ARG PROJECT 0B
5f35156022ae 7 weeks ago COPY scripts/shared/ . # buildkit 1.05MB buildkit.dockerfile.v0
<missing> 7 weeks ago WORKDIR /opt/shipyard/scripts 0B buildkit.dockerfile.v0
...
我启动了两个容器,仅使用入口点 shell:
$ pstree -p
...
├─containerd-shim(530457)─┬─bash(530477)
│ ├─{containerd-shim}(530458)
...
├─containerd-shim(530622)─┬─entry(530643)───sh(530685)
│ ├─{containerd-shim}(530624)
...
让我们检查一下这两个 shell 使用的 C 库:
$ sudo grep libc-2.33 /proc/{530477,530685}/maps
/proc/530477/maps:7fc127f81000-7fc127fa7000 r--p 00000000 00:1f 3117 /usr/lib64/libc-2.33.so
/proc/530477/maps:7fc127fa7000-7fc1280f4000 r-xp 00026000 00:1f 3117 /usr/lib64/libc-2.33.so
/proc/530477/maps:7fc1280f4000-7fc128140000 r--p 00173000 00:1f 3117 /usr/lib64/libc-2.33.so
/proc/530477/maps:7fc128140000-7fc128141000 ---p 001bf000 00:1f 3117 /usr/lib64/libc-2.33.so
/proc/530477/maps:7fc128141000-7fc128144000 r--p 001bf000 00:1f 3117 /usr/lib64/libc-2.33.so
/proc/530477/maps:7fc128144000-7fc128147000 rw-p 001c2000 00:1f 3117 /usr/lib64/libc-2.33.so
/proc/530685/maps:7f6a5df94000-7f6a5dfba000 r--p 00000000 00:1f 3117 /usr/lib64/libc-2.33.so
/proc/530685/maps:7f6a5dfba000-7f6a5e107000 r-xp 00026000 00:1f 3117 /usr/lib64/libc-2.33.so
/proc/530685/maps:7f6a5e107000-7f6a5e153000 r--p 00173000 00:1f 3117 /usr/lib64/libc-2.33.so
/proc/530685/maps:7f6a5e153000-7f6a5e154000 ---p 001bf000 00:1f 3117 /usr/lib64/libc-2.33.so
/proc/530685/maps:7f6a5e154000-7f6a5e157000 r--p 001bf000 00:1f 3117 /usr/lib64/libc-2.33.so
/proc/530685/maps:7f6a5e157000-7f6a5e15a000 rw-p 001c2000 00:1f 3117 /usr/lib64/libc-2.33.so
两个映射库具有相同的设备和索引节点,因此它们是相同的文件,并且它们的映射将在可能的情况下共享。