06入门:Compose起全栈

1353 字
7 分钟
06入门:Compose起全栈

第 6 关 Compose 起全栈#

本关的产出:一份 compose 文件,把第 5 关那三个各带参数的容器收进去,一条命令整体拉起、整体拆掉。

6.1 命令写不下的那天#

第 5 关收尾时,三个容器终于能互相叫到名字。代价是命令行里多出一串东西:一条 docker network create、每个容器各一次 --network、外加「记住谁叫什么」。往后想改个端口、加个变量、换台机器重跑,都得把这串命令原样再敲一遍。

第 2 关说过:容器可以随手删掉重造,值钱的是那条命令本身。这句话现在要再进一步——那条值钱的命令,也该从命令行里搬进一个文件:能复制、能备份、能改版、能交给别人。Docker Compose 干的就是这件事。

命令是 docker compose,随 Docker Desktop 一起装好。网上老教程里带连字符的 docker-compose 是旧的独立脚本,已经停止维护,别照着敲。

6.2 把三条命令翻译成一份文件#

起点是第 5 关 5.6 那段命令:

docker network create app-net
docker run -d --name web --network app-net -p 8080:80 nginx
docker run -d --name db --network app-net \
-e MYSQL_ROOT_PASSWORD=hecloud \
-v ~/docker/mysql-data:/var/lib/mysql \
mysql
docker run -d --name cache --network app-net \
-v ~/docker/redis-data:/data \
redis

建一个空目录(比如叫 demo),在里面放一个文件,叫 compose.yaml:

services:
web:
image: nginx
ports:
- "8080:80"
db:
image: mysql
environment:
- MYSQL_ROOT_PASSWORD=hecloud
volumes:
- mysql-data:/var/lib/mysql
cache:
image: redis
volumes:
- redis-data:/data
volumes:
mysql-data:
redis-data:

和上面那段命令逐段对照:

  1. services: 下面是三个服务,键名 web、db、cache 就是第 5 关那三个容器名。
  2. image: 是镜像名,对应 docker run 结尾那个参数。
  3. ports: 对应 -p,"8080:80" 还是「宿主在前,容器在后」。
  4. environment: 对应 -e。
  5. 服务里的 volumes: 对应 -v。挂的东西从第 5 关的宿主机目录 ~/docker/mysql-data 换成了命名卷 mysql-data——第 4 关说过,要交付、换台机器还能跑出一样结果,用卷。compose 是「交出去」的东西,所以写卷。
  6. 文件末尾那个孤立的 volumes: 是顶层声明:两个卷要在这儿登记一次,服务里才能用名字指它们。

要观察:文件里没有 docker network create、也没有 --network。网络整块不见了——6.4 说为什么。

6.3 一条命令顶掉那一整串#

在 compose.yaml 所在目录里:

docker compose up -d

-d 是后台跑,跟 docker run -d 一个意思。第一次会先拉缺失的镜像,然后三个容器一起起来。看一眼:

docker compose ps

三行,NAME 那列是 demo-web-1、demo-db-1、demo-cache-1(demo 就是目录名,6.4 细说)。

要观察:docker ps 也看得到它们,但 docker compose ps 只看这个项目。往后凡是「对整体」的操作都走 docker compose 开头——logs、exec、stop、down,作用域是整个项目,不用逐个写容器名。

6.4 网络和名字:第 5 关的活它替你干了#

第 5 关为了让容器互相叫到名字,手动做了两件事:建一张网、把每个容器接上去。Compose 一上来就把这两件都办了:

docker network ls

多出一张 demo_default。compose 给每个项目自动建一张 bridge 网络,所有服务自动接上去——这就是 6.2 里为什么没有 --network。

更关键的是名字:

docker compose exec web getent hosts db

打出来是 db 的 172.x 地址。服务名直接就是容器之间互相叫的名字——在 web 里写 db、cache 就能访问它们,这正是第 5 关那张自定义网络 + 内建 DNS 的效果,只是不用手敲了。

第 5 关 5.6 结尾那句「下一关的 Compose 会自己建网、也会把每个服务名注册成互相叫得到的名字」,到这里兑现。

易踩的坑:

  • 缩进。YAML 靠缩进分层,同一层要对齐,而且只能用空格、不能混用 Tab;冒号后要留一个空格。错一格,docker compose up 报语法错,报错的行号常常指不准——先盯缩进,拿不准就先 docker compose config 校验一遍。
  • 端口映射要加引号:写 "8080:80"。不加引号,中间那个冒号会让 YAML 把它当成一个数(六十进制),不再是字符串。
  • 项目名 = 目录名。换目录、给目录改名,项目就成了另一个,网络名、容器名前缀全跟着变。down 的时候要认准自己在哪个目录。

6.5 整体拆掉#

第 2 关那句「容器是消耗品」,到 compose 变成「整个项目都是消耗品」:

docker compose down

一条命令:停掉三个容器、删掉它们、删掉那张自动建的网。

要观察:down 不删卷。第 4 关那句「容器是消耗品,卷不是」在这里照样成立——db 里的数据还躺在卷里,下次 up -d 原样回来。想连数据一起清掉,加 -v:

docker compose down -v

会删掉文件里声明的那两个卷,数据就真没了,用之前想清楚。

这一关的产出:一份 compose 文件、一条拉起和拆掉整个项目的命令,以及「网络和名字都不用自己管了」的确定性。但名字叫得到,不等于服务已经就绪——mysql 第一次启动要先初始化十几秒(第 2 关那个坑),这期间 db 是通的、3306 上却还没人应答。怎么表达「等它真的就绪」,是第 7 关的事。

本关命令速查#

Compose 的命令与用法,见 命令速查表 · Compose。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
06入门:Compose起全栈
https://hecloud.top/posts/tech/docker/06compose起全栈/
作者
贺小云
发布于
2026-09-22
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
贺小云
热爱技术与折腾的博客 Dancing with uncertainty. 与不确定性共舞
公告
出于成本考虑,贺云已全面转型Serverless架构
分类
标签
碎碎念
站点统计
文章
91
分类
5
标签
44
总字数
103,192
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Cloudflare Pages
博客版本
Firefly v6.16.5
文章许可
CC BY-NC-SA 4.0