Files

5.4 KiB
Raw Permalink Blame History

id, title, type, domain, stack, tags, level, prerequisites, est_hours, related, week
id title type domain stack tags level prerequisites est_hours related week
devops.docker.scenario.compose-dev-env 一条命令拉起开发环境:Go API + Python AI 服务 + PostgreSQL + Redis scenario devops
docker
go
fastapi
postgresql
redis
Compose
开发环境
多语言
健康检查
配置管理
intermediate
devops.docker.compose-network-volume
2
devops.docker.interview
devops.nginx.index
3
13
14

一条命令拉起开发环境:Go API + Python AI 服务 + PostgreSQL + Redis

业务背景与问题

一个企业知识库项目:Go 写业务 API(用户、权限、文档管理),Python FastAPI 写 AI 服务(RAG 检索、调用大模型),PostgreSQL(含 pgvector)存数据和向量,Redis 做缓存。新人入职配环境要一两天:Python 版本不一致、pgvector 扩展装不上、各自本机的端口冲突;测试环境和开发环境配置也对不上。

约束

  • 开发者使用 macOS、Windows(WSL2)、Linux 都有。
  • 模型 API Key 不能出现在仓库里。
  • 开发时改代码要能快速生效,不想每次都重新构建镜像。
  • 同一套编排文件,尽量也能用于单机测试环境。

方案设计

flowchart LR
  Dev[浏览器 / 前端 dev server] -->|:8080| GW[Nginx 网关]
  GW -->|/api| API[Go API]
  GW -->|/ai| AI[Python AI 服务]
  API --> DB[(PostgreSQL + pgvector)]
  AI --> DB
  API --> R[(Redis)]
  AI --> R
  1. 一个 compose.yaml 描述全部服务,服务之间用服务名通信;只有网关映射端口到宿主机。
  2. 数据库用带 pgvector 的镜像,初始化 SQL 放在 ./db/init/ 挂载到 /docker-entrypoint-initdb.d/(只在数据卷为空的首次启动时执行)。
  3. 健康检查 + 依赖条件:数据库健康后才启动 API 和 AI 服务。
  4. 配置分层:compose.yaml 写公共配置;compose.override.yaml(开发时自动合并)挂载源码目录、开启热重载;密钥放在不提交的 .env,仓库里提供 .env.example。
  5. Makefile 封装常用命令:make up、make logs、make psql、make reset-db。

关键代码

# compose.yaml(节选)
services:
  gateway:
    image: nginx:1.27-alpine
    ports: ["8080:80"]
    volumes:
      - ./deploy/nginx.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on: [api, ai]

  api:
    build: ./api
    environment:
      DATABASE_URL: postgres://app:${DB_PASSWORD}@db:5432/app?sslmode=disable
      REDIS_URL: redis://redis:6379/0
    depends_on:
      db: { condition: service_healthy }

  ai:
    build: ./ai
    environment:
      DATABASE_URL: postgresql://app:${DB_PASSWORD}@db:5432/app
      LLM_API_KEY: ${LLM_API_KEY}          # 来自不提交的 .env
    depends_on:
      db: { condition: service_healthy }

  db:
    image: pgvector/pgvector:pg17
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: app
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./db/init:/docker-entrypoint-initdb.d:ro   # 首次启动执行 CREATE EXTENSION vector 等
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app -d app"]
      interval: 5s
      retries: 10

  redis:
    image: redis:7-alpine

volumes:
  pgdata:
# compose.override.yaml:docker compose up 时自动合并,只用于本地开发
services:
  ai:
    volumes:
      - ./ai/app:/app/app                  # 挂载源码
    command: ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]

单个 Go 服务的完整 Dockerfile 和 compose 示例见 examples/go-api-compose。镜像标签仅作示例,以各镜像的官方页面为准。

取舍

方案 优点 缺点
Compose 全部容器化(本方案) 环境一致、上手快 在 macOS / Windows 上挂载源码的文件性能较差;镜像占磁盘
只把依赖(数据库、Redis)放容器,应用本机跑 调试方便、热更新快 应用的语言版本仍需各自安装
远程开发环境(云端开发机 / Dev Container) 环境完全统一 需要网络和额外成本

实际团队里常见做法是两者结合:依赖一律容器化;应用代码可以选择容器内运行或本机运行。

易错点

  • 修改了 db/init 里的 SQL 却发现没执行:初始化脚本只在数据卷为空时执行,需要 docker compose down -v 重建(会清空数据)或写迁移脚本。
  • .env 被提交到仓库,泄露模型 API Key。加入 .gitignore,提交前扫描(仓库里的 tools/check_sensitive.py)。
  • 所有服务都映射端口,本机端口冲突;只映射网关。
  • 开发环境的 --reload、源码挂载被带到测试环境:override 文件只用于开发,测试环境显式指定 -f compose.yaml。
  • 容器内时区、字符集和生产不一致,导致时间相关的 bug 只在某个环境出现。统一使用 UTC 存储。

面试怎么讲

“项目是 Go 业务 API 加 Python AI 服务,依赖 PostgreSQL(pgvector)和 Redis。我用 Compose 把它们编排起来:服务之间用服务名通信,只有网关对外映射端口;数据库加了健康检查,应用等数据库就绪才启动;密钥放在不提交的 .env,开发时用 override 文件挂载源码热重载。新人 clone 后一条命令就能跑起来。”

追问准备:生产环境也用 Compose 吗(单机小规模可以,多机用 Kubernetes)?数据怎么备份?