Python Docker 化部署
大约 10 分钟约 2857 字
Python Docker 化部署
简介
Docker 是 Python 应用容器化部署的标准方案,通过 Dockerfile 定义应用运行环境,确保开发、测试、生产环境一致性。多阶段构建、镜像优化和 docker-compose 编排是 Python 项目 Docker 化的核心能力。
特点
实现
生产级 Dockerfile(多阶段构建)
# ---- 第一阶段:构建依赖 ----
FROM python:3.11-slim AS builder
WORKDIR /app
# 安装构建工具
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
&& rm -rf /var/lib/apt/lists/*
# 安装 Poetry
RUN pip install --no-cache-dir poetry==1.7.1
# 先复制依赖文件(利用 Docker 缓存层)
COPY pyproject.toml poetry.lock ./
# 安装生产依赖到虚拟环境
RUN poetry config virtualenvs.in-project true \
&& poetry install --only main --no-root --no-interaction --no-ansi
# ---- 第二阶段:运行镜像 ----
FROM python:3.11-slim AS runtime
# 安装运行时依赖(如需要)
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
&& rm -rf /var/lib/apt/lists/*
# 创建非 root 用户
RUN groupadd -r appuser && useradd -r -g appuser appuser
WORKDIR /app
# 从构建阶段复制虚拟环境
COPY --from=builder /app/.venv /app/.venv
# 复制应用代码
COPY src/ src/
COPY alembic/ alembic/
COPY alembic.ini .
# 设置环境变量
ENV PATH="/app/.venv/bin:$PATH" \
PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONPATH=/app
# 切换到非 root 用户
USER appuser
# 健康检查
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
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"]docker-compose 多服务编排
# docker-compose.yml
version: "3.8"
services:
# Web 服务
web:
build:
context: .
dockerfile: Dockerfile
ports:
- "8000:8000"
environment:
- APP_ENV=production
- DATABASE_URL=postgresql://appuser:apppass@db:5432/appdb
- REDIS_URL=redis://redis:6379/0
- SECRET_KEY=${SECRET_KEY}
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
deploy:
resources:
limits:
memory: 512M
cpus: "1.0"
networks:
- app-network
# Celery Worker
worker:
build:
context: .
dockerfile: Dockerfile
command: celery -A src.celery_app worker --loglevel=info -Q default,email --concurrency=4
environment:
- APP_ENV=production
- DATABASE_URL=postgresql://appuser:apppass@db:5432/appdb
- REDIS_URL=redis://redis:6379/0
- CELERY_BROKER_URL=redis://redis:6379/0
depends_on:
- redis
restart: unless-stopped
networks:
- app-network
# Celery Beat
beat:
build:
context: .
dockerfile: Dockerfile
command: celery -A src.celery_app beat --loglevel=info
environment:
- CELERY_BROKER_URL=redis://redis:6379/0
depends_on:
- redis
restart: unless-stopped
networks:
- app-network
# PostgreSQL
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: appuser
POSTGRES_PASSWORD: apppass
POSTGRES_DB: appdb
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
interval: 10s
timeout: 5s
retries: 5
networks:
- app-network
# Redis
redis:
image: redis:7-alpine
command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
- redis_data:/data
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
- app-network
# Nginx 反向代理
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
depends_on:
- web
restart: unless-stopped
networks:
- app-network
volumes:
postgres_data:
redis_data:
networks:
app-network:
driver: bridgeDocker 开发环境配置
# docker-compose.dev.yml - 开发环境覆盖
version: "3.8"
services:
web:
build:
target: builder # 使用构建阶段,包含 dev 依赖
command: uvicorn src.main:app --host 0.0.0.0 --port 8000 --reload
volumes:
- ./src:/app/src # 挂载代码目录,支持热重载
- ./tests:/app/tests
environment:
- APP_ENV=development
- DEBUG=true
ports:
- "8000:8000"
- "5678:5678" # debugpy 调试端口# Dockerfile.dev - 开发用 Dockerfile
FROM python:3.11-slim
WORKDIR /app
# 安装开发工具
RUN pip install --no-cache-dir \
poetry \
watchfiles \
debugpy
COPY pyproject.toml poetry.lock ./
RUN poetry config virtualenvs.in-project true \
&& poetry install --no-root
ENV PATH="/app/.venv/bin:$PATH" \
PYTHONUNBUFFERED=1
# 开发时挂载代码
VOLUME ["/app/src"]
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]镜像优化与安全
# Dockerfile.slim - 极致优化的生产镜像
FROM python:3.11-alpine AS builder
RUN apk add --no-cache gcc musl-dev libffi-dev
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
FROM python:3.11-alpine
# 安全:非 root 用户、最小基础镜像
RUN adduser -D -s /bin/sh appuser
COPY --from=builder /install /usr/local
COPY --chown=appuser:appuser src/ /app/src/
USER appuser
WORKDIR /app
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1
CMD ["python", "-m", "src.main"]# === 常用 Docker 命令 ===
# 构建镜像
docker build -t myapp:latest .
docker build --target runtime -t myapp:prod .
# 运行容器
docker run -d --name myapp -p 8000:8000 --env-file .env myapp:latest
# docker-compose 操作
docker-compose up -d # 后台启动所有服务
docker-compose up -d --build # 重新构建并启动
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up
docker-compose logs -f web # 查看日志
docker-compose exec web bash # 进入容器
docker-compose down -v # 停止并删除容器和卷
# 镜像管理
docker images # 列出镜像
docker image prune # 清理未使用镜像
docker system prune -a # 清理所有未使用资源
# 查看镜像层
docker history myapp:latest
dive myapp:latest # 使用 dive 工具分析镜像层优点
缺点
总结
Docker 是 Python 应用部署的标准方案,多阶段构建可以将生产镜像控制在 100MB 以内。docker-compose 适合单机多服务编排,Kubernetes 适合大规模集群管理。关键是合理利用 Docker 缓存层优化构建速度,使用非 root 用户和最小镜像提升安全性。
关键知识点
- 多阶段构建:构建阶段安装编译依赖,运行阶段只保留运行时文件
- Docker 缓存层:不常变的层(依赖安装)放前面,常变的层(代码复制)放后面
- .dockerignore 文件排除不需要的文件,加速构建并减小镜像体积
- HEALTHCHECK 指令让 Docker 了解容器健康状态,支持自动重启
项目落地视角
- 使用多阶段构建,生产镜像只包含运行时依赖
- 通过 docker-compose 管理开发环境,新成员 clone 后一条命令即可运行
- CI/CD 中构建镜像并推送到私有仓库(Harbor、ECR)
- 容器日志统一采集到日志平台,不依赖 docker logs
常见误区
- 在容器中使用 root 用户运行应用,存在安全风险
- 把代码放在依赖安装之前,导致每次代码变更都重新安装依赖
- 在容器中存储持久化数据,容器重启后数据丢失
- 开发环境与生产环境使用不同的 Dockerfile,行为不一致
进阶路线
- 学习 Kubernetes 部署和管理 Python 应用
- 研究多架构镜像构建(docker buildx)支持 ARM/AMD64
- 了解 Docker 镜像安全扫描(Trivy、Snyk)
- 探索无 Dockerfile 方案(Cloud Native Buildpacks)
适用场景
- Web 服务、API 服务、微服务的标准化部署
- 开发环境统一管理,消除环境差异
- CI/CD 流水线中的构建和测试环境
落地建议
- 使用 .dockerignore 排除 .git、pycache、.venv 等文件
- 生产镜像使用非 root 用户,限制 CPU 和内存资源
- 统一使用 docker-compose 管理开发和测试环境
排错清单
- 检查容器是否正常启动:docker logs [container_id]
- 确认端口映射和网络配置是否正确
- 排查容器内是否能正常连接数据库和 Redis
复盘问题
- 你的 Docker 镜像体积是多少?是否做过优化?
- 容器启动到服务就绪需要多长时间?是否满足健康检查要求?
- 开发环境和生产环境的 Docker 配置差异有哪些?如何保证一致性?
CI/CD 集成
# .github/workflows/docker-build.yml
name: Build and Push Docker Image
on:
push:
branches: [main]
tags: ["v*"]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=sha
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max.dockerignore 最佳实践
# .dockerignore — 排除不需要的文件
# 版本控制
.git
.gitignore
# Python 缓存
__pycache__
*.pyc
*.pyo
.pytest_cache
.mypy_cache
.ruff_cache
htmlcov
.coverage
# 虚拟环境
.venv
venv
env
# IDE
.vscode
.idea
*.swp
*.swo
# 环境配置(通过环境变量传入)
.env
.env.local
.env.production
# 测试和开发文件
tests/
docs/
*.md
LICENSE
# Docker 本身
Dockerfile*
docker-compose*.yml
# 其他
.DS_Store
*.log
node_modules多阶段构建优化实战
# Dockerfile.optimized — 极致优化的 Python 生产镜像
# ---- 阶段 1: 依赖安装 ----
FROM python:3.11-slim AS deps
# 只安装编译所需的系统依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc g++ libffi-dev && \
rm -rf /var/lib/apt/lists/*
WORKDIR /install
COPY requirements.txt .
# 优化 pip 安装:不缓存、禁用版本检查
RUN pip install --no-cache-dir --no-compile \
--upgrade pip setuptools wheel && \
pip install --no-cache-dir --no-compile \
-r requirements.txt
# ---- 阶段 2: 运行时 ----
FROM python:3.11-slim AS runtime
# 安装最小运行时依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
curl tini && \
rm -rf /var/lib/apt/lists/*
# 创建非 root 用户
RUN groupadd -r appuser && useradd -r -g appuser -d /app -s /sbin/nologin appuser
WORKDIR /app
# 只复制安装好的包
COPY --from=deps /install /usr/local/lib/python3.11/site-packages
# 复制应用代码
COPY src/ src/
COPY alembic/ alembic/
COPY alembic.ini pyproject.toml ./
# 设置环境变量
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PYTHONPATH=/app \
PATH="/app/.venv/bin:$PATH"
# 切换用户
USER appuser
# 使用 tini 作为 init 进程(正确处理信号)
ENTRYPOINT ["tini", "--"]
# 健康检查
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
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"]# 镜像大小对比
# python:3.11-slim 基础镜像: ~150MB
# 未优化(包含 dev 依赖): ~500-800MB
# 多阶段构建优化后: ~200-300MB
# Alpine 基础: ~100-200MB(注意:Alpine 使用 musl libc,部分 Python 包不兼容)
# 查看镜像层详情
docker history myapp:latest
# 使用 dive 工具分析镜像
# dive myapp:latest
# 显示每层的大小、命令和可优化的空间Docker 安全加固
def explain_docker_security():
"""Docker 安全最佳实践
1. 镜像安全:
- 使用官方基础镜像
- 固定镜像版本标签(不用 :latest)
- 定期扫描镜像漏洞(Trivy、Snyk)
- 使用 distroless 或 scratch 作为基础镜像
2. 运行时安全:
- 使用非 root 用户运行(USER appuser)
- 限制容器资源(--memory, --cpus)
- 设置只读文件系统(--read-only)
- 禁止特权模式(--privileged=false)
- 使用 --security-opt no-new-privileges
3. 网络安全:
- 使用自定义网络(不使用默认 bridge)
- 限制容器间通信
- 不暴露不必要的端口
4. 密钥管理:
- 不在镜像中硬编码密钥
- 使用 Docker secrets 或外部密钥管理
- 通过环境变量传入敏感配置
5. 镜像扫描工具:
- Trivy: 开源,速度快,支持多扫描器
- Snyk: 商业,漏洞数据库全
- Grype: Anchore 开源扫描器
- docker scout: Docker 官方工具
"""
print("安全检查清单:")
print(" [ ] 使用非 root 用户运行")
print(" [ ] 限制 CPU 和内存资源")
print(" [ ] 定期扫描镜像漏洞")
print(" [ ] 密钥不写入镜像")
print(" [ ] 固定基础镜像版本")
explain_docker_security()Docker 健康检查与日志管理
# docker-compose.yml 中的高级健康检查
services:
web:
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"]
interval: 30s
timeout: 5s
retries: 3
start_period: 15s
logging:
driver: json-file
options:
max-size: "50m"
max-file: "5"# 日志管理
# 查看容器日志
docker logs --tail 100 -f myapp
# 清理容器日志(不影响运行中的容器)
docker-compose logs --tail=0 web
# 限制日志大小(推荐在 compose 中配置)
# max-size: 单个日志文件最大 50MB
# max-file: 最多保留 5 个日志文件