Shell脚本如何检查容器文件变化

wen 实用脚本 1

Shell脚本如何检查容器文件变化:5种实用方法与自动监控方案

目录导读

  1. 为什么需要监控容器文件变化?
  2. 使用 diff 命令对比时间戳
  3. 利用 inotify 实现实时检测
  4. 通过 md5sum/sha256sum 校验文件完整性
  5. 结合 find 与 stat 捕获元数据变化
  6. 使用定时任务(cron)轮询监控
  7. 常见问题与答疑
  8. 完整脚本示例与最佳实践

为什么需要监控容器文件变化?

容器化环境(如 Docker、Kubernetes)中,文件变更往往是运维事故的导火索:配置文件被意外覆盖、日志文件爆满、恶意脚本写入,或者 mount 卷同步失败,通过 Shell 脚本监控容器文件变化,可以实现:

Shell脚本如何检查容器文件变化

  • 自动化警报:文件被修改时立即通知运维人员
  • 安全审计:追踪容器内敏感目录(如 /etc/var/www)的每一次改动
  • 数据同步验证:确认绑定挂载(bind mount)的文件是否按预期同步
  • 应用热更新:配置文件变更时触发容器重启或重加载

关键原则:脚本应运行在宿主机或专门的监控容器中,避免侵入被监控容器。


方法一:使用 diff 命令对比时间戳

适用场景:简单检查文件是否被修改,无需实时性。

核心思路

保存文件的上次修改时间(stat -c %Y)到本地缓存文件,每次轮询时对比当前时间戳。

脚本片段

#!/bin/bash
CONTAINER_NAME="my_app"
FILE_TO_WATCH="/app/config.yml"
CACHE_FILE="/tmp/.file_ts_$CONTAINER_NAME"
# 获取当前时间戳
CUR_TS=$(docker exec $CONTAINER_NAME stat -c %Y "$FILE_TO_WATCH" 2>/dev/null)
if [ -z "$CUR_TS" ]; then
    echo "ERROR: 无法读取容器内文件"
    exit 1
fi
# 对比缓存
if [ -f "$CACHE_FILE" ]; then
    OLD_TS=$(cat "$CACHE_FILE")
    if [ "$CUR_TS" -ne "$OLD_TS" ]; then
        echo "ALERT: 文件 $FILE_TO_WATCH 已被修改!"
    fi
else
    echo "INFO: 首次记录时间戳"
fi
# 更新缓存
echo "$CUR_TS" > "$CACHE_FILE"

局限:只能检测文件修改时间变化,无法捕捉内容被还原但时间戳未变的场景。


方法二:利用 inotify 实现实时检测

适用场景:需要秒级或毫秒级响应的重要文件监控。

核心思路

通过 inotifywait(需安装 inotify-tools)监听容器内文件或目录的 modifydeletecreate 事件。

重要注意点

Docker 容器默认不支持 inotify 接口直接暴露给宿主机,需采用以下两种变通方案:

  1. 在容器内安装 inotify-tools,挂载 socket 通信
  2. 使用 docker exec 间接触发(性能较差,推荐方案3)

推荐方案:宿主机的 inotify 挂载目录监控

如果你将宿主机目录绑定挂载到容器内(如 -v /data:/data),可以直接在宿主机侧使用 inotify:

#!/bin/bash
WATCH_DIR="/data/config"  # 宿主机挂载点
inotifywait -m -r -e modify,create,delete,move "$WATCH_DIR" | while read path action file; do
    echo "检测到变更: $action on $path$file"
    # 调用重新加载脚本
    docker exec my_container app_reload
done

优势:不依赖容器内工具,性能最优。


方法三:通过 md5sum/sha256sum 校验文件完整性

适用场景:防篡改审计、配置文件内容校验。

核心思路

预先计算文件哈希值并持久化,每次检查时重新计算并对比,这比时间戳更准确。

脚本片段

#!/bin/bash
CONTAINER="web_app"
HASH_FILE="/tmp/.hash_$CONTAINER"
declare -A TARGET_FILES=(
    "/etc/nginx/nginx.conf"
    "/var/www/html/index.html"
)
# 生成当前哈希快照
CURRENT_HASH=$(docker exec $CONTAINER sh -c "md5sum ${TARGET_FILES[@]}" 2>/dev/null)
# 如果没有历史记录,初始化
if [ ! -f "$HASH_FILE" ]; then
    echo "$CURRENT_HASH" > "$HASH_FILE"
    echo "首次哈希已记录"
    exit 0
fi
# 对比
if diff <(cat "$HASH_FILE") <(echo "$CURRENT_HASH") > /dev/null 2>&1; then
    echo "文件无变化"
else
    echo "WARNING: 文件摘要已变更!"
    diff <(cat "$HASH_FILE") <(echo "$CURRENT_HASH")
    # 更新哈希文件
    echo "$CURRENT_HASH" > "$HASH_FILE"
fi

性能提示:大文件(>100MB)使用 sha256sum 较慢,可改用 ckhash 或只对文件头尾块做部分校验。


方法四:结合 find 与 stat 捕获元数据变化

适用场景:批量监控目录下所有文件,包括新增/删除。

核心思路

使用 find -newerstat 组合,将文件列表及其元数据(inode、权限、大小)保存为快照文件。

脚本片段

#!/bin/bash
CONTAINER="data_worker"
MOUNT_POINT="/mnt/storage"
SNAPSHOT="/tmp/snap_$CONTAINER.txt"
# 获取当前文件列表及元数据
docker exec $CONTAINER find "$MOUNT_POINT" -type f -exec stat -c "%n %i %s %Y" {} \; 2>/dev/null | sort > /tmp/current_snap.txt
# 对比
if [ -f "$SNAPSHOT" ]; then
    DIFF_COUNT=$(diff "$SNAPSHOT" /tmp/current_snap.txt | wc -l)
    if [ "$DIFF_COUNT" -gt 0 ]; then
        echo "检测到 $DIFF_COUNT 个文件差异:"
        diff "$SNAPSHOT" /tmp/current_snap.txt | head -20
    else
        echo "无变化"
    fi
fi
mv /tmp/current_snap.txt "$SNAPSHOT"

注意:容器内 find 命令可能受资源限制,对超大规模目录建议分片处理。


方法五:使用定时任务(cron)轮询监控

适用场景:低频检查(每N分钟/小时),资源消耗最低。

配置步骤

  1. 将上述任一脚本保存为 /usr/local/bin/check_container_files.sh
  2. 添加 cron 任务:
    # 每5分钟执行一次
    */5 * * * * /usr/local/bin/check_container_files.sh

增强可靠性

  • 结果持久化:将检测结果写入日志文件 /var/log/container_file_monitor.log
  • 通知集成:在变更时触发 curl 调用 webhook 或发送邮件
MAIL_SCRIPT="/root/send_alert.sh"
if [ "$CHANGED" = true ]; then
    /root/send_alert.sh "容器$CONTAINER 文件发生变更"
fi

常见问题与答疑

Q1:脚本运行失败提示“docker exec 权限不足”怎么办?

A:确保脚本以 root 用户运行,或使用 docker exec -u root ...,在生产环境建议使用 Docker socket 权限控制而非直接暴露 root。

Q2:监控大量文件(>10000)时内存飙升?

A:默认的 inotifywait -m -r 会为每个子目录分配 watch,建议改为监控顶级目录,或者使用 --exclude 过滤无意义路径。

Q3:容器被重启后,时间戳缓存失效如何避免?

A:将缓存文件写入持久化位置(如 /data/monitor_cache),并在容器启动时自动计算初始快照。

Q4:如何检测文件是否被替换为新文件(inode变化)?

A:对比 stat -c %i(inode号),inode 变了但时间戳相同,说明文件被替换而非修改。


完整脚本示例与最佳实践

下面是一个生产可用的综合脚本,集成了多种检测机制:

#!/bin/bash
# container_file_monitor.sh - 综合文件监控脚本
CONTAINER_NAME="production_app"
WATCH_PATHS=("/etc" "/var/www" "/app/config")
ALERT_WEBHOOK="https://hooks.example.com/alerts"
LOG_FILE="/var/log/container_file_monitor.log"
monitor() {
    local container="$1"
    local path="$2"
    local state_file="/tmp/.monitor_${container}_$(echo $path | tr '/' '_')"
    # 使用 md5sum 一次性分析整个路径结构
    docker exec "$container" find "$path" -type f -exec md5sum {} \; 2>/dev/null | sort > /tmp/current_state.txt
    if [ -f "$state_file" ]; then
        if ! cmp -s "$state_file" /tmp/current_state.txt; then
            echo "$(date '+%Y-%m-%d %H:%M:%S') [ALERT] $path 发生变更" >> "$LOG_FILE"
            curl -s -X POST -d "container=$container&path=$path" "$ALERT_WEBHOOK" &>/dev/null
        fi
    fi
    mv /tmp/current_state.txt "$state_file"
}
# 主循环
for path in "${WATCH_PATHS[@]}"; do
    monitor "$CONTAINER_NAME" "$path" &
done
wait

部署建议

  • 使用 chmod +x 赋予执行权限
  • 设置 cron 每2分钟执行一次
  • 配合 systemd timer 实现更精细的间隔控制

选择哪种 Shell脚本检查容器文件变化 的方法,主要取决于你的需求:

需求场景 推荐方法 优先级
低负载、不频繁的审计 diff + 时间戳
简单快速的内容验证 md5sum 哈希法
实时、高精度的监控 inotify 挂载目录
超大规模文件遍历 find + stat 快照
合规性要求严格 sha256 完整性校验

无论选择哪种方式,建议始终配合 日志记录告警通知,形成完整的监控闭环,如果拥有 Kubernetes 集群环境,还可以使用 Admission Controller 或 Kyverno 实现更底层的文件变更阻断。

抱歉,评论功能暂时关闭!