多个FROM - 意味着什么

ekk*_*kis 78 docker dockerfile

我想在github上为Linkurious项目构建一个docker镜像,它需要运行Neo4j数据库和Node.js.

我的第一种方法是为我的图像声明一个包含Neo4j的基本图像.参考文档没有以任何有用的方式定义"基本图像":

基本图像:没有父图像的图像是基本图像

从中我读到,如果该图像本身没有基本图像,我可能只有一个基本图像.

但什么是基本形象?这是否意味着如果我在FROM指令中声明neo4j/neo4j,那么当运行我的映像时,neo数据库将自动运行并在端口7474上的容器中可用?

阅读Docker参考资料(参见:https://docs.docker.com/reference/builder/#from)我看到:

FROM可以在单个Dockerfile中多次出现,以创建多个图像.只需在每个新的FROM命令之前记下提交输出的最后一个图像ID.

我想创建多个图像吗?看起来我想要的是拥有一个包含其他图像内容的图像,例如neo4j和node.js

我没有找到在参考手册中声明依赖项的指令.没有像RPM这样的依赖关系,为了运行我的图像,调用上下文必须首先安装它需要的图像?

我糊涂了...

Nag*_*gev 98

我总结一下我对问题和答案的理解,希望对其他人有用。

\n

问题:让\xe2\x80\x99s 说我有三个图像:appleBananaOrange。我可以有一个包含FROM apple,的 Dockerfile ,FROM bananaFROM orange会告诉 docker 神奇地将所有三个应用程序合并到一个映像(包含三个单独的应用程序)中,我可以将其称为smoothie吗?

\n

回答不,你不能。如果这样做,您最终将得到四个图像,即您拉出的三个水果图像,加上基于最后一个FROM图像的新图像。例如,如果FROM orangeDockerfile 中的最后一条语句没有添加任何内容,则Smoothie图像将只是橙色图像的克隆。

\n

为什么他们不合并?我真的想要它

\n

典型的 docker 映像将包含应用程序运行所需的几乎所有内容(不包括内核),这通常意味着它们\xe2\x80\x99 是根据其所选操作系统和特定版本或发行版的基础映像构建的。

\n

可以理解的是,在不考虑所有可能的发行版、文件系统、库和应用程序的情况下成功合并镜像并不是 Docker 想要做的事情。相反,开发人员应该拥抱微服务范例,运行多个容器,根据需要相互通信。

\n

替代方案是什么\xe2\x80\x99?

\n

图像合并的一种可能的用例是将 Linux 发行版与我们所需的应用程序(例如 Ubuntu 和 Node.js)混合搭配。这不是解决方案:

\n
FROM ubuntu\nFROM node\n
Run Code Online (Sandbox Code Playgroud)\n

如果我们不想坚持使用应用程序映像选择的 Linux 发行版,我们可以从我们选择的发行版开始,并使用包管理器来安装应用程序,例如

\n
FROM ubuntu\nRUN apt-get update &&\\\n    apt-get install package1 &&\\\n    apt-get install package2\n
Run Code Online (Sandbox Code Playgroud)\n

但你可能已经知道了。通常,所选发行版中没有\xe2\x80\x99可用的快照或软件包,或者\xe2\x80\x99不是所需的版本,或者它在开箱即用的 docker 容器中无法正常工作,这就是想要使用图像的动机。I\xe2\x80\x99m 只是确认,据我所知,如果您真的想遵循单一方法,唯一的选择就是长期这样做。

\n

Node.js为例,您可能需要手动安装最新版本,因为apt它提供了一个古老的版本,并且snap不附带 Ubuntu 映像。对于Neo4j,我们可能需要根据文档和许可证下载该包并手动将其添加到映像中。

\n

如果大小不重要,一种策略是从最难手动安装的基本映像开始,然后将其余的添加到顶部。

\n

何时使用多个 FROM 指令

\n

还可以选择使用多个FROM语句并在构建阶段之间或将内容手动复制到最终阶段。换句话说,如果您知道自己在做什么,则可以手动合并图像。根据文档

\n
\n

AS name(可选)可以通过在指令中添加\n来为新的构建阶段指定名称FROM。该名称可以在后续指令中使用FROM,\nCOPY --from=<name>来引用此阶段构建的映像。

\n
\n

就我个人而言,我\xe2\x80\x99d只习惯将这种合并方法与我自己的图像一起使用,或者遵循应用程序供应商的文档,但如果您需要它或者您只是感到幸运,它\xe2\x80\x99s就在那里。

\n

不过,这种方法的一个更好的应用是,当我们确实想要使用来自不同图像的临时容器来构建或执行某些操作并在复制所需的输出后将其丢弃时。

\n
例子
\n

我想要一个精简映像gpgv,并且基于此Unix & Linux 答案,我安装了整个映像gpgyum然后仅将所需的二进制文件复制到最终映像:

\n
FROM docker.io/photon:latest AS builder\nRUN yum install gnupg -y\n\nFROM docker.io/photon:latest\nCOPY --from=builder /usr/bin/gpgv /usr/bin/\nCOPY --from=builder /usr/lib/libgcrypt.so.20 /usr/lib/libgpg-error.so.0 /usr/lib/\n
Run Code Online (Sandbox Code Playgroud)\n

其余的Dockerfile继续照常进行。

\n

  • 我觉得 Docker 缺少一个非常关键的功能:能够导入/扩展 Dockerfile,而不仅仅是 Docker 镜像。在几乎所有情况下,我想要做的似乎是以一种智能方式组合两个 Dockerfile 来构建一个映像:基本上,将一个 Dockerfile 中的所有“ENV”和“RUN”指令附加到另一个,也许在检查它们的“FROM”是否“相似”(相同的发行版)之后。如果这对您来说听起来太空洞,请考虑一下,很多人可能已经在这样做了,只是通过复制粘贴而不是将其内置到工具中。 (2认同)

Von*_*onC 87

什么是基本形象?

一组文件,加上EXPOSE'd端口,ENTRYPOINTCMD.
您可以添加文件并基于该基本图像构建新图像,使用新DockerfileFROM指令开头:后面提到的图像FROM是新图像的"基本图像".

这是否意味着如果我neo4j/neo4j在一个FROM指令中声明,当我的图像运行时,neo数据库将自动运行并在端口7474上的容器中可用?

只有你没有覆盖CMDENTRYPOINT.
但是图像本身就足够了:FROM neo4j/neo4j如果必须添加与neo4j您的特定用法相关的文件,则可以使用neo4j.

FROM 可以在单个Dockerfile中多次出现

:有提议删除该"功能"无论如何(issue 13026)

问题14412提到:

使用multiple FROM并不是一个真正的功能,而是一个bug(哦,极限很紧,FROMDockerfile中的多个用例很少).


2017年5月更新(18个月后),码头(moby)17.05-ce.

多个FROM 可以在单个Dockerfile中使用.
请参阅" 生成器模式与Docker中的多阶段构建 "(Alex Ellis)和TõnisTiigiPR 31257.

之前:

构建器模式涉及使用两个Docker镜像 - 一个用于执行构建,另一个用于传送第一个构建的结果,而不会在第一个图像中构建构建链和工具.

后:

一般语法涉及FROM在Dockerfile中添加额外的时间 - 最后一个FROM语句是最终基本映像.要从中间图像中复制工件和输出,请使用COPY --from=<base_image_number>.

Dockerfile的第一部分:

FROM golang:1.7.3 as builder
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html  
COPY app.go    .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
Run Code Online (Sandbox Code Playgroud)

相同(!)Dockerfile的第二部分:

FROM alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/github.com/alexellis/href-counter/app    .
CMD ["./app"]  
Run Code Online (Sandbox Code Playgroud)

结果将是两个图像,一个用于构建,一个仅用于生成的应用程序(更多,小)

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

multi               latest              bcbbf69a9b59        6 minutes ago       10.3MB  
golang              1.7.3               ef15416724f6        4 months ago        672MB  
Run Code Online (Sandbox Code Playgroud)

  • 正如我在上一个答案(http://stackoverflow.com/a/33295292/6309)中提到的那样,@ ekkis是通过编排多个容器(每个容器提供一种特定的服务)并通过--link(https: //docs.docker.com/userguide/dockerlinks/#communication-across-links)。 (3认同)
  • 很遗憾删除多个FROM。在我看来,它最有用,尤其是在没有依赖机制的情况下。例如,对于RPM,我可以声明我的程序包需要另一个程序包才能运行,因此在安装时一切都为我设置好了。现实是,大多数事物都将需要多个依赖关系,因此在没有多个FROM的情况下,该如何工作? (2认同)
  • @VonC当然,在理想的情况下,拥有一个新的应用程序,并且可以理解所有模式。同时,我希望会有更多人尝试将其解决方案迁移到Docker,并且有无法通过网络解决的需求,例如软件依赖项,全部使用兼容的基础,但使用多个Dockerfile。相反,到目前为止,我能弄清的最好的办法是破解他们的Dockerfile来创建自己的Dockerfile。 (2认同)

Eva*_*oll 19

第一个答案对我的口味来说太复杂、太具有历史意义且没有信息量。


其实很简单。Docker 提供了一种称为多阶段构建的功能,这里的基本思想是,

  • 通过强制您将您想要的内容列入白名单,让您不必手动删除您不想要的内容,
  • 否则会因为 Docker 的实现而被占用的免费资源。

让我们从第一个开始。很多时候你会看到像 Debian 这样的东西。

RUN apt-get update \ 
  && apt-get dist-upgrade \
  && apt-get install <whatever> \
  && apt-get clean
Run Code Online (Sandbox Code Playgroud)

我们可以根据上述内容来解释所有这些。上面的命令链接在一起,因此它表示不需要中间图像的单个更改。如果是这样写的

RUN apt-get update ;
RUN apt-get dist-upgrade;
RUN apt-get install <whatever>;
RUN apt-get clean;
Run Code Online (Sandbox Code Playgroud)

这将导致 3 个更多的临时中间图像。将其缩减为一张图像后,还存在一个问题:apt-get clean不清理安装中使用的工件。如果 Debian 维护者在他的安装中包含一个修改系统的脚本,该修改也将出现在最终解决方案中(请参阅类似pepperflashplugin-nonfree示例)。

通过使用多阶段构建,您可以获得单个更改操作的所有好处,但它需要您使用COPY --from此处记录的语法手动将临时映像中引入的文件列入白名单并复制。此外,这是一个很好的解决方案,没有替代品(如apt-get clean),否则您的最终图像中将有很多不需要的文件。

也可以看看

  • 谢谢,但我不明白你是如何解决我的问题的。对我来说,FROM 是一种继承机制,拥有多个指令意味着我可以从多个父级继承。在您的回答中,您没有提到 FROM 或利用其他人的软件打包的概念 (7认同)
  • 也许这就是混乱。`FROM` 主要是一个命名空间声明。那里的限定符更像是扩展而不是继承。您可以声明多个命名空间。这些命名空间中的每一个都可以扩展另一个命名空间。@ekkis如果其他答案适合您,那么请务必坚持下去。 (2认同)

Dea*_*n P 7

FROM这可能是使用多个(又名多阶段构建)的最基本用例之一。

我想要一个 dockerfile,并且我想更改一个单词,根据我设置该单词的内容,我会根据是否要运行、开发或发布应用程序获得不同的图像!

运行- 我只想运行该应用程序

开发- 我想编辑代码并运行应用程序

发布- 在生产环境中运行应用程序

假设我们在 dotnet 环境中工作。这是一个 Dockerfile。如果没有多阶段构建,将会有多个文件(构建器模式)

#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.

FROM mcr.microsoft.com/dotnet/runtime:5.0 AS base
WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
COPY ["ConsoleApp1/ConsoleApp1.csproj", "ConsoleApp1/"]
RUN dotnet restore "ConsoleApp1/ConsoleApp1.csproj"
COPY . .
WORKDIR "/src/ConsoleApp1"
RUN dotnet build "ConsoleApp1.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "ConsoleApp1.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "ConsoleApp1.dll"]
Run Code Online (Sandbox Code Playgroud)

想要运行该应用程序吗?保留FROM base AS final上面 dockerfile 中的当前位置。

想要在容器中开发源代码吗?将同一行更改为FROM build AS final

想要发布到产品中吗?将同一行更改为FROM publish AS final