Docker入门到实战:从零构建Python应用容器

3 阅读1分钟

摘要:Docker已经是开发者必备技能。本文从零开始,手把手教你把一个Python Web应用容器化,涵盖Dockerfile编写、镜像优化、docker-compose编排、常见踩坑等实战内容。

为什么要用Docker?

一句话:"在我机器上能跑"不再是借口。

Docker把应用和它的所有依赖打包成一个标准化的容器,在任何环境都能一致运行。

安装Docker

# Ubuntu
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER

# 验证
docker --version
docker run hello-world

第一个Dockerfile

假设我们有一个FastAPI应用:

myapp/
├── app/
│   ├── __init__.py
│   └── main.py
├── requirements.txt
└── Dockerfile

app/main.py

from fastapi import FastAPI

app = FastAPI()

@app.get('/')
def root():
    return {'message': 'Hello Docker!'}

@app.get('/health')
def health():
    return {'status': 'ok'}

requirements.txt

fastapi==0.109.0
uvicorn==0.27.0

Dockerfile

FROM python:3.12-slim

WORKDIR /app

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

# 再复制源码
COPY app/ ./app/

EXPOSE 8000

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

构建和运行:

docker build -t myapp .
docker run -d -p 8000:8000 --name myapp myapp
curl http://localhost:8000/health

Dockerfile最佳实践

1. 多阶段构建(减小镜像体积)

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

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

# 运行阶段
FROM python:3.12-slim

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

# 非root用户运行
RUN useradd -m appuser
USER appuser

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

2. 利用构建缓存

Docker每一层都有缓存。把变化频率低的放前面:

# ✅ 好:依赖不常变,源码常变
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY app/ ./app/

# ❌ 差:每次改代码都要重新装依赖
COPY . .
RUN pip install -r requirements.txt

3. .dockerignore

.git
__pycache__
*.pyc
.env
.venv
node_modules
*.md
.pytest_cache

docker-compose编排

真实项目通常需要多个服务(应用+数据库+缓存):

# docker-compose.yml
version: '3.8'

services:
  app:
    build: .
    ports:
      - '8000:8000'
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/mydb
      - REDIS_URL=redis://redis:6379/0
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    restart: unless-stopped

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: mydb
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -U user']
      interval: 5s
      timeout: 5s
      retries: 5

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

volumes:
  pgdata:
  redisdata:

常用命令:

docker-compose up -d          # 启动所有服务
docker-compose logs -f app    # 查看应用日志
docker-compose down           # 停止并删除容器
docker-compose down -v        # 连数据卷一起删
docker-compose exec app bash  # 进入容器

镜像体积优化

# 查看镜像大小
docker images myapp
基础镜像最终大小说明
python:3.12~1.0GB完整版,太大
python:3.12-slim~150MB精简版,推荐
python:3.12-alpine~60MB最小,但可能有兼容问题

Alpine的坑:很多Python包需要编译C扩展,Alpine用musl而不是glibc,可能导致编译失败或性能下降。建议用slim。

常用Docker命令

# 镜像管理
docker build -t myapp:v1 .           # 构建
docker tag myapp:v1 registry/myapp:v1  # 打标签
docker push registry/myapp:v1         # 推送

# 容器管理
docker ps                              # 查看运行中的容器
docker ps -a                           # 查看所有容器
docker logs -f --tail 100 myapp        # 查看日志
docker exec -it myapp bash             # 进入容器
docker stats                           # 资源使用情况

# 清理
docker system prune -a                 # 清理所有未使用的资源
docker volume prune                    # 清理未使用的卷

环境变量管理

# Dockerfile中设置默认值
ENV APP_ENV=production
ENV LOG_LEVEL=info
# 运行时覆盖
docker run -e APP_ENV=development myapp

# 用env文件
docker run --env-file .env myapp
# docker-compose中用.env文件
services:
  app:
    env_file:
      - .env
      - .env.local  # 本地覆盖

健康检查

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

常见踩坑

1. 容器内访问宿主机服务

# ❌ 容器内localhost是容器自己
requests.get('http://localhost:3306')

# ✅ 用特殊域名
requests.get('http://host.docker.internal:3306')

2. 时区问题

ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

3. 文件权限

# 创建非root用户
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
RUN chown -R appuser:appgroup /app
USER appuser

4. 日志输出

# Python在容器中要禁用输出缓冲
# Dockerfile中加:
ENV PYTHONUNBUFFERED=1

生产部署清单

  • 使用slim基础镜像
  • 多阶段构建减小体积
  • 非root用户运行
  • 配置健康检查
  • 设置资源限制(CPU/内存)
  • 日志输出到stdout
  • 敏感信息用环境变量,不要写在镜像里
  • .dockerignore排除无关文件
  • 固定依赖版本(pip freeze)

总结

Docker的核心价值是环境一致性和部署标准化。掌握Dockerfile编写、compose编排、镜像优化这三板斧,就能应对大部分场景。

建议从自己的一个小项目开始练手,把它容器化部署一遍,比看十篇教程都有用。