Atlantis
GitHub Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

2. 使用脚本管理Docker容器

1. 前言

上面是一个 Docker 容器的生命周期,Docker 容器就像 可执行文件 + 临时存储,在容器创建后到删除前之间,可以重复地启动、停止和暂停,如果将这些操作形成规范的脚本,就有类似 systemd 管理进程的体验。

下面记录下笔者常用的容器管理脚本。

2. 管理Docker容器的脚本

这里约定:

  1. 给每个 Docker 容器分配一个目录,该目录下存放管理容器的脚本,并保存容器需要挂载的目录和文件
  2. 目录内有以下管理脚本:
    1. run.sh:负责启动/重启容器
    2. reload.sh:重载容器内的应用程序,这个需要应用本身支持重载才行
    3. stop.sh:停止容器

2.1 启动和重启容器

docker run 命令实际包含两个部分:

  1. docker create:
    1. 拉取镜像:检查镜像,若本地不存在容器镜像,则从镜像仓库拉取
    2. 创建容器:根据命令行参数和镜像创建容器,分配容器所需的资源
  2. docker start:启动容器,执行镜像中预设的或者命令行参数中指定的启动命令

而通常情况下运行一个容器会配置哪些参数?笔者常用的参数如下:

  1. 镜像版本:例如 caddy:2,它属于 dockerhub 的 Official Images,完整链接是:docker.io/library/caddy:2
  2. 容器名:例如 caddy
  3. 是否后台运行-d 标志位可以让容器后台运行,未设置时默认在当前 shell 进程下启动子进程前台运行容器
  4. 重启策略--restart 参数可选的值有
    1. no:默认选项,如果容器退出,不会尝试重新启动
    2. always:总是重新启动容器。无论容器是正常退出还是异常退出,都会尝试重新启动
    3. on-failure:在容器以非零状态退出时重新启动。你可以使用 –restart on-failure: 来指定最大的重试次数,默认为无限次重试
    4. unless-stopped:除非显式停止容器(使用 docker stop命令),否则总是重新启动容器
  5. 网络类型--network 参数可选的值有
    1. bridge:默认网络模式,将容器连接到 docker0 网桥,这时可以使用 -p <host_port>:<container_port> 来暴露容器端口
    2. host:使用宿主机网络
    3. none:不使用网络
    4. 自定义网络:使用用户定义的网络,自定义网络可以通过 docker network create 命令创建
  6. 环境变量:使用 -e <key>=<value>
  7. 挂载目录或文件:使用 -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 引用当前目录下的文件和子目录,方便按目录管理不同的容器;

最后由于我们最常变动的就是镜像版本,这里将镜像版本和容器名提取为变量,简单修改就可以作为其他容器的启动脚本使用。

2.2 重载容器内的应用程序

有一种常见的场景,我们需要在不重启进程的情况下通知容器内的进程重载配置文件,例如已经运行了一个 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 信号。

2.3 停止容器

使用 docker stop 命令即可停止容器,例如停止 caddy 容器:

#!/bin/bash

# 容器名称
container="caddy"

# 如果容器未处于停止状态,则停止容器
if [ "$(docker ps -q -f name=$container)" ]; then
    docker stop $container
fi

将上述命令保存成 stop.sh 脚本存放在目录下可以实现停止容器的功能。

当然上述的脚本也可以整合成一个,按命令行传入参数控制执行哪一个操作。