Docker 容器化 Python 应用的最佳实践

一个不合格的 Dockerfile

FROM python:3.12
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt
CMD ["python", "app.py"]

这个 Dockerfile 能跑,但问题很多:镜像巨大、以 root 运行、缓存无效。下面是改进版。

最佳实践 Dockerfile

# 1. 明确基础镜像版本 + slim 减小体积
FROM python:3.12-slim AS builder

# 2. 先安装系统依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
    gcc libpq-dev && \
    rm -rf /var/lib/apt/lists/*

# 3. 虚拟环境
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

# 4. 先复制依赖文件(利用 Docker 缓存层)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 5. 生产阶段,最小化
FROM python:3.12-slim

# 6. 创建非 root 用户
RUN useradd --create-home --shell /bin/bash app

# 7. 复制虚拟环境
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

# 8. 复制应用代码
COPY --chown=app:app . /app
WORKDIR /app

# 9. 切换到非 root 用户
USER app

# 10. 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
    CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"

# 11. 明确端口
EXPOSE 8000

CMD ["gunicorn", "-w", "4", "-k", "uvicorn.workers.UvicornWorker", "-b", "0.0.0.0:8000", "app:app"]

关键原则

1. 多阶段构建

编译依赖不进最终镜像,大幅减小体积。

2. 非 root 运行

# docker-compose.yml 中也不要乱加 privileged
services:
  app:
    user: "1000:1000"  # 明确用户

3. .dockerignore

__pycache__
*.pyc
.git
.env
venv/
*.egg-info/

4. 日志输出到 stdout

import logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s %(levelname)s %(message)s',
    handlers=[logging.StreamHandler()]  # stdout
)

总结

一条好 Dockerfile 的五个标准:
- (多阶段构建 + slim 镜像)
- 安全(非 root + 最小权限)
- (利用缓存层)
- 可观测(健康检查 + 日志)
- 可重现(固定版本号)

评论功能已关闭