当前位置: 首页 > news >正文

【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 应用程序。

你可以这样理解它的核心:

  1. 它是一个“编排”工具:当你的应用需要多个服务(例如:一个 Web 服务器、一个数据库、一个缓存服务)才能正常工作时,每个服务通常都运行在独立的容器中。Docker Compose 可以帮助你轻松地将这些分散的容器“组合”成一个完整的、相互协作的应用。

  2. 它使用一个 YAML 格式的配置文件:你不再需要手动输入一长串复杂的 docker run 命令来启动每个容器。相反,你可以在一个名为 docker-compose.yml 的文件中,用清晰的语法描述出你的应用需要哪些容器、每个容器如何配置、容器之间如何连接等所有信息。

  3. 它通过一条命令管理整个应用的生命周期:使用 docker-compose up 这一条命令,就可以根据你的配置文件,启动所有容器,并建立起它们之间的网络连接。同样,你也可以用 docker-compose down 这一条命令,停止并移除所有相关的容器。

简单总结:Docker Compose 让你用一个文件、一条命令,就能管理一组相互关联的 Docker 容器。

Docker Compose 有什么用?(解决了什么问题)

在没有 Docker Compose 之前,如果你要运行一个由三个容器(比如 Nginx, Python Web App, MySQL)组成的应用,你可能需要:

  1. 打开一个终端,输入一长串 docker run ... 命令启动 MySQL 容器,设置 root 密码、数据卷等。

  2. 打开另一个终端,输入另一长串 docker run ... 命令启动 Python 应用容器,链接到 MySQL 容器。

  3. 再打开一个终端,输入又一长串 docker run ... 命令启动 Nginx 容器,链接到 Python 应用容器。

这个过程非常繁琐、容易出错,并且难以记录和分享。别人要运行你的项目,你只能给他一份复杂的操作文档。

Docker Compose 解决了以下核心问题:

  1. 简化多容器应用的管理:将多个容器的启动、停止、连接等操作聚合为一个简单的命令。

  2. 实现环境配置的代码化docker-compose.yml 文件本身就是你的应用运行环境的声明。你可以将这个文件放入版本控制系统(如 Git),这样任何拿到这个文件的人,都可以在任何装有 Docker 和 Docker Compose 的机器上,一键复现出一模一样的环境。这极大地促进了开发、测试和生产环境的一致性。

  3. 提高效率:省去了重复输入复杂命令的时间,也减少了因命令参数输入错误导致的调试时间。

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个配置即可

  1. 驱动
  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 参数用于声明容器在内部网络中开放哪些端口,但这些端口不会映射到宿主机上。

换句话说:

  1. 使用 expose 开放的端口,只有同一 Docker 网络中的其他容器可以访问
  2. 使用 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 没有将端口映射到宿主机。

注意事项

  1. 与 ports 的区别

    • ports:将容器端口映射到宿主机端口,允许外部访问(从宿主机或宿主机外部)。

    • expose:仅将端口开放给同一网络中的其他容器,宿主机不映射端口。

  2. 默认网络:在 Docker Compose 中,所有服务默认加入一个名为 projectname_default 的网络(其中 projectname 是你的项目目录名)。因此,它们可以通过服务名互相访问。

  3. 协议指定:你可以指定端口是 TCP 还是 UDP,例如 expose: - "53/udp"

  4. 多个端口:你可以暴露多个端口,只需在 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

但是那些存储卷都还在

还是很简单的吧!

 

http://www.dtcms.com/a/589092.html

相关文章:

  • 4.1.8 【2022 统考真题】
  • 深圳网站设计官网番禺人才网上
  • Tailwind CSS的Flex布局
  • 深入解析 LeetCode 1:两数之和
  • 重庆网站制作福州嘉兴网络科技有限公司
  • OpenCV(二十二):图像的翻转与旋转
  • 权限维持:操作系统后门技术分析与防护
  • 网闸与防火墙:构建纵深防御的“门卫”与“断桥”
  • 室内设计找工作网站wordpress app源码
  • 河北seo网站优化公司光辉网站建设
  • android 网络访问拦截器使用后的bug处理
  • mysql视图和存储过程
  • VRRP的补充
  • 天津 交友 网站建设自建网站做外贸谷歌推广
  • Young GC 的触发时机
  • 专业做网站照片免费咨询法律律师电话号码
  • GAN vs. VAE:生成对抗网络 vs. 变分自编码机
  • Docker打包步骤
  • 【数据集+源码】基于yolov11+streamlit的玉米叶片病虫害检测系统
  • VP8 是什么?
  • Rust底层编程:安全与性能的完美平衡
  • 如何删除自己建的网站无忧网站建设公司
  • HTTP-大文件传输处理
  • [linux仓库]线程同步与生产者消费者模型[线程·陆]
  • 【算法】day17 多维动态规划
  • 网站建设费算什么费用山东青岛网站建设seo优化
  • 【复习】计网每日一题1109---iBGP、AS
  • 30.注意力汇聚:Nadaraya-Watson 核回归
  • 广州营销型网站建设培训班网站设计制作太原
  • RV1126 NO.46:RV1126+OPENCV对视频流进行视频膨胀操作