有没有办法将 Docker 个图像合并到 1 个容器中?

Is there a way to combine Docker images into 1 container?

我现在有几个 Docker 文件。

一个用于 Cassandra 3.5,它是 FROM cassandra:3.5

我也有一个 Kafka 的 Docker 文件,但它要复杂得多。它是 FROM java:openjdk-8-fre,它运行一个长命令来安装 Kafka 和 Zookeeper。

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

对于那个 Docker 文件,它是 FROM broadinstitute/scala-baseimage,它让我得到 Java 8、Scala 2.11.7 和 STB 0.13.9,它们是我需要的。

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

如何合并这些 Docker 文件?我如何简单地创建一个包含这些东西的环境?

是的,您 可以 将大量软件整合到一个 Docker 图像中(GitLab does this, with one image that includes Postgres and everything else), but generalhenry 是正确的 - 这不是典型的使用方式Docker.

正如您所说,Cassandra 和 Kafka 是您的 Scala 应用程序的 依赖项 ,它们不是该应用程序的一部分,因此它们不属于同一映像。

必须使用 Docker Compose 编排许多容器会增加一个额外的管理层,但它为您提供了更大的灵活性:

  • 你的容器可以有不同的生命周期,所以当你有一个新版本的应用程序要部署时,你只需要 运行 一个新的应用程序容器,你可以离开依赖项 运行ning ;
  • 您可以在任何环境中使用相同的应用程序映像,为您的依赖项使用不同的配置 - 例如在开发中,你可以 运行 一个基本的 Kafka 容器,在生产中,它集群在许多节点上,你的应用程序容器是相同的;
  • 您的依赖项也可以被其他应用程序使用 - 因此多个消费者可以 运行 在不同的容器中,并且都使用相同的 Kafka 和 Cassandra 容器;
  • 加上已经提到的所有可伸缩性、日志记录等。

您不能合并 docker 文件,因为可能会发生冲突。您想要做的是创建一个新的 docker 文件或构建一个自定义图像。

TL;DR; 如果您当前的开发容器包含您需要和工作的所有工具,则将其保存为图像并将其保存到存储库并创建一个 docker 文件以从该存储库中提取该图像。

详情: 构建自定义图像比使用 public 图像创建 docker 文件要容易得多,因为您可以将任何 hack 和 mod 存储到图像中。为此,请使用基本 Linux 图像(或 broadinstitute/scala-baseimage)启动一个空白容器,安装所需的任何工具并配置它们直到一切正常,然后将其(容器)保存为图像。从这个图像创建一个新容器并测试你是否可以通过 docker-compose(或者你想要 do/build 它)在它之上构建你的代码。如果它有效,那么你就有了一个可以工作的基础图像,你可以将其上传到存储库,以便其他人可以拉取它。

要使用 public 图像构建 docker 文件,您需要将所有 hack、mod 和设置放在 docker 文件本身上。也就是说,您需要将您使用的每个命令行放入一个文本文件中,并减少命令行中的任何 hack、mod 和设置。最后,您的 docker 文件将自动创建一个图像,您不需要将此图像存储到存储库中,您需要做的就是将 docker 文件提供给其他人,他们可以旋转图像在他们自己 docker.

请注意,一旦您有了一个可用的 docker 文件,您就可以轻松地调整它,因为它会在您每次使用 docker 文件时创建一个新图像。使用自定义映像,您可能 运行 遇到由于冲突需要重建映像的问题。例如,您的所有工具都可以与 openjdk 一起使用,直到您安装了一个不起作用的工具。修复可能涉及卸载 openjdk 并使用 oracle,但是您为已安装的所有工具所做的所有配置都损坏了。

Docker 不会合并图像,但是没有任何东西可以阻止您合并 dockerfiles(如果可用),然后将它们滚动到您需要构建的胖图像中。然而,有时这是有道理的,至于 运行 容器中的多个进程,大多数 Docker 教条会指出这是不太可取的,尤其是对于微服务架构(但是规则是要被打破的,对吗?)

您无法将 docker 个图像合并到 1 个容器中。请参阅 Moby 问题中的详细讨论,How do I combine several images into one via Dockerfile

对于您的情况,最好不要包含整个 Cassandra 和 Kafka 图像。该应用程序只需要 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/

The end result is the same tiny production image as before, with a significant reduction in complexity. You don’t need to create any intermediate images and you don’t need to extract any artifacts to your local system at all.

How does it work? The second FROM instruction starts a new build stage with the alpine:latest image as its base. The COPY --from=0 line copies just the built artifact from the previous stage into this new stage. The Go SDK and any intermediate artifacts are left behind, and not saved in the final image.

以下答案适用于 docker 1.7 及更高版本:

我更愿意使用 --from=NAMEfrom image as NAME 为什么? 您可以使用 --from=0 及更高版本,但是当您在 docker 文件中有许多 docker 阶段时,这可能会变得难以管理。

示例:

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

注意:正确管理 docker 文件将有助于更快地构建 docker 图像。在内部 docker 使用 docker 层缓存来帮助完成此过程,以防必须重建图像。

我需要 docker:latest 和 python:latest 图像用于 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 图像包装“单个进程”/“运行时”。这允许扩展每个进程 up/down 并单独重新启动。

假设您想使用一些共享的 C-libraries/executables,这些共享 C-libraries/executables 在您正在使用的图像的包管理器中不可用,但其他人创建了 an image where they are precompiled - 您可能不想重新编译这些二进制文件作为构建的一部分(取决于这需要多长时间)。有没有一种方法可以根据现有图像快速创建包含所有这些 executables/libraries 的 POC-Docker 图像?

Docker 和组合

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

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

就是说,我个人创建了一个 custom frontend/parser for 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

nixpkgs.dockerTools

如果您想要真正可组合的 Docker 构建,我建议查看 dockerTools in nixpkgs. This will also result in more reproducible (and typically very small) images. See 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 ];
}