如何:Docker 重用具有不同基础镜像的层

Ako*_*kom 10 docker dockerfile

我正在做跨平台测试(工具,而不是内核),所以我为每个操作系统都有一个自定义映像(用于临时 Jenkins 从属),基于标准基础映像:centos6、centos7、ubuntu14、sles11、sles12 等。

除了基础不同之外,我的图像彼此之间有很多共同点(为了速度,它们都获得了预先构建且经常更改的 maven/gradle/npm 存储库的副本)。

这是创建图像方式的简化示例(图像中的 tarball 相同):

   # Dockerfile one
   FROM centos:centos6
   ADD some-files.tar.gz

   # Dockerfile two
   FROM ubuntu:14.04
   ADD some-files.tar.gz
Run Code Online (Sandbox Code Playgroud)

这导致必须定期重建大图像(多 GB)。由于 docker 构建缓存,一些层重用发生在重建之间,但如果我可以完全停止重建图像,那就更好了。

如何在我的图像之间可靠地共享公共内容?

在这些目录之外,图像不会发生太大变化。这不能是一个简单的挂载卷,因为在使用中这一层中的目录被修改,所以它不能是只读的,并且源不能被更改(所以我正在寻找更接近 COW 但应用于特定图像的子集)

BMi*_*tch 6

--cache-from 的问题:

使用建议--cache-from不起作用:

$ cat df.cache-from
FROM busybox
ARG UNIQUE_ARG=world
RUN echo Hello ${UNIQUE_ARG}
COPY . /files

$ docker build -t test-from-cache:1 -f df.cache-from --build-arg UNIQUE_ARG=docker .
Sending build context to Docker daemon   26.1MB
Step 1/4 : FROM busybox
 ---> 54511612f1c4
Step 2/4 : ARG UNIQUE_ARG=world
 ---> Running in f38f6e76bbca
Removing intermediate container f38f6e76bbca
 ---> fada1443b67b
Step 3/4 : RUN echo Hello ${UNIQUE_ARG}
 ---> Running in ee960473d88c
Hello docker
Removing intermediate container ee960473d88c
 ---> c29d98e09dd8
Step 4/4 : COPY . /files
 ---> edfa35e97e86
Successfully built edfa35e97e86
Successfully tagged test-from-cache:1

$ docker build -t test-from-cache:2 -f df.cache-from --build-arg UNIQUE_ARG=world --cache-from test-from-cache:1 .                                                                                
Sending build context to Docker daemon   26.1MB
Step 1/4 : FROM busybox
 ---> 54511612f1c4
Step 2/4 : ARG UNIQUE_ARG=world
 ---> Using cache
 ---> fada1443b67b
Step 3/4 : RUN echo Hello ${UNIQUE_ARG}
 ---> Running in 22698cd872d3
Hello world
Removing intermediate container 22698cd872d3
 ---> dc5f801fc272
Step 4/4 : COPY . /files
 ---> addabd73e43e
Successfully built addabd73e43e
Successfully tagged test-from-cache:2

$ docker inspect test-from-cache:1 -f '{{json .RootFS.Layers}}' | jq .
[
  "sha256:6a749002dd6a65988a6696ca4d0c4cbe87145df74e3bf6feae4025ab28f420f2",
  "sha256:01bf0fcfc3f73c8a3cfbe9b7efd6c2bf8c6d21b6115d4a71344fa497c3808978"
]

$ docker inspect test-from-cache:2 -f '{
{json .RootFS.Layers}}' | jq .                                                                                         
[
  "sha256:6a749002dd6a65988a6696ca4d0c4cbe87145df74e3bf6feae4025ab28f420f2",
  "sha256:c70c7fd4529ed9ee1b4a691897c2a2ae34b192963072d3f403ba632c33cba702"
]
Run Code Online (Sandbox Code Playgroud)

当命令更改时,构建会准确显示停止使用缓存的位置。即使COPY在每个层中运行相同的命令,inspect 也会显示第二层 id 的变化。并且只要前一层不同,缓存就不能从其他镜像构建中使用。

--cache-from选项允许您信任从注册表中提取的映像的构建步骤。默认情况下,docker 只信任本地构建的层。但即使您提供此选项,同样的规则也适用。


选项1:

如果要重用构建缓存,则两个映像中的前面层必须相同。如果每个的基本映像足够小,您可以尝试使用多阶段构建。但是,这样做会丢失文件系统之外的所有设置(环境变量、入口点规范等),因此您还需要重新创建:

ARG base_image
FROM ${base_image} as base
# the above from line makes the base image available for later copying
FROM scratch
COPY large-content /content
COPY --from=base / /
# recreate any environment variables, labels, entrypoint, cmd, or other settings here
Run Code Online (Sandbox Code Playgroud)

然后使用以下方法构建它:

docker build --build-arg base_image=base1 -t image1 .
docker build --build-arg base_image=base2 -t image2 .
docker build --build-arg base_image=base3 -t image3 .
Run Code Online (Sandbox Code Playgroud)

如果您需要更改其他设置,这也可以是多个 Dockerfile。这将导致复制每个基本图像的全部内容,因此请确保您的基本图像明显更小,以使其值得付出努力。


选项 2:

重新排序您的构建以将常见组件保留在顶部。我知道这对您不起作用,但它可能会帮助其他人稍后遇到这个问题。这是大多数人使用的首选和最简单的解决方案。


选项 3:

从您的图像中删除大量内容,并将其作为卷从外部添加到您的容器中。你失去了 docker 文件系统层的不变性 + 写时复制特性。并且您需要手动将卷内容发送到每个 docker 主机(或使用网络共享文件系统)。我已经看到,其中“同步容器”是在其每一个执行的搬运工主机运行的解决方案git pullrsync或任何其他等效的命令保持更新的体积。如果可以,请考虑:ro在末尾安装卷,使其仅在您使用它的容器内只读,从而为您提供不变性。