有没有一种方法可以将 Docker 图像组合到一个容器中?

我现在有一些 Dockerfiles 文件。

一个是 Cassandra 3.5,一个是 FROM cassandra:3.5

我还有一个卡夫卡的 Dockerfile 文件,但它要复杂得多。它是 FROM java:openjdk-8-fre,它运行一个长的命令来安装卡夫卡和动物园管理员。

最后,我有一个用 Scala 编写的使用 SBT 的应用程序。

对于那个 Dockerfile,它是 FROM broadinstitute/scala-baseimage,它为我带来了 Java 8、 Scala 2.11.7和 STB 0.13.9,这正是我所需要的。

也许,我不知道 Docker 是如何工作的,但是我的 Scala 程序有 Cassandra 和 Kafka 作为依赖,出于开发目的,我希望其他人能够简单地用 Dockerfile克隆我的 repo,然后能够用 Cassandra、 Kafka、 Scala、 Java 和 SBT 构建它,这样他们就可以编译源代码。不过我对此有很多疑问。

如何组合这些 Dockerfiles? 如何简单地创建一个包含这些内容的环境?

145912 次浏览

是的,你 可以将大量的软件卷成一个单独的 Docker 映像(GitLab做到了这一点,一个映像包括 Postgres 和其他所有的东西) ,但是 Henry 将军是正确的——这不是使用 Docker 的典型方法。

如你所说,Cassandra 和 Kafka 是 Scala 应用程序的 依赖关系,它们不是应用程序的一部分,所以它们不属于同一个图像。

使用 Docker Compose 编排许多容器增加了额外的管理层,但是它提供了更多的灵活性:

  • 你的容器可以有不同的生命周期,所以当你有一个新版本的应用要部署,你只需要运行一个新的应用容器,你可以让依赖运行;
  • 你可以在任何环境中使用相同的应用程序图像,为你的依赖使用不同的配置-例如,在开发中你可以运行一个基本的卡夫卡容器,在产品集群在许多节点上,你的应用程序容器是相同的;
  • 你的依赖性也可以被其他应用程序使用——这样多个消费者可以在不同的容器中运行,所有人都使用相同的卡夫卡和卡珊德拉容器;
  • 加上所有已经提到的可伸缩性、日志记录等。

由于可能发生冲突,不能合并 dockerfiles。您要做的是创建一个新的 dockerfile 或构建一个自定义映像。

; 如果您当前的开发容器包含所有您需要和工作的工具,那么将其保存为一个图像,并将其保存到一个回购文件中,然后创建一个 dockerfile 从该回购文件中提取该图像。

详情: 构建自定义映像要比使用公共映像创建 dockerfile 容易得多,因为您可以将任何修改和调整存储到映像中。为此,使用一个基本的 Linux 映像(或 Broadinstitute/scala-baseimage)启动一个空白容器,安装任何您需要的工具并配置它们,直到一切正常工作,然后将它(容器)保存为一个映像。从这个映像中创建一个新的容器,并测试是否可以通过 docker-compose (或者以您想要的方式进行/构建)在其上构建代码。如果它工作,然后你有一个工作的基础图像,你可以上传到一个回购,以便其他人可以拉它。

要构建一个带有公共映像的 dockerfile,您需要将所有的 hacks、 mods 和设置放在 dockerfile 本身上。也就是说,您需要将您使用的每个命令行放置到一个文本文件中,并将任何修改、修改和设置减少到命令行中。最后,你的 dockerfile 会自动创建一个图像,你不需要将这个图像存储到一个回购,所有你需要做的就是给其他人的 dockerfile,他们可以在自己的 docker 旋转图像。

注意,一旦有了一个可以工作的 dockerfile,就可以轻松地对其进行调整,因为每次使用 dockerfile 时,它都会创建一个新的映像。对于自定义映像,您可能会遇到由于冲突而需要重新构建映像的问题。例如,您的所有工具都可以使用 openjdk,直到您安装了一个不能工作的工具。修复可能涉及卸载 openjdk 并使用 Oracle one,但是您为已安装的所有工具所做的所有配置都中断了。

Docker 不执行映像的合并,但是没有任何东西可以阻止您合并 dockerfiles (如果可用的话) ,并将它们滚入到您需要构建的胖映像中。然而,对于在容器中运行多个进程,多数 Docker 教条都会指出这种做法不太可取,特别是在微服务架构中(但是规则是可以打破的,对吗?)

无法将 Docker 图像组合到一个容器中。请参阅 Moby 问题 如何通过 Dockerfile 将多个图像组合成一个图像中的详细讨论。

对于你的情况,最好不要包括整个卡桑德拉和卡夫卡的图像。这个应用程序只需要 Cassandra Scala 驱动程序和 Kafka Scala 驱动程序。容器应该只包括驱动程序。

可以使用 Docker 1.17中引入的 多阶段建造特性

看看这个:

FROM golang:1.7.3
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 .


FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]

然后正常构建图像:

docker build -t alexellis2/href-counter:latest

发信人: https://docs.docker.com/develop/develop-images/multistage-build/

最终的结果是与以前相同的微小生产映像,复杂性大大降低。您不需要创建任何中间映像,也不需要将任何构件提取到本地系统中。

它是怎么工作的?第二个 FROM 指令开始了一个新的构建阶段,其基础是高山: 最新的映像。From = 0行只将前一阶段构建的构件复制到这个新阶段。Go SDK 和任何中间工件被留下,并且没有保存在最终映像中。

以下答案适用于 Docker 1.7及以上版本:

我更喜欢使用 --from=NAMEfrom image as NAME 为什么? 您可以使用 --from=0及以上版本,但是当您在 dockerfile 中有许多 docker stage 时,这可能不太难管理。

例子:

FROM golang:1.7.3 as backend
WORKDIR /backend
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN  #install some stuff, compile assets....
    

FROM golang:1.7.3 as assets
WORKDIR /assets
RUN ./getassets.sh


FROM nodejs:latest as frontend
RUN npm install
WORKDIR /assets
COPY --from=assets /asets .
CMD ["./app"]


FROM alpine:latest as mergedassets
WORKDIR /root/
COPY --from=frontend . /
COPY --from=backend ./backend .
CMD ["./app"]

注意: 正确管理 dockerfile 将有助于更快地构建 docker 映像。内部 Docker 使用 Docker 层缓存来帮助完成这个过程,以防需要重新构建映像。

我需要 docker: best 和 python: 针对 Gitlab CI 的最新图像:

FROM ubuntu:latest
RUN apt update
RUN apt install -y sudo
RUN sudo apt install -y docker.io
RUN sudo apt install -y python3-pip
RUN sudo apt install -y python3
RUN docker --version
RUN pip3 --version
RUN python3 --version

在我构建并推送到我的 Docker Hub 回购之后:

docker build -t docker-hub-repo/image-name:latest path/to/Dockerfile
docker push docker-hub-repo/image-name:latest

在推之前不要忘记 docker login

希望能有帮助

什么时候可能需要“组合”Docker 图像?

正如其他人在这里指出的那样,您通常不希望将数据库和应用程序放入同一个 Docker 映像中。理想情况下,您希望 Docker 映像包装“单个进程”/“运行时”。这使得每个进程可以单独地向上/向下缩放和重新启动。

假设你想使用一些共享的 C 库/可执行文件,这些文件在你所使用的映像的包管理器中是不可用的,但是其他人已经创建了 预编译的图像——你可能不想重新编译这些二进制文件作为构建的一部分(取决于这需要多长时间)。有没有一种方法可以快速创建一个 POC-Docker 映像,其中包含基于现有映像的所有这些可执行程序/库?

码头和构造

相关讨论: https://github.com/moby/moby/issues/3378

Docker 缺少的是一种很好的构图方法。您可以使用 COPY --from=<image> <from-path> <to-path>将单个文件或整个文件系统从其他图像复制到您自己的图像中。没有内置的方法可以将环境变量从另一个映像复制到您自己的映像中。

也就是说,我个人创建了一个 Dockerfiles 的自定义前端/解析器,它添加了一个 INCLUDE <image>-关键字。这会将整个文件系统以及环境变量复制到您的映像中:

DOCKER_BUILDKIT=1 docker build -t myimage .
#syntax=bergkvist/includeimage
FROM alpine:3.12.0
INCLUDE rust:1.44-alpine3.12
INCLUDE python:3.8.3-alpine3.12

工具

如果您想要真正可组合的 Docker 构建,我建议检查 干杯中的 工具。这也将导致更多的 可重现性(通常是非常小的)图像。见 https://nix.dev/tutorials/building-and-running-docker-images

docker load < $(nix-build docker-image.nix)
# docker-image.nix
let
pkgs = import <nixpkgs> {};
python = pkgs.python38;
rustc = pkgs.rustc;
in pkgs.dockerTools.buildImage {
name = "myimage";
tag = "latest";
contents = [ python rustc ];
}