怎么用 Shell 脚本实现 Docker 容器自动重启监控?

文章导读
优先使用 Docker 自带的重启策略,Shell 脚本仅建议在需要发送告警或进行复杂健康检查时使用。
📋 目录
  1. 环境准备与权限配置
  2. 命令速用版
  3. 进阶:防止重启风暴与健康检查
  4. 分步处理
  5. 怎么验证是否生效
  6. 常见坑
  7. 参考来源
A A

优先使用 Docker 自带的重启策略,Shell 脚本仅建议在需要发送告警或进行复杂健康检查时使用。

先说结论:大部分场景下 Docker 内置的 restart 策略已足够,脚本方案适合需要额外通知或自定义健康判断的场景。

  • 适合:需要邮件/短信告警、内置策略无法覆盖的假死状态检测。
  • 先准备:确认容器名称或 ID、准备好 Docker 命令执行权限、配置日志文件写入权限。
  • 验收:手动停止容器后观察脚本是否触发重启并记录日志,验证防风暴逻辑是否生效。

环境准备与权限配置

脚本若需写入系统日志目录(如 /var/log),默认普通用户无权限,会导致脚本静默失败。请执行以下命令创建日志文件并授权:

sudo touch /var/log/docker-monitor.log
sudo chown $(whoami):$(whoami) /var/log/docker-monitor.log
chmod 644 /var/log/docker-monitor.log

若不便配置 sudo,建议将日志路径改为用户主目录,如 ~/docker-monitor.log

命令速用版

以下脚本可保存为 monitor.sh,用于检查容器状态并在退出时重启。注意使用 Docker 命令绝对路径:

#!/bin/bash
DOCKER_PATH="/usr/bin/docker"
CONTAINER_NAME="your_container_name"
LOG_FILE="/var/log/docker-monitor.log"

STATUS=$($DOCKER_PATH inspect -f '{{.State.Status}}' $CONTAINER_NAME 2>/dev/null)

if [ "$STATUS" == "exited" ]; then
    echo "$(date): Container $CONTAINER_NAME exited, restarting..." >> $LOG_FILE
    $DOCKER_PATH start $CONTAINER_NAME
fi

配合 crontab 每分钟执行一次:

* * * * * /path/to/monitor.sh

进阶:防止重启风暴与健康检查

基础脚本存在风险:若容器因配置错误启动后立即退出,脚本会无限循环重启,消耗系统资源。此外,仅检查 Status 无法发现服务假死。建议使用以下进阶脚本:

#!/bin/bash
DOCKER_PATH="/usr/bin/docker"
CONTAINER_NAME="your_container_name"
LOG_FILE="/var/log/docker-monitor.log"
LAST_RESTART_FILE="/tmp/docker_last_restart_$CONTAINER_NAME"
HEALTH_URL="http://localhost:8080/health"
RESTART_INTERVAL=300

CURRENT_TIME=$(date +%s)

# 1. 防止重启风暴:检查上次重启时间
if [ -f "$LAST_RESTART_FILE" ]; then
    LAST_TIME=$(cat "$LAST_RESTART_FILE")
    if [ $((CURRENT_TIME - LAST_TIME)) -lt $RESTART_INTERVAL ]; then
        echo "$(date): Skip restart, too frequent." >> $LOG_FILE
        exit 0
    fi
fi

# 2. 健康检查:优先检测业务接口,其次检测容器状态
HEALTH_CHECK=$(curl -sf `--connect-timeout` 5 "$HEALTH_URL" 2>/dev/null)
STATUS=$($DOCKER_PATH inspect -f '{{.State.Status}}' $CONTAINER_NAME 2>/dev/null)

if [ -z "$HEALTH_CHECK" ] || [ "$STATUS" == "exited" ]; then
    echo "$(date): Container $CONTAINER_NAME unhealthy or exited, restarting..." >> $LOG_FILE
    $DOCKER_PATH start $CONTAINER_NAME
    echo "$CURRENT_TIME" > "$LAST_RESTART_FILE"
fi

分步处理

1. 确认内置策略是否足够
先检查容器启动参数是否已包含重启策略。运行 docker inspect <容器名> | grep RestartPolicy。如果已有 always 且业务无假死问题,无需额外脚本。

2. 编写监控脚本
创建脚本文件,确保逻辑中包含状态判断。进阶脚本中增加了时间戳判断逻辑,避免短时间内重复重启。

3. 配置定时任务
使用 crontab -e 添加定时任务。注意 cron 环境变量较少,脚本中必须使用命令绝对路径(如 /usr/bin/docker),可通过 which docker 查询。

怎么用 Shell 脚本实现 Docker 容器自动重启监控?

4. 权限处理
确保执行 cron 的用户有权限操作 Docker。通常需要将用户加入 docker 组:sudo usermod -aG docker $USER,登录后生效。

怎么验证是否生效

1. 手动测试
执行 docker stop <容器名> 停止容器,等待脚本执行周期(如 1 分钟),然后运行 docker ps 查看容器是否重新运行。

2. 检查日志
查看脚本中定义的日志文件,确认是否有重启记录和时间戳:tail -f /var/log/docker-monitor.log

3. 验证防风暴逻辑
连续手动停止容器两次,间隔小于 5 分钟。检查日志中是否出现 Skip restart, too frequent 记录,确认第二次未触发重启。

4. 查看 Cron 日志
/var/log/cron/var/log/syslog 中确认定时任务是否正常触发,排除 cron 服务本身的问题。

常见坑

1. 重启风暴
容器启动后立即退出会导致无限循环。解决方案:在脚本中记录上次重启时间戳,若间隔小于设定值(如 300 秒)则跳过本次重启。

2. 环境变量缺失
Cron 执行环境与终端不同,常因找不到 docker 命令而失败。务必在脚本中声明 PATH 或使用命令绝对路径。

3. 权限不足
普通用户无法访问 Docker Socket 或写入系统日志。如果遇到 permission denied,需检查用户是否在 docker 组,或修改日志文件归属。

4. 忽略健康状态
仅检查 Statusexited 无法发现服务假死。解决方案:结合 curl 请求业务接口,无响应时视为故障。

参考来源

  • Docker Official Documentation, "Restart Policies (`--restart`)", https://docs.docker.com/engine/reference/run/#restart-policies-`--restart`
  • Docker Official Documentation, "docker inspect", https://docs.docker.com/engine/reference/commandline/inspect/