36

我正在寻找一种使用 python 和 Dockerfile 创建多阶段构建的方法:

例如,使用以下图像:

第一张图片:安装所有编译时要求,并安装所有需要的 python 模块

第二个图像:将所有编译/构建的包从第一个图像复制到第二个图像,没有编译器本身(gcc、postgers-dev、python-dev 等)

最终目标是拥有一个更小的图像,运行 python 和我需要的 python 包。

简而言之:我如何“包装”在第一个图像中创建的所有已编译模块(站点包/外部库),并以“干净”的方式将它们复制到第二个图像。

4

4 回答 4

39

好的,所以我的解决方案是使用轮子,它让我们在第一个图像上编译,为所有依赖项创建轮子文件并将它们安装在第二个图像中,而无需安装编译器

FROM python:2.7-alpine as base

RUN mkdir /svc
COPY . /svc
WORKDIR /svc

RUN apk add --update \
    postgresql-dev \
    gcc \
    musl-dev \
    linux-headers

RUN pip install wheel && pip wheel . --wheel-dir=/svc/wheels

FROM python:2.7-alpine

COPY --from=base /svc /svc

WORKDIR /svc

RUN pip install --no-index --find-links=/svc/wheels -r requirements.txt

您可以在以下博客文章中看到我对此的回答

https://www.blogfoobar.com/post/2018/02/10/python-and-docker-multistage-build

于 2018-02-09T21:42:54.043 回答
14

我推荐本文(第 2 节)中详述的方法。他使用 virtualenv,因此 pip install 将所有 python 代码、二进制文件等存储在一个文件夹下,而不是分散在整个文件系统中。然后很容易将那个文件夹复制到最终的“生产”图像中。总之:

编译镜像

  • 在您选择的某个路径中激活 virtualenv。
  • 将该路径添加到您的 docker ENV。这是所有 virtualenv 需要为所有未来的 docker RUN 和 CMD 操作运行。
  • pip install xyz像往常一样安装系统开发包。

生产形象

  • 从编译映像复制 virtualenv 文件夹。
  • 将 virtualenv 文件夹添加到 docker 的 PATH
于 2020-05-18T21:52:21.613 回答
0

这是在 Docker 中使用 Python 虚拟环境很有用的地方。复制虚拟环境通常很棘手,因为它需要在完全相同的 Python 版本上具有完全相同的文件系统路径,但在 Docker 中你可以保证这一点。

(这与@mpoisot 在他们的答案中描述的基本配方相同,它也出现在其他 SO 答案中。)

假设您正在安装psycopg PostgreSQL 客户端库。它的扩展形式需要 Python C 开发库加上 PostgreSQL C 客户端库头文件;但要运行它,您只需要 PostgreSQL C 运行时库。所以在这里你可以使用多阶段构建:第一阶段使用完整的 C 工具链安装虚拟环境,最后阶段复制构建的虚拟环境,但只包含最少的所需库。

典型的 Dockerfile 可能如下所示:

# Name the single Python image we're using everywhere.
ARG python=python:3.10-slim

# Build stage:
FROM ${python} AS build

# Install a full C toolchain and C build-time dependencies for
# everything we're going to need.
RUN apt-get update \
 && DEBIAN_FRONTEND=noninteractive \
    apt-get install --no-install-recommends --assume-yes \
      build-essential \
      libpq-dev

# Create the virtual environment.
RUN python3 -m venv /venv
ENV PATH=/venv/bin:$PATH

# Install the Python library dependencies, including those with
# C extensions.  They'll get installed into the virtual environment.
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt

# Final stage:
FROM ${python}

# Install the runtime-only C library dependencies we need.
RUN apt-get update \
 && DEBIAN_FRONTEND=noninteractive \
    apt-get install --no-install-recommends --assume-yes \
      libpq5

# Copy the virtual environment from the first stage.
COPY --from=build /venv /venv
ENV PATH=/venv/bin:$PATH

# Copy the application in.
COPY . .
CMD ["./main.py"]

如果您的应用程序使用Python 入口点脚本,那么您可以在第一阶段执行所有操作:RUN pip install .将应用程序复制到虚拟环境中并/venv/bin为您创建一个包装脚本。在最后阶段,您不需要COPY再次申请。设置CMD以在虚拟环境之外运行包装脚本,该环境已经位于$PATH.

再次注意,这种方法之所以有效,是因为它在两个阶段都是相同的 Python 基础映像,并且因为虚拟环境位于完全相同的路径上。如果是不同的 Python 或不同的容器路径,移植的虚拟环境可能无法正常工作。

于 2022-02-12T02:29:17.113 回答
-17

这个文档准确地解释了如何做到这一点。

https://docs.docker.com/engine/userguide/eng-image/multistage-build/#before-multi-stage-builds

基本上你完全按照你所说的去做。多阶段构建功能的神奇之处在于您可以从一个 dockerfile 完成所有这些操作。

IE:

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

这会构建一个 go 二进制文件,然后下一个图像运行该二进制文件。第一个镜像包含所有构建工具,第二个镜像只是一个可以运行二进制文件的基本 linux 机器。

于 2018-01-31T13:58:52.653 回答