Share feedback
Answers are generated based on the documentation.

将 Django 应用容器化

本指南介绍如何使用 Docker 将 Django 应用容器化。你将使用 uv 搭建项目骨架, 使用 Docker Hardened Image 创建一个可用于生产的 Dockerfile,然后添加一个 开发阶段以及 Compose Watch 以实现快速迭代。

先决条件

  • 你已安装最新版本的 Docker Desktop
  • 你已安装 uv,或者你可以借助 Docker,在无需本地 Python 或 uv 安装的情况下搭建项目。
Tip

如果你是 Docker 新手,请先从 Docker 基础 指南开始,熟悉镜像、容器和 Dockerfile 等关键概念。


概述

本指南将带你通过 Docker 将 Django 应用容器化。完成之后,你将能够:

  • 使用 uv 在本地或在 Docker Hardened Image 容器中初始化一个 Django 项目。
  • 使用 Docker Hardened Images(DHI) 创建一个可用于生产的 Dockerfile。
  • 向你的 Dockerfile 添加一个 development(开发)阶段,并配置 Compose Watch 以实现自动代码同步。

创建 Django 项目

你可以使用本地的 uv 安装来引导项目,也可以完全在容器中使用与 Dockerfile 相同的 DHI 镜像来完成,无需本地 Python。

  1. 初始化项目并固定到 Python 3.14,然后进入该项目目录:

    $ uv init --python 3.14 django-docker
    $ cd django-docker
    
  2. 添加 Django 和 Gunicorn,然后搭建 Django 项目骨架:

    $ uv add django gunicorn
    $ uv run django-admin startproject myapp .
    

DHI dev 镜像已经包含 Python 3.14,因此搭建出的项目会与 Dockerfile 完全一致。

  1. 创建项目目录并进入:

    $ mkdir django-docker && cd django-docker
    
  2. 初始化项目、添加依赖并搭建骨架。全部在一次性容器运行中完成:

    $ docker run --rm -v $PWD:$PWD -w $PWD \
      -e UV_LINK_MODE=copy \
      dhi.io/python:3.14-alpine3.23-dev \
      sh -c "pip install --quiet --root-user-action=ignore uv && uv init --name django-docker --python 3.14 . && uv add django gunicorn && uv run django-admin startproject myapp ."
    
    Note

    上述命令使用了 Mac/Linux shell 语法。在 Windows 上,需要调整路径:PowerShell 使用 ${PWD},命令提示符使用 %cd%,Git Bash 需要配合 MSYS_NO_PATHCONV=1$(pwd -W)

你的目录现在应当包含以下文件:

├── .python-version
├── main.py
├── manage.py
├── myapp/
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── pyproject.toml
├── uv.lock
└── README.md

创建生产用 Dockerfile

Docker Hardened Images 是由 Docker 维护的、可用于生产的基础镜像,能够最大程度地减小攻击面。更多详情,请参阅 Docker Hardened Images

  1. 登录到 DHI 镜像仓库:

    $ docker login dhi.io
    
  2. 创建一个 .dockerignore 文件,将本地构建产物排除在构建上下文之外:

    .dockerignore
    .venv/
    __pycache__/
    *.pyc
    .git/
  3. 创建一个包含以下内容的 Dockerfile

    Dockerfile
    # syntax=docker/dockerfile:1
    
    # Build stage: the -dev image includes tools needed to install packages.
    FROM dhi.io/python:3.14-alpine3.23-dev AS builder
    
    # Prevent Python from writing .pyc files to disk.
    ENV PYTHONDONTWRITEBYTECODE=1
    # Prevent Python from buffering stdout/stderr so logs appear immediately.
    ENV PYTHONUNBUFFERED=1
    
    RUN pip install --quiet --root-user-action=ignore uv
    # Use copy mode since the cache and build filesystem are on different volumes.
    ENV UV_LINK_MODE=copy
    
    WORKDIR /app
    
    # Install dependencies into a virtual environment using cache and bind mounts
    # so neither uv nor the lock files need to be copied into the image.
    RUN --mount=type=cache,target=/root/.cache/uv \
        --mount=type=bind,source=uv.lock,target=uv.lock \
        --mount=type=bind,source=pyproject.toml,target=pyproject.toml \
        uv sync --frozen --no-install-project
    
    # Runtime stage: minimal DHI image with no shell or package manager,
    # already runs as the nonroot user.
    FROM dhi.io/python:3.14-alpine3.23
    
    # Prevent Python from buffering stdout/stderr so logs appear immediately.
    ENV PYTHONUNBUFFERED=1
    # Activate the virtual environment copied from the build stage.
    ENV PATH="/app/.venv/bin:$PATH"
    
    WORKDIR /app
    
    # Copy the pre-built virtual environment and application source code.
    COPY --from=builder /app/.venv /app/.venv
    COPY . .
    
    EXPOSE 8000
    
    # Run Gunicorn as the production WSGI server.
    CMD ["gunicorn", "myapp.wsgi:application", "--bind", "0.0.0.0:8000"]
  4. 创建一个 compose.yaml 文件:

    compose.yaml
    services:
      web:
        build: .
        ports:
          - "8000:8000"

运行应用

django-docker 目录中运行:

$ docker compose up --build

打开浏览器并访问 http://localhost:8000。你应该会看到 Django 的欢迎页面。

ctrl+c 停止应用。


搭建开发环境

生产环境使用 Gunicorn,并且需要完整重建镜像才能获取代码改动。对于开发,你可以向 Dockerfile 添加一个 development 阶段,使用 Django 内置的服务器,并配置 Compose Watch 以在无需重建的情况下自动将代码改动同步到运行中的容器里。

更新 Dockerfile

将你的 Dockerfile 替换为多阶段版本,在 production 之外再添加一个 development 阶段:

Dockerfile
# syntax=docker/dockerfile:1

# Build stage: the -dev image includes tools needed to install packages.
FROM dhi.io/python:3.14-alpine3.23-dev AS builder

# Prevent Python from writing .pyc files to disk.
ENV PYTHONDONTWRITEBYTECODE=1
# Prevent Python from buffering stdout/stderr so logs appear immediately.
ENV PYTHONUNBUFFERED=1

RUN pip install --quiet --root-user-action=ignore uv
# Use copy mode since the cache and build filesystem are on different volumes.
ENV UV_LINK_MODE=copy

WORKDIR /app

# Install dependencies into a virtual environment using cache and bind mounts
# so neither uv nor the lock files need to be copied into the image.
RUN --mount=type=cache,target=/root/.cache/uv \
    --mount=type=bind,source=uv.lock,target=uv.lock \
    --mount=type=bind,source=pyproject.toml,target=pyproject.toml \
    uv sync --frozen --no-install-project

# The development stage inherits the -dev image and virtual environment from
# the builder. Django's built-in server reloads when Compose Watch syncs files.
FROM builder AS development

ENV PATH="/app/.venv/bin:$PATH"

COPY . .
EXPOSE 8000
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]

# The production stage uses the minimal runtime image, which has no shell,
# no package manager, and already runs as the nonroot user.
FROM dhi.io/python:3.14-alpine3.23 AS production

# Prevent Python from buffering stdout/stderr so logs appear immediately.
ENV PYTHONUNBUFFERED=1
# Activate the virtual environment copied from the build stage.
ENV PATH="/app/.venv/bin:$PATH"

WORKDIR /app

# Copy only the pre-built virtual environment and application source code.
COPY --from=builder /app/.venv /app/.venv
COPY . .

EXPOSE 8000

# Run Gunicorn as the production WSGI server.
CMD ["gunicorn", "myapp.wsgi:application", "--bind", "0.0.0.0:8000"]

更新 Compose 文件

将你的 compose.yaml 替换为以下内容。它针对 development 阶段,添加了一个 PostgreSQL 数据库,并配置了 Compose Watch:

compose.yaml
services:
  web:
    build:
      context: .
      # Build the development stage from the multi-stage Dockerfile.
      target: development
    ports:
      - "8000:8000"
    environment:
      # Enable Django's verbose debug error pages (the dev server always auto-reloads).
      - DEBUG=1
      # Database connection settings passed to Django via environment variables.
      - POSTGRES_DB=myapp
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=password
      - POSTGRES_HOST=db
      - POSTGRES_PORT=5432
    # Wait for the database to pass its healthcheck before starting the web service.
    depends_on:
      db:
        condition: service_healthy
    develop:
      watch:
        # Sync source file changes directly into the container so Django's
        # dev server can reload them without a full image rebuild.
        - action: sync
          path: .
          target: /app
          ignore:
            - __pycache__/
            - "*.pyc"
            - .git/
            - .venv/
        # Rebuild the image when dependencies change.
        - action: rebuild
          path: pyproject.toml
        - action: rebuild
          path: uv.lock
  db:
    image: dhi.io/postgres:18
    restart: always
    volumes:
      # Persist database data across container restarts.
      - db-data:/var/lib/postgresql
    environment:
      - POSTGRES_DB=myapp
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=password
    # Expose the port only to other services on the Compose network,
    # not to the host machine.
    expose:
      - 5432
    # Only report healthy once PostgreSQL is ready to accept connections,
    # so the web service doesn't start before the database is available.
    healthcheck:
      test: ["CMD", "pg_isready"]
      interval: 10s
      timeout: 5s
      retries: 5
volumes:
  db-data:

sync 动作会将文件改动直接推送进运行中的容器,以便 Django 的开发服务器自动重新加载它们。对 pyproject.tomluv.lock 的改动则改为触发一次完整的镜像重建。

Note

要了解更多关于 Compose Watch 的内容,请参阅 使用 Compose Watch

添加 PostgreSQL 驱动

psycopg 适配器添加到你的项目中:

$ uv add 'psycopg[binary]'
$ docker run --rm -v $PWD:$PWD -w $PWD \
  -e UV_LINK_MODE=copy \
  dhi.io/python:3.14-alpine3.23-dev \
  sh -c "pip install --quiet --root-user-action=ignore uv && uv add 'psycopg[binary]'"

然后更新 myapp/settings.py,以从环境变量中读取 DEBUGDATABASES

myapp/settings.py
import os

DEBUG = os.environ.get('DEBUG', '0') == '1'

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": os.environ.get("POSTGRES_DB", "myapp"),
        "USER": os.environ.get("POSTGRES_USER", "postgres"),
        "PASSWORD": os.environ.get("POSTGRES_PASSWORD", "password"),
        "HOST": os.environ.get("POSTGRES_HOST", "localhost"),
        "PORT": os.environ.get("POSTGRES_PORT", "5432"),
    }
}

使用 Compose Watch 运行

启动开发栈:

$ docker compose watch

打开浏览器并访问 http://localhost:8000

尝试编辑一个文件,例如向 myapp/views.py 添加一个视图。Compose Watch 会将改动同步进容器,Django 开发服务器会自动重新加载。如果你更新了 pyproject.tomluv.lock,Compose Watch 会触发一次完整的镜像重建。

ctrl+c 停止。


总结

在本指南中,你:

  • 使用 uv 引导了一个 Django 项目,提供了本地和容器化两种搭建方式。
  • 使用 Docker Hardened Images 和 uv(用于依赖管理)创建了一个可用于生产的 Dockerfile。
  • Dockerfile 添加了一个 development 阶段,并配置了 Compose Watch,配合 PostgreSQL 数据库实现快速迭代开发。

相关信息: