通过 docker 多阶段构建与挂载构建和部署 C++

Sig*_*aud 3 c++ deployment build docker

我正在尝试使用 docker 构建一个自定义 C++ 工具,该工具具有许多自定义库依赖项。

共有三个库:libA、libB 和 libC。libB 依赖于 libA,libC 依赖于 libA 和 libB。在我的家庭系统上,我通常会更新 libA 的源代码,然后安装其下游的所有内容,因为 libB 使用 CMake 来查找 libA 等。

在 docker 中实现这一目标的最佳方法是什么?难道也是用make install吗?我不希望在最终的图像中包含源代码 - 只是二进制文件和库。如果我使用多阶段构建,我如何知道我已将所有必要的其他库(例如curl、protobuf)复制到最终“层”中。仅安装构建映像的源而不安装部署映像的源不是更好吗?

我发现关于 docker 和 C++ 的信息并不多。

Dav*_*aze 7

您通常不想为应用程序代码或依赖项使用卷。最佳实践是 Docker 镜像是独立的。您可能会遇到大量使用卷和绑定安装的解释性语言(尤其是 Node)的 Docker 设置,但这些实际上只是通过 Docker 在主机代码上运行 Node,而不是在主机上安装 Node。

对于需要编译但只需要编译的工件来运行的东西的典型方法是使用 Docker多阶段构建。这个想法是,您设置一个包含完整构建工具链的映像并编译所有内容,然后在同一个 Dockerfile 中创建第二个映像,COPY --from第一个映像仅包含已编译的工件。

我可能会在这里尝试使用标准/usr/local目录树,原因有两个:它通常是 Autoconf 等工具的默认安装前缀,并且在标准 Linux 发行版 Docker 映像中它是空的。

# The first stage only builds things.  We'll COPY files out of it
# later.
FROM ubuntu:20.04 AS builder

# Install any dependencies we need here, like the C toolchain.
RUN apt-get update \
 && DEBIAN_FRONTEND=noninteractive \
    apt-get install --assume-yes --no-install-recommends \
      build-essential \
      libbar-dev \
      libfoo-dev \
      libquux-dev

# Build library A and install it into /usr/local.
WORKDIR /usr/src/libA
COPY libA ./
RUN ./configure \
 && make \
 && make install

# Similarly with library B
WORKDIR /usr/src/libB
COPY libB ./
RUN ./configure \
 && make \
 && make install

# ...and library C, and the application proper
...

# The final FROM line is the actual runtime image.  Anything before
# this will be built, and will show up in `docker images`, but isn't
# "the output" of the build.

FROM ubuntu:20.04

# Get the installed libraries and applications from the earlier stage.
COPY --from=builder /usr/local/ /usr/local/

# Regenerate the shared-library cache.
RUN ldconfig

# Set ordinary metadata to run a Docker container.  (The binary is
# probably in /usr/local/bin which is on the default PATH.)
EXPOSE 80
CMD ["myapp"]
Run Code Online (Sandbox Code Playgroud)

如果不同的库具有非常不同的构建时依赖性,则可以扩展此方法以具有更多构建阶段。请记住,每条FROM线都重新开始,您需要将RUN apt-get install事物COPY放入每个图像阶段以使它们呈现。

这里的一个公开的复杂性是,C 库构建通常会包含一些仅用于其他 C 库构建所需的部分。在您的示例中,libB依赖于libA,因此libA安装步骤将包括 C 头文件,通常还包括静态库。它们可能很大(尤其是.a静态库)并且在运行时不需要。您可能希望通过RUN rm在构建阶段结束时进行战略性使用来清除这些内容。一般来说,RUN rm不会使图像变小,但如果您RUN rm在构建阶段进行某些操作,然后将COPY编辑后的文件放入最终阶段,则只有留下的内容才会复制到最终图像中。