强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

23 - Docker 部署

第 23 章:Docker 部署

使用 Docker 容器化 Python 应用,优化镜像构建和依赖管理。


23.1 Docker 基础

23.1.1 安装

# Ubuntu
$ sudo apt install docker.io docker-compose-plugin

# macOS
$ brew install --cask docker

# 验证
$ docker --version
$ docker compose version

23.1.2 基本命令

命令说明
docker build -t myapp .构建镜像
docker run -p 8000:8000 myapp运行容器
docker ps查看运行中的容器
docker images查看镜像
docker stop <id>停止容器
docker rm <id>删除容器
docker rmi <id>删除镜像
docker logs <id>查看日志

23.2 Python Dockerfile

23.2.1 基本 Dockerfile

FROM python:3.12-slim

WORKDIR /app

# 安装依赖(利用缓存层)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 复制应用代码
COPY . .

# 非 root 用户
RUN useradd --create-home appuser
USER appuser

EXPOSE 8000

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

23.2.2 多阶段构建(推荐)

# 构建阶段
FROM python:3.12-slim AS builder

WORKDIR /app

# 安装构建依赖
RUN pip install --no-cache-dir poetry

# 复制依赖文件
COPY pyproject.toml poetry.lock ./

# 安装依赖
RUN poetry config virtualenvs.in-project true && \
    poetry install --only main --no-interaction

# 运行阶段
FROM python:3.12-slim AS runtime

WORKDIR /app

# 复制虚拟环境
COPY --from=builder /app/.venv /app/.venv

# 复制应用代码
COPY src/ ./src/

# 非 root 用户
RUN useradd --create-home appuser
USER appuser

# 使用虚拟环境
ENV PATH="/app/.venv/bin:$PATH"

EXPOSE 8000

CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]

23.3 镜像优化

23.3.1 基础镜像选择

镜像大小适用场景
python:3.12~1GB开发环境
python:3.12-slim~150MB生产环境推荐
python:3.12-alpine~50MB极简镜像(注意兼容性)

23.3.2 优化技巧

# ✅ 1. 合并 RUN 指令减少层数
RUN apt-get update && \
    apt-get install -y --no-install-recommends gcc libpq-dev && \
    rm -rf /var/lib/apt/lists/*

# ✅ 2. 利用缓存(先复制依赖文件,再复制代码)
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .  # 代码变化不会使依赖层失效

# ✅ 3. 使用 .dockerignore
# .dockerignore
.git
.venv
__pycache__
*.pyc
.pytest_cache
.mypy_cache
.ruff_cache
.env
*.md
tests/
docs/

23.3.3 镜像大小对比

$ docker images myapp
REPOSITORY   TAG        SIZE
myapp        latest     150MB   # slim 基础镜像
myapp        alpine     55MB    # alpine 基础镜像
myapp        full       1.1GB   # 完整基础镜像

23.4 Docker Compose

# docker-compose.yml
version: "3.9"

services:
  web:
    build: .
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql://postgres:password@db:5432/myapp
      - REDIS_URL=redis://redis:6379
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    volumes:
      - ./uploads:/app/uploads
    restart: unless-stopped

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data

volumes:
  postgres_data:
  redis_data:
# 启动
$ docker compose up -d

# 查看日志
$ docker compose logs -f web

# 停止
$ docker compose down

# 重建
$ docker compose up -d --build

23.5 健康检查

# Dockerfile
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
    CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')" || exit 1
# FastAPI 健康检查端点
@app.get("/health")
async def health():
    return {"status": "healthy"}

23.6 环境变量管理

# 使用 pydantic-settings
from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    database_url: str = "sqlite:///app.db"
    redis_url: str = "redis://localhost:6379"
    secret_key: str = "change-me-in-production"
    debug: bool = False

    class Config:
        env_file = ".env"

settings = Settings()

23.7 注意事项

🔴 注意

  • 不要在 Dockerfile 中使用 COPY . . 在安装依赖之前
  • 不要在镜像中存储敏感信息(使用环境变量或 Docker secrets)
  • 使用非 root 用户运行应用
  • 使用 .dockerignore 排除不需要的文件

💡 提示

  • 使用 slim 基础镜像减小体积
  • 多阶段构建分离构建和运行环境
  • 使用 Docker Compose 管理多服务
  • 使用 docker compose watch 实现开发热重载

📌 业务场景

# 生产级 Dockerfile
FROM python:3.12-slim AS base

# 安装系统依赖
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
    libpq5 curl && \
    rm -rf /var/lib/apt/lists/*

FROM base AS builder

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt

FROM base AS runtime

WORKDIR /app
COPY --from=builder /install /usr/local
COPY src/ ./src/

RUN useradd --create-home appuser
USER appuser

HEALTHCHECK --interval=30s --timeout=5s \
    CMD curl -f http://localhost:8000/health || exit 1

EXPOSE 8000
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]

23.8 扩展阅读