2. 使用脚本管理Docker容器
上面是一个 Docker 容器的生命周期,Docker 容器就像 可执行文件 + 临时存储
,在容器创建后到删除前之间,可以重复地启动、停止和暂停,如果将这些操作形成规范的脚本,就有类似 systemd 管理进程的体验。
下面记录下我常用的容器管理脚本。
这里约定:
- 给每个 Docker 容器分配一个目录,该目录下存放管理容器的脚本,并保存容器需要挂载的目录和文件
- 目录内有以下管理脚本:
- run.sh:负责启动/重启容器
- reload.sh:重载容器内的应用程序,这个需要应用本身支持重载才行
- stop.sh:停止容器
docker run
命令实际包含两个部分:
- docker create:
- 拉取镜像:检查镜像,若本地不存在容器镜像,则从镜像仓库拉取
- 创建容器:根据命令行参数和镜像创建容器,分配容器所需的资源
- docker start:启动容器,执行镜像中预设的或者命令行参数中指定的启动命令
而通常情况下运行一个容器会配置哪些参数?我常用的参数如下:
- 镜像版本:例如 caddy:2,它属于 dockerhub 的 Official Images,完整链接是:docker.io/library/caddy:2
- 容器名:例如 caddy
- 是否后台运行:
-d
标志位可以让容器后台运行,未设置时默认在当前 shell 进程下启动子进程前台运行容器 - 重启策略:
--restart
参数可选的值有- no:默认选项,如果容器退出,不会尝试重新启动
- always:总是重新启动容器。无论容器是正常退出还是异常退出,都会尝试重新启动
- on-failure:在容器以非零状态退出时重新启动。你可以使用 –restart on-failure:
来指定最大的重试次数,默认为无限次重试 - unless-stopped:除非显式停止容器(使用 docker stop命令),否则总是重新启动容器
- 网络类型:
--network
参数可选的值有- bridge:默认网络模式,将容器连接到 docker0 网桥,这时可以使用
-p <host_port>:<container_port>
来暴露容器端口 - host:使用宿主机网络
- none:不使用网络
- 自定义网络:使用用户定义的网络,自定义网络可以通过 docker network create 命令创建
- bridge:默认网络模式,将容器连接到 docker0 网桥,这时可以使用
- 环境变量:使用
-e <key>=<value>
- 挂载目录或文件:使用
-v <host_path>:<container_path>
其他参数可以从 docker run --help
输出的帮助文档获取,根据以上参数可以形成一个 run.sh 脚本后台运行 caddy,另外要求重启宿主机后 caddy 能自动重启,脚本如下:
#!/bin/bash
# 容器镜像
image="caddy:2"
# 容器名称
container="caddy"
# 检查容器是否存在
if [ "$(docker ps -a -q -f name=$container)" ]; then
# 如果容器未处于停止状态,则停止容器
if [ "$(docker ps -q -f name=$container)" ]; then
docker stop $container
fi
# 删除容器
docker rm $container
fi
# 启动容器: 容器名称-> 是否后台运行 -> 重启策略 -> 网络类型 -> 环境变量 -> 挂载项 -> 镜像名
docker run --name $container -d \
--restart unless-stopped \
--network bridge -p 80:80 -p 443:443 \
-e KEY=VALUE \
-v $PWD/Caddyfile:/etc/caddy/Caddyfile \
-v $PWD/root:/usr/share/caddy \
-v $PWD/log:/var/log \
$image
首先容器的大部分参数在容器创建后是无法修改的,这个脚本会根据容器名作为唯一标识,先删除已存在容器,再创建新容器;
其次运行容器时,-v
参数中的宿主机目录或文件路径可以是环境变量,这里使用 $PWD
引用当前目录下的文件和子目录,方便按目录管理不同的容器;
最后由于我们最常变动的就是镜像版本,这里将镜像版本和容器名提取为变量,简单修改就可以作为其他容器的启动脚本使用。
有一种常见的场景,我们需要在不重启进程的情况下通知容器内的进程重载配置文件,例如已经运行了一个 nginx 或者 caddy 容器,它的配置文件发生变更,添加了一个的反向代理配置。
对于 nginx 容器来说,我们只需要进入容器内执行 nginx -s reload,即可向 nginx 进程发送 SIGHUP 信号,告诉 nginx 进程重新加载配置文件,如下:
#!/bin/bash
# 容器名称
container="nginx"
# 重载命令
cmd="nginx -s reload"
# 执行重载
docker exec $container $cmd
对于 caddy 容器来说,对应的命令如下:
#!/bin/bash
# 容器名称
container="caddy"
# 重载命令
cmd="caddy reload --config /etc/caddy/Caddyfile --force"
# 执行重载
docker exec $container $cmd
将上述命令保存成 reload.sh 脚本存放在目录下可以实现重载容器内应用的功能。
如果是我们自己开发的程序需要实现类似的重载功能,可以考虑启动程序后将进程 ID 保存在一个固定位置,并监听 SIGHUP 信号,在接收到信号时重载配置文件;然后为程序的添加一个 reload 子命令,执行 reload 时就读取文件获取进程 ID,并向该进程发送 SIGHUP 信号。
使用 docker stop
命令即可停止容器,例如停止 caddy 容器:
#!/bin/bash
# 容器名称
container="caddy"
# 如果容器未处于停止状态,则停止容器
if [ "$(docker ps -q -f name=$container)" ]; then
docker stop $container
fi
将上述命令保存成 stop.sh 脚本存放在目录下可以实现停止容器的功能。
当然上述的脚本也可以整合成一个,按命令行传入参数控制执行哪一个操作。