
Docker在我机器上能跑这句话大概是每个程序员都说过也听过的话。环境不一致、依赖冲突、部署困难——这些问题困扰了开发者很多年直到容器化技术出现。这篇文章不讲Docker的理论直接从实践出发聊聊怎么用Docker把Python项目从开发环境平滑地部署到生产环境。一、为什么要用Docker在聊具体操作之前先搞清楚一个问题为什么Python项目需要容器化环境一致性开发环境、测试环境、生产环境保持一致杜绝在我机器上是好的这种问题。依赖隔离不同的项目可能需要不同版本的Python、不同的依赖包Docker让它们互不干扰。快速部署只需要docker run就能启动服务不需要手动配置环境。可移植性在任何安装了Docker的机器上都能运行不管是Windows、Linux还是macOS。资源隔离CPU、内存、网络都可以限制避免一个应用影响其他应用。二、Docker基础概念快速回顾如果你对Docker还不熟悉先花一分钟了解这三个核心概念镜像Image一个只读的模板包含了运行应用所需的一切代码、运行时、依赖、配置文件。可以理解为安装包。容器Container镜像的运行实例。可以理解为正在运行的程序。Dockerfile一个文本文件描述了如何构建镜像。可以理解为安装脚本。三、Python项目的Dockerfile编写这是容器化最核心的部分。我们从一个最简单的Flask项目开始。项目结构textmyapp/ ├── app.py ├── requirements.txt ├── Dockerfile └── .dockerignore简单的Flask应用python# app.py from flask import Flask, jsonify import os app Flask(__name__) app.route(/) def hello(): return jsonify({ message: Hello Docker!, env: os.getenv(ENV, development) }) app.route(/health) def health(): return jsonify({status: ok}) if __name__ __main__: app.run(host0.0.0.0, port5000)txt# requirements.txt flask2.3.2基础版Dockerfiledockerfile# 基础镜像使用官方Python 3.11镜像 FROM python:3.11-slim # 设置工作目录 WORKDIR /app # 复制依赖文件并安装 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 复制项目代码 COPY . . # 暴露端口 EXPOSE 5000 # 启动命令 CMD [python, app.py]构建和运行bash# 构建镜像 docker build -t myapp:latest . # 运行容器 docker run -p 5000:5000 myapp:latest # 访问 http://localhost:5000四、生产级Dockerfile的优化上面的Dockerfile能用但远不够好。生产环境的Dockerfile需要考虑更多因素。优化1多阶段构建dockerfile# 第一阶段构建阶段 FROM python:3.11-slim AS builder WORKDIR /app # 安装编译工具有些Python包需要编译 RUN apt-get update apt-get install -y \ gcc \ build-essential \ rm -rf /var/lib/apt/lists/* # 安装依赖 COPY requirements.txt . RUN pip install --no-cache-dir --user -r requirements.txt # 第二阶段运行阶段 FROM python:3.11-slim # 复制上阶段安装的包 COPY --frombuilder /root/.local /root/.local WORKDIR /app # 将本地用户目录添加到PATH ENV PATH/root/.local/bin:$PATH # 复制项目代码 COPY . . # 创建非root用户 RUN useradd -m -u 1000 appuser chown -R appuser:appuser /app USER appuser EXPOSE 5000 CMD [python, app.py]优化2利用Docker缓存加速构建Docker的每一层都会缓存如果文件没变那一层就不用重建。dockerfileFROM python:3.11-slim WORKDIR /app # 先复制requirements.txt这层变化频率低 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 最后复制代码这层变化频率高 COPY . . # 这样配置只要requirements.txt不变pip install这层就会被缓存优化3使用.dockerignoredockerfile# .dockerignore __pycache__ *.pyc *.pyo *.pyd .Python *.so *.egg *.egg-info dist build .env .venv venv .git .gitignore README.md tests/ logs/ *.log优化4健康检查dockerfileFROM python:3.11-slim # ... 之前的指令 ... HEALTHCHECK --interval30s --timeout10s --start-period5s --retries3 \ CMD curl -f http://localhost:5000/health || exit 1 CMD [python, app.py]完整的生产级Dockerfiledockerfile# 多阶段构建 FROM python:3.11-slim AS builder WORKDIR /app # 安装系统依赖如果有需要编译的包 RUN apt-get update apt-get install -y \ gcc \ build-essential \ rm -rf /var/lib/apt/lists/* COPY requirements.txt . # 使用国内镜像加速可选 RUN pip install --no-cache-dir --user -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple # 运行阶段 FROM python:3.11-slim # 安装运行时需要的系统依赖不包含编译工具 RUN apt-get update apt-get install -y \ curl \ rm -rf /var/lib/apt/lists/* WORKDIR /app # 复制依赖 COPY --frombuilder /root/.local /root/.local # 复制代码 COPY . . ENV PATH/root/.local/bin:$PATH ENV PYTHONUNBUFFERED1 # 创建非root用户 RUN useradd -m -u 1000 appuser chown -R appuser:appuser /app USER appuser HEALTHCHECK --interval30s --timeout10s --start-period5s --retries3 \ CMD curl -f http://localhost:5000/health || exit 1 EXPOSE 5000 CMD [python, app.py]五、Docker Compose多容器编排实际项目很少只有一个容器通常需要数据库、缓存、消息队列等。Docker Compose可以同时管理多个容器。docker-compose.ymlyamlversion: 3.8 services: # Web应用 web: build: . container_name: myapp-web ports: - 5000:5000 environment: - ENVdevelopment - DATABASE_URLpostgresql://postgres:passworddb:5432/myapp - REDIS_URLredis://redis:6379/0 volumes: - ./app.py:/app/app.py # 开发时挂载代码实现热更新 depends_on: - db - redis restart: unless-stopped # 数据库 db: image: postgres:15-alpine container_name: myapp-db environment: - POSTGRES_USERpostgres - POSTGRES_PASSWORDpassword - POSTGRES_DBmyapp ports: - 5432:5432 volumes: - postgres_data:/var/lib/postgresql/data restart: unless-stopped # Redis缓存 redis: image: redis:7-alpine container_name: myapp-redis ports: - 6379:6379 restart: unless-stopped # Nginx可选 nginx: image: nginx:alpine container_name: myapp-nginx ports: - 80:80 volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro depends_on: - web restart: unless-stopped volumes: postgres_data:使用Docker Composebash# 启动所有服务 docker-compose up -d # 查看日志 docker-compose logs -f web # 停止所有服务 docker-compose down # 停止并删除数据卷 docker-compose down -v # 重新构建并启动 docker-compose up -d --build六、环境配置管理不同环境开发、测试、生产需要不同的配置用环境变量来管理。使用.env文件bash# .env不要提交到Git ENVdevelopment DATABASE_URLpostgresql://postgres:passworddb:5432/myapp SECRET_KEYyour-secret-key-here DEBUGtruepython# app.py import os from dotenv import load_dotenv load_dotenv() config { env: os.getenv(ENV, development), database_url: os.getenv(DATABASE_URL), secret_key: os.getenv(SECRET_KEY), debug: os.getenv(DEBUG, false).lower() true, }多环境配置yaml# docker-compose.yml version: 3.8 services: web: build: . environment: - ENV${ENV:-production} - DATABASE_URL${DATABASE_URL} - SECRET_KEY${SECRET_KEY} env_file: - .env.${ENV:-production}text# .env.development ENVdevelopment DATABASE_URLpostgresql://postgres:passworddb:5432/myapp_dev DEBUGtrue # .env.production ENVproduction DATABASE_URLpostgresql://postgres:secure_passworddb:5432/myapp_prod DEBUGfalse七、部署到服务器方案1直接在服务器上运行bash# 在服务器上 git clone https://github.com/yourusername/myapp.git cd myapp docker-compose -f docker-compose.prod.yml up -d方案2使用CI/CD自动化部署yaml# .github/workflows/deploy.yml name: Deploy to Production on: push: branches: [ main ] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkoutv3 - name: Login to Docker Hub uses: docker/login-actionv2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_TOKEN }} - name: Build and push uses: docker/build-push-actionv4 with: context: . push: true tags: yourusername/myapp:latest - name: Deploy to server uses: appleboy/ssh-actionv0.1.5 with: host: ${{ secrets.SERVER_HOST }} username: ${{ secrets.SERVER_USER }} key: ${{ secrets.SERVER_SSH_KEY }} script: | cd /app docker pull yourusername/myapp:latest docker-compose down docker-compose up -d方案3使用云平台部署到阿里云/腾讯云容器服务bash# 登录阿里云容器镜像服务 docker login --usernameyourname registry.cn-hangzhou.aliyuncs.com # 打标签 docker tag myapp:latest registry.cn-hangzhou.aliyuncs.com/yournamespace/myapp:latest # 推送 docker push registry.cn-hangzhou.aliyuncs.com/yournamespace/myapp:latest # 在云平台上拉取并运行八、常见问题与解决方案问题1容器启动慢dockerfile# 使用更轻量的基础镜像 FROM python:3.11-alpine # 比slim更小 # 使用国内镜像源加速 RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple问题2容器内时区不对dockerfile# 设置时区 ENV TZAsia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime echo $TZ /etc/timezone问题3日志丢失python# app.py import logging # 配置日志输出到stdout logging.basicConfig( levellogging.INFO, format%(asctime)s - %(name)s - %(levelname)s - %(message)s, handlers[ logging.StreamHandler() # 输出到控制台 ] )问题4容器内存不足bash# 限制内存使用 docker run -m 512m --memory-swap 512m myapp:latest # 在docker-compose中配置 services: web: deploy: resources: limits: memory: 512M reservations: memory: 256M问题5敏感信息泄露dockerfile# ❌ 不要在Dockerfile中硬编码密码 ENV DB_PASSWORD123456 # ✅ 使用环境变量或Docker secrets # 在运行时通过 -e 或 --env-file 传入九、最佳实践总结使用轻量级基础镜像优先选择-slim或-alpine版本利用缓存层把变化频率低的层放在前面多阶段构建最终镜像只包含运行时所需的内容使用非root用户提高安全性健康检查让Docker能监控应用状态环境变量管理配置不要硬编码配置日志输出到stdout方便Docker收集日志使用.dockerignore避免无关文件进入镜像固定基础镜像版本不要用latest标签最小化权限只给容器必要的权限写在最后Docker彻底改变了我们开发和部署软件的方式。Python项目使用Docker后环境问题大幅减少部署效率显著提升。从开发到测试再到生产整个流程更加标准化和自动化。几个进一步学习的建议学习Kubernetes当容器数量增多时需要容器编排学习容器监控Prometheus Grafana监控容器状态学习容器安全镜像扫描、安全配置学习服务网格Istio等流量管理工具容器化这条路越走越宽。希望这篇文章能帮你迈出第一步。