ITPub博客

首页 > 应用开发 > Python > 使用多阶段构建和精简Python运行时优化Django Docker镜像 | HackerNoon

使用多阶段构建和精简Python运行时优化Django Docker镜像 | HackerNoon

Python 作者:ITPUB社群 时间:1970-01-01 08:33:46 0 删除 编辑

推动数字化转型,整合新兴技术,并扩展运营规模以保持竞争优势。

让我们坦诚一点。你为 Python 应用编写过多少次 Dockerfile,运行 docker build,然后惊恐地盯着最终镜像大小超过 1 GB?

对于一个倡导简洁、优雅代码的框架,我们的生产 Docker(容器化平台) 镜像往往看起来像一个数字杂物抽屉。我们将构建时依赖、系统编译器(gcc)、开发头文件和缓存的 pip wheel 打包进同一个本应在生产中轻量高效运行的容器中。

如果你要将 Django 应用部署到云端,容器臃肿不仅是审美问题;它还会损害你的成本和安全性。更大的镜像在自动扩展触发时需要更长的拉取时间,在容器注册表中占用更多存储空间,并且因不必要的系统包而带来更大的攻击面。

在这里,我们将解决这个问题。我们将使用多阶段构建和精简基础镜像来构建一个生产级、安全且精简的 Django Docker 镜像。

“标准”Django Dockerfile 的问题

看看一个典型的初学者 Django Dockerfile:

FROM python:3.11

WORKDIR /app

# Install system dependencies
RUN apt-get update && apt-get install -y \
    build-essential \
    libpq-dev \
    && rm -rf /var/lib/apt/lists/*

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000"]

这张图有什么问题?

  • 沉重的基础镜像:python:3.11 使用完整的 Debian 镜像,包含数百个你的 Python Web 应用永远不需要的包。

  • 构建时污染:我们安装 build-essential(编译器和头文件)只是为了编译像 psycopg2 这样的依赖。但一旦 pip install 完成,这些编译器就不再需要了——然而它们却舒适地留在我们的生产层中,增加了数百兆字节。

  • 安全风险:默认以 root 用户运行容器(在许多标准设置中会发生)加上臃肿的操作系统层,如果漏洞被利用,会给恶意行为者提供巨大的游乐场。

解决方案:多阶段构建

多阶段构建允许我们在 Dockerfile 中使用多个 FROM 指令。每个 FROM 使用不同的基础镜像,我们可以将前一阶段的构建产物(如编译好的 wheels 或 site-packages)复制到最终阶段,从而将所有繁重的构建工具抛在身后。

我们为生产环境就绪的 Django Docker 配置所制定的蓝图。

步骤 1:构建阶段

我们从一个较重的镜像开始,它包含编译 Python 包、将其安装到目标目录以及处理静态资源收集所需的一切。

# --- Stage 1: Build dependencies ---
FROM python:3.11-slim AS builder

WORKDIR /app

# Install system build dependencies required for Python packages
RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential \
    libpq-dev \
    && rm -rf /var/lib/apt/lists/*

# Install python dependencies into a local prefix directory
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt

注意 --prefix=/install 标志。这告诉 pip 将所有内容干净地安装到一个特定文件夹中,以便我们之后可以轻松复制过来。

步骤 2:运行时阶段

现在,我们启动一个全新的、纯净的 python:3.11-slim 镜像。这个阶段对 build-essential 或编译器一无所知。我们只需从构建阶段复制编译好的 Python 包,放入应用程序代码,并配置一个安全的非 root 用户。

# --- Stage 2: Final runtime image ---
FROM python:3.11-slim AS runtime

WORKDIR /app

# Install only the absolute runtime necessities (e.g., PostgreSQL client libraries)
RUN apt-get update && apt-get install -y --no-install-recommends \
    libpq5 \
    && rm -rf /var/lib/apt/lists/*

# Copy installed python packages from the builder stage
COPY --from=builder /install /usr/local

# Create a non-privileged system user for security
RUN groupadd -g 1000 django && \
    useradd -u 1000 -g django -s /bin/bash -m django

# Copy project code
COPY --chown=django:django . /app

# Switch to the non-root user
USER django

# Expose port and define entrypoint
EXPOSE 8000
CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "3"]

此配置中包含的关键最佳实践

  • 使用 python:3.11-slim 而非 python:3.11:slim 变体去除了不必要的 Debian 包,同时仍保持稳定、标准的 Python 运行时环境。

  • no-install-recommends:在使用 apt-get 时,这可以防止包管理器安装会膨胀镜像层的可选依赖项。

  • 非 Root 执行(USER django):切勿以 root 身份运行生产 Web 服务器。如果攻击者发现了远程代码执行漏洞,他们不会自动获得底层宿主容器的 root 访问权限。

  • 复制时使用 --chown=django:django:确保项目文件(如静态文件夹或媒体目录)被复制时归属于正确的非 root 用户。

额外提示:使用 BuildKit 加速 CI/CD 构建

如果你在 GitHub Actions、GitLab CI 或云构建流水线中构建镜像,请确保启用 Docker BuildKit。

BuildKit 能智能地并行化多阶段构建,高效缓存中间层,并跳过未发生变化的构建阶段。

要在本地测试多阶段构建,请运行:

DOCKER_BUILDKIT=1 docker build -t my-django-app:latest .

使用 docker images 检查镜像大小。你很可能会注意到最终镜像从 1.2 GB 降至 250 MB 以下。

所以,

将 Django 容器化并不意味着要交付一个臃肿的、包含编译器和缓存文件的单体镜像。通过采用多阶段构建、将构建环境与运行时环境分离,并强制执行严格的用户权限,你可以获得更快、更精简且显著更安全的生产环境占用。

你的容器镜像仓库账单会感谢你,你的部署流水线会加速,而且知道你的基础设施已经加固完善,你也能睡得更安稳一些。

Python 中的整洁架构:构建可维护的 API,无需过度设计

关于作者

推动数字化转型,整合新兴技术,并扩展运营规模以保持竞争优势。


转载声明:本文为转载整理,原文版权归原作者及原站点所有。

原文来源:Hacker Noon

原创地址:https://hackernoon.com/optimizing-django-docker-images-with-multi-stage-builds-and-slim-python-runtimes?source=rss

原文作者:Himashi Meththasena;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2022-11-21

  • 博文量
    1148
  • 访问量
    547933