docker-compose编排多容器怎么写

多个容器要一起跑时一个个启动太累。compose 用一个 YAML 文件把所有服务的镜像、端口、环境变量、数据卷、网络、依赖全描述清楚,一条命令全拉起。同一网络下服务名能直接当主机名互连。depends_on 只保证启动顺序,依赖数据库要配 healthcheck 才稳。

用 Docker 跑单个容器挺爽,一行 docker run 搞定。

但真实项目哪有单打独斗的?一个后端要配 MySQL、要配 Redis,可能还有 Nginx。三四个容器,每个都 docker run 带一长串参数,敲到手软,还得记住谁先启谁后启、谁连谁。

这时候 docker-compose 就该登场了。它让你用一个 YAML 文件把这一摊服务描述清楚,一条命令全拉起来。这篇把 compose 文件怎么写、怎么生效、效果如何,讲明白。

compose 解决什么

先说清它的定位:docker-compose 是用来编排多个容器的工具。

单容器用 docker run,多容器之间有依赖、要互联、要统一管理,就用 compose。

它的核心是一个 docker-compose.yml 文件。你把每个服务(容器)长什么样、用什么镜像、开什么端口、挂什么目录、依赖谁,全写进去。

然后 docker compose up 一条命令,它照着文件把所有容器按顺序创建、连好网络、跑起来。

一句话:把一堆 docker run 参数,变成一个可读、可版本管理的配置文件。

一个完整例子

直接上实例最直观。假设要跑"一个 Go 后端 + MySQL + Redis"的组合,docker-compose.yml 这么写:

# compose 文件版本,3.8 兼容性较好
version: "3.8"

# services 下面每一项就是一个容器
services:
  # 服务名叫 app,就是我们的后端
  app:
    # 用当前目录的 Dockerfile 构建镜像
    build: .
    container_name: mebugs-app
    # 端口映射:宿主机 8080 -> 容器 8080
    ports:
      - "8080:8080"
    # 环境变量,传给程序读
    environment:
      - DB_HOST=mysql
      - REDIS_HOST=redis
    # 依赖关系:等 mysql、redis 起来再启动 app
    depends_on:
      - mysql
      - redis
    # 加入同一个网络,服务间才能用服务名互相访问
    networks:
      - mebugs-net

  mysql:
    image: mysql:8.0
    container_name: mebugs-mysql
    environment:
      - MYSQL_ROOT_PASSWORD=mebugs123
      - MYSQL_DATABASE=site_ol
    ports:
      - "3306:3306"
    # 数据卷:把数据存到宿主机,容器删了数据还在
    volumes:
      - mysql-data:/var/lib/mysql
    networks:
      - mebugs-net

  redis:
    image: redis:7
    container_name: mebugs-redis
    ports:
      - "6379:6379"
    networks:
      - mebugs-net

# 声明命名数据卷
volumes:
  mysql-data:

# 声明自定义网络
networks:
  mebugs-net:
​

一个文件,三个服务,依赖、端口、数据卷、网络全交代清楚了。

几个关键配置解释

上面的例子里有几个常用项,单独说说它们干嘛的。

  • image / build:image 直接用现成镜像(如 mysql:8.0);build 是用 Dockerfile 现场构建。二选一。
  • ports:端口映射,格式 宿主机端口:容器端口。不写就外部访问不到。
  • environment:往容器里塞环境变量,程序和镜像都靠它读配置(比如数据库密码)。
  • depends_on:控制启动顺序。注意它只保证"容器启动顺序",不保证"服务真的 ready"——MySQL 容器起来了不代表数据库能连了,这点坑后面说。
  • volumes:数据持久化。容器是临时的,删了数据就没,挂 volume 把数据存宿主机上才安全。
  • networks:自定义网络。同一网络下的服务,可以直接用服务名当主机名互相访问。

最后这条特别关键。看例子里 app 的环境变量 DB_HOST=mysql——这个 mysql 就是服务名。compose 自动做了内部 DNS,app 容器里连 mysql:3306 就能连到 MySQL 容器,不用管它的 IP。这是 compose 最省心的地方之一。

怎么让它生效

文件写好,常用命令就这几个。

启动全部服务:

# -d 表示后台运行,不加会把日志糊你一脸
docker compose up -d
​

第一次跑会拉镜像、建网络、建卷、按 depends_on 顺序启动容器。之后再 up 只会启没在跑的。

看状态和日志:

docker compose ps          # 看哪些服务在跑
docker compose logs -f app # 跟踪 app 服务的实时日志
​

停止和清理:

docker compose stop        # 停止但保留容器
docker compose down        # 停止并删除容器、网络(卷默认保留)
docker compose down -v     # 连数据卷一起删,谨慎用,数据会没
​

改了 docker-compose.yml 之后,重新 up -d,compose 会对比差异,只重建变化的服务,没变的不动。

补充:老版本是 docker-compose(带横杠,独立命令),新版 Docker 把它集成成了 docker compose(空格,子命令)。两者用法基本一致,新环境优先用后者。

depends_on 的坑

前面埋了个伏笔,这里展开。

depends_on 只管"容器谁先启动谁后启动",不管"服务是否真的可用"。

MySQL 容器"启动"很快,但容器起来到数据库真正能接受连接,中间还要初始化几秒。这期间 app 要是急吼吼去连,照样连不上报错。

想真正等到服务 ready,得加健康检查 healthcheck:

  mysql:
    image: mysql:8.0
    # ... 省略其他
    healthcheck:
      # 用 mysqladmin ping 探测数据库是否真能响应
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 5s
      timeout: 3s
      retries: 5

  app:
    # 等 mysql 健康检查通过再启动,而不只是容器起来
    depends_on:
      mysql:
        condition: service_healthy
​

加上 condition: service_healthy,app 才会真正等到 MySQL 能连了再启动。这个细节不处理,本地偶尔好偶尔坏,排查半天才发现是启动竞态。

小结

docker-compose 是多容器编排工具,用一个 docker-compose.yml 把一堆服务的镜像、端口、环境变量、数据卷、网络、依赖关系全描述清楚,docker compose up -d 一键拉起。

记住几个关键点:同一自定义网络下服务名可直接当主机名互连;数据要持久化就挂 volume;端口要外部访问就配 ports。

最容易栽的是 depends_on 只保证启动顺序不保证服务就绪,依赖数据库这类场景要配 healthcheck + service_healthy 才稳。

把常用的那几个命令和配置项记熟,日常本地起一套开发环境,比敲一长串 docker run 舒服太多了。

更多推荐

章节目录