【Docker】docker compose
目录
一.Docker Compose
二.docker-compose.yml的参数设置
2.1.image参数
2.2.command参数
2.3.entrypoint参数
2.4.environment 参数
2.5.network参数
2.6.volumes参数
2.7.ports参数
2.8.expose参数
2.9.depends_on参数
2.10.env_file
三.docker compose命令合集
3.1.docker compose命令全局选项
3.2.docker compose up
3.3.docker compose down
3.4.docker compose run
四.综合实战
一.Docker Compose
Docker Compose 是一个由 Docker 官方提供的工具,用于定义和运行多容器的 Docker 应用程序。
你可以这样理解它的核心:
-
它是一个“编排”工具:当你的应用需要多个服务(例如:一个 Web 服务器、一个数据库、一个缓存服务)才能正常工作时,每个服务通常都运行在独立的容器中。Docker Compose 可以帮助你轻松地将这些分散的容器“组合”成一个完整的、相互协作的应用。
-
它使用一个 YAML 格式的配置文件:你不再需要手动输入一长串复杂的
docker run命令来启动每个容器。相反,你可以在一个名为docker-compose.yml的文件中,用清晰的语法描述出你的应用需要哪些容器、每个容器如何配置、容器之间如何连接等所有信息。 -
它通过一条命令管理整个应用的生命周期:使用
docker-compose up这一条命令,就可以根据你的配置文件,启动所有容器,并建立起它们之间的网络连接。同样,你也可以用docker-compose down这一条命令,停止并移除所有相关的容器。
简单总结:Docker Compose 让你用一个文件、一条命令,就能管理一组相互关联的 Docker 容器。
Docker Compose 有什么用?(解决了什么问题)
在没有 Docker Compose 之前,如果你要运行一个由三个容器(比如 Nginx, Python Web App, MySQL)组成的应用,你可能需要:
-
打开一个终端,输入一长串
docker run ...命令启动 MySQL 容器,设置 root 密码、数据卷等。 -
打开另一个终端,输入另一长串
docker run ...命令启动 Python 应用容器,链接到 MySQL 容器。 -
再打开一个终端,输入又一长串
docker run ...命令启动 Nginx 容器,链接到 Python 应用容器。
这个过程非常繁琐、容易出错,并且难以记录和分享。别人要运行你的项目,你只能给他一份复杂的操作文档。
Docker Compose 解决了以下核心问题:
-
简化多容器应用的管理:将多个容器的启动、停止、连接等操作聚合为一个简单的命令。
-
实现环境配置的代码化:
docker-compose.yml文件本身就是你的应用运行环境的声明。你可以将这个文件放入版本控制系统(如 Git),这样任何拿到这个文件的人,都可以在任何装有 Docker 和 Docker Compose 的机器上,一键复现出一模一样的环境。这极大地促进了开发、测试和生产环境的一致性。 -
提高效率:省去了重复输入复杂命令的时间,也减少了因命令参数输入错误导致的调试时间。
docker compose的安装
在安装docker的时候,我们默认已经安装了docker-compose

怎么使用Docker Compose?
它的核心思想是:“声明式配置”。
意思是,您在一个配置文件中,用一种固定的格式,清清楚楚地写下您想要的所有服务(容器)是什么样的,它们之间如何连接。
然后,用一个简单的命令,Compose 就会读取这个文件,并自动帮您把所有事情都安排好。
对初学者而言,您可以将 Docker Compose 的使用流程高度概括为:
- 编写蓝图:在一个目录下,用一个 docker-compose.yml 文件,以文字和特定格式定义好所有服务及其配置。
- 一键部署:在该目录下,执行 docker compose up 这一条命令,启动整个复杂应用。
- 统一管理:使用 docker compose 前缀的一系列命令(如 ps, logs, down)来统一管理整个应用的生命周期,而不需要去分别处理每个容器。
二.docker-compose.yml的参数设置
查看帮助文档:Compose file reference | Docker Docs
2.1.image参数
image 参数用于告诉 Docker Compose:基于哪个现有的镜像来创建容器。
你可以把它理解为一份“蓝图”或“食谱”。Docker 镜像是一个打包好的、只读的文件模板,里面包含了你需要运行的程序(比如一个 Nginx 服务器、一个 MySQL 数据库、或者一个你自己编写的 Python 应用)以及它所有的运行环境依赖。而 image 参数就是指定使用哪一份“蓝图”来“建造”出可以运行的容器(即实例)。
image 参数的值是一个字符串,其完整格式如下:
镜像名[:标签]
我们把它拆解开来看:
- a. 镜像名
这是最核心、必须的部分。它指明了你想要拉取的基础镜像是什么。
示例1: nginx
这指的是官方的 Nginx 镜像。
示例2: mysql
这指的是官方的 MySQL 镜像。
示例3: my-python-app
这可以是你自己构建并命名的镜像。
- b. 标签
标签用于区分同一个镜像的不同版本。它通常是可选的,但如果需要指定特定版本,就必须写上。
格式: 在镜像名后面加上一个冒号 :,然后是标签名。
示例1: nginx:latest
latest 是一个特殊的标签,代表最新稳定版。如果你不写标签,Docker 默认就会使用 latest。
示例2: nginx:1.25
这指定了 Nginx 的 1.25 版本。
示例3: python:3.9-slim
这指定了基于 Python 3.9 的轻量级(slim)版本。
示例4: mysql:8.0
这指定了 MySQL 的 8.0 版本。
最佳实践:在正式环境中,强烈建议总是明确指定标签(如 python:3.9-slim),而不是依赖默认的 latest,因为这能保证环境的一致性。
话不多说,我们直接看例子

接下来我们编写一个
vim docker-compose.yml
services: # 顶级键,没有缩进myweb: # services 的子键,缩进 2 个空格image: nginx:1.22.0 # web 服务的子键,缩进 4 个空格
注意这个语法
-
键值对:文件的基本构成单位是“键”和“值”,用冒号加空格分隔(
key: value)。 -
缩进:这是 YAML 格式中最重要的规则。它使用缩进来表示层级关系,就像大纲的层次一样。
-
必须使用空格进行缩进,绝对不能使用 Tab 键。
-
缩进的空间数必须一致。通常约定使用 2 个空格 作为每一级的缩进。虽然理论上可以用任意数量的空格,但保持一致性是关键。
-
接下来我们就在这个目录里面执行下面这个命令
docker compose up -d

嗯?看提升信息,我们创建了一个容器,还有一个自定义网络??我什么时候指定过这个网络。
当您的 docker-compose.yml 文件只有:
yaml
services:myweb:image: nginx:1.22.0
Docker Compose 实际上会将其视为:
yaml
services:myweb:image: nginx:1.22.0networks:- default # 这个默认网络是隐式存在的networks:default: # 这个默认网络配置也是隐式存在的driver: bridge
现在就算是建立好了一个容器,我们现在去看看
root@VM-16-14-ubuntu:~/test# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
28f19827a4cd nginx:1.22.0 "/docker-entrypoint.…" 24 seconds ago Up 23 seconds 80/tcp test-myweb-1
可以看到,就创建了一个容器。
容器名称:test-myweb-1
- test:项目名称(基于目录名)
- myweb:您在 docker-compose.yml 中定义的服务名称
- 1:实例编号(如果扩展多个实例,会是 2、3 等)
我们现在再去看看网络的情况

确实是存在的
我们也是可以删除掉我们运行的这些服务
docker compose down

现在我们去看看容器和网络是不是都被删除了

很好,都被删除了。
2.2.command参数
简单来说,command 参数用于覆盖 Docker 镜像在构建时设定的默认启动命令。
-
每个 Docker 镜像都有一个默认的、在构建时通过
CMD指令定义好的命令。 -
当容器启动时,它会执行这个默认命令。
-
使用
command,你可以让容器在启动时执行一个不同的命令,或者为默认命令添加额外的参数。
在 docker-compose.yml 文件中,command 可以用两种格式来写:
a) 字符串格式 (Shell 格式)
services:my-service:image: some-imagecommand: echo "Hello, World"
这种写法类似于在 shell 中输入命令。它会被 /bin/sh -c 来执行。
b) 列表格式 (Exec 格式) - 推荐使用
services:my-service:image: some-imagecommand: ["echo", "Hello, World"]
这种写法是一个 JSON 数组,数组中的每个元素都是命令的一部分。Docker 会直接执行这个数组,而不会通过 shell 解释器。这能避免一些因为 shell 解析导致的问题,更清晰、更直接。
话不多说,我们直接看例子

接下来我们编写一个
vim docker-compose.yml
services:myweb:image: nginx:1.22.0command: ["tail","-f","/etc/hosts"]
我们这里需要了解一个新的命令
docker compose config 命令用于验证和查看解析后的docker-compose.yml文件内容。
如果文件有错误,它会明确地指出错误所在的行和原因,而不是在启动服务时失败。
docker compose config

可以看到,检查是没有问题的。也就是说我们写的docker-compose.yml是没有问题的。
现在我们运行下面这个命令
docker compose up -d

我们现在去查看一下
root@VM-16-14-ubuntu:~/test# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
be86351225f3 nginx:1.22.0 "/docker-entrypoint.…" 31 seconds ago Up 30 seconds 80/tcp test-myweb-1

可以看到确实是创建成功了
我们现在去看看容器的信息
docker inspect test-myweb-1

可以看到,启动命令已经换成了我们设置的
我们运行一下
docker exec -it test-myweb-1 curl 127.0.0.1

我们发现失败了,其实是因为我们把启动命令换掉了,所以内容的nginx服务就没有启动起来。
不信的话,我们可以去看看容器内部的日志
docker logs test-myweb-1

它内部一启动就只执行了tail -f /etc/hosts

大家看是不是和上面容器内部日志是一模一样的。
我们现在来做一下清理
docker compose down

没有问题的
2.3.entrypoint参数
entrypoint 和 command 都是用来修改容器启动时执行的命令的,但它们有不同的角色和优先级。
在 docker-compose.yml 文件中,entrypoint 可以用三种格式来写:
a) 字符串格式 (Shell 格式)
services:my-service:image: some-imageentrypoint: echo "Hello, World"
容器启动时,会执行/bin/sh -c 'echo "Hello, World"'。也就是说,会在Shell中执行这个命令。执行后输出字符串"Hello, World",然后echo命令执行完毕,进程退出,容器也会随之停止。
b) 列表格式 (Exec 格式) - 推荐使用
services:my-service:image: some-imageentrypoint: ["echo", "Hello, World"]
这种写法是一个 JSON 数组,数组中的每个元素都是命令的一部分。
容器启动时直接执行echo Hello, World,而不通过Shell。执行后输出"Hello, World",然后echo命令执行完毕,进程退出,容器停止。
c)使用数组形式 (Exec 格式)
services:my_app:image: my-imageentrypoint:- tail- "-f"- /etc/hosts
容器启动时会在内部执行命令:tail -f /etc/hosts。
- tail:是一个用于查看文件末尾内容的命令。
- -f:表示持续跟踪文件的变化(即持续输出文件新增加的内容)。
- /etc/hosts:是容器内的一个文件(主机名映射文件)。
- 这个命令会持续监控/etc/hosts文件的变化,并输出新增的内容。由于tail -f会持续运行,所以容器也会一直运行,直到你停止它。
话不多说,我们直接看例子

接下来我们编写一个
vim docker-compose.yml
services:myweb:image: nginx:1.22.0entrypoint:- tail- "-f"- /etc/os-release
检查一下

没有问题。
接下来我们就执行下面这个命令
docker compose up -d

至于检查,我就不检查了。
docker inspect test-myweb-1

docker exec -it test-myweb-1 curl 127.0.0.1

这个也在说报错啊!!
其实就是修改了启动命令,不信的话,我们就去看看容器内部的日志

我们发现它一启动就只是执行了tail -f /etc/os-release,不信的话,我们看下面

怎么样?信了吗
我们现在来做一下清理
docker compose down

没有问题的
2.4.environment 参数
environment 参数用于在容器启动时,向其内部的操作系统环境设置环境变量。
你可以把它理解为,在容器启动的那一刻,自动执行了一系列 export VARIABLE=value 的命令。容器内的应用程序可以直接读取这些环境变量,从而改变自己的行为。
在 docker-compose.yml 文件中,environment 参数可以用两种格式来书写:
方法一:数组格式(推荐,清晰直观)
services:你的服务名:image: 某镜像environment:- VARIABLE1=value1- VARIABLE2=value2- DATABASE_HOST=db
说明:
- 每个环境变量独占一行,以一个短横线 - 开头。
- 变量名和值用等号 = 连接。
- 这是最常用、可读性最好的方式。
方法二:字典格式(紧凑)
services:你的服务名:image: 某镜像environment:VARIABLE1: value1VARIABLE2: value2DATABASE_HOST: db
说明:
- 使用缩进来表示层级。
- 变量名和值用冒号 : 连接。
- 这种方式写起来更紧凑,但当变量很多时,可读性稍差。
两种方式在功能上完全等效,你可以根据喜好选择。初学者建议使用数组格式,因为它更不容易因缩进而出错。
话不多说,我们直接看例子

接下来我们编写一个
vim docker-compose.yml
services:myweb:image: nginx:1.22.0environment:- test=1
检查一下

没有任何问题
现在我们就启动
docker compose up -d

现在我们去看看容器的信息
docker inspect test-myweb-1

我们也可以进去容器内部看看

完全没有问题。
做一下清理

2.5.network参数
1. 顶级网络(Top-level networks)
顶级网络用于声明或定义本项目将使用哪些网络。您可以在docker-compose.yml文件的根级别下定义网络。这样定义的网络可以被多个服务共享。
networks:[网络名称]:配置1: 值配置2: 值……
这些配置有很多,我们也没有必要去了解那么多
我们在这里就了解一下2个配置即可
- 驱动
- 子网IP设定
我们看看
networks:网络名称:driver: 驱动类型
驱动类型就bridge,none那些啥的
我们还可以设定子网IP
networks:网络名称:driver: bridgeipam:config:- subnet: CIDR网段
我们看个综合的例子
networks:# 定义第一个网络:network_anetwork_a:# 使用桥接驱动,这是Docker最常用的网络类型# 桥接网络会在宿主机上创建一个虚拟交换机,容器连接到这个交换机driver: bridge# IP地址管理配置块ipam:# 配置列表,可以定义多个子网配置config:# 第一个子网配置(使用短横线表示列表项)- subnet: "192.168.100.0/24"# CIDR表示法:192.168.100.0/24# 网络地址:192.168.100.0# 广播地址:192.168.100.255# 可用IP范围:192.168.100.1 - 192.168.100.254# 子网掩码:255.255.255.0# 总IP数量:256个(其中254个可用)# 网关通常设为:192.168.100.1# 定义第二个网络:network_bnetwork_b:driver: bridgeipam:config:- subnet: "10.10.0.0/16"# CIDR表示法:10.10.0.0/16# 网络地址:10.10.0.0# 广播地址:10.10.255.255# 可用IP范围:10.10.0.1 - 10.10.255.254# 子网掩码:255.255.0.0# 总IP数量:65536个(其中65534个可用)# 这是一个较大的子网,适合需要大量容器的场景# 网关通常设为:10.10.0.1# 定义第三个网络:network_cnetwork_c:driver: bridgeipam:config:- subnet: "172.20.0.0/20"# CIDR表示法:172.20.0.0/20# 网络地址:172.20.0.0# 广播地址:172.20.15.255# 可用IP范围:172.20.0.1 - 172.20.15.254# 子网掩码:255.255.240.0# 二进制掩码:11111111.11111111.11110000.00000000# 总IP数量:4096个(其中4094个可用)# 网关通常设为:172.20.0.1# /20子网大小介于/24和/16之间,提供中等规模的IP地址池
好了,我们就看到这里,作为一个初学者,我们确实是没有必要去了解那么多。
2. 服务级网络(Service-level networks)
服务级网络用于指定某个服务要连接到哪些已定义的网络(这些网络必须在顶级网络中定义,或者是外部网络)。每个服务都可以连接到一个或多个网络。
语法:
在服务的定义中,使用networks键来指定该服务要连接的网络。
services:[服务名称]:# ... 其他服务配置(如image, ports等)networks:- [网络名称]- [另一个网络名称]
或者,您可以在服务级网络连接中指定更详细的配置,例如IP地址、别名等:
services:[服务名称]:networks:[网络名称]:ipv4_address: [IPv4地址]ipv6_address: [IPv6地址]aliases:- [别名1]- [别名2]
更详细的配置
services:# 定义名为 web 的服务web:# 使用 nginx 镜像image: nginx# 网络配置部分networks:# 连接到 my_bridge 网络(使用短语法,只指定网络名称)- my_bridge# 连接到 my_external_network 网络- my_external_network# 这种短语法表示服务将连接到这些网络,使用网络的默认配置# 定义名为 app 的服务app:# 使用自定义的 myapp 镜像image: myapp# 网络配置部分(使用长语法,可以为每个网络指定详细配置)networks:# 连接到 my_bridge 网络,并配置该网络上的特定设置my_bridge:# 网络别名配置:为该服务在此网络上设置额外的DNS名称aliases:- app-alias# 在此网络中,除了容器名 "app" 外,还可以通过 "app-alias" 访问该服务# 其他容器可以使用 app-alias 作为主机名来解析到这个服务的IP# 连接到 my_external_network 网络,并配置该网络上的特定设置my_external_network:# 为该服务在此网络上分配静态IPv4地址ipv4_address: 172.16.1.100# 注意:要使用静态IP,该网络必须在顶级networks中明确定义了subnet# 且 172.16.1.100 必须在网络的子网范围内# 这样可以确保服务每次启动都获得相同的IP地址# 注意:为了使上述配置正常工作,必须在文件的顶级networks部分定义这些网络
# 例如:
# networks:
# my_bridge:
# driver: bridge
# my_external_network:
# external: true
# 或者
# driver: bridge
# ipam:
# config:
# - subnet: 172.16.1.0/24
话不多说,我们直接看例子

接下来我们编写一个
vim docker-compose.yml
services:myweb:image: nginx:1.22.0networks:- mynet1- mynet2
networks:mynet1:mynet2:
我们检查一下

完全是没有问题的
现在我们直接运行起来吧
docker compose up -d

确实是创建了2个网络

现在我们去看看容器的信息


果然,这个容器是连接了我们创建的两个自定义网络。
2.6.volumes参数
大家千万不要忘了这个s啊
这个语法其实很简单
管理卷的语法:
services:你的服务名:volumes:- "数据卷名称:/容器内/路径"
绑定卷的语法:
services:你的服务名:volumes:- 宿主机绝对路径:容器内路径# 或者使用相对路径(相对于 docker-compose.yml 文件的位置)- 宿主机相对路径:容器内路径
扩展语法(长语法)
除了上面提到的短横线(-)短语法,Docker Compose 还支持一种更清晰、功能更丰富的长语法。
services:web:volumes:- type: bind # 类型为 ‘绑定’,即挂载宿主机路径source: ./src # 宿主机路径target: /app/src # 容器内路径read_only: true # 设置为只读(等同于 :ro)- type: volume # 类型为 ‘数据卷’source: mysql_data # 数据卷名称target: /var/lib/mysql# volume: 还可以有其他子选项,如 nocopy
话不多说,我们直接看例子

接下来我们编写一个
vim docker-compose.yml
services:myweb:image: nginx:1.22.0volumes:- /data/maxhou/vol1:/usr/share/nginx/html

可以看到我们宿主机这个目录是不存在的!!
现在我们执行下面这个命令
docker compose config

这个就说明是没有问题的。
接下来我们看看
docker compose up -d

现在我们回去宿主机那个目录看看

这个目录已经被创建了
我们进去看看

现在我们试试看

怎么样?是不是就是我们上面在宿主机上进行修改的
注意最后清理一下

2.7.ports参数
ports 参数的作用就是在宿主机和容器之间建立一个桥梁,将宿主机上的一个端口“映射”到容器内部的一个端口。这样,当有人访问宿主机 IP 的特定端口时,这个请求就会被自动转发到对应容器的内部端口。
ports 参数是一个数组,可以包含一个或多个映射规则。每条规则的格式如下:
[宿主机端口]:[容器内部端口]
请注意,分隔符是英文冒号 :
话不多说,我们直接看例子
短语法
假设你的容器内部运行了一个 Web 服务器,它在容器的 80 端口上监听。
1. 将宿主机端口 8080 映射到容器端口 80
services:my-web-app:image: nginxports:- "8080:80"
-
解释:所有发往 宿主机 IP 地址的 8080 端口 的流量,都会被转发到 名为
my-web-app的容器的 80 端口。 -
访问方式:在你的电脑浏览器里输入
http://localhost:8080或http://<你的宿主机IP>:8080,你就能看到容器内 Nginx 服务的页面。 -
结果:外部可以通过 8080 端口访问容器内的 80 端口服务。
2. 只指定容器端口(随机映射)
services:my-web-app:image: nginxports:- "80"
-
解释:你只告诉了 Docker 容器内部的端口是 80,但没有指定宿主机端口。Docker 会自动在宿主机上随机选择一个未被使用的高端口(通常是 32768 以上) 来映射到容器的 80 端口。
-
如何查看:运行
docker-compose ps命令,你会看到类似0.0.0.0:49153->80/tcp的输出。这意味着 Docker 为你选择了 49153 端口。 -
访问方式:
http://localhost:49153(具体端口号以docker-compose ps输出为准)。 -
适用场景:当你不在意外部通过哪个端口访问,或者需要启动多个相同服务的实例而避免端口冲突时。
3. 将宿主机特定 IP 的端口映射到容器端口
services:my-web-app:image: nginxports:- "127.0.0.1:8080:80"
-
解释:将 宿主机上只有本地回环地址(127.0.0.1)的 8080 端口 映射到容器的 80 端口。
-
访问方式:你只能在本机(宿主机)上通过
http://localhost:8080或http://127.0.0.1:8080来访问。同一局域网内的其他机器无法通过你的宿主机 IP 访问到这个服务。 -
适用场景:非常安全,通常用于只有本机需要访问的服务,或者用于开发和测试。
4. 映射多个端口
一个服务可以提供多个网络服务,比如同时提供 HTTP(80)和 HTTPS(443)。
services:my-web-app:image: nginxports:- "8080:80"- "8443:443"
长语法
ports:- target: <容器内部端口>published: <宿主机端口>protocol: <协议> # 可选,tcp 或 udp,默认为 tcpmode: <模式> # 可选,host 或 ingress,默认为 ingress
让我们逐一分解每个参数:
target (必需)
- 含义:容器内部服务实际监听的端口号。
- 等同于短语法中冒号:右边的部分。
- 示例:如果您的应用在容器内的 3000 端口运行,那么 target 就是 3000。
published (可选,但通常需要)
- 含义:在宿主机上公开的端口号。
- 等同于短语法中冒号:左边的部分。
- 特殊值:如果省略此参数,Docker 会像短语法中只指定容器端口一样,自动选择一个随机宿主机端口。
- 示例:如果您希望从宿主机的 8080 端口访问,那么 published 就是 8080。
protocol (可选)
- 含义:指定端口协议。
- 可选值:tcp 或 udp。
- 默认值:如果不指定,默认为 tcp。
- 示例:对于 DNS 服务,您可能需要 protocol: udp。
mode (可选)
- 这是长语法才有的高级功能,用于 Swarm 模式(集群部署)。
- host:在每个 Swarm 节点(宿主机)上发布一个唯一的端口。这意味着同一个服务在不同节点上可能使用不同的 published 端口。适用于需要直接访问每个实例的场景。
- ingress (默认):在所有 Swarm 节点上发布相同的 published 端口。请求会通过负载均衡器路由到健康的服务实例。这是最常见的模式,用于对外提供统一访问入口。
示例 1:将宿主机 8080 映射到容器 80 (TCP)
短语法: - "8080:80"
长语法:
services:my-web-app:image: nginxports:- target: 80published: 8080# protocol: tcp # 因为 tcp 是默认值,所以可以省略# mode: ingress # 因为 ingress 是默认值,所以可以省略
示例 2:只指定容器端口(随机映射)
短语法: - "80"
长语法:
services:my-web-app:image: nginxports:- target: 80# 省略 'published' 参数,Docker 会自动选择宿主机端口
示例 3:映射 UDP 端口
短语法: - "53:53/udp"
长语法:
services:dns-server:image: some-dnsports:- target: 53published: 53protocol: udp # 明确指定协议为 UDP
示例 4:在 Swarm 中使用 host 模式
这个例子展示了长语法独有的功能。
services:my-web-app:image: nginxports:- target: 80published: 8080mode: host # 使用 host 模式,仅在 Swarm 集群中有效
解释:在 Swarm 集群的每个节点上,服务都会在该节点的 8080 端口发布。如果服务有多个副本分布在多个节点上,那么访问 节点A-IP:8080 会连接到节点 A 上的实例,访问 节点B-IP:8080 会连接到节点 B 上的实例。
话不多说我们直接来进行实操
话不多说,我们直接看例子

接下来我们编写一个
vim docker-compose.yml
services:myweb:image: nginx:1.22.0ports:- 8080:80
我们自己检查一下

完全没有问题。
现在我们就
docker compose up -d

我们检查一下容器的信息
docker inspect test-myweb-1

完全没有问题

我们使用浏览器去访问一下这个目录

完全没有问题。
2.8.expose参数
首先要理解 Docker Compose 的一个重要特性:默认情况下,Compose 会为您的应用创建一个专用网络,在这个网络中的所有容器可以通过服务名相互发现和通信。
expose 参数就是用来控制在这个内部网络中的端口可见性。
expose 参数的作用
expose 参数用于声明容器在内部网络中开放哪些端口,但这些端口不会映射到宿主机上。
换句话说:
- 使用 expose 开放的端口,只有同一 Docker 网络中的其他容器可以访问
- 使用 expose 开放的端口,宿主机和外部网络无法直接访问
expose 是一个数组,每个元素是一个端口号,可以可选地加上协议(TCP 或 UDP)。如果未指定协议,默认为 TCP。
services:service-name:image: ...expose:- "端口号"- "端口号/协议"
示例
假设我们有两个服务:一个 Web 应用(web)和一个数据库(db)。Web 应用需要访问数据库的 5432 端口(PostgreSQL),但数据库不需要被宿主机访问,只需要被 Web 应用访问。
定义数据库服务
services:db:image: postgresexpose:- "5432" # 这表示该容器内部的 5432 端口将对其他容器开放
定义 Web 应用服务
web:image: my-web-appports:- "80:80" # 将宿主的80端口映射到容器的80端口,这样外部可以访问Weblinks:- db # 传统的链接方式,现在更推荐使用自定义网络,但Compose默认已经创建了网络
在这个例子中:
-
db服务通过expose暴露了 5432 端口。这意味着在同一 Docker 网络中的其他容器(比如web)可以连接到db:5432。 -
但是,从宿主机(你的机器)无法直接访问
db的 5432 端口,因为expose没有将端口映射到宿主机。
注意事项
-
与
ports的区别:-
ports:将容器端口映射到宿主机端口,允许外部访问(从宿主机或宿主机外部)。 -
expose:仅将端口开放给同一网络中的其他容器,宿主机不映射端口。
-
-
默认网络:在 Docker Compose 中,所有服务默认加入一个名为
projectname_default的网络(其中projectname是你的项目目录名)。因此,它们可以通过服务名互相访问。 -
协议指定:你可以指定端口是 TCP 还是 UDP,例如
expose: - "53/udp"。 -
多个端口:你可以暴露多个端口,只需在
expose下列出。
话不多说,我们直接看例子
services:myweb:image: nginx:1.22.0expose:- 80
我们检查一下,完全是没有问题的

现在我们运行下面这个命令
docker compose up -d

现在我们查看一下容器的端口映射信息

我们发现并没有和宿主机进行映射

我们在外部浏览器访问一下这个界面,还是失败了
2.9.depends_on参数
在 Docker Compose 中,一个项目通常由多个服务(容器)组成。这些服务之间可能存在依赖关系:
- Web 应用需要数据库先启动
- 后端 API 需要消息队列先启动
- 数据处理器需要缓存服务先启动
depends_on 参数就是用来明确声明服务之间的依赖关系,控制它们的启动顺序。
depends_on 的基本语法
depends_on 是一个数组,列出当前服务所依赖的其他服务:
services:current-service:image: some-imagedepends_on:- other-service-1- other-service-2
这表示 current-service 会在 other-service-1 和 other-service-2 启动之后才开始启动。
实际示例说明
假设我们有一个典型的 Web 应用架构:Nginx 前端、Web 应用后端、PostgreSQL 数据库。
services:nginx:image: nginxports:- "80:80"depends_on:- webappwebapp:image: my-web-appdepends_on:- databasedatabase:image: postgresenvironment:POSTGRES_PASSWORD: example
启动顺序分析:
- 第一启动:database 服务(没有依赖任何其他服务)
- 第二启动:webapp 服务(等待 database 启动后开始)
- 第三启动:nginx 服务(等待 webapp 启动后开始)
depends_on 的两种模式
1. 简单依赖(仅控制启动顺序)
services:webapp:image: my-appdepends_on:- database- redis
行为:
- Docker Compose 会先启动 database 和 redis
- 当这两个服务的容器进入运行状态后,开始启动 webapp
注意:这不保证 database 和 redis 内部的应用程序已经完全启动并可以接受连接
2. 条件依赖(等待服务就绪)
在较新版本的 Docker Compose 中,可以使用更详细的语法来等待服务真正就绪:
services:nginx:image: nginxports:- "80:80"depends_on:webapp:condition: service_healthy # 等待 Web 应用健康healthcheck:test: ["CMD", "curl", "-f", "http://localhost"]interval: 30stimeout: 10sretries: 3webapp:image: my-web-appdepends_on:database:condition: service_healthy # 等待数据库健康redis:condition: service_healthy # 等待 Redis 健康migrations:condition: service_completed_successfully # 等待迁移完成healthcheck:test: ["CMD", "curl", "-f", "http://localhost:3000/health"]interval: 30stimeout: 10sretries: 3database:image: postgresenvironment:POSTGRES_PASSWORD: examplehealthcheck:test: ["CMD-SHELL", "pg_isready -U postgres"]interval: 5stimeout: 5sretries: 5start_period: 10sredis:image: redishealthcheck:test: ["CMD", "redis-cli", "ping"]interval: 5stimeout: 3sretries: 5migrations:image: my-web-appcommand: ["npm", "run", "db:migrate"]depends_on:database:condition: service_healthy # 迁移任务等待数据库健康
条件说明:
- service_started:默认行为,容器启动后就开始下一服务
- service_healthy:等待服务的健康检查通过(需要配合 healthcheck 配置)
- service_completed_successfully:等待服务运行完成并成功退出(用于一次性任务)
这3个我想好好讲讲
1. service_started - 服务已启动
这是最基础的条件,等同于简单的 depends_on 数组语法。
语法:
depends_on:target-service:condition: service_started
行为:
- 等待目标服务的容器进入运行状态
- 不检查容器内的应用程序是否真正就绪
- 这是默认条件,如果省略 condition,就使用这个
示例:
services:webapp:image: my-appdepends_on:redis:condition: service_started # 等待 redis 容器启动redis:image: redis
2. service_healthy - 服务健康检查通过
这是最常用的条件依赖,需要配合 healthcheck 配置使用。
语法:
depends_on:target-service:condition: service_healthy
前提条件: 目标服务必须配置了 healthcheck
行为:
- 等待目标服务的健康检查返回 "healthy" 状态
- 确保容器内的应用程序真正就绪并可接受连接
要使用 condition: service_healthy,必须正确配置健康检查。以下是完整的 healthcheck 参数说明:
healthcheck 基本语法
services:service-name:image: some-imagehealthcheck:test: <检查命令>interval: <检查间隔>timeout: <命令超时时间>retries: <重试次数>start_period: <启动后等待时间>
healthcheck 参数说明
test (必需):健康检查命令
- 可以是字符串数组:["CMD", "curl", "-f", "http://localhost"]
- 也可以是 Shell 命令:ls -l
- 或者禁用健康检查:["NONE"]
interval (可选):检查间隔,默认 30秒
- interval: 10s - 每 10 秒检查一次(也就是每10秒执行一次健康检查命令)
timeout (可选):命令超时时间,默认 30秒
- timeout: 10s - 健康检查命令超过 10 秒未完成视为失败
retries (可选):重试次数,默认 3
- retries: 5 - 健康检查命令连续失败 5 次才标记为不健康
start_period (可选):容器启动后的等待时间,默认 0秒
- start_period: 40s - 容器启动后 40 秒内不标记为不健康
完整示例:
# 定义所有服务(容器)
services:# Web 应用程序服务 - 主要的业务逻辑容器webapp:# 使用自定义的 Web 应用镜像,这个镜像包含了你的应用程序代码image: my-app# depends_on 定义了服务启动的依赖关系# 确保依赖的服务就绪后再启动当前服务depends_on:database:# condition: service_healthy 表示等待数据库的健康检查通过# 这不仅仅是容器启动,而是确保 PostgreSQL 数据库进程真正就绪并可接受连接# 避免了 Web 应用启动时连接数据库失败的问题condition: service_healthy # 等待数据库健康检查通过redis:# 同样等待 Redis 缓存服务的健康检查通过# 确保 Redis 服务就绪并且可以接受连接# 这样 Web 应用启动时就可以立即使用缓存功能condition: service_healthy # 等待 Redis 健康检查通过# 注意:webapp 本身没有配置 healthcheck,这意味着其他服务无法使用 condition: service_healthy 来等待它# PostgreSQL 数据库服务 - 数据持久化存储database:# 使用官方的 PostgreSQL 镜像image: postgres# healthcheck 配置定义了如何检查这个服务是否健康# 其他服务可以通过 condition: service_healthy 来等待这个检查通过healthcheck:# test 命令是健康检查的核心# ["CMD-SHELL", "pg_isready -U postgres"] 表示通过 shell 执行 pg_isready 命令# pg_isready 是 PostgreSQL 自带的工具,专门用于检查数据库连接状态# -U postgres 指定使用 postgres 用户进行连接检查# 如果命令返回 0 (成功),表示数据库就绪;非 0 表示不健康test: ["CMD-SHELL", "pg_isready -U postgres"]# interval: 5s 表示每 5 秒执行一次健康检查# 这个频率比较高,可以快速发现数据库状态变化interval: 5s# timeout: 5s 表示健康检查命令最多执行 5 秒# 如果 pg_isready 命令超过 5 秒没有完成,就视为本次检查失败timeout: 5s# retries: 5 表示需要连续失败 5 次才标记服务为不健康# 这提供了容错能力,避免因网络抖动等临时问题误判为不健康# 5次 × 5秒间隔 = 需要连续 25 秒失败才会标记为不健康retries: 5# start_period: 10s 表示容器启动后的前 10 秒内,健康检查失败不计入重试# 这给了 PostgreSQL 足够的启动时间,避免启动过程中的正常初始化被误判为不健康start_period: 10s# Redis 缓存服务 - 用于会话存储、缓存等redis:# 使用官方的 Redis 镜像image: redis# Redis 服务的健康检查配置healthcheck:# test 命令使用 redis-cli ping 来检查 Redis 状态# Redis 的 PING 命令会返回 PONG 如果服务正常# 使用 CMD 格式(非 shell)直接执行命令,更高效test: ["CMD", "redis-cli", "ping"]# interval: 5s 每 5 秒检查一次 Redis 状态interval: 5s# timeout: 3s Redis 的 ping 命令应该很快,3 秒超时足够timeout: 3s# retries: 5 需要连续 5 次检查失败才标记为不健康# 5次 × 5秒间隔 = 需要连续 25 秒失败才会标记为不健康retries: 5# 注意:redis 服务没有配置 start_period,因为 Redis 启动通常很快
话不多说,我们直接看个例子
services:myweb:image: nginx:1.22.0depends_on:mysql:condition: service_healthy # 等待MySQL健康后再启动mysql:image: mysql:8.0.42environment:MYSQL_ROOT_PASSWORD: 123456 # 设置数据库root密码healthcheck:test: mysql -u root -p123456 -e 'select 1;' # 检查数据库是否就绪interval: 10s # 每10秒检查一次timeout: 5s # 5秒超时retries: 10 # 失败10次才标记不健康
我们检查一下

完全没有问题,我们直接运行好吧

可以看到只有等到我们的mysql启动之后,才会去启动这个myweb啊

现在我们使用docker ps去查看这个mysql的信息

现在你明白了吧
2.10.env_file
env_file 是 Docker Compose 文件中的一个配置参数。它的作用很简单:指定一个包含环境变量的文件,然后将这个文件中的所有环境变量“注入”到容器中。
你可以把它看作是一个“批量设置环境变量”的方法。
为什么需要它?
假设你的应用需要连接数据库,需要设置以下环境变量:
- DB_HOST=mysql-db
- DB_USER=app_user
- DB_PASS=my_secret_password
你可以在 docker-compose.yml 里直接用 environment 参数来设置:
services:my-app:image: my-app-imageenvironment:- DB_HOST=mysql-db- DB_USER=app_user- DB_PASS=my_secret_password
但这样做有两个问题:
不安全:密码等敏感信息直接写在了 YAML 文件里,如果你把这个文件上传到代码仓库(如 GitHub),密码就泄露了。
不灵活:当你需要为不同环境(开发、测试、生产)配置不同的变量值时,你需要修改这个 YAML 文件本身,很麻烦。
使用 env_file 就是为了解决这两个问题。你可以把环境变量写在一个单独的 .env 文件里,然后在 docker-compose.yml 中引用它。
3. 如何使用 env_file?
第一步:创建环境变量文件
首先,你需要创建一个文件,通常以 .env 开头。例如,我们创建一个名为 .env.db 的文件。
文件内容如下,遵循 KEY=VALUE 的格式,每行一个变量:
# 这是 .env.db 文件的内容
DB_HOST=mysql-db
DB_USER=app_user
DB_PASS=my_secret_password
APP_ENV=production
重要规则:
- 每行一个变量。
- 格式必须是 KEY=VALUE。
- # 号用于注释,该行会被忽略。
变量名通常使用大写字母和下划线。
第二步:在 docker-compose.yml 中引用这个文件
现在,在你的 docker-compose.yml 文件中,使用 env_file 参数来指向你刚刚创建的文件。
services:my-app:image: my-app-image# 关键在这里:使用 env_file 指定文件路径env_file:- .env.db# 你可以继续使用 environment 参数设置其他变量,它们会合并# environment:# - ANOTHER_VAR=value
4. 路径和多个文件
路径:上面的例子中,.env.db 指的是与 docker-compose.yml 文件在同一目录下的 .env.db 文件。你也可以使用相对路径(如 ./config/.env.prod)或绝对路径(不推荐,因为不利于移植)。
多个文件:你可以指定多个环境变量文件。Docker Compose 会按顺序加载它们,如果后面文件中的变量与前面文件重名,则后面的值会覆盖前面的值。
services:my-app:image: my-app-imageenv_file:- .env.common # 存放通用变量- .env.secrets # 存放敏感信息,这个文件被 .gitignore 忽略- .env.override # 存放特定环境的覆盖变量
5. env_file 和 environment 的优先级
你可以在同一个服务中同时使用 env_file 和 environment 参数。
规则是:在 environment 参数中直接定义的变量,会覆盖 env_file 中定义的同名变量。
举例:
.env.db 文件内容:DB_PASS=password_from_file
docker-compose.yml 内容:
services:my-app:image: my-app-imageenv_file:- .env.dbenvironment:- DB_PASS=password_from_yml_override
那么,容器启动后,环境变量 DB_PASS 的值将是 password_from_yml_override,因为它直接在 environment 中被定义了,覆盖了来自 .env.db 文件的值。
话不多说,我们直接看例子
echo "TEST_COM=1" > ./comenv
echo "TEST_WEB=1" > ./webenv

接下来我们修改一下
services:myweb:image: nginx:1.22.0env_file:- ./comenv- ./webenv

完全没有问题!!我们直接启动

我们进去容器内部看看

完美!!!
三.docker compose命令合集
3.1.docker compose命令全局选项
在 Docker Compose 中,你操作的目标(我们称之为“对象”)主要分为三个层次:
- 项目:这是最高层级。一个项目通常代表一个完整的应用,它由一个 docker-compose.yml 文件定义,并包含了一个或多个服务。项目本身有一个名字。
- 服务:这是在 docker-compose.yml 文件中定义的一个应用组件。例如,一个 Web 应用项目可能包含一个 web 服务(你的应用代码)和一个 db 服务(数据库)。
- 容器:这是最底层。每个服务在运行时,会创建一个或多个容器实例。容器是服务在 Docker 中运行时的具体表现。
关键规则是:
- 大部分 docker compose 命令既可以作用于整个项目,也可以作用于项目中的单个或多个服务,甚至可以作用于具体的容器。
- 当你不指定任何服务名时,命令的默认对象就是整个项目。这意味着该命令会对项目中定义的所有服务生效。
- 当你指定了一个或多个服务名时,命令的对象就变成了这些特定的服务。
docker compose 命令的通用文字结构如下:
docker compose [全局选项] 命令 [命令选项] [服务名]
在Docker Compose命令中,选项分为两类:全局选项和命令选项。
它们的主要区别在于作用范围和放置的位置。
全局选项 (Global Options)
- 位置:必须放在 docker compose 之后,具体命令(如 up、down)之前。
- 作用:这些选项会影响整个Compose命令的执行环境,比如指定项目名称、配置文件、环境文件等。它们不是针对某个具体命令的,而是为整个Compose命令设置上下文。
- 例子:-f(指定Compose文件)、-p(指定项目名称)、--env-file(指定环境文件)等。
命令选项 (Command Options)
- 位置:必须放在具体命令之后。
- 作用:这些选项只对它们所跟随的命令起作用,用于调整该命令的行为。不同的命令有不同的命令选项。
- 例子:对于 up 命令,-d 表示后台运行;对于 logs 命令,-f 表示跟随日志输出。
重要规则:全局选项在命令之前,命令选项在命令之后。
3. 常见全局选项详解
全局选项(OPTIONS)不是必须的,但它们可以改变命令的默认行为。
-f, --file
- 作用:指定 Docker Compose 要使用的配置文件。
- 默认行为:如果你不使用这个选项,Docker Compose 会在当前目录下自动寻找名为 docker-compose.yml 的文件。
- 如何使用:你可以通过这个选项指定一个不同名字或位于不同路径的配置文件。
- 示例:
- docker compose -f docker-compose.prod.yml up:使用 docker-compose.prod.yml 这个文件来启动项目。
- docker compose -f /path/to/your/compose-file.yml up:使用绝对路径指定的文件。
-p, --project-name
- 作用:指定项目的名称。
- 默认行为:如果你不使用这个选项,Docker Compose 会使用你当前所在目录的名称作为项目名。
- 为何重要:Docker 会使用“项目名称_服务名称_序号”的规则来为每个容器命名。指定项目名可以让你在多个项目使用相同目录时,或者想明确管理时,清晰地区分它们。
- 示例:
- docker compose -p my_web_app up:启动一个名为 my_web_app 的项目。它产生的容器名字会像 my_web_app-web-1、my_web_app-db-1。
我们看个例子


注意这个选项的位置啊
像下面3种情况都是不行的

接下来我们看看 -p全局选项

一般来说,系统都会将docker-compose.yml所在目录下的目录名作为项目名

当项目名不同时,他会重新创建


我们可以看到,针对每个项目,都会创建属于自己的!!
3.2.docker compose up
docker compose up 是 Docker Compose 的核心命令,它的主要作用是:根据 docker-compose.yml 文件中的定义,启动整个应用程序堆栈。
所谓“应用程序堆栈”,可以简单理解为你的项目所依赖的所有服务(例如一个 Web 服务器、一个数据库、一个缓存服务等)。这个命令会确保这些服务按照你定义的方式运行起来。
命令格式
docker compose up [options] [SERVICE...]
- docker compose up:这是命令的主体。
- [options]:方括号表示这是可选的。你可以在这里添加一个或多个选项来改变命令的行为,例如 -d。
- [SERVICE...]:这也是可选的。你可以指定一个或多个在 docker-compose.yml 文件中定义的服务名称。如果不指定,则默认启动文件中定义的所有服务。
选项详解
下面我们来逐一讲解你提到的几个选项。
-d 或 --detach
- 作用:在后台运行服务容器。
- 如果不加 -d 选项,命令会在前台运行。你的终端会附着到所有容器的日志输出上,你会实时看到所有服务的日志混杂在一起。要退出,你需要按 Ctrl+C,这通常会停止所有正在运行的服务。
- 如果加上 -d 选项,命令会在后台运行。它会启动所有容器,然后立即将控制权交还给你的终端。容器会在后台继续运行,你看不到实时的日志。这不会影响服务的正常运行。
--force-recreate
- 作用:强制重新创建容器,即使它们的配置和镜像没有发生变化。
- 正常情况下,docker compose up 会检查容器是否已经存在。如果容器已经存在,并且其配置(来自 docker-compose.yml)和使用的镜像都没有改变,Compose 会直接启动这个已有的容器,而不会重新创建它。这有利于提高启动速度。
- 使用 --force-recreate 会跳过这个检查,无论有无变化,它都会先删除已有的容器,然后根据当前配置创建一个全新的容器。
--no-recreate
- 作用:如果容器已经存在,则不重新创建它。
- 这个选项与 --force-recreate 完全相反。
- 它规定:只启动那些还没有运行的容器。对于那些已经存在且停止的容器,直接启动它,即使它的配置可能已经发生了改变。
重要提示:--force-recreate 和 --no-recreate 是互斥的选项,你不能在同一个命令中同时使用它们。
看一些例子来
docker compose up
#启动所有服务并在前台运行(日志直接显示在终端)docker compose up -d
#启动所有服务并在后台运行docker compose up web
#只启动名为 web 的服务(前台)docker compose up -d db redis
#在后台启动 db 和 redis 两个服务docker compose up --force-recreate
#强制重新创建所有容器(即使配置没变)docker compose up -d --force-recreate web
#强制重新创建 web 服务并在后台运行docker compose up -d --force-recreate
#在后台强制重建所有服务docker compose up --no-recreate
#如果容器已存在就不重新创建,只启动
话不多说,我们直接看例子
示例1
首先先准一下我们的docker-compose.yml

接下来我们执行下面这个命令
docker compose up

但是它卡在这里了
我们打开一个新的shell去看看
root@VM-16-14-ubuntu:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
77bf47aa4553 nginx:1.22.0 "/docker-entrypoint.…" About a minute ago Up About a minute 80/tcp test-myweb-1

我们现在回到现在这个终端,按下ctrl+c

现在我们再次查看一下状态
root@VM-16-14-ubuntu:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
00eb4005cbb8 bridge bridge local
60c055686fa2 host host local
b6def881f40f none null local
43b270e9aee7 test_default bridge local
root@VM-16-14-ubuntu:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
77bf47aa4553 nginx:1.22.0 "/docker-entrypoint.…" 2 minutes ago Exited (0) 36 seconds ago test-myweb-1
容器已经退出了

示例2
首先我们先修改一下docker-compose.yml

现在我们执行下面这个命令
docker compose up -d

这个时候我们怎么按ctrl+c,就是不会影响到我们的容器
root@VM-16-14-ubuntu:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
35187fc9a189 nginx:1.22.0 "/docker-entrypoint.…" 7 seconds ago Up 7 seconds 80/tcp test-myweb-1

我们进去容器内部看看环境变量

现在我们修改一下这个yml文件

我们接下来执行下面这个命令
docker compose up -d --force-recreate

我们看到这个容器的创建时间被刷新了,这意味着我们创建了一个新的容器
root@VM-16-14-ubuntu:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
64fc56a78bc8 nginx:1.22.0 "/docker-entrypoint.…" 14 seconds ago Up 14 seconds 80/tcp test-myweb-1

现在我们再次进行修改

接下来我们执行下面这个命令
docker compose up -d --no-recreate

我们现在去看看容器的状态
root@VM-16-14-ubuntu:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS S
64fc56a78bc8 nginx:1.22.0 "/docker-entrypoint.…" 3 minutes ago Up 3 minutes 80/tcp -myweb-1
可以看到,创建时间是3分钟前,这就说明不是新创建的

我们发现容器内部的环境变量居然没有进行改变!!
3.3.docker compose down
docker compose down 是一个用于停止并清理由 docker compose up 命令启动的整个应用环境的命令。
它的核心作用与 docker compose up 相反。如果说 up 是“创建并启动”,那么 down 就是“停止并拆除”。
命令语法解析
docker compose down [options] [SERVICE...]
我们来分解这个命令的每个部分:
docker compose
- 这是 Docker Compose 命令行工具的主命令,用于管理多容器的 Docker 应用。
down
- 这是子命令,它定义了要执行的具体操作:“拆除”整个 Compose 项目。
[SERVICE...] (可选)
- 在 docker-compose.yml 文件中定义的一个或多个服务的名称。
- 如果不指定任何服务名:命令将作用于整个项目,停止并删除所有在 up 时启动的服务(容器)。
- 如果指定了服务名:例如 docker compose down nginx db,那么它将只停止和删除名为 nginx 和 db 的容器。这是一个不常用的用法,通常我们不加服务名,直接清理所有。
核心执行动作(默认行为)
当你直接运行 docker compose down(不加任何选项)时,它会按顺序执行以下操作:
停止所有正在运行的容器:
- 向所有由当前 Compose 项目启动的容器发送 SIGTERM 信号,允许它们进行优雅关闭。如果容器在超时后(默认10秒)仍未停止,则发送 SIGKILL 信号强制停止。
- 效果:你的所有服务,如 Web 服务器、数据库等,都会停止运行。
删除已停止的容器:
- 在容器被停止后,自动将它们删除。
- 效果:容器本身被销毁。你之前在容器内进行的任何未持久化的更改(例如,安装的临时软件、在容器内创建的文件)都会随之消失。
删除网络:
- 删除 Compose 为此项目创建的默认网络(通常是 <项目名>_default)以及其它在 docker-compose.yml 中定义的非外部网络。
- 效果:容器之间用于通信的专用网络通道被移除。
关键选项讲解:-v, --volumes
这是你最需要理解的一个重要选项。
它做什么:删除在 docker-compose.yml 文件的 volumes 配置项中声明的、并且是 Compose 为此项目自动创建的 命名卷。
详细解释:
卷 是 Docker 中用于持久化存储数据的机制。
在你的 docker-compose.yml 文件中,你可能会看到这样的配置:
services:db:image: mysql:8.0volumes:- db_data:/var/lib/mysql # <-- 这里使用了命名卷 `db_data`volumes:db_data: # <-- 这里声明了名为 `db_data` 的卷
- 当你运行 docker compose up 时,如果 db_data 卷不存在,Compose 会自动创建它。数据库的所有数据(如表、数据等)都保存在这个卷里。
- 当你运行 docker compose down 时,默认情况下,这个 db_data 卷会被保留下来。这样,下次你运行 docker compose up 时,数据库数据依然存在。
- 当你运行 docker compose down -v 时,这个 db_data 卷也会被一并删除。这意味着所有的数据库数据将被永久清除。
重要提示:
- --volumes 不会删除“绑定挂载”。“绑定挂载”是指直接映射到宿主机文件系统某个具体目录的卷,例如 - ./local/folder:/container/path。
- 它主要删除的是在 volumes 顶级配置块中声明的、由 Docker 管理的命名卷。
话不多说,我们直接看例子
services:myweb:image: nginx:1.22.0environment:TEST: 3volumes:- mytestvol1:/usr/share/nginx/htmlvolumes:mytestvol1:

现在我们就运行一下
docker compose up -d

现在我们去看看它的信息

都创建好了
现在我们执行下面这个命令
docker compose down

可以看到它没有清理管理卷啊!!

如果说,我们想要清理管理卷,就需要加上-v选项
docker compose down -v

管理卷就被删除了!!还是很简单的!
3.4.docker compose run
docker compose up 是用于启动你在 docker-compose.yml 文件中定义的所有服务,并让它们按照配置长期运行。
而 docker compose run 则完全不同,它的核心目的是:为了针对某个已定义的服务,执行一个一次性的、临时的任务。
一个关键的比喻(虽然你说不要,但这个概念性比喻至关重要):
- docker compose up 就像是 按下整个工厂的生产线启动按钮,所有机器开始协同工作。
- docker compose run 就像是 派一个工人到生产线的某个特定工位上,执行一个特定的、一次性的任务(比如检查一台机器的状态、清理一个临时工具),任务完成工人就离开。它不会影响生产线其他部分的正常运行。
基本命令格式是:
docker compose run [选项] <服务名> [命令]
现在,我们来详细讲解你列出的每一个选项,以及 服务名 和 命令 部分。
<服务名>
- 这是必须的。它对应你在 docker-compose.yml 文件中 services 下面的某个服务的名称。
- 例如,如果你的 docker-compose.yml 里有一个服务叫 web,那么 <服务名> 就是 web。
[命令]
- 这是可选的,但非常重要。
- 它会覆盖 docker-compose.yml 文件中为该服务定义的 command 指令。
- 如果指定了 [命令],容器就会执行你指定的这个命令,而不是配置文件里原本的命令。
例子:你的 web 服务原本是启动一个 Python 服务器(command: python app.py)。你可以用 docker compose run web bash 来启动一个容器,但里面运行的是 bash shell,让你可以进入容器内部进行操作,而不是启动 Web 服务器。
选项详解
-d 后台运行容器
- 和 docker run -d 一样,让容器在后台运行。
- 但请注意:对于 docker compose run 这种用于执行一次性任务的命令,通常我们不常用 -d。因为我们往往需要立即看到命令执行的结果(比如数据库迁移的日志)。如果你使用了 -d,那么你需要用 docker compose logs 来查看输出。
--name NAME 为容器指定一个名字
- Docker 会为每个运行的容器自动生成一个随机名字。
- 使用这个选项可以给你通过 run 启动的容器一个自定义的名字,便于后续管理(比如查看日志、停止容器)。
- 例子:docker compose run --name my-custom-task web bash,这个容器的名字就是 my-custom-task,而不是一长串随机字符。
--entrypoint CMD 覆盖默认的容器启动指令
- 这个选项比 [命令] 更底层。它会覆盖 Docker 镜像中定义的 ENTRYPOINT。
- 通常情况下,你只需要用上面的 [命令] 参数就够了,因为它覆盖的是 CMD。只有在需要修改镜像的默认入口点时才使用 --entrypoint。
- 例子:如果一个镜像的 ENTRYPOINT 是 ["/bin/my-app"],你使用 --entrypoint bash 会把入口点替换成 bash。
-e KEY=VAL 设置环境变量
- 为你这次运行的容器设置环境变量。这个变量会覆盖 docker-compose.yml 中定义的同名环境变量,但只对本次 run 生效。
- 可以多次使用 -e 来设置多个变量。
- 例子:docker compose run -e DEBUG=True -e LOG_LEVEL=debug web python manage.py test,这次运行测试时,临时开启了 Debug 模式和提高日志级别。
-u, --user="" 指定运行容器的用户名或者 uid
- 指定容器内部以哪个用户身份来运行命令。这可以解决文件权限等问题。
- 例子:如果你在宿主机上创建的文件,在容器内没有权限修改,可以指定 -u root 以 root 用户身份运行命令来获得最高权限。
--rm 运行命令后自动删除容器
- 这是一个强烈推荐使用的选项!
- 当容器内运行的命令执行完毕后,自动删除这个容器。
- 因为 docker compose run 是用来执行一次性任务的,任务完成后容器就失去了作用。使用 --rm 可以自动清理,避免产生大量无用的、停止状态的容器,占用磁盘空间。
-p, --publish=[] 映射容器端口到本地主机
- 这个选项和 docker run -p 一样,将容器的端口映射到宿主机。
- 注意:它会覆盖 docker-compose.yml 中为该服务定义的 ports 配置,但仅对本次 run 启动的容器生效。
- 通常,由 docker compose up 启动的服务已经做好了端口映射。run 命令通常用于执行内部命令(如数据库迁移、测试),不需要对外暴露端口,所以不常用到此选项。
话不多说,我们直接看例子
先看看docker run

但是docker compose run可不是这么使用的

它的实际用法其实是下面这样子

这个就很简单,当然,如果说我们需要进行清理的话,我们还是使用docker compose down来进行清理。
四.综合实战
1.配置拓扑关系(也就是配置依赖关系
我们想要实现的结构是下面这样子的

首先我们先创建一个docker-compose.yml,往里面写入下面这些内容
# 定义服务部分
services:# 定义一个名为 myweb 的服务myweb:# 使用 nginx:1.22.0 镜像image: nginx:1.22.0# 定义服务依赖关系depends_on:# 依赖于 mysql 服务mysql:# 依赖条件:mysql 服务健康检查通过condition: service_healthy# 依赖于 redis 服务redis:# 依赖条件:redis 服务健康检查通过condition: service_healthy# 定义一个名为 mysql 的服务mysql:# 使用 mysql:8.0.42 镜像image: mysql:8.0.42# 设置环境变量environment:# 设置 MySQL root 用户密码为 123456MYSQL_ROOT_PASSWORD: 123456# 定义健康检查healthcheck:# 健康检查命令:使用 mysql 客户端连接并执行简单查询test: mysql -uroot -p123456 -e 'select 1;'# 检查间隔:每 10 秒检查一次interval: 10s# 命令超时时间:5 秒timeout: 5s# 重试次数:失败 10 次后才标记为不健康retries: 10# 定义一个名为 redis 的服务redis:# 使用 redis:8.0 镜像image: redis:8.0# 定义健康检查healthcheck:# 健康检查命令:使用 redis-cli 发送 ping 命令test: redis-cli ping# 检查间隔:每 10 秒检查一次interval: 10s# 命令超时时间:5 秒timeout: 5s# 重试次数:失败 10 次后才标记为不健康retries: 10
我们检查一下


完全没有问题的。
2.配置网络,首页以及存储卷
还是打开我们上面写的那个docker-compose.yml,往里面写入下面这些东西
services:myweb:image: nginx:1.22.0#映射端口号ports:- 8080:80# 指定服务连接的网络networks:# 连接到名为 mytestnet1 的网络- mytestnet1# 定义数据卷挂载volumes:# 将主机上的 ./nginxhome 目录挂载到容器的 /usr/share/nginx/html 目录- ./nginxhome:/usr/share/nginx/htmldepends_on:mysql:condition: service_healthyredis:condition: service_healthymysql:image: mysql:8.0.42# 指定服务连接的网络networks:# 连接到名为 mytestnet1 的网络- mytestnet1# 定义数据卷挂载volumes:# 将主机上的 ./mysqllib 目录挂载到容器的 /var/lib/mysql 目录- ./mysqllib:/var/lib/mysqlenvironment:MYSQL_ROOT_PASSWORD: 123456healthcheck:test: mysql -uroot -p123456 -e 'select 1;'interval: 10stimeout: 5sretries: 10redis:image: redis:8.0# 指定服务连接的网络networks:# 连接到名为 mytestnet1 的网络- mytestnet1healthcheck:test: redis-cli pinginterval: 10stimeout: 5sretries: 10# 定义网络部分
networks:# 定义一个名为 mytestnet1 的网络mytestnet1:
可以检查一下
docker compose config
我这里就不说了
我们现在想要修改一下这个nginx服务的首页

到现在就完美了
3.服务启动和资源管理
docker compose up -d

我们去看看
root@VM-16-14-ubuntu:~/test# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bae0d96cbf2c nginx:1.22.0 "/docker-entrypoint.…" 53 seconds ago Up 32 seconds 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp test-myweb-1
d687ba484ec7 mysql:8.0.42 "docker-entrypoint.s…" 53 seconds ago Up 53 seconds (healthy) 3306/tcp, 33060/tcp test-mysql-1
27e6ac6f3a77 redis:8.0 "docker-entrypoint.s…" 53 seconds ago Up 53 seconds (healthy) 6379/tcp test-redis-1
root@VM-16-14-ubuntu:~/test# docker network ls
NETWORK ID NAME DRIVER SCOPE
00eb4005cbb8 bridge bridge local
60c055686fa2 host host local
b6def881f40f none null local
bb887de8e21e test_mytestnet1 bridge local
果然是创建成功了。

我们去浏览器访问一下:

果然是没有问题的。
现在我们进行清理一下
docker compose down

但是那些存储卷都还在

还是很简单的吧!
