Docker Docker-Compose配置两个容器依赖启动(depends_on和healthcheck)

一、参数解释说明

1.1 depends_on

depends_on 是 Docker-Compose 文件中的一个关键字,用于指定服务之间的依赖关系。
具体来说,它定义了一个服务所依赖的其他服务,只有在所依赖的服务已经启动并且处于运行状态时,该服务才会被启动。
例如:

1
2
3
4
5
6
7
8
9
10
11
12
version: '3.9'
services:
db:
image: mysql
ports:
- "3306:3306"
app:
build: .
ports:
- "80:80"
depends_on:
- db

在启动 Docker-Compose 时,Compose 会先启动 db 服务,等待它启动并处于运行状态后,才会启动 app 服务

1.2 healthchec

在docker-compose中加入healthcheck
healthcheck 支持下列选项:
test:健康检查命令,例如 [“CMD”, “curl”, “-f”, “http://localhost/actuator/health"]
interval:健康检查的间隔,默认为 30 秒,单位(h/m/s);
timeout:健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒,单位(h/m/s);
retries:当连续失败指定次数后,则将容器状态视为 unhealthy,默认 3 次。
start-period:应用的启动的初始化时间,在启动过程中的健康检查失效不会计入,默认 0 秒; (从17.05)引入
说明:在此期间的探测失败将不计入最大重试次数。但是,如果健康检查在启动期间成功,则认为容器已启动,所有连续的失败都将计入最大重试次数。
和 CMD, ENTRYPOINT 一样,healthcheck 只可以出现一次,如果写了多个,只有最后一个生效。

在 healthcheck [选项] CMD 后面的命令,格式和 ENTRYPOINT 一样,分为 shell 格式,和 exec 格式。命令的返回值决定了该次健康检查的成功与否:

0:成功;
1:失败;
2:保留值,不要使用
容器启动之后,初始状态会为 starting (启动中)。Docker Engine会等待 interval 时间,开始执行健康检查命令,并周期性执行。
如果单次检查返回值非0或者运行需要比指定 timeout 时间还长,则本次检查被认为失败。
如果健康检查连续失败超过了 retries 重试次数,状态就会变为 unhealthy (不健康)。

一旦有一次健康检查成功,Docker会将容器置回 healthy (健康)状态

二、验证

2.1 延迟启动的nginx 容器

首先创建一个延迟启动nginx容器

1
2
3
4
5
6
7
8
9
10
11
12
#docker-compose -f sleepnginx-compose.yml up -d
#sleepnginx-compose.yml 配置文件如下
version: "3.9"
services:
sleepnginx1:
image: nginx
ports:
- "13001:80"
restart: always
container_name: sleepnginx1
# 添加STARTUP_DELAY参数并设置为60s(单位为秒)
command: sh -c "sleep 60 && nginx -g 'daemon off;'"

启动

1
2
3
(base) [root@localhost sleepNginx]# docker-compose -f sleepnginx-compose.yml up -d 
[+] Running 1/1
⠿ Container sleepnginx1 Started 0.4s

60秒内执行
查看docker 容器情况发现 启动成功

1
2
3
(base) [root@localhost ~]# docker ps 
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cdf4e23d11c9 nginx "/docker-entrypoint.…" 37 seconds ago Up 36 seconds 0.0.0.0:13001->80/tcp, :::13001->80/tcp sleepnginx1

但是访问http://ip:13001 并不能访问到nginx页面
并且容器日志也查看不到

1
(base) [root@localhost ~]# docker logs -f cdf4e23d11c9

60秒以外则可以访问nginx 并且查看日志,则证明nginx 延迟启动成功

1
2
3
4
5
6
7
8
(base) [root@localhost ~]# docker ps 
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cdf4e23d11c9 nginx "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:13001->80/tcp, :::13001->80/tcp sleepnginx1
(base) [root@localhost ~]# docker logs -f cdf4e23d11c9
2024/01/01 13:26:58 [notice] 8#8: using the "epoll" event method
2024/01/01 13:26:58 [notice] 8#8: nginx/1.23.1
2024/01/01 13:26:58 [notice] 8#8: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
....

2.2 验证 depends_on 依赖启动

创建一个延迟启动nginx容器sleepnginx1,创建另一个容器sleepnginx2,依赖此容器,并且将此容器配置文件放到前面,
若sleepnginx2 在 sleepnginx1 之后启动,则证明 sleepnginx2是在sleepnginx1启动成功后启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#docker-compose -f sleepnginx-compose-v2.yml up -d
#sleepnginx-compose-v2.yml 配置文件如下
version: "3.9"
services:

sleepnginx2:
image: nginx
ports:
- "13002:80"
restart: always
depends_on:
- "sleepnginx1"
container_name: sleepnginx2

sleepnginx1:
image: nginx
ports:
- "13001:80"
restart: always
container_name: sleepnginx1
# 添加STARTUP_DELAY参数并设置为60s(单位为秒)
command: sh -c "sleep 60 && nginx -g 'daemon off;'"

查看启动结果如下

1
2
3
4
(base) [root@localhost ~]# docker ps 
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
671c750e5828 nginx "/docker-entrypoint.…" 7 seconds ago Up 6 seconds 0.0.0.0:13002->80/tcp, :::13002->80/tcp sleepnginx2
61d87d89be45 nginx "/docker-entrypoint.…" 7 seconds ago Up 7 seconds 0.0.0.0:13001->80/tcp, :::13001->80/tcp sleepnginx1

sleepnginx1 在 sleepnginx2 之前启动 ,及 sleepnginx2 通过 配置 depends_on 依赖 sleepnginx1

2.3 验证 healthchec 和 depends_on 配置 健康检查,并检查通过之后启动

创建一个延迟启动nginx容器sleepnginx1,并进行健康检查
创建另一个容器sleepnginx2,依赖sleepnginx1健康检查通过,sleepnginx1启动,并证明启动成功后启动sleepnginx2,
若sleepnginx2 在 sleepnginx1 健康检查通过之后启动,则证明 healthchec 生效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#docker-compose -f sleepnginx-compose-v3.yml up -d
#sleepnginx-compose-v3.yml 配置文件如下
version: "3.9"
services:
sleepnginx1:
image: nginx
ports:
- "13001:80"
restart: always
container_name: sleepnginx1
# 添加STARTUP_DELAY参数并设置为60s(单位为秒)
command: sh -c "sleep 60 && nginx -g 'daemon off;'"
healthcheck:
test: ["CMD-SHELL", "curl -fs http://127.0.0.1:80 || exit 1"] # 检测方式
interval: 30s # 多次检测间隔多久 (default: 30s)
timeout: 10s # 超时时间 (default: 30s)
retries: 5 # 尝试次数(default: 3)
start_period: 5s # 容器启动后多久开始检测 (default: 0s)

sleepnginx2:
image: nginx
ports:
- "13002:80"
restart: always
depends_on:
sleepnginx1:
condition: service_healthy
container_name: sleepnginx2

注意,此处的healthcheck: test curl -fs http://127.0.0.1:80 地址端口均在容器内执行,端口为映射前的端口

查看启动结果如下(60秒内)

1
2
3
4
5
6
7
8
9
(base) [root@localhost sleepNginx]# docker-compose -f sleepnginx-compose-v3.yml  up -d 
[+] Running 2/2
⠿ Container sleepnginx1 Started 0.4s
⠿ Container sleepnginx2 Created 0.0s

(base) [root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
aa714e257691 nginx "/docker-entrypoint.…" 15 seconds ago Created sleepnginx2
42b98514a96f nginx "/docker-entrypoint.…" 15 seconds ago Up 15 seconds (health: starting) 0.0.0.0:13001->80/tcp, :::13001->80/tcp sleepnginx1

查看启动结果如下(60秒外)

1
2
3
4
5
6
7
8
9
(base) [root@localhost sleepNginx]# docker-compose -f sleepnginx-compose-v3.yml  up -d 
[+] Running 2/2
⠿ Container sleepnginx1 Running 0.0s
⠿ Container sleepnginx2 Started 0.4s

(base) [root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
aa714e257691 nginx "/docker-entrypoint.…" About a minute ago Up 15 seconds 0.0.0.0:13002->80/tcp, :::13002->80/tcp sleepnginx2
42b98514a96f nginx "/docker-entrypoint.…" About a minute ago Up About a minute (healthy) 0.0.0.0:13001->80/tcp, :::13001->80/tcp sleepnginx1

及 sleepnginx2 依赖 sleepnginx1 的健康检查成功后进行启动

三、验证过程中出现的问题记录

延迟命令中,为什么要加 nginx -g “daemon off;”

在常规的虚机上,nginx默认是以守护进程来运行的(daemon on),在后台默默提供服务,同时部署多个ngxin服务也不会相互干扰。
在容器环境,one container == one process,容器要能持续运行,必须有且仅有一个前台进程,所以对nginx进程容器化,需要将nginx转为前后进程( daemon off)。
能顺利执行docker run nginx,启动容器并不退出,是因为nginx的官方镜像Dockerfile 已经指定 nginx -g “daemon off;”
再回到上文,为什么此处脚本中要加”nginx -g “daemon off;” 呢?
CMD在执行的shell脚本[“sh”, “replace_api_url.sh”],实际上是启动shell进程来执行,脚本执行完,进程就会退出(此时nginx还是一摊死的物理文件),
所以要在脚本内再添加nginx -g “daemon off;” 将整个shell进程转为前台能持续运行的进程。

容器=进程, 有且仅有一个前台能持续运行的进程
nginx 默认是后台守护进程的形式运行, nginx -g “daemon off;” 以前台形式持续运行。

depends_on 配置后出现报错

在配置 depends_on 过程中出现报错

1
2
3
depends_on:
sleepnginx1:
condition: service_healthy

报错如下

1
services.sleepnginx1.depends_on contains an invalid type, it should be an array

解决办法 升级docker-compose 版本支持 version: “3.9”

在Docker compose规范的版本3中被删除,但随后在版本3.9中重新引入。
参见https://github.com/compose-spec/compose-spec/blob/master/spec.md#long-syntax-1

升级方法如下

1
2
3
rm /usr/local/bin/docker-compose
sudo curl -L "https://github.com/docker/compose/releases/download/v2.2.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

一辈子很短,努力的做好两件事就好;
第一件事是热爱生活,好好的去爱身边的人;
第二件事是努力学习,在工作中取得不一样的成绩,实现自己的价值,而不是仅仅为了赚钱。

继开 wechat
欢迎加我的微信,共同交流技术