Docker Compose

核心知识点

1. Docker Compose 概述

1.1 什么是 Docker Compose

Docker Compose 是一个用于定义和运行多容器 Docker 应用的工具。通过 YAML 文件配置应用的服务,然后使用一个命令创建并启动所有服务。

1.2 Compose 工作流程

┌─────────────────────────────────────────────────────────────┐
│               Docker Compose 工作流程                    │
├─────────────────────────────────────────────────────────────┤
│                                                      │
│  1. 编写 docker-compose.yml                          │
│     ↓                                                │
│  2. 运行 docker-compose up                          │
│     ↓                                                │
│  3. 创建网络和数据卷                                │
│     ↓                                                │
│  4. 启动所有服务容器                                │
│     ↓                                                │
│  5. 应用运行中                                      │
│                                                      │
└─────────────────────────────────────────────────────────────┘

1.3 安装 Docker Compose

# 在 Linux 上安装 Docker Compose
sudo curl -L "https://github.com/docker/compose/releases/download/v2.20.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 CLI 中)
docker compose version

2. docker-compose.yml 基础

2.1 基本结构

version: '3.8'

services:
  web:
    image: nginx:latest
    ports:
      - "80:80"
  
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: password

2.2 常用指令

指令 说明 示例
image 指定镜像 image: nginx:latest
build 构建镜像 build: ./app
ports 端口映射 ports: - "80:80"
volumes 数据卷挂载 volumes: - data:/app
environment 环境变量 environment: - DEBUG=1
depends_on 服务依赖 depends_on: - db
networks 网络配置 networks: - frontend
restart 重启策略 restart: always

3. 服务配置

3.1 镜像与构建

version: '3.8'

services:
  # 使用现有镜像
  web:
    image: nginx:alpine
    ports:
      - "80:80"
  
  # 从 Dockerfile 构建
  app:
    build: ./app
    ports:
      - "3000:3000"
  
  # 使用构建参数
  custom-app:
    build:
      context: ./app
      dockerfile: Dockerfile.prod
      args:
        VERSION: "1.0"
    ports:
      - "3000:3000"

3.2 端口与网络

version: '3.8'

services:
  web:
    image: nginx:latest
    ports:
      - "80:80"           # 主机端口:容器端口
      - "443:443"
      - "8080-8090:8080-8090"  # 端口范围
  
  app:
    image: node:16-alpine
    networks:
      - frontend
      - backend

networks:
  frontend:
  backend:

3.3 环境变量

version: '3.8'

services:
  web:
    image: nginx:latest
    environment:
      - NODE_ENV=production
      - DEBUG=false
      - API_URL=http://api:3000
  
  # 使用 .env 文件
  app:
    image: node:16-alpine
    env_file:
      - .env
      - .env.local
    environment:
      - DB_HOST=${DB_HOST}
      - DB_PORT=${DB_PORT}

3.4 数据卷

version: '3.8'

services:
  db:
    image: mysql:5.7
    volumes:
      - db-data:/var/lib/mysql
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
      - /host/path:/container/path:ro
  
  app:
    image: node:16-alpine
    volumes:
      - ./app:/app
      - node_modules:/app/node_modules

volumes:
  db-data:
  node_modules:

4. 服务依赖与编排

4.1 服务依赖

version: '3.8'

services:
  web:
    image: nginx:latest
    depends_on:
      - app
  
  app:
    image: node:16-alpine
    depends_on:
      - db
    environment:
      DB_HOST: db
      DB_PORT: 3306
  
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: myapp

4.2 健康检查

version: '3.8'

services:
  web:
    image: nginx:latest
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
  
  app:
    image: node:16-alpine
    depends_on:
      web:
        condition: service_healthy

5. 常用命令

5.1 启动与停止

# 启动所有服务
docker-compose up

# 后台启动
docker-compose up -d

# 启动特定服务
docker-compose up web db

# 停止所有服务
docker-compose stop

# 停止并删除容器
docker-compose down

# 停止并删除容器、网络、数据卷
docker-compose down -v

5.2 查看服务状态

# 查看运行中的服务
docker-compose ps

# 查看服务日志
docker-compose logs

# 查看特定服务的日志
docker-compose logs web

# 实时跟踪日志
docker-compose logs -f

# 查看服务详细信息
docker-compose config

5.3 服务管理

# 重启服务
docker-compose restart

# 重启特定服务
docker-compose restart web

# 重新构建服务
docker-compose build

# 重新构建并启动
docker-compose up --build

# 拉取最新镜像
docker-compose pull

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

6. 高级配置

6.1 多文件配置

# docker-compose.yml
version: '3.8'

services:
  web:
    image: nginx:latest
    ports:
      - "80:80"
  
  app:
    extends:
      file: docker-compose.common.yml
      service: app

# docker-compose.common.yml
version: '3.8'

services:
  app:
    image: node:16-alpine
    environment:
      NODE_ENV: production

6.2 生产环境配置

# docker-compose.prod.yml
version: '3.8'

services:
  web:
    image: nginx:latest
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./ssl:/etc/nginx/ssl
    restart: always
  
  app:
    image: myapp:latest
    environment:
      NODE_ENV: production
      DEBUG: "false"
    restart: always
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: '0.50'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M

# 使用生产配置
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d

实用案例分析

案例 1:Web 应用与数据库

场景描述

部署一个包含 Nginx、Node.js 和 MySQL 的完整 Web 应用。

docker-compose.yml

version: '3.8'

services:
  # Nginx Web 服务器
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
      - ./app/dist:/usr/share/nginx/html
    depends_on:
      - app
    restart: always
  
  # Node.js 应用
  app:
    build: ./app
    ports:
      - "3000:3000"
    environment:
      NODE_ENV: production
      DB_HOST: mysql
      DB_USER: myapp
      DB_PASSWORD: password
      DB_NAME: myapp
    depends_on:
      - mysql
    restart: always
  
  # MySQL 数据库
  mysql:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: myapp
      MYSQL_USER: myapp
      MYSQL_PASSWORD: password
    volumes:
      - mysql-data:/var/lib/mysql
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    restart: always

volumes:
  mysql-data:

操作步骤

# 1. 创建项目目录
mkdir ~/my-web-app
cd ~/my-web-app

# 2. 创建目录结构
mkdir -p app nginx

# 3. 创建 docker-compose.yml 文件
cat > docker-compose.yml << 'EOF'
version: '3.8'

services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
      - ./app/dist:/usr/share/nginx/html
    depends_on:
      - app
    restart: always
  
  app:
    build: ./app
    ports:
      - "3000:3000"
    environment:
      NODE_ENV: production
      DB_HOST: mysql
      DB_USER: myapp
      DB_PASSWORD: password
      DB_NAME: myapp
    depends_on:
      - mysql
    restart: always
  
  mysql:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: myapp
      MYSQL_USER: myapp
      MYSQL_PASSWORD: password
    volumes:
      - mysql-data:/var/lib/mysql
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    restart: always

volumes:
  mysql-data:
EOF

# 4. 创建 Nginx 配置
cat > nginx/nginx.conf << 'EOF'
server {
    listen 80;
    server_name localhost;

    location / {
        proxy_pass http://app:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
EOF

# 5. 创建应用 Dockerfile
cat > app/Dockerfile << 'EOF'
FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
RUN npm run build
CMD ["npm", "start"]
EOF

# 6. 创建 package.json
cat > app/package.json << 'EOF'
{
  "name": "my-web-app",
  "version": "1.0.0",
  "scripts": {
    "start": "node server.js",
    "build": "echo 'Build complete'"
  },
  "dependencies": {
    "express": "^4.18.0",
    "mysql2": "^3.0.0"
  }
}
EOF

# 7. 创建应用代码
cat > app/server.js << 'EOF'
const express = require('express');
const mysql = require('mysql2/promise');
const app = express();

const pool = mysql.createPool({
    host: process.env.DB_HOST,
    user: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    database: process.env.DB_NAME
});

app.get('/', async (req, res) => {
    try {
        const [rows] = await pool.query('SELECT NOW() as now');
        res.json({ message: 'Hello from Docker!', time: rows[0].now });
    } catch (err) {
        res.status(500).json({ error: err.message });
    }
});

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

# 8. 创建初始化 SQL
cat > init.sql << 'EOF'
CREATE TABLE IF NOT EXISTS test (
    id INT AUTO_INCREMENT PRIMARY KEY,
    message VARCHAR(255)
);
EOF

# 9. 启动服务
docker-compose up -d

# 10. 查看服务状态
docker-compose ps

# 11. 查看日志
docker-compose logs

# 12. 测试访问
curl http://localhost

# 13. 清理
docker-compose down -v

案例 2:开发环境配置

场景描述

配置开发环境,支持代码热重载和调试。

docker-compose.dev.yml

version: '3.8'

services:
  # 应用服务(开发模式)
  app:
    build:
      context: ./app
      dockerfile: Dockerfile.dev
    volumes:
      - ./app:/app
      - /app/node_modules
    ports:
      - "3000:3000"
      - "9229:9229"  # 调试端口
    environment:
      NODE_ENV: development
      DEBUG: myapp:*
    command: npm run dev
  
  # 数据库服务
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: myapp_dev
    ports:
      - "3306:3306"
    volumes:
      - db-dev-data:/var/lib/mysql
  
  # Redis 缓存
  redis:
    image: redis:alpine
    ports:
      - "6379:6379"
    volumes:
      - redis-dev-data:/data

volumes:
  db-dev-data:
  redis-dev-data:

操作步骤

# 1. 创建开发配置
cat > docker-compose.dev.yml << 'EOF'
version: '3.8'

services:
  app:
    build:
      context: ./app
      dockerfile: Dockerfile.dev
    volumes:
      - ./app:/app
      - /app/node_modules
    ports:
      - "3000:3000"
      - "9229:9229"
    environment:
      NODE_ENV: development
      DEBUG: myapp:*
    command: npm run dev
  
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: myapp_dev
    ports:
      - "3306:3306"
    volumes:
      - db-dev-data:/var/lib/mysql
  
  redis:
    image: redis:alpine
    ports:
      - "6379:6379"
    volumes:
      - redis-dev-data:/data

volumes:
  db-dev-data:
  redis-dev-data:
EOF

# 2. 创建开发 Dockerfile
cat > app/Dockerfile.dev << 'EOF'
FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["npm", "start"]
EOF

# 3. 更新 package.json
cat > app/package.json << 'EOF'
{
  "name": "my-web-app",
  "version": "1.0.0",
  "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js"
  },
  "dependencies": {
    "express": "^4.18.0",
    "mysql2": "^3.0.0"
  },
  "devDependencies": {
    "nodemon": "^3.0.0"
  }
}
EOF

# 4. 启动开发环境
docker-compose -f docker-compose.dev.yml up -d

# 5. 查看日志
docker-compose -f docker-compose.dev.yml logs -f app

# 6. 测试代码热重载
# 修改 app/server.js 文件,观察日志输出

# 7. 清理
docker-compose -f docker-compose.dev.yml down -v

案例 3:服务扩展与负载均衡

场景描述

扩展应用服务,实现负载均衡。

docker-compose.yml

version: '3.8'

services:
  # 负载均衡器
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - app
  
  # 应用服务(可扩展)
  app:
    image: myapp:latest
    environment:
      NODE_ENV: production
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: '0.50'
          memory: 256M

nginx 配置

upstream backend {
    least_conn;
    server app:3000;
    server app:3000;
    server app:3000;
}

server {
    listen 80;
    
    location / {
        proxy_pass http://backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

操作步骤

# 1. 创建负载均衡配置
mkdir -p nginx
cat > nginx/nginx.conf << 'EOF'
upstream backend {
    least_conn;
    server app:3000;
    server app:3000;
    server app:3000;
}

server {
    listen 80;
    
    location / {
        proxy_pass http://backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
EOF

# 2. 创建 docker-compose.yml
cat > docker-compose.yml << 'EOF'
version: '3.8'

services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - app
  
  app:
    image: nginx:alpine
    environment:
      NODE_ENV: production
EOF

# 3. 启动服务
docker-compose up -d

# 4. 扩展应用服务
docker-compose up -d --scale app=3

# 5. 查看服务状态
docker-compose ps

# 6. 测试负载均衡
for i in {1..10}; do
    curl -s http://localhost | grep -o "Welcome"
done

# 7. 清理
docker-compose down

最佳实践

  1. 使用版本控制:将 docker-compose.yml 文件纳入版本控制。

  2. 分离配置文件:使用多个配置文件管理不同环境。

# 开发环境
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d

# 生产环境
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
  1. 使用 .env 文件:管理敏感信息。
# .env 文件
DB_PASSWORD=secret
API_KEY=your-api-key

# docker-compose.yml
services:
  app:
    environment:
      DB_PASSWORD: ${DB_PASSWORD}
      API_KEY: ${API_KEY}
  1. 合理设置重启策略:确保服务高可用。
services:
  app:
    restart: unless-stopped
  1. 使用健康检查:确保服务正常运行。
services:
  app:
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000"]
      interval: 30s
      timeout: 10s
      retries: 3

总结

本教程详细介绍了 Docker Compose 的使用方法,包括 docker-compose.yml 文件的编写、服务配置、常用命令等。通过实际案例,我们学习了如何部署多容器应用、配置开发环境以及实现负载均衡。掌握这些技能后,可以高效地编排和管理多容器 Docker 应用。

« 上一篇 Docker 存储管理 下一篇 » Kubernetes 基础