让我总结一下对问题和答案的理解,希望对其他人有所帮助。
问题:假设我有三个图像,苹果、香蕉和橙子。我能否有一个 Dockerfile,其中包含
FROM apple
、
FROM banana
和
FROM orange
,告诉 docker 将这三个应用程序神奇地合并成一个
单一的图像(包含三个个别的应用程序),我可以称之为
smoothie?
答案:
不行。如果你这样做,你最终会得到四个图像,即你拉取的三个水果图像,加上基于最后一个
FROM
图像的新图像。例如,如果
FROM orange
是 Dockerfile 中的最后一个语句而没有添加任何内容,则
smoothie 图像只是
orange 图像的克隆。
为什么它们没有被合并?我真的想要它
典型的 Docker 镜像将包含应用程序运行所需的几乎所有内容(不包括内核),这通常意味着它们是从所选操作系统的基础镜像和特定版本或发行版构建的。
不考虑所有可能的发行版、文件系统、库和应用程序的情况下成功合并图像,这是 Docker 不想做的事情。相反,开发人员应该采用微服务范式,运行多个容器,根据需要彼此通信。
替代方案是什么?
图像合并的一个可能的用例是混合和匹配 Linux 发行版和我们想要的应用程序,例如 Ubuntu 和 Node.js。这不是解决方案:
FROM ubuntu
FROM node
如果我们不想使用应用程序镜像选择的Linux发行版,我们可以选择自己喜欢的发行版,并使用软件包管理器来安装应用程序,例如:
FROM ubuntu
RUN apt-get update &&\
apt-get install package1 &&\
apt-get install package2
但是你可能已经知道了。往往在所选的发行版中没有可用的
快照或包,或者它不是所需的版本,或者它不能在docker容器中直接使用,这就是想要使用镜像的动机。据我所知,如果你真的想遵循单体应用程序的方法,唯一的选择是按照长时间的方式进行。
例如,在
Node.js的情况下,您可能希望
手动安装最新版本,因为
apt
提供的是一个古老的版本,而
snap
不包含在Ubuntu镜像中。对于
neo4j,我们可能需要
下载该软件包并手动将其添加到镜像中,根据文档和许可证。
如果大小不重要,一种策略是从最难手动安装的基础映像开始,然后在其上添加其余映像。
何时使用多个FROM指令
还有一个选项是使用多个
FROM
语句,并在构建阶段之间或最终阶段之间手动复制内容。换句话说,如果您知道自己在做什么,可以手动合并图像。根据
文档:
引入新的构建阶段时,可以通过向
FROM
指令添加
AS name
来为其命名。名称可用于后续的
FROM
和
COPY --from=<name>
指令,以引用在该阶段中构建的映像。
个人而言,我只会在使用自己的映像或遵循应用程序供应商的文档时才使用此合并方法,但如果您需要它或者感觉幸运,那么它就在那里。
但是,这种方法更好的应用是当我们实际上想要使用来自
不同镜像的临时容器进行构建或执行某些操作,并在复制所需输出后将其丢弃。
例子
我想要一个只有
gpgv
的精简映像,并基于这个
Unix & Linux答案,我安装了整个
gpg
,然后只复制所需的二进制文件到最终映像中。
FROM docker.io/photon:latest AS builder
RUN yum install gnupg -y
FROM docker.io/photon:latest
COPY --from=builder /usr/bin/gpgv /usr/bin/
COPY --from=builder /usr/lib/libgcrypt.so.20 /usr/lib/libgpg-error.so.0 /usr/lib/
Dockerfile
的其余部分按照通常的方式进行。
FROM
语句,出于某种原因,它会跳过第一个并仅执行第二个。 - pfincent