Files

130 lines
5.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: devops.docker.scenario.compose-dev-env
title: 一条命令拉起开发环境:Go API + Python AI 服务 + PostgreSQL + Redis
type: scenario
domain: devops
stack: [docker, go, fastapi, postgresql, redis]
tags: [Compose, 开发环境, 多语言, 健康检查, 配置管理]
level: intermediate
prerequisites: [devops.docker.compose-network-volume]
est_hours: 2
related: [devops.docker.interview, devops.nginx.index]
week: [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 不能出现在仓库里。
- 开发时改代码要能快速生效,不想每次都重新构建镜像。
- 同一套编排文件,尽量也能用于单机测试环境。
## 方案设计
```mermaid
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`。
## 关键代码
```yaml
# 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:
```
```yaml
# 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](../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)?数据怎么备份?