Java 项目部署之 Docker工具快速入门: Docker Compose 初识与安装

概述

服务拆成微服务之后,部署的痛点从"怎么写"变成了"怎么管":一个能跑起来的应用往往要同时拉起 MySQL、Nacos、网关和好几个业务服务,靠手敲 docker run 一条条起,参数容易漏、启动顺序难保证、环境重建一次就要重来一遍。Docker Compose 就是解决这一层问题的工具------用一份 yml 描述整个应用,一条命令拉起全部容器。

纲要

  • Compose 解决什么问题 :单条 docker run 只能管一个容器,集群部署需要"声明式"描述
  • Compose 文件长什么样 :version / services 两层结构,字段与 docker run 参数的映射关系
  • 字段速查 :image build ports environment volumes depends_on networks
  • 两种安装方式 :二进制文件放到 /usr/local/bin 与 Docker 插件(docker compose 空格版)
  • 版本差异 :v1 横线版 docker-compose 与 v2 空格版 docker compose 的区别与共存
  • 安装验证 :docker-compose --version / docker compose version 的预期输出
  • 常用子命令速查 :up -d ps logs -f restart stop down down -v build config
  • 实战坑 :yml 缩进、version 字段告警、执行目录与 -f、service 名与容器名不是一回事

Compose 解决什么问题

前面几节我们已经能用 Docker 部署中间件(Redis、MySQL),也能用 Dockerfile 把自研的微服务打成镜像再跑起来。但这些动作都有一个共同点:手动,一个个来。

bash 复制代码
docker run -d --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123 -v /tmp/mysql/data:/var/lib/mysql mysql:5.7.25
docker run -d --name nacos -p 8848:8848 -e MODE=standalone nacos/nacos-server
docker run -d --name userservice -p 8081:8081 userservice:1.0
docker run -d --name orderservice -p 8082:8082 orderservice:1.0
docker run -d --name gateway -p 10010:10010 gateway:1.0

小公司几十个服务,大公司几百上千个。逐个敲命令的问题不在于累,而在于不可复现:

  • 某次漏了一个 -e,容器起来了但连不上数据库,排查半天
  • Nacos 还没就绪,业务服务已经注册失败退出
  • 换台机器重建环境,得把上面这串命令从头再抄一遍,抄错一个端口就出问题
  • 想销毁重来,得记住每个容器的名字挨个 docker rm

Compose 的思路是把"怎么起"从命令式改写成声明式:用一份文本文件描述"集群里有哪些容器、各自怎么运行",然后交给 Compose 一次性翻译成对应的容器操作。
#mermaid-svg-wHFk1m2GbHxNBfKv{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wHFk1m2GbHxNBfKv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wHFk1m2GbHxNBfKv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wHFk1m2GbHxNBfKv .error-icon{fill:#552222;}#mermaid-svg-wHFk1m2GbHxNBfKv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wHFk1m2GbHxNBfKv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wHFk1m2GbHxNBfKv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wHFk1m2GbHxNBfKv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wHFk1m2GbHxNBfKv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wHFk1m2GbHxNBfKv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wHFk1m2GbHxNBfKv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wHFk1m2GbHxNBfKv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wHFk1m2GbHxNBfKv .marker.cross{stroke:#333333;}#mermaid-svg-wHFk1m2GbHxNBfKv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wHFk1m2GbHxNBfKv p{margin:0;}#mermaid-svg-wHFk1m2GbHxNBfKv .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-wHFk1m2GbHxNBfKv .cluster-label text{fill:#333;}#mermaid-svg-wHFk1m2GbHxNBfKv .cluster-label span{color:#333;}#mermaid-svg-wHFk1m2GbHxNBfKv .cluster-label span p{background-color:transparent;}#mermaid-svg-wHFk1m2GbHxNBfKv .label text,#mermaid-svg-wHFk1m2GbHxNBfKv span{fill:#333;color:#333;}#mermaid-svg-wHFk1m2GbHxNBfKv .node rect,#mermaid-svg-wHFk1m2GbHxNBfKv .node circle,#mermaid-svg-wHFk1m2GbHxNBfKv .node ellipse,#mermaid-svg-wHFk1m2GbHxNBfKv .node polygon,#mermaid-svg-wHFk1m2GbHxNBfKv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wHFk1m2GbHxNBfKv .rough-node .label text,#mermaid-svg-wHFk1m2GbHxNBfKv .node .label text,#mermaid-svg-wHFk1m2GbHxNBfKv .image-shape .label,#mermaid-svg-wHFk1m2GbHxNBfKv .icon-shape .label{text-anchor:middle;}#mermaid-svg-wHFk1m2GbHxNBfKv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wHFk1m2GbHxNBfKv .rough-node .label,#mermaid-svg-wHFk1m2GbHxNBfKv .node .label,#mermaid-svg-wHFk1m2GbHxNBfKv .image-shape .label,#mermaid-svg-wHFk1m2GbHxNBfKv .icon-shape .label{text-align:center;}#mermaid-svg-wHFk1m2GbHxNBfKv .node.clickable{cursor:pointer;}#mermaid-svg-wHFk1m2GbHxNBfKv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-wHFk1m2GbHxNBfKv .arrowheadPath{fill:#333333;}#mermaid-svg-wHFk1m2GbHxNBfKv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wHFk1m2GbHxNBfKv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wHFk1m2GbHxNBfKv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wHFk1m2GbHxNBfKv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wHFk1m2GbHxNBfKv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wHFk1m2GbHxNBfKv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-wHFk1m2GbHxNBfKv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wHFk1m2GbHxNBfKv .cluster text{fill:#333;}#mermaid-svg-wHFk1m2GbHxNBfKv .cluster span{color:#333;}#mermaid-svg-wHFk1m2GbHxNBfKv div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-wHFk1m2GbHxNBfKv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wHFk1m2GbHxNBfKv rect.text{fill:none;stroke-width:0;}#mermaid-svg-wHFk1m2GbHxNBfKv .icon-shape,#mermaid-svg-wHFk1m2GbHxNBfKv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wHFk1m2GbHxNBfKv .icon-shape p,#mermaid-svg-wHFk1m2GbHxNBfKv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-wHFk1m2GbHxNBfKv .icon-shape .label rect,#mermaid-svg-wHFk1m2GbHxNBfKv .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wHFk1m2GbHxNBfKv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wHFk1m2GbHxNBfKv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wHFk1m2GbHxNBfKv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} docker-compose.yml

声明式描述
Docker Compose
容器: mysql
容器: nacos
容器: userservice
容器: orderservice
容器: gateway
创建专用网络

服务间用服务名互访

一句话概括:Compose 文件 ≈ 一组 docker run 命令的集合,只是把命令行参数换成了 yml 里的指令,并且额外接管了网络创建和启动顺序。

与 docker run 的能力对照

理解 Compose 最快的方式是拿一条现成的 docker run 和 yml 里的对应写法做对比。左边是命令行,右边是 Compose 字段:

能力 docker run 参数 Compose 字段 说明
容器标识 --name mysql service 名(mysql:) Compose 里用服务名代替容器名
镜像 命令末尾 mysql:5.7.25 image: mysql:5.7.25 直接拉取现成镜像
临时构建 docker build . + docker run build: ./user-service 两件事合并成一条字段
端口映射 -p 8848:8848 ports: - "8848:8848" 内部服务可不暴露端口
环境变量 -e MYSQL_ROOT_PASSWORD=123 environment: 下键值对 语法不同,语义相同
数据卷挂载 -v /tmp/mysql/data:/var/lib/mysql volumes: 列表 支持 $PWD 相对路径
后台运行 -d 默认就是后台 Compose 无 -d 字段
网络 --network xxx networks: 不写则自动建默认网络
依赖顺序 无(靠人工等) depends_on: 控制启动先后
一键重建 无 up -d / down 整组容器一起生命周期管理

注意表的最后三行------这是 Compose 相比"把命令抄进脚本"真正多出来的东西。docker run 对"服务之间的依赖关系"和"整组容器的生命周期"完全无感,而这些恰恰是集群部署最容易翻车的地方。

Compose 文件结构

顶层只有两大部分:version 声明语法版本,services 下面每个子节点代表一个容器。

version

历史上 Compose 文件格式分 1.x、2.x、3.x 三大版本,不同版本支持的字段有差异。教学里常见的 version: "3.8" 就是 3.x 的最新小版本。

yaml 复制代码
version: "3.8"

services:
  mysql:
    image: mysql:5.7.25

这里有个坑要提前说清:Compose V2 已经废弃 version 字段 。用新版 docker compose 执行带 version 的文件时,会打印一行告警:

text 复制代码
WARN[0000] /path/docker-compose.yml: the attribute `version` is obsolete,
it will be ignored, please remove it to avoid potential confusion

这行只是提醒,不影响执行,现代写法是把 version 整行删掉。反过来,如果你的目标是兼容旧的 docker-compose v1,那 version 最好保留,而且服务名、字段名大小写都要按老版本规则来。

services

services 下的每个键是一个服务,对应一个容器。拿课程资料里的真实文件举例:

yaml 复制代码
version: "3.2"

services:
  nacos:
    image: nacos/nacos-server
    environment:
      MODE: standalone
    ports:
      - "8848:8848"
  mysql:
    image: mysql:5.7.25
    environment:
      MYSQL_ROOT_PASSWORD: 123
    volumes:
      - "$PWD/mysql/data:/var/lib/mysql"
      - "$PWD/mysql/conf:/etc/mysql/conf.d/"
  userservice:
    build: ./user-service
  orderservice:
    build: ./order-service
  gateway:
    build: ./gateway
    ports:
      - "10010:10010"

这份文件里有两种典型写法,值得分开看:

  • 基于现成镜像 :nacos 和 mysql 都给了 image,Compose 直接拉镜像起容器。mysql 挂了两个目录出来------数据目录和配置目录,容器删了数据还在。
  • 临时构建再运行 :userservice、orderservice、gateway 只给了 build,没有 image。意思是先到指定目录下执行 docker build 生成镜像,再拿这个镜像创建容器。build 一个字段相当于原来的 docker build . && docker run ... 两步。

还有一点容易疑惑:mysql 服务没有配 ports。这是有意的------微服务集群里 MySQL 只给集群内部的服务用,属于内网访问,没必要把 3306 暴露到宿主机上,少暴露一个端口就少一份风险。

字段速查

字段 作用 等价命令 注意
image 指定镜像 命令末尾的镜像名 建议带 tag,如 mysql:5.7.25
build 从目录构建镜像 docker build 值是目录路径,./ 代表当前目录
ports 端口映射,宿主机:容器 -p 字符串形式,必须加引号
environment 注入环境变量 -e 键值对;值含特殊字符要引号
volumes 数据卷 / 目录挂载 -v 支持 $PWD 变量
depends_on 声明启动依赖 无 只保证启动顺序,不保证服务就绪
networks 指定网络 --network 同名网络内的服务可用服务名互访
container_name 强制指定容器名 --name 不写则自动生成,且不允许扩容到多副本
command 覆盖容器启动命令 命令末尾的 CMD 用于临时改启动参数
restart 重启策略 --restart 常见值 always、on-failure

depends_on 这一行的注意点值得单独强调:它只控制容器创建和启动的先后顺序 ,不会等 MySQL 真正 ready。MySQL 进程起来了但还没初始化完接收连接,业务服务照样会连不上退出。真要等就绪,得配合健康检查 healthcheck 加 condition: service_healthy,或者干脆在应用层加重试。

安装 Docker Compose

Compose 是一个独立于 Docker 引擎的可执行工具,有两种获取方式。

方式一:二进制文件(v1 横线版)

从 GitHub Release 拉对应架构的二进制,放到 /usr/local/bin:

bash 复制代码
# 下载并直接写入 /usr/local/bin/docker-compose
curl -L https://github.com/docker/compose/releases/download/1.23.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose

uname -s 和 uname -m 会分别替换成系统名(如 Linux)和架构(如 x86_64),所以下载到的文件名形如 docker-compose-Linux-x86_64,落地后重命名为 docker-compose。

GitHub 直连速度通常很慢甚至超时,此时可以直接用课前资料里提供的 docker-compose 二进制文件,手动上传到 /usr/local/bin/ 目录,效果一样。

无论用哪种,都要补上执行权限:

bash 复制代码
# 赋予可执行权限
chmod +x /usr/local/bin/docker-compose

chmod +x 就是给文件加执行位,加完之后 ls -l 能看到文件名变色,表示可以直接执行。

方式二:Docker 插件(v2 空格版)

Docker 官方后来把 Compose 收编为 CLI 插件,随 Docker 一起安装或通过包管理器装:

bash 复制代码
# Debian / Ubuntu 系
apt-get update && apt-get install -y docker-compose-plugin

# RHEL / CentOS 系
yum install -y docker-compose-plugin

装完之后命令形式变了:不是 docker-compose(横线),而是 docker compose(空格,作为 docker 的子命令)。

v1 与 v2 的区别与共存

这是最容易踩的坑,两者的调用方式、可执行文件位置、行为都不完全一样。

维度 Compose v1(横线版) Compose v2(空格版)
命令形式 docker-compose up -d docker compose up -d
可执行文件 独立二进制 docker-compose Docker CLI 插件,通常在 /usr/libexec/docker/cli-plugins/
安装方式 手动下载二进制 随 Docker / 包管理器安装
version 字段 必需 已废弃,写了会告警
容器命名 项目名_服务名_序号 基本一致,部分默认行为调整
现状 1.x 已停止维护 官方推荐

两个版本可以共存 ,二进制路径不冲突,docker-compose 和 docker compose 能同时执行。风险在于"你以为在跑 v2,实际敲的是 v1"------比如脚本里写了 docker-compose,但宿主机只装了插件,就会直接报 command not found。排查手段是先确认两个命令各自的版本和路径:

bash 复制代码
which docker-compose
which docker
docker compose version

命令自动补全(可选项)

给 bash 加上 Compose 的补全脚本,敲 docker-compose 后按 Tab 能提示子命令和参数:

bash 复制代码
curl -L https://raw.githubusercontent.com/docker/compose/1.29.1/contrib/completion/bash/docker-compose > /etc/bash_completion.d/docker-compose

国内网络访问 raw.githubusercontent.com 常解析失败,表现为命令卡住或报"无法解析主机"。临时方案是往 hosts 里补一条记录:

bash 复制代码
echo "199.232.68.133 raw.githubusercontent.com" >> /etc/hosts

补全只是锦上添花,装不上不影响 Compose 本身使用。

安装验证

装完必须先验证,别等到部署时才发现在用哪个版本。

bash 复制代码
# v1 横线版
docker-compose --version

预期输出:

text 复制代码
docker-compose version 1.23.1, build b02f1306
bash 复制代码
# v2 空格版
docker compose version

预期输出:

text 复制代码
Docker Compose version v2.24.5

两条命令都报 command not found,说明没装成功或者路径不对,先 ls -l /usr/local/bin/docker-compose 确认文件在不在、有没有 x 权限。

常用子命令速查

up 和 down 是核心,其余都是围绕运行状态的观察和微调。

命令 作用 说明
docker compose up -d 创建并启动全部服务 -d 后台运行;首次会自动 build/拉镜像
docker compose up -d --build 强制重新构建镜像再启动 改了 Dockerfile 或代码后必用
docker compose ps 查看本组容器状态 只看当前项目,比 docker ps 清爽
docker compose logs -f 服务名 跟踪日志 不加服务名则输出全部
docker compose restart 服务名 重启指定服务 只重启,不重建容器
docker compose stop 停止容器,保留容器和数据卷 可以再 start 回来
docker compose down 停止并删除容器、网络 数据卷默认保留
docker compose down -v 连同数据卷一起删除 危险,数据会丢
docker compose build 只构建镜像不起容器 用于调试构建过程
docker compose config 校验并打印合并后的配置 排查 yml 语法和变量替换

down -v 单独拎出来说:down 只删容器和网络,挂载出来的数据卷(比如 MySQL 的 data 目录)还在,下次 up 数据能恢复;down -v 会把匿名卷和命名卷一起删掉。-v 在某些版本里还会顺手带走 volumes 里 $PWD 映射的目录内容,生产环境敲之前一定确认是不是真的要清库。

实战坑

缩进必须是空格,不能是 Tab。 YAML 的层级完全靠缩进表达,混入 Tab 会直接报 found character '\t' that cannot start any token。IDE 里先把"Tab 转空格"打开,统一两格或四格都行,关键是整个文件一致。

version 写错会产生告警但不报错。 比如写 version: "3.x" 或者漏了引号被解析成数字,V2 会忽略它继续执行,V1 则可能直接报 Unsupported config option。用 V2 干脆删掉这一行最省心。

必须在 Compose 文件所在目录执行,或用 -f 指定。 docker compose up 默认读当前目录的 docker-compose.yml(也兼容 compose.yaml)。在别处执行会报找不到文件,正确姿势:

bash 复制代码
# 显式指定文件路径
docker compose -f /opt/cloud-demo/docker-compose.yml up -d

service 名和容器名不是一回事。 service 名是 yml 里的键,比如 mysql;Compose 实际创建的容器名默认是 项目名_服务名_序号,形如 cloud-demo_mysql_1。好处是同名网络内其他服务可以直接用 mysql 这个服务名做主机名访问,不用关心真实容器名;代价是如果你手动写了 container_name: mysql,就锁死了单副本,没法 --scale mysql=2 扩容。

$PWD 是执行时的当前目录。 volumes 里写 $PWD/mysql/data 时,挂载的是你执行 docker compose 那一刻所在目录下的路径。用 -f 在别处执行时,$PWD 不会自动跟着变成文件所在目录,容易挂载到空目录上去。稳妥写法是用绝对路径,或者借助 Compose 提供的变量插值。
Docker 引擎 docker-compose.yml docker compose 开发者 Docker 引擎 docker-compose.yml docker compose 开发者 #mermaid-svg-COnImCKQzSneZ33p{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-COnImCKQzSneZ33p .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-COnImCKQzSneZ33p .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-COnImCKQzSneZ33p .error-icon{fill:#552222;}#mermaid-svg-COnImCKQzSneZ33p .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-COnImCKQzSneZ33p .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-COnImCKQzSneZ33p .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-COnImCKQzSneZ33p .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-COnImCKQzSneZ33p .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-COnImCKQzSneZ33p .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-COnImCKQzSneZ33p .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-COnImCKQzSneZ33p .marker{fill:#333333;stroke:#333333;}#mermaid-svg-COnImCKQzSneZ33p .marker.cross{stroke:#333333;}#mermaid-svg-COnImCKQzSneZ33p svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-COnImCKQzSneZ33p p{margin:0;}#mermaid-svg-COnImCKQzSneZ33p .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-COnImCKQzSneZ33p text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-COnImCKQzSneZ33p .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-COnImCKQzSneZ33p .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-COnImCKQzSneZ33p .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-COnImCKQzSneZ33p .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-COnImCKQzSneZ33p #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-COnImCKQzSneZ33p .sequenceNumber{fill:white;}#mermaid-svg-COnImCKQzSneZ33p #sequencenumber{fill:#333;}#mermaid-svg-COnImCKQzSneZ33p #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-COnImCKQzSneZ33p .messageText{fill:#333;stroke:none;}#mermaid-svg-COnImCKQzSneZ33p .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-COnImCKQzSneZ33p .labelText,#mermaid-svg-COnImCKQzSneZ33p .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-COnImCKQzSneZ33p .loopText,#mermaid-svg-COnImCKQzSneZ33p .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-COnImCKQzSneZ33p .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-COnImCKQzSneZ33p .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-COnImCKQzSneZ33p .noteText,#mermaid-svg-COnImCKQzSneZ33p .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-COnImCKQzSneZ33p .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-COnImCKQzSneZ33p .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-COnImCKQzSneZ33p .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-COnImCKQzSneZ33p .actorPopupMenu{position:absolute;}#mermaid-svg-COnImCKQzSneZ33p .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-COnImCKQzSneZ33p .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-COnImCKQzSneZ33p .actor-man circle,#mermaid-svg-COnImCKQzSneZ33p line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-COnImCKQzSneZ33p :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} loop 每个 service docker compose up -d 解析 yml(校验缩进/字段) 服务定义列表 创建/复用项目网络 build 或 pull 镜像 创建并启动容器 输出容器状态与名称 docker compose ps 列出本组容器

项目结构

Compose 项目通常和要被部署的应用放在一起,build 字段里的相对路径就从这里开始算:

tree 复制代码
cloud-demo/
├── docker-compose.yml          # 编排入口,一条命令拉起全部服务
├── mysql/
│   ├── data/                   # 挂载到容器的 /var/lib/mysql
│   └── conf/                   # 挂载到容器的 /etc/mysql/conf.d/
├── user-service/
│   ├── Dockerfile              # build: ./user-service 据此构建
│   └── app.jar
├── order-service/
│   ├── Dockerfile
│   └── app.jar
└── gateway/
    ├── Dockerfile
    └── app.jar

这个目录结构本身说明了一件事:Compose 把"每个服务的构建物料 + 整个集群的编排描述"收在一个目录下,搬机器时整目录拷走,up -d 就能还原环境,不再依赖散落在各处的命令历史。

官方文档

总结

  • Compose 的本质是声明式替代命令式 :一份 yml 描述整个应用,docker run 的每个参数都能在文件里找到对应字段。
  • 它比"把命令写进 shell 脚本"多出来的价值,在于整组容器的生命周期管理 和服务间网络与依赖的声明 ,这两点单条 run 天生做不到。
  • 文件结构只有 version + services 两层,version 在 V2 已废弃;每个 service 要么给 image 直接拉,要么给 build 现构建。
  • 安装记住两条路:二进制放 /usr/local/bin 加 chmod +x 是 v1 横线版,包管理器装的插件是 v2 空格版,两者能共存但要清楚自己敲的是哪个。
  • 最容易出事的三个地方:yml 用 Tab 缩进、在错误目录执行导致找不到文件、以及手滑敲了 down -v 删库。
相关推荐
Rain的Java大神之路1 小时前
被问麻了!Function Calling、MCP、Skill 到底啥关系?一张图讲透三层架构,附 Java 生产落地全流程+30个坑
java·后端·ai编程
niucloud-admin1 小时前
JAVA V6 多商户商城 开发文档——项目启动
java·开发语言·python
学逆向的1 小时前
C 控制台指令
c语言·开发语言·网络安全
沫璃染墨1 小时前
从零入门数据结构与算法(一):复杂度分析——从算法效率到时间与空间复杂度
c语言·开发语言·数据结构·笔记·算法·飞书
众链网络1 小时前
Agent 重试的工程解法:幂等、断点续跑与熔断怎么落地
java·前端·数据库·人工智能
贾宝玉的玉宝贾2 小时前
FreeSWITCH 简单图形化界面 70 - 监听/密语的lua脚本
开发语言·junit·lua·voip·freeswitch·sip·ippbx
Wang's Blog2 小时前
Java 项目部署之 Docker工具快速入门: 镜像命令从 pull 到 load 的完整实操
java·开发语言·docker
北京盛世宏博2 小时前
异地机房采集踩坑笔记:以太网温湿度记录仪跨网段通信调试实战
开发语言·笔记·单片机·php·传感器·温湿度