目标

  • Poetry 和 Python 开箱即用,包括项目依赖,不用在启动时额外运行 entrypoint.sh 这类脚本初始化环境。
  • 为开发和部署都准备好镜像。
  • 使用国内镜像源以加速构建。

Dockerfile

假设你的项目是这样的结构:

.
├── Dockerfile
├── pyproject.toml
├── poetry.lock
├── scripts
└── my_awesome_ai_project

复制下列的内容到你的编辑器中,按你的用例做对应的修改。如果你需要,用法在本文的下一节。

Dockerfile
# syntax=docker/dockerfile:1
# 保留上面这个注释以使用 Docker BuildKit

################################
# PYTHON-BASE
# 准备好所有构建和运行时的环境变量,替换国内软件源。
################################
FROM python:3.9.17-slim as python-base

    # Python
ENV PYTHONUNBUFFERED=1 \
    # pip
    PIP_DISABLE_PIP_VERSION_CHECK=on \
    PIP_DEFAULT_TIMEOUT=100 \
    \
    # Poetry的版本
    # https://python-poetry.org/docs/configuration/#using-environment-variables
    POETRY_VERSION=1.6.1 \
    # Poetry的安装位置
    POETRY_HOME="/opt/poetry" \
    # 没有用户互动
    POETRY_NO_INTERACTION=1 \
    # 不要自动创建新的虚拟环境,我们只让Python和Poetry用待会我们自己创建好的,
    # 无论是构建还是运行时都是如此。
    POETRY_VIRTUALENVS_CREATE=false \
    \
    # 项目依赖和虚拟环境最后会放在这里
    VIRTUAL_ENV="/venv" \
    \
    # Node.js大版本,如果你不需要可以去掉
    NODE_MAJOR=18

# 让系统能够找到Poetry和VIRTUAL_ENV
ENV PATH="$POETRY_HOME/bin:$VIRTUAL_ENV/bin:$PATH"

# 手动创建虚拟环境
RUN python -m venv $VIRTUAL_ENV

# 我们的项目目录,这里我们让Python命令行在以后找依赖时考虑这个目录
WORKDIR /app
ENV PYTHONPATH="/app:$PYTHONPATH"

# Huggingface的权重缓存路径,如果你不需要可以去掉
# https://huggingface.co/docs/transformers/installation?highlight=transformers_cache#caching-models
ENV TRANSFORMERS_CACHE="/opt/transformers_cache/"

# 国内镜像源
RUN sed -i 's/deb.debian.org/mirrors.cloud.tencent.com/g' /etc/apt/sources.list.d/debian.sources && \
    pip config set global.index-url https://mirrors.cloud.tencent.com/pypi/simple

################################
# BUILDER-BASE
# 安装和编译依赖
################################
FROM python-base as builder-base
RUN apt-get update && \
    apt-get install -y \
    apt-transport-https \
    gnupg \
    ca-certificates \
    build-essential \
    git \
    nano \
    curl

# 如果你不需要Node.js,可以去掉这一段
RUN mkdir -p /etc/apt/keyrings && \
    curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \
    echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://mirrors.ustc.edu.cn/nodesource/deb/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \
    apt-get update && apt-get install -y nodejs && \
    npm config -L global set registry http://mirrors.cloud.tencent.com/npm/

# 安装poetry - 会依赖 $POETRY_VERSION & $POETRY_HOME
# --mount 参数会提示 buildx 缓存 /root/.cache 以加速下一次构建
RUN --mount=type=cache,target=/root/.cache \
    curl -sSL https://install.python-poetry.org | python -

# 开始安装依赖,注意 /app 目录下的内容在后面其实会被覆盖掉
WORKDIR /app
COPY poetry.lock pyproject.toml ./
COPY scripts scripts/
COPY my_awesome_ai_project/ my_awesome_ai_project/

# 安装项目运行依赖到VIRTUAL_ENV目录中
RUN --mount=type=cache,target=/root/.cache \
    poetry install --no-root --only main

# 预载Huggingface模型权重,这里作为例子,按你的用例修改
RUN poetry run python scripts/bootstrap.py

# 编译C依赖,这里作为例子,按你的用例修改
RUN --mount=type=cache,target=/app/scripts/vendor \
    poetry run python scripts/build-c-denpendencies.py && \
    cp scripts/lib/*.so /usr/lib

################################
# DEVELOPMENT
# 本地开发和CI测试用的镜像
################################
FROM builder-base as development

WORKDIR /app

# 安装测试和lint所用的依赖
# 运行时依赖已经安装过了,所以这里会比较快
RUN --mount=type=cache,target=/root/.cache \
    poetry install --no-root --with test,lint

# 要暴露的端口,按你的用例修改
EXPOSE 8080
CMD ["bash"]


################################
# PRODUCTION
# 生产用的镜像
################################
FROM python-base as production

# 根证书,你可能还会想加上时区信息
RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
    apt-get install -y --no-install-recommends \
    ca-certificates && \
    apt-get clean

# 拷贝已经构建好的 Poetry + venv
COPY --from=builder-base $POETRY_HOME $POETRY_HOME
COPY --from=builder-base $VIRTUAL_ENV $VIRTUAL_ENV
# 拷贝已经构建好的C依赖,这里作为例子,按你的用例修改
COPY --from=builder-base /app/scripts/lib/*.so /usr/lib
# 拷贝预载的模型权重,这里作为例子,按你的用例修改
COPY --from=builder-base $TRANSFORMERS_CACHE $TRANSFORMERS_CACHE

# 拷贝程序
WORKDIR /app
COPY poetry.lock pyproject.toml ./
COPY my_awesome_ai_project/ my_awesome_ai_project/

EXPOSE 8080
CMD ["python", "my_awesome_ai_project/app.py"]

用法

构建生产用的镜像:

docker build --progress plain -t registry.example.com/project/production .

构建开发和测试用的镜像:

docker build --progress plain -t registry.example.com/project/dev --target development .

测试用的镜像可以作为CI运行的环境,将程序挂载到容器中,这能大幅缩短CI时间。

原理

VIRTUAL_ENV 让 Python 和 Poetry 知道我们在虚拟环境中,好让它们查找和安装依赖。

PYTHONPATH 让 Python 考虑你在 /app 中存放的,自己编写的包。

对PATH修改使得我们可以直接调用在 VIRTUAL_ENV 当中安装的可执行依赖。

生产和开发的镜像是同源的,它们可以复用大部分的镜像层,这提升了镜像构建和存储的效率。

参考资料

  • 无标签
写评论...