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

时间:2018-01-25 18:09:44

标签: 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

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

如何在图片中可靠地分享常用内容?

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

4 个答案:

答案 0 :(得分:1)

--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"
]

当命令发生变化时,构建会准确显示停止使用缓存的位置。即使在每个中运行相同的COPY命令,检查也会显示第二层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

然后用:

构建它
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 .

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

选项2:

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

选项3:

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

答案 1 :(得分:0)

在不同的泊坞窗图像之间共享公共内容的最可靠和泊坞方式是将图像之间的共性重构为其他图像扩展的基本图像。

示例,如果所有图像都构建在基础映像之上并在其中安装包x,y和z。你将包x,y和z的包装重构为基础图像到更新的基础图像,下游图像构建在顶部。

答案 2 :(得分:0)

原来,从Docker 1.13开始,您可以使用 - cache-from OTHER_IMAGE 标志。 (Docs

在这种情况下,解决方案将如下所示:

docker build -t image1
docker build -t image2 --cache-from image1
docker build -t image3 --cache-from image1 --cache-from image2
... and so on

这将确保重复使用这些图像的任何层。

更新:如其他答案所述,这并不符合我的预期。我承认我仍然不明白这是做什么的,因为它肯定会改变推送行为,但层最终不会重复使用。

答案 3 :(得分:0)

鉴于听起来这个额外的4GB数据的内容与底层容器映像无关,有没有办法在容器构建/创建过程之外挂载这些数据?我知道这会创建一个额外的管理步骤(在想要图像的任何地方获取数据),但假设它可以是只读共享挂载(然后根据需要由映像主进程解压缩到容器文件系统中),这可能是比在每张图片中构建它更容易。