Docker Compose:把多个容器组织成一个应用

一个 SpringBoot 应用通常不会单独运行。它依赖 MySQL 存数据,依赖 Redis 做缓存,有的还依赖 RabbitMQ、Elasticsearch 等中间件。上一篇讲了如何把单个应用装进容器,但实际开发中你需要同时启动这一整套服务。

docker run 逐个启动显然不太方便。Docker Compose 提供了一种更适合多服务应用的管理方式。一个 YAML 文件描述所有服务及其依赖关系,一条命令全部启动。

目录

从单个容器到完整应用环境

一个典型的 SpringBoot 开发环境,至少需要这几个服务同时运行:

SpringBoot 应用
    │
    ├── MySQL     存储业务数据
    ├── Redis     缓存和会话管理
    └── RabbitMQ  消息队列(可选)

手动启动这些服务,大致是这样的流程:

# 启动 MySQL
docker run -d --name mysql \
  -p 3306:3306 \
  -e MYSQL_ROOT_PASSWORD=root123 \
  -e MYSQL_DATABASE=myapp \
  -v mysql-data:/var/lib/mysql \
  mysql:8.0

# 启动 Redis
docker run -d --name redis \
  -p 6379:6379 \
  redis:7

# 启动应用
docker run -d --name my-app \
  -p 8080:8080 \
  -e SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/myapp \
  -e SPRING_REDIS_HOST=redis \
  my-app:1.0

真正麻烦的是配置分散在命令参数中,难以维护和复用。每次修改配置要删掉容器重新创建,换台机器又要全部重来。服务之间的启动顺序、网络连接、环境变量配置都靠人记住,出错了排查也麻烦。

Compose 和 docker run 的关系

上一篇用 docker run 启动单个容器,这篇用 Compose 管理多个容器。两者不是替代关系,而是适用场景不同。

docker run 适合一次性操作——快速测试一个镜像、临时跑个工具容器、调试某个服务。它的问题是每条命令只管一个容器,多个服务之间的配置全靠命令参数传递,没有统一的声明文件。

docker compose 适合需要多个服务协作的场景——开发环境、集成测试、本地 demo。它的核心价值是把所有服务的配置集中到一个 YAML 文件中,用一条命令管理整个应用环境。

docker rundocker compose
管理范围单个容器多个服务
配置方式命令参数YAML 文件
网络需手动创建和连接自动创建共享网络
启动顺序不支持depends_on
适用场景临时测试、单服务开发环境、多服务编排

日常开发中,两者经常配合使用。用 docker run 快速验证镜像是否能跑,确认没问题后再写进 docker-compose.yml

Compose 如何描述一个应用环境

Compose 用一个 YAML 文件定义所有服务。核心结构:

services:
  服务名:
    image: 镜像名
    ports:
      - "宿主机端口:容器端口"
    environment:
      - 环境变量=值
    volumes:
      - 卷名:容器内路径

几个关键字段:

  • services:定义所有服务,每个服务是一个独立的容器
  • image:指定使用的镜像。也可以用 build 指向 Dockerfile,让 Compose 自己构建
  • ports:端口映射,和 docker run -p 等价
  • environment:环境变量,和 docker run -e 等价
  • volumes:数据持久化,和 docker run -v 等价

Compose 文件不需要记命令参数,所有配置都在一个文件里,版本管理也方便。

用 Compose 编排 SpringBoot + MySQL + Redis

把前面手动启动的三个服务用 Compose 编排:

services:
  mysql:
    image: mysql:8.0
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: root123
      MYSQL_DATABASE: myapp
    volumes:
      - mysql-data:/var/lib/mysql

  redis:
    image: redis:7
    ports:
      - "6379:6379"
    command: redis-server --requirepass redis123

  my-app:
    image: my-app:1.0
    ports:
      - "8080:8080"
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/myapp
      SPRING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: root123
      SPRING_REDIS_HOST: redis
      SPRING_REDIS_PASSWORD: redis123
    depends_on:
      - mysql
      - redis

volumes:
  mysql-data:

把这个文件保存为 docker-compose.yml,在同目录下执行:

docker compose up -d

三个服务同时启动。docker compose down 停止并删除所有容器。

Redis 通过 command 覆盖了默认启动命令,加上了密码认证。即使是开发环境,也不建议跑无密码的 Redis——配置泄露时没有密码就是裸奔。

注意 environment 中数据库连接地址写的是 mysql:3306,不是 localhost:3306。这是因为 Compose 会自动创建一个网络,所有服务都在这个网络中,可以直接用服务名互相访问。

服务通信:容器之间如何通过服务名访问

手动 docker run 时,如果不在同一个 Docker 网络中,容器之间是无法用名称通信的。Compose 会自动创建网络,并将所有服务加入同一个网络。

在这个网络中,每个服务名就是它的主机名。my-app 连接 MySQL 时,地址写 mysql:3306 就能连上,不需要知道 MySQL 容器的实际 IP。

在这里插入图片描述

这也是为什么 application.yml 中的 localhost 在容器内不生效:容器内的 localhost 指向容器自己,不是宿主机,也不是其他容器。用 Compose 编排时,所有依赖服务的地址都应该写服务名。

服务启动依赖与健康检查

depends_on 只保证启动顺序,不保证服务就绪。MySQL 容器启动后,还需要几秒钟初始化数据库。如果应用在这几秒内尝试连接,会报连接失败。

my-app:
  depends_on:
    - mysql
    - redis

上面的配置只保证 MySQL 容器先启动,但不保证 MySQL 已经准备好接受连接。

要等服务真正就绪,需要配合健康检查:

mysql:
  image: mysql:8.0
  ports:
    - "3306:3306"
  environment:
    MYSQL_ROOT_PASSWORD: root123
    MYSQL_DATABASE: myapp
  volumes:
    - mysql-data:/var/lib/mysql
  healthcheck:
    test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
    interval: 5s
    timeout: 3s
    retries: 10

my-app:
  image: my-app:1.0
  ports:
    - "8080:8080"
  environment:
    SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/myapp
    SPRING_DATASOURCE_USERNAME: root
    SPRING_DATASOURCE_PASSWORD: root123
    SPRING_REDIS_HOST: redis
    SPRING_REDIS_PASSWORD: redis123
  depends_on:
    mysql:
      condition: service_healthy
    redis:
      condition: service_started

healthcheck 通过 mysqladmin ping 检测 MySQL 是否就绪。depends_on 配合 condition: service_healthy,Compose 会根据健康检查状态决定是否启动依赖服务后的容器。

Redis 启动很快,用 service_started 就够了。MySQL 和 PostgreSQL 这类需要初始化的服务,建议都加健康检查。

开发环境中的 Compose 常用操作

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

# 查看运行状态
docker compose ps

# 查看某个服务的日志
docker compose logs my-app

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

# 停止所有服务
docker compose down

# 停止并删除数据卷(慎用,会清掉数据库数据)
docker compose down -v

# 重新构建某个服务的镜像
docker compose build my-app

# 重启某个服务
docker compose restart my-app

开发中最常用的组合是 docker compose up -d 启动,docker compose logs -f my-app 跟踪应用日志,docker compose down 停止。

如果修改了 docker-compose.yml,需要重新执行 docker compose up -d,Compose 会自动检测变化并重建受影响的容器。

总结

Docker Compose 把多个服务的启动、配置和网络连接集中到一个 YAML 文件中。docker compose up -d 一条命令启动整套环境,docker compose down 一条命令全部停止。

Compose 自动创建网络,容器间用服务名互相访问,不需要手动配置 IP。depends_on 配合 healthcheck 可以控制服务启动顺序,确保依赖服务就绪后再启动应用。

更多推荐