跳至内容

使用 Docker 构建容器

如果您想将 Python 代码放入容器中,您可能有一些服务器代码没有提交到 PyPI 或其他注册中心。如果是这样,请继续阅读。否则,跳过 下一节

本指南要求您熟悉 Docker 和 Dockerfile。

从源代码创建容器

  1. 确保您的项目已设置为 虚拟项目。这意味着您不能安装它,它也不会将自己标记为依赖项。如果您需要您的项目可安装,请转到 下一节

    • 您的 pyproject.toml 应该在 [tool.rye] 部分包含 virtual = true。如果没有,请添加它并运行 rye sync
    • 如果您只是在设置项目,请运行 rye init --virtual 而不是 rye init
  2. 在您的项目根目录中创建一个包含以下内容的 Dockerfile,使用 uv

    FROM python:slim
    
    RUN pip install uv
    
    WORKDIR /app
    COPY requirements.lock ./
    RUN uv pip install --no-cache --system -r requirements.lock
    
    COPY src .
    CMD python main.py
    

    或者,使用 pip

    FROM python:slim
    
    WORKDIR /app
    COPY requirements.lock ./
    RUN PYTHONDONTWRITEBYTECODE=1 pip install --no-cache-dir -r requirements.lock
    
    COPY src .
    CMD python main.py
    
  3. 现在您可以像这样构建您的镜像

    docker build .
    

Dockerfile 调整

本指南中的 Dockerfile 是示例。您可能需要进行一些调整

  • 命令 (CMD python src/main.py) 应该指向您的脚本。
  • 调整基本镜像 (FROM python:slim)
  • 优先使用与您的 .python-version 文件中版本匹配的标记版本,例如 FROM python:3.12.0-slim
  • -slim 变体通常是镜像大小和兼容性之间的一个很好的折衷方案,对于大多数工作负载来说应该可以正常工作。但是,您也可以使用 -alpine 创建更小的镜像(但存在潜在的兼容性问题),或者使用不带后缀的镜像,这些镜像包含更多系统工具。
  • 如果您需要其他系统包,请在复制源代码之前安装它们,即在 COPY src . 行之前。在使用基于 Debian 的镜像(即 -slim 或不带后缀的变体)时,这可能看起来像这样
RUN apt-get update \
    && apt-get install -y --no-install-recommends some-dependency another-dependency \
    && rm -rf /var/lib/apt/lists/*

从 Python 包创建容器

如果您的代码是一个可安装的包,建议您先构建它,然后在 Docker 镜像中安装它。这样,您可以确保镜像与用户安装完全相同。

使用 uvDockerfile 示例可能看起来像这样

FROM python:slim
RUN pip install uv
RUN --mount=source=dist,target=/dist uv pip install --no-cache /dist/*.whl
CMD python -m my_package

要构建 Docker 镜像,您必须先构建您的轮子,像这样

rye build --wheel --clean
docker build . --tag your-image-name

请注意,这种方法将您的依赖项和代码捆绑在一个层中。这可能对性能有利,但它也意味着每次镜像构建都会重新安装所有依赖项,并且不同的版本不会共享依赖项的磁盘空间。

上一节中的 Dockerfile 调整 适用。

解释

Rye 的锁定文件标准是 piprequirements.txt 格式(由 uv 使用),因此您实际上不需要在容器中使用 rye 即可安装依赖项。这使得 Dockerfile 变得更简单,并且如果需要小镜像,则可以避免多阶段构建的必要性。

分别传递给 pipuv--no-cache-dir--no-cache 参数通过不写入任何临时文件来使镜像更小。虽然缓存可以加快后续构建速度,但在容器中,镜像只构建一次并使用多次,因此缓存不是必需的。

类似地,设置了 PYTHONDONTWRITEBYTECODE=1 环境变量以避免写入 .pyc 文件,这些文件在容器中不需要。(uv 默认情况下会跳过写入 .pyc 文件。)