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:和上面那段命令逐段对照:
services:下面是三个服务,键名web、db、cache就是第 5 关那三个容器名。image:是镜像名,对应docker run结尾那个参数。ports:对应-p,"8080:80"还是「宿主在前,容器在后」。environment:对应-e。- 服务里的
volumes:对应-v。挂的东西从第 5 关的宿主机目录~/docker/mysql-data换成了命名卷mysql-data——第 4 关说过,要交付、换台机器还能跑出一样结果,用卷。compose 是「交出去」的东西,所以写卷。 - 文件末尾那个孤立的
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。
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!





