Docker Compose实战指南

Docker Compose实战指南

概述与背景

Docker Compose是Docker官方推出的多容器编排工具,它让你能够通过一个YAML配置文件定义、运行和管理多个Docker容器。对于开发环境搭建、微服务应用部署等场景,Docker Compose是不可或缺的利器。

graph TB
    subgraph Docker Compose架構
        A[docker-compose.yml] --> B[Docker Compose]
        B --> C[服務編排]
        
        C --> D[Web服務<br/>nginx:alpine]
        C --> E[應用服務<br/>backend:node]
        C --> F[數據庫服務<br/>postgres:15]
        C --> G[緩存服務<br/>redis:7]
        
        D --> H[Docker Network<br/>app_network]
        E --> H
        F --> H
        G --> H
        
        F --> I[Volume<br/>db_data]
        G --> J[Volume<br/>redis_data]
    end

    style A fill:#fff9c4
    style B fill:#e1f5fe
    style C fill:#f3e5f5
    style H fill:#c8e6c9
graph LR
    subgraph 手動Docker vs Compose對比
        A[手動Docker] --> A1[多個docker run命令]
        A --> A2[命令冗長易錯]
        A --> A3[配置分散]
        A --> A4[難以版本控制]
        
        B[Docker Compose] --> B1[單個YAML文件]
        B --> B2[聲明式配置]
        B --> B3[一鍵部署]
        B --> B4[Git友好]
    end

    style A fill:#ffcdd2
    style B fill:#c8e6c9

为什么需要Docker Compose?

传统Docker命令的局限

# 手动启动多个容器(繁琐且易错)
docker run -d --name db -e POSTGRES_PASSWORD=pass postgres:15
docker run -d --name redis redis:7
docker run -d --name backend --link db:db --link redis:redis -p 3000:3000 myapp
docker run -d --name nginx --link backend:backend -p 80:80 nginx

# 问题:
# 1. 命令冗长,难以维护
# 2. 容器间通信配置复杂
# 3. 启动顺序难以控制
# 4. 环境变量分散管理
# 5. 无法一键重建整个环境

Docker Compose解决方案

# docker-compose.yml - 声明式配置
services:
  db:
    image: postgres:15
    environment:
      POSTGRES_PASSWORD: pass
  redis:
    image: redis:7
  backend:
    build: ./backend
    ports:
      - "3000:3000"
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"

# 一键启动所有服务
# docker compose up -d

核心优势对比

维度手动Docker命令Docker Compose
配置管理分散在脚本中集中在YAML文件
一键部署❌ 需要多个命令docker compose up
版本控制❌ 困难✅ Git友好
环境一致性❌ 易出错✅ 声明式配置
服务依赖❌ 手动控制✅ 自动管理
多环境支持❌ 复杂✅ override文件
团队协作❌ 文档说明✅ 配置即文档

核心概念

graph TD
    subgraph Compose核心概念
        A[Services<br/>服務] --> A1[容器定義]
        A1 --> A2[映像來源]
        A1 --> A3[端口映射]
        A1 --> A4[環境變量]
        
        B[Networks<br/>網絡] --> B1[服務間通信]
        B --> B2[網絡隔離]
        
        C[Volumes<br/>卷] --> C1[數據持久化]
        C --> C2[配置文件掛載]
        
        D[Configs<br/>配置] --> D1[敏感配置]
        D --> D2[環境特定配置]
        
        E[Secrets<br/>密鑰] --> E1[密碼/證書]
        E --> E2[API密鑰]
    end

    style A fill:#e1f5fe
    style B fill:#fff3e0
    style C fill:#c8e6c9
    style D fill:#f3e5f5
    style E fill:#ffcdd2
sequenceDiagram
    participant D as 開發者
    participant C as Docker Compose
    participant N as Docker Network
    participant S as 服務容器

    Note over D,S: 服務啟動流程
    D->>C: docker compose up -d
    C->>C: 解析docker-compose.yml
    C->>N: 創建網絡
    C->>S: 按依賴順序啟動服務
    loop 健康檢查
        C->>S: 檢查服務狀態
        S-->>C: 返回健康狀態
    end
    C-->>D: 所有服務就緒

    Note over D,S: 服務擴縮容
    D->>C: docker compose up -d --scale backend=3
    C->>S: 啟動多個backend實例
    S-->>C: 註冊到負載均衡
    C-->>D: 擴容完成

    Note over D,S: 服務更新
    D->>C: docker compose up -d --build
    C->>S: 重建映像
    C->>S: 滾動更新服務
    S-->>C: 更新完成
    C-->>D: 服務已更新

服务(Service)

服务是Compose的基本单元,对应一个应用容器。

services:
  # 单实例服务
  web:
    image: nginx:alpine
  
  # 多实例服务(可扩展)
  api:
    image: myapi:latest
    deploy:
      replicas: 3  # 运行3个实例

项目(Project)

项目是一组相关服务的集合,默认使用目录名作为项目名。

# 项目名规则
# 默认:目录名(如 myapp)
# 自定义:COMPOSE_PROJECT_NAME=myproject docker compose up
# 或在.env中设置:COMPOSE_PROJECT_NAME=myproject

网络(Network)

服务之间通过服务名通信,Compose自动创建默认网络。

services:
  backend:
    # 可以通过 "db" 或 "redis" 访问其他服务
    environment:
      - DB_HOST=db
      - REDIS_HOST=redis
  
  db:
    image: postgres:15
  
  redis:
    image: redis:7

# Compose自动创建:myapp_default 网络
# 所有服务默认加入该网络

卷(Volume)

卷用于持久化数据,即使容器删除数据也不会丢失。

services:
  db:
    image: postgres:15
    volumes:
      - postgres_data:/var/lib/postgresql/data  # 命名卷
      - ./init.sql:/docker-entrypoint-initdb.d/  # 主机挂载

volumes:
  postgres_data:  # 定义命名卷

实战步骤

第一步:安装与验证

# Docker Desktop 已内置 Compose
# 检查版本
docker compose version
# Docker Compose version v2.24.0

# Linux 单独安装(如果未内置)
sudo curl -L "https://github.com/docker/compose/releases/download/v2.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

# 验证安装
docker compose version

版本说明

  • Docker Compose V2:使用 docker compose 命令(推荐)
  • Docker Compose V1:使用 docker-compose 命令(已废弃)

第二步:第一个Compose项目

2.1 项目结构

my-nginx/
├── docker-compose.yml
├── html/
│   └── index.html
└── nginx.conf

2.2 创建基础文件

index.html

<!DOCTYPE html>
<html>
<head>
  <title>My Docker App</title>
</head>
<body>
  <h1>Hello from Docker Compose!</h1>
</body>
</html>

nginx.conf

events {
    worker_connections 1024;
}

http {
    server {
        listen 80;
        server_name localhost;
        
        location / {
            root /usr/share/nginx/html;
            index index.html;
        }
    }
}

docker-compose.yml

version: '3.8'

services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./html:/usr/share/nginx/html
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    restart: unless-stopped

2.3 启动服务

# 前台运行(看到日志)
docker compose up

# 后台运行(推荐)
docker compose up -d

# 输出
[+] Running 2/2
 Network my-nginx_default    Created
 Container my-nginx-web-1    Started

# 查看运行状态
docker compose ps
# NAME              IMAGE          STATUS         PORTS
# my-nginx-web-1    nginx:alpine   Up 2 minutes   0.0.0.0:8080->80/tcp

# 访问应用
curl http://localhost:8080
# <h1>Hello from Docker Compose!</h1>

# 停止服务
docker compose down

第三步:多服务编排

3.1 完整的Web应用栈

构建一个典型的三层架构:Nginx → Node.js → PostgreSQL + Redis

项目结构

fullstack-app/
├── docker-compose.yml
├── .env
├── nginx/
│   └── nginx.conf
├── backend/
│   ├── Dockerfile
│   ├── package.json
│   └── src/
│       └── server.js
└── init/
    └── init.sql

docker-compose.yml

version: '3.8'

services:
  # ==================== 前端 - Nginx ====================
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/ssl:/etc/nginx/ssl:ro
    depends_on:
      backend:
        condition: service_healthy
    networks:
      - frontend
      - backend
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "nginx", "-t"]
      interval: 30s
      timeout: 10s
      retries: 3

  # ==================== 后端 - Node.js ====================
  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
      - REDIS_URL=redis://redis:6379
      - JWT_SECRET=${JWT_SECRET}
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    networks:
      - backend
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    deploy:
      resources:
        limits:
          cpus: '1'
          memory: 512M

  # ==================== 数据库 - PostgreSQL ====================
  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    networks:
      - backend
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 10s
      timeout: 5s
      retries: 5

  # ==================== 缓存 - Redis ====================
  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
    volumes:
      - redis_data:/data
    networks:
      - backend
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 3

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true  # 内部网络,不暴露到外部

volumes:
  postgres_data:
  redis_data:

3.2 服务间通信示例

backend/src/server.js

const express = require('express');
const { Pool } = require('pg');
const Redis = require('ioredis');

const app = express();

// 使用服务名作为主机名
const pool = new Pool({
  host: 'db',           // Docker Compose服务名
  port: 5432,
  user: process.env.POSTGRES_USER,
  password: process.env.POSTGRES_PASSWORD,
  database: process.env.POSTGRES_DB,
});

const redis = new Redis({
  host: 'redis',        // Docker Compose服务名
  port: 6379,
});

// 健康检查端点
app.get('/health', (req, res) => {
  res.json({ status: 'ok', timestamp: new Date().toISOString() });
});

// 缓存示例
app.get('/api/users/:id', async (req, res) => {
  const cacheKey = `user:${req.params.id}`;
  
  // 先查缓存
  const cached = await redis.get(cacheKey);
  if (cached) {
    return res.json(JSON.parse(cached));
  }
  
  // 查数据库
  const result = await pool.query('SELECT * FROM users WHERE id = $1', [req.params.id]);
  const user = result.rows[0];
  
  if (user) {
    // 写入缓存,过期10分钟
    await redis.setex(cacheKey, 600, JSON.stringify(user));
  }
  
  res.json(user);
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

第四步:构建自定义镜像

4.1 多阶段构建Dockerfile

backend/Dockerfile

# ==================== 构建阶段 ====================
FROM node:20-alpine AS builder

WORKDIR /app

# 复制依赖文件
COPY package*.json ./

# 安装所有依赖(包括devDependencies)
RUN npm ci

# 复制源代码
COPY . .

# 构建应用
RUN npm run build

# ==================== 生产阶段 ====================
FROM node:20-alpine

WORKDIR /app

# 安全:创建非root用户
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodeapp -u 1001

# 复制生产依赖
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force

# 从构建阶段复制构建产物
COPY --from=builder /app/dist ./dist

# 设置权限
RUN chown -R nodeapp:nodejs /app

# 切换用户
USER nodeapp

# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
  CMD wget -q --spider http://localhost:3000/health || exit 1

# 暴露端口
EXPOSE 3000

# 启动命令
CMD ["node", "dist/server.js"]

4.2 在Compose中使用构建

services:
  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
      args:
        - NODE_ENV=production
        - BUILD_VERSION=1.0.0
    # 或直接使用镜像(生产环境)
    # image: myregistry/backend:1.0.0

4.3 构建最佳实践

services:
  app:
    build:
      context: ./app
      dockerfile: Dockerfile
      args:
        - NODE_ENV=production
      # 多平台构建
      platforms:
        - linux/amd64
        - linux/arm64
    # 缓存配置
    cache_from:
      - myapp:latest
    # 标签
    tags:
      - myapp:latest
      - myapp:${VERSION}

第五步:环境配置管理

5.1 环境变量文件

.env(开发环境)

# 项目配置
COMPOSE_PROJECT_NAME=myapp-dev

# 数据库配置
POSTGRES_USER=myuser
POSTGRES_PASSWORD=mypassword
POSTGRES_DB=myapp_dev

# Redis配置
REDIS_PASSWORD=redispassword

# 应用配置
NODE_ENV=development
JWT_SECRET=dev-secret-key-change-in-production
API_PORT=3000

# 外部服务
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=noreply@example.com
SMTP_PASSWORD=smtp-password

.env.production(生产环境)

# 项目配置
COMPOSE_PROJECT_NAME=myapp-prod

# 数据库配置(生产环境使用强密码)
POSTGRES_USER=${DB_USER}
POSTGRES_PASSWORD=${DB_PASSWORD}
POSTGRES_DB=myapp

# 应用配置
NODE_ENV=production
JWT_SECRET=${JWT_SECRET}

# 日志配置
LOG_LEVEL=info

5.2 在Compose中使用环境变量

services:
  db:
    image: postgres:15
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
    # 或使用env_file
    env_file:
      - .env
      - .env.local  # 本地覆盖(不提交到Git)

  backend:
    environment:
      - DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
      - NODE_ENV=${NODE_ENV:-production}  # 默认值

5.3 多环境配置策略

方式1:使用多个env文件

# 开发环境
docker compose --env-file .env.development up -d

# 生产环境
docker compose --env-file .env.production up -d

# 测试环境
docker compose --env-file .env.test up -d

方式2:使用Override文件

docker-compose.yml(基础配置)

version: '3.8'
services:
  backend:
    build: ./backend
    environment:
      - NODE_ENV=development
    volumes:
      - ./backend:/app
      - /app/node_modules
    ports:
      - "3000:3000"

docker-compose.override.yml(开发覆盖,自动加载)

version: '3.8'
services:
  backend:
    environment:
      - DEBUG=true
    volumes:
      - ./backend:/app
    # 开发环境启用了热重载

docker-compose.prod.yml(生产配置)

version: '3.8'
services:
  backend:
    image: myregistry/backend:${VERSION}
    environment:
      - NODE_ENV=production
    restart: always
    deploy:
      resources:
        limits:
          memory: 512M
    # 不挂载源码
# 开发环境(自动加载override)
docker compose up -d

# 生产环境(显式指定)
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

第六步:常用命令速查

启动和停止

# 启动所有服务(后台)
docker compose up -d

# 启动特定服务
docker compose up -d backend redis

# 停止所有服务
docker compose down

# 停止并删除卷(数据清空)
docker compose down -v

# 停止并删除镜像
docker compose down --rmi all

# 优雅停止(等待任务完成)
docker compose stop

# 强制停止
docker compose kill

查看状态

# 查看运行状态
docker compose ps

# 查看所有容器(包括停止的)
docker compose ps -a

# 查看资源使用
docker compose top

# 查看服务日志
docker compose logs
docker compose logs -f           # 实时跟踪
docker compose logs -f backend   # 特定服务
docker compose logs --tail=100   # 最后100行
docker compose logs --since=1h   # 最近1小时

管理操作

# 重启服务
docker compose restart
docker compose restart nginx     # 重启特定服务

# 重新构建
docker compose build
docker compose build --no-cache  # 不使用缓存
docker compose up --build        # 构建并启动

# 进入容器
docker compose exec backend sh
docker compose exec -u root backend sh  # 以root用户
docker compose exec db psql -U user -d myapp  # 执行命令

# 运行一次性命令
docker compose run --rm backend npm test
docker compose run --rm db pg_dump -U user myapp > backup.sql

# 扩展服务
docker compose up -d --scale backend=3

# 查看网络
docker compose network ls
docker network inspect myapp_default

# 查看卷
docker compose volume ls
docker volume inspect myapp_postgres_data

调试命令

# 查看配置(解析后的结果)
docker compose config

# 验证配置文件
docker compose config --quiet

# 查看服务依赖关系
docker compose config --services

# 查看环境变量
docker compose config | grep -A 20 environment

# 拉取所有镜像
docker compose pull

# 推送镜像
docker compose push

高级配置

健康检查配置

services:
  web:
    image: nginx:alpine
    healthcheck:
      # 检查命令
      test: ["CMD", "curl", "-f", "http://localhost/"]
      # 或使用shell命令
      # test: ["CMD-SHELL", "curl -f http://localhost/ || exit 1"]
      
      # 检查间隔
      interval: 30s
      
      # 超时时间
      timeout: 10s
      
      # 重试次数
      retries: 3
      
      # 启动等待时间
      start_period: 10s

健康检查状态

  • starting:启动期间
  • healthy:健康
  • unhealthy:不健康(超过重试次数)
  • none:未配置健康检查

资源限制

services:
  app:
    image: myapp:latest
    deploy:
      resources:
        # 资源限制(硬限制)
        limits:
          cpus: '2'        # 最多使用2个CPU
          memory: 1G       # 最多使用1GB内存
        
        # 资源预留(软限制)
        reservations:
          cpus: '0.5'      # 预留0.5个CPU
          memory: 256M     # 预留256MB内存

安全配置

services:
  app:
    image: myapp:latest
    # 只读文件系统
    read_only: true
    
    # 安全选项
    security_opt:
      - no-new-privileges:true
    
    # 能力控制
    cap_drop:
      - ALL
    cap_add:
      - CHOWN
      - SETGID
      - SETUID
    
    # 临时文件系统(只读文件系统需要)
    tmpfs:
      - /tmp:size=100M,mode=1777
      - /var/cache:size=50M
      - /var/run:size=10M
    
    # 用户和组
    user: "1000:1000"
    
    # 禁止特权
    privileged: false

日志配置

services:
  app:
    image: myapp:latest
    logging:
      # 日志驱动
      driver: "json-file"
      options:
        # 单个日志文件最大10MB
        max-size: "10m"
        # 保留3个日志文件
        max-file: "3"
        # 日志标签
        labels: "service,environment"
        # 标签值
        tag: "{{.Name}}/{{.ID}}"
    
    # 或使用syslog驱动
    # logging:
    #   driver: "syslog"
    #   options:
    #     syslog-address: "tcp://logs.example.com:514"
    #     tag: "myapp"

网络高级配置

networks:
  # 前端网络(可从外部访问)
  frontend:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 172.20.0.0/16
          gateway: 172.20.0.1
    driver_opts:
      com.docker.network.bridge.enable_icc: "true"
      com.docker.network.bridge.enable_ip_masquerade: "true"

  # 后端网络(内部网络,隔离外部访问)
  backend:
    driver: bridge
    internal: true  # 无法访问外部网络
    ipam:
      config:
        - subnet: 172.21.0.0/16

  # 数据库网络(最严格隔离)
  database:
    driver: bridge
    internal: true
    attachable: false  # 不允许手动连接容器

services:
  nginx:
    networks:
      - frontend
      - backend
  
  backend:
    networks:
      - backend
      - database
  
  db:
    networks:
      - database

实战案例:微服务架构

完整微服务配置

version: '3.8'

services:
  # ==================== 网关服务 ====================
  gateway:
    image: traefik:v2.10
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
    ports:
      - "80:80"
      - "8080:8080"  # Dashboard
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - frontend
    restart: unless-stopped

  # ==================== 用户服务 ====================
  user-service:
    build:
      context: ./services/user
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.user.rule=PathPrefix(`/api/users`)"
      - "traefik.http.routers.user.middlewares=auth"
    environment:
      - DATABASE_URL=postgresql://user:pass@user-db:5432/users
      - JWT_SECRET=${JWT_SECRET}
      - REDIS_URL=redis://redis:6379
    depends_on:
      - user-db
      - redis
    networks:
      - frontend
      - backend
    restart: unless-stopped
    deploy:
      replicas: 2
      resources:
        limits:
          memory: 256M

  user-db:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: users
    volumes:
      - user_db_data:/var/lib/postgresql/data
    networks:
      - backend
    restart: unless-stopped

  # ==================== 订单服务 ====================
  order-service:
    build:
      context: ./services/order
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.order.rule=PathPrefix(`/api/orders`)"
    environment:
      - DATABASE_URL=mongodb://order-db:27017/orders
      - USER_SERVICE_URL=http://user-service:3000
      - RABBITMQ_URL=amqp://rabbitmq:5672
    depends_on:
      - order-db
      - rabbitmq
    networks:
      - frontend
      - backend
    restart: unless-stopped

  order-db:
    image: mongo:6
    volumes:
      - order_db_data:/data/db
    networks:
      - backend
    restart: unless-stopped

  # ==================== 消息队列 ====================
  rabbitmq:
    image: rabbitmq:3-management-alpine
    ports:
      - "15672:15672"  # Management UI
    volumes:
      - rabbitmq_data:/var/lib/rabbitmq
    networks:
      - backend
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "rabbitmqctl", "status"]
      interval: 30s
      timeout: 10s
      retries: 5

  # ==================== 缓存 ====================
  redis:
    image: redis:7-alpine
    command: redis-server --maxmemory 512mb --maxmemory-policy allkeys-lru
    volumes:
      - redis_data:/data
    networks:
      - backend
    restart: unless-stopped

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true

volumes:
  user_db_data:
  order_db_data:
  rabbitmq_data:
  redis_data:

常见问题解决

Q1: 网络连接问题

症状:服务间无法通信

# 诊断步骤
# 1. 检查网络
docker network ls
docker network inspect myapp_default

# 2. 检查容器是否在同一网络
docker inspect myapp-backend-1 | grep Networks -A 10

# 3. 测试连接
docker compose exec backend ping db
docker compose exec backend nc -zv db 5432

# 4. 检查DNS解析
docker compose exec backend nslookup db

解决方案

# 确保服务在同一网络
services:
  backend:
    networks:
      - app-network
  
  db:
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

Q2: 数据持久化问题

症状:重启后数据丢失

# 正确的卷配置
services:
  db:
    image: postgres:15
    volumes:
      # 命名卷(推荐)
      - postgres_data:/var/lib/postgresql/data
      # 主机挂载(用于初始化脚本)
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro

volumes:
  postgres_data:  # 必须在顶层声明

查看卷位置

docker volume inspect myapp_postgres_data
# Mountpoint: /var/lib/docker/volumes/myapp_postgres_data/_data

Q3: 服务启动顺序

问题:后端在数据库就绪前启动导致连接失败

解决方案

services:
  backend:
    depends_on:
      db:
        condition: service_healthy  # 等待健康检查通过
      redis:
        condition: service_started  # 只等待启动
  
  db:
    image: postgres:15
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user -d myapp"]
      interval: 5s
      timeout: 5s
      retries: 5
      start_period: 10s

Q4: 端口冲突

症状:端口被占用

# 查看端口占用
lsof -i :3000
# 或
netstat -tulpn | grep :3000

# 解决方案1:修改端口
ports:
  - "3001:3000"

# 解决方案2:动态分配
ports:
  - "3000"  # 宿主机随机分配端口

# 查看分配的端口
docker compose port backend 3000
# 0.0.0.0:32768

Q5: 权限问题

症状:无法写入挂载目录

# 方案1:修改容器用户
services:
  app:
    user: "${UID:-1000}:${GID:-1000}"
    volumes:
      - ./data:/app/data

# 方案2:使用命名卷(避免主机权限问题)
services:
  app:
    volumes:
      - app_data:/app/data

volumes:
  app_data:
# 主机上修复权限
sudo chown -R 1000:1000 ./data

最佳实践总结

1. 配置文件组织

project/
├── docker-compose.yml          # 基础配置
├── docker-compose.override.yml # 开发覆盖(自动加载)
├── docker-compose.prod.yml     # 生产配置
├── .env                        # 环境变量(不提交)
├── .env.example                # 环境变量示例(提交)
└── .gitignore
    .env
    .env.local

2. 安全检查清单

  • 使用 .env 管理敏感信息
  • 不使用 privileged: true
  • 限制资源使用(CPU、内存)
  • 配置只读文件系统
  • 使用非root用户运行
  • 限制网络访问(internal网络)
  • 定期更新基础镜像

3. 性能优化

  • 使用 .dockerignore 排除不必要文件
  • 多阶段构建减小镜像体积
  • 使用 depends_on.condition 避免启动竞争
  • 配置健康检查
  • 合理设置日志轮转

4. 运维建议

  • 定期备份数据卷
  • 监控资源使用
  • 配置日志收集
  • 使用健康检查
  • 准备回滚方案

参考资料

💬 評論區

返回文章列表