22.09-Docker Compose本地环境

要点

  • Docker Compose 是本地多服务编排的工具
  • 一个典型的 Hono 项目可能需要:API + 数据库 + Redis + 前端
  • Compose 用一个 YAML 文件定义所有服务,一条命令启动
  • 本地开发环境和生产环境用同一套配置,减少「在我机器上能跑」的问题

内容

1. 为什么需要 Docker Compose

一个完整的后端项目通常不只一个服务:

API 服务 → 需要 PostgreSQL、Redis、可能还有消息队列
前端服务 → 需要 API 地址
数据库 → 需要持久化存储

如果每个服务都手动 docker run,命令会很长而且难以管理:

// terminal
# 启动数据库
docker run -d --name postgres -e POSTGRES_PASSWORD=secret -v pgdata:/var/lib/postgresql/data -p 5432:5432 postgres:16
 
# 启动 Redis
docker run -d --name redis -p 6379:6379 redis:7
 
# 启动 API
docker run -d --name api --link postgres --link redis -e DATABASE_URL=postgresql://postgres:secret@postgres:5432/mydb -e REDIS_URL=redis://redis:6379 -p 3000:3000 my-api
 
# 启动前端
docker run -d --name web --link api -e API_URL=http://api:3000 -p 3001:3000 my-web

Docker Compose 把这些命令变成一个 YAML 文件。

2. docker-compose.yml

# docker-compose.yml
services:
  # API 服务
  api:
    build:
      context: ./apps/api
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgresql://postgres:secret@postgres:5432/mydb
      - REDIS_URL=redis://redis:6379
      - NODE_ENV=development
    depends_on:
      - postgres
      - redis
    volumes:
      - ./apps/api/src:/app/src  # 开发期热更新
    command: bun run --watch src/index.ts
 
  # 前端服务
  web:
    build:
      context: ./apps/web
      dockerfile: Dockerfile
    ports:
      - "3001:3000"
    environment:
      - API_URL=http://api:3000
    depends_on:
      - api
 
  # PostgreSQL 数据库
  postgres:
    image: postgres:16-alpine
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=secret
      - POSTGRES_DB=mydb
    volumes:
      - pgdata:/var/lib/postgresql/data
 
  # Redis 缓存
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redisdata:/data
 
volumes:
  pgdata:
  redisdata:

3. 基本操作

// terminal
# 启动所有服务(后台运行)
docker compose up -d
 
# 启动所有服务(前台运行,看日志)
docker compose up
 
# 停止所有服务
docker compose down
 
# 停止并删除数据卷(清空数据库)
docker compose down -v
 
# 查看服务状态
docker compose ps
 
# 查看某个服务的日志
docker compose logs api
docker compose logs -f api  # 实时跟踪
 
# 重新构建镜像
docker compose build
 
# 只启动某个服务
docker compose up -d api
 
# 在服务里执行命令
docker compose exec api sh
docker compose exec postgres psql -U postgres -d mydb

4. 开发期配置

开发期通常需要热更新和源码挂载:

# docker-compose.dev.yml(覆盖默认配置)
services:
  api:
    build:
      context: ./apps/api
      target: builder  # 用构建阶段,包含开发依赖
    volumes:
      - ./apps/api/src:/app/src
      - ./apps/api/package.json:/app/package.json
    command: bun run --watch src/index.ts
    environment:
      - NODE_ENV=development
 
  web:
    volumes:
      - ./apps/web/src:/app/src
    command: npm run dev
// terminal
# 用开发配置启动
docker compose -f docker-compose.yml -f docker-compose.dev.yml up

5. 网络和容器间通信

Compose 会自动创建一个网络,所有服务在同一个网络里,可以用服务名作为主机名互相访问:

services:
  api:
    environment:
      # 直接用服务名 "postgres" 作为主机名
      - DATABASE_URL=postgresql://postgres:secret@postgres:5432/mydb
// apps/api/src/db.ts
// 连接数据库,主机名是 "postgres"
const db = drizzle(postgres(process.env.DATABASE_URL!))

6. 数据库初始化

init.sql 初始化数据库:

# docker-compose.yml
services:
  postgres:
    image: postgres:16-alpine
    volumes:
      - ./dev/init.sql:/docker-entrypoint-initdb.d/init.sql
-- dev/init.sql
CREATE TABLE IF NOT EXISTS users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  email VARCHAR(255) UNIQUE NOT NULL,
  created_at TIMESTAMP DEFAULT NOW()
);
 
INSERT INTO users (name, email) VALUES ('Admin', '[email protected]');

PostgreSQL 会在第一次启动时自动执行 /docker-entrypoint-initdb.d/ 目录下的 SQL 文件。

7. 健康检查

services:
  api:
    build: .
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s
    depends_on:
      postgres:
        condition: service_healthy
 
  postgres:
    image: postgres:16-alpine
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5

健康检查让 depends_on 可以等待服务真正就绪,而不只是容器启动。

8. 和 K8s 的关系

Docker Compose 适合本地开发和小规模部署。如果需要大规模容器编排(自动扩缩容、滚动更新、跨节点调度),需要用 Kubernetes(K8s)。

维度Docker ComposeKubernetes
适用规模本地/单机集群
复杂度
学习曲线几天几周到几个月
自动扩缩容不支持支持
服务发现DNS内置
负载均衡简单完整

对于个人项目和小团队,Docker Compose + 云服务器通常够用。K8s 更适合中大型团队和多服务微服务架构。

9. 小结

Docker Compose 的核心:

  1. 一个 YAML 文件定义所有服务
  2. docker compose up 一条命令启动
  3. 服务间用服务名通信
  4. 开发期用 volume 挂载源码,实现热更新
  5. 健康检查确保服务就绪后再启动依赖

Docker Compose 是本地开发环境的标准工具,也是学习容器化和 K8s 的起点。