You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

8.0 KiB

Docker 容器化开发环境实施计划

目标: 在 Docker 容器内运行 FastAPI,使用 asyncpg 驱动,解决 macOS GSSAPI 问题
预计时间: 30 分钟
状态: 待执行
创建时间: 2026-01-17

背景

问题

  • macOS 上 asyncpg 存在 GSSAPI 认证问题
  • 无法直接连接 PostgreSQL("role postgres does not exist" 错误)
  • 已尝试多种方案均无效

解决方案

在 Docker 容器内运行 FastAPI,完全避免 macOS 环境问题,同时保留 asyncpg 的性能优势(比 psycopg 快 2.8 倍)。

实施步骤

Step 1: 恢复 asyncpg 依赖

文件: server/requirements.txt

操作:

# 数据库驱动(使用 asyncpg 获得最佳性能)
asyncpg==0.29.0

说明: 移除 psycopg,恢复 asyncpg


Step 2: 恢复数据库连接配置

文件: server/.env

操作:

# 数据库配置(容器内网络)
DATABASE_URL=postgresql+asyncpg://postgres:postgres@postgres:5432/jointo
DATABASE_ECHO=True

说明:

  • 使用容器名称 postgres 作为主机名(Docker 内部 DNS)
  • 不再使用 127.0.0.1(宿主机地址)

文件: server/app/core/database.py

操作:

# 创建异步引擎
engine = create_async_engine(
    settings.DATABASE_URL,
    echo=settings.DATABASE_ECHO,
    future=True,
    pool_pre_ping=True,
    pool_size=10,
    max_overflow=20
)

说明: 移除 connect_args(容器内不需要禁用 GSSAPI)


Step 3: 优化 Dockerfile(支持热重载)

文件: server/Dockerfile

操作:

FROM python:3.11-slim

WORKDIR /app

# 安装系统依赖
RUN apt-get update && apt-get install -y \
    gcc \
    postgresql-client \
    && rm -rf /var/lib/apt/lists/*

# 复制依赖文件
COPY requirements.txt .

# 安装 Python 依赖
RUN pip install --no-cache-dir -r requirements.txt

# 复制应用代码(开发模式下会被挂载覆盖)
COPY . .

# 暴露端口
EXPOSE 8000

# 启动命令(支持热重载)
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]

关键点:

  • 使用 Python 3.11(与本地一致)
  • 安装 gcc(编译 asyncpg C 扩展)
  • --reload 支持代码热重载

Step 4: 更新 docker-compose.yml

文件: server/docker-compose.yml

操作: 添加 app 服务

version: "3.8"

services:
  # PostgreSQL 数据库
  postgres:
    image: postgres:14-alpine
    container_name: jointo-server-postgres
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: jointo
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - jointo-network

  # Redis 缓存
  redis:
    image: redis:7-alpine
    container_name: jointo-server-redis
    ports:
      - "6381:6379"
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - jointo-network

  # MinIO 对象存储
  minio:
    image: minio/minio:latest
    container_name: jointo-server-minio
    environment:
      MINIO_ROOT_USER: minioadmin
      MINIO_ROOT_PASSWORD: minioadmin
    ports:
      - "9100:9000"
      - "9101:9001"
    volumes:
      - minio_data:/data
    command: server /data --console-address ":9001"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 30s
      timeout: 20s
      retries: 3
    networks:
      - jointo-network

  # FastAPI 应用(新增)
  app:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: jointo-server-app
    ports:
      - "8000:8000"
    volumes:
      - .:/app # 挂载代码目录(支持热重载)
      - /app/venv # 排除虚拟环境
    environment:
      - DATABASE_URL=postgresql+asyncpg://postgres:postgres@postgres:5432/jointo
      - REDIS_URL=redis://redis:6379/0
      - MINIO_ENDPOINT=minio:9000
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
      minio:
        condition: service_healthy
    networks:
      - jointo-network
    restart: unless-stopped

volumes:
  postgres_data:
  redis_data:
  minio_data:

networks:
  jointo-network:
    driver: bridge

关键配置:

  • volumes: - .:/app - 挂载代码目录,支持热重载
  • depends_on - 等待数据库服务健康后再启动
  • networks - 所有服务在同一网络,可通过服务名互相访问

Step 5: 创建 .dockerignore

文件: server/.dockerignore

操作:

venv/
__pycache__/
*.pyc
*.pyo
*.pyd
.Python
*.so
*.egg
*.egg-info/
dist/
build/
.pytest_cache/
.coverage
htmlcov/
.env
.DS_Store
*.log

说明: 避免将不必要的文件复制到镜像中


Step 6: 更新启动脚本

文件: server/start_docker.sh(新建)

操作:

#!/bin/bash

# 停止并移除旧容器
docker-compose down

# 构建并启动所有服务
docker-compose up --build -d

# 查看日志
docker-compose logs -f app

说明: 简化 Docker 启动流程


Step 7: 更新 README.md

文件: server/README.md

操作: 添加 Docker 开发指南

## 开发环境

### 方式 1: Docker 容器(推荐)

**优点**: 环境一致,性能最优(使用 asyncpg)

```bash
# 启动所有服务
docker-compose up --build -d

# 查看日志
docker-compose logs -f app

# 停止服务
docker-compose down
```

访问:

方式 2: 本地运行(仅限 Linux)

macOS 用户请使用 Docker 方式,避免 asyncpg GSSAPI 问题。

# 启动依赖服务
docker-compose up -d postgres redis minio

# 本地运行 FastAPI
./start_dev.sh

---

## 验证步骤

### 1. 构建并启动服务

```bash
cd server
docker-compose up --build -d

2. 检查容器状态

docker-compose ps

预期输出:

NAME                        STATUS
jointo-server-app         Up (healthy)
jointo-server-postgres    Up (healthy)
jointo-server-redis       Up (healthy)
jointo-server-minio       Up (healthy)

3. 查看应用日志

docker-compose logs -f app

预期输出:

🚀 Starting Jointo API...
✅ Database initialized
INFO: Uvicorn running on http://0.0.0.0:8000

4. 测试 API 端点

# 健康检查
curl http://localhost:8000/health

# 数据库健康检查
curl http://localhost:8000/api/v1/health/db

# API 文档
open http://localhost:8000/api/docs

5. 测试热重载

修改 app/main.py 中的任意代码,保存后观察日志:

INFO: Detected file change, reloading...

预期结果

FastAPI 在 Docker 容器内成功启动
asyncpg 正常连接 PostgreSQL
数据库表自动创建
代码修改自动热重载
所有健康检查端点正常响应


回滚方案

如果遇到问题,可以快速回滚:

# 停止 Docker 服务
docker-compose down

# 使用本地 + psycopg 方案
# 1. 修改 requirements.txt: asyncpg -> psycopg[binary]
# 2. 修改 .env: postgresql+asyncpg -> postgresql+psycopg
# 3. 重新安装依赖
# 4. 启动本地服务

相关文件

  • server/Dockerfile - 应用镜像定义
  • server/docker-compose.yml - 服务编排配置
  • server/.dockerignore - Docker 忽略文件
  • server/start_docker.sh - 启动脚本
  • server/requirements.txt - Python 依赖
  • server/.env - 环境变量配置
  • server/README.md - 项目文档

后续优化

  1. 多阶段构建: 减小镜像体积
  2. 健康检查: 为 app 服务添加健康检查
  3. 日志管理: 配置日志收集和轮转
  4. 性能监控: 集成 Prometheus + Grafana
  5. 调试支持: 配置远程调试器

更新日志

  • 2026-01-17: 初始版本,实施 Docker 容器化开发环境