> Docker Compose实战:一键部署多容器应用

Docker Compose解决什么问题

用Docker跑单个容器很简单,但实际项目往往需要多个服务配合:Web应用 + 数据库 + 缓存 + 反向代理。如果每个都手动docker run,参数一大堆,管理起来非常痛苦。

Docker Compose就是来解决这个问题的——用一个YAML文件定义所有服务,一条命令启动全部。

安装Docker Compose

Docker Desktop自带Compose。如果是Linux服务器:

1
2
3
4
5
6
7
8
# 新版Docker已经自带compose插件
$ docker compose version
Docker Compose version v2.21.0

# 如果没有,手动安装
$ sudo apt install -y docker-compose-plugin
$ docker compose version
Docker Compose version v2.21.0

注意:新版用docker compose(空格),老版用docker-compose(连字符)。本文使用新版语法。

基本语法

一个docker-compose.yml文件的基本结构:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
version: "3.8"

services:
  web:
    image: nginx:latest
    ports:
      - "80:80"
    volumes:
      - ./html:/usr/share/nginx/html
    restart: always

  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: mypassword
    volumes:
      - db_data:/var/lib/mysql
    restart: always

volumes:
  db_data:
配置项作用示例
image使用的镜像nginx:latest
build从Dockerfile构建build: ./app
ports端口映射"8080:80"
volumes数据卷挂载./data:/app/data
environment环境变量MYSQL_ROOT_PASSWORD: xxx
depends_on依赖关系先启动依赖的服务
restart重启策略always/unless-stopped
networks网络配置自定义网络

常用命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 启动所有服务(后台运行)
$ docker compose up -d
[+] Running 3/3
 ✔ Network myapp_default  Created
 ✔ Container myapp-db-1   Started
 ✔ Container myapp-web-1  Started

# 查看运行状态
$ docker compose ps
NAME            IMAGE        STATUS         PORTS
myapp-web-1     nginx:latest Up 30 seconds  0.0.0.0:80->80/tcp
myapp-db-1      mysql:8.0    Up 30 seconds  3306/tcp

# 查看日志
$ docker compose logs
$ docker compose logs -f web  # 跟踪指定服务的日志

# 停止所有服务
$ docker compose down

# 停止并删除数据卷(谨慎!)
$ docker compose down -v

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

# 只启动某个服务
$ docker compose up -d web

# 进入某个容器
$ docker compose exec web bash

实战一:Nginx + PHP + MySQL

典型的LNMP环境:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# docker-compose.yml
version: "3.8"

services:
  nginx:
    image: nginx:1.24
    ports:
      - "80:80"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./www:/var/www/html
    depends_on:
      - php
    restart: always

  php:
    image: php:8.2-fpm
    volumes:
      - ./www:/var/www/html
    restart: always

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: myapp
      MYSQL_USER: appuser
      MYSQL_PASSWORD: apppassword
    volumes:
      - mysql_data:/var/lib/mysql
    ports:
      - "3306:3306"
    restart: always

volumes:
  mysql_data:

Nginx配置文件 nginx/conf.d/default.conf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
server {
    listen 80;
    server_name localhost;
    root /var/www/html;
    index index.php index.html;

    location ~ \.php$ {
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 创建测试文件
$ mkdir -p www nginx/conf.d
$ echo '<?php phpinfo(); ?>' > www/index.php

# 启动
$ docker compose up -d
[+] Running 4/4
 ✔ Network lnmp_default    Created
 ✔ Container lnmp-mysql-1  Started
 ✔ Container lnmp-php-1    Started
 ✔ Container lnmp-nginx-1  Started

# 访问 http://服务器IP 就能看到phpinfo页面了

实战二:Node.js + Redis + MongoDB

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
version: "3.8"

services:
  app:
    build: ./app
    ports:
      - "3000:3000"
    environment:
      - REDIS_URL=redis://redis:6379
      - MONGO_URL=mongodb://mongo:27017/myapp
    depends_on:
      - redis
      - mongo
    restart: always

  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data
    restart: always

  mongo:
    image: mongo:7
    volumes:
      - mongo_data:/data/db
    environment:
      MONGO_INITDB_DATABASE: myapp
    restart: always

volumes:
  redis_data:
  mongo_data:

使用.env文件管理配置

不要把密码硬编码在docker-compose.yml里:

1
2
3
4
5
# .env 文件
MYSQL_ROOT_PASSWORD=your_secure_password
MYSQL_DATABASE=production_db
MYSQL_USER=produser
MYSQL_PASSWORD=prodpassword
1
2
3
4
5
6
7
8
9
# docker-compose.yml
services:
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
1
2
# 验证变量替换是否正确
$ docker compose config

多环境配置

1
2
3
4
5
6
7
8
9
# docker-compose.yml      - 基础配置
# docker-compose.dev.yml  - 开发环境覆盖
# docker-compose.prod.yml - 生产环境覆盖

# 开发环境启动
$ 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

常见问题

服务启动顺序

depends_on只保证启动顺序,不保证服务就绪。数据库可能还没初始化完,应用就开始连了:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
services:
  app:
    depends_on:
      db:
        condition: service_healthy
  db:
    image: mysql:8.0
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

容器间通信

同一个docker-compose.yml里的服务可以用服务名直接通信:

1
2
3
4
# 在app容器里可以直接用 "db" 作为主机名
$ docker compose exec app ping db
PING db (172.18.0.3) 56(84) bytes of data.
64 bytes from lnmp-db-1.lnmp_default (172.18.0.3): icmp_seq=1 ttl=64

总结

Docker Compose是管理多容器应用的利器。掌握它之后,部署一个完整的Web应用只需要一个YAML文件和一条命令。

Docker基础操作可以看Docker入门教程。如果想用Docker快速搭建WordPress,可以看Docker一键部署WordPress

Docker Docker Compose 容器编排
cd ..