Python 编程教程 / 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"]