Shell脚本如何配置容器存储驱动

wen 实用脚本 1

Shell脚本配置容器存储驱动:从原理到自动化部署

目录导读

  1. 存储驱动核心概念

    Shell脚本如何配置容器存储驱动

    • 什么是容器存储驱动?
    • OverlayFS、Device Mapper、Btrfs、ZFS 对比分析
    • 驱动选择对性能与稳定性的影响
  2. Shell脚本配置实战

    • 检测当前存储驱动
    • 修改容器运行时配置(Docker/Containerd)
    • 自动化切换存储驱动脚本编写
  3. 常见问题与问答

    • Q1:为什么改完驱动后容器无法启动?
    • Q2:不同Linux发行版对驱动支持有何差异?
    • Q3:如何通过脚本批量配置多节点?
  4. 总结与调优建议


存储驱动核心概念

容器存储驱动是容器运行时(如Docker、Containerd)与主机文件系统之间的抽象层,负责管理镜像层、容器层的数据读写,常见的驱动包括:

驱动名 内核要求 适用场景 性能特点
Overlay2 Linux kernel ≥4.0 默认推荐 页缓存共享,性能极佳
Device Mapper 需配置direct-lvm 老系统兼容 性能依赖底层配置
Btrfs 需Btrfs分区 快照与压缩场景 原生支持子卷
ZFS 需安装ZFS模块 数据完整性要求高 稳定但内存消耗大

选择原则

  • 生产环境优先使用Overlay2(Docker 20.10+默认)
  • 避免使用Device Mapper的loopback模式(性能极差)
  • 特殊需求(如文件系统快照管理)可选用Btrfs

Shell脚本配置实战

检测当前存储驱动

#!/bin/bash
# 检测Docker当前使用的存储驱动
docker info --format '{{.Driver}}'
# 检测Containerd的驱动(需安装ctr或crictl)
crictl info | grep storageDriver

修改容器运行时配置

方案A:直接修改Docker daemon.json
// /etc/docker/daemon.json
{
  "storage-driver": "overlay2",
  "storage-opts": [
    "overlay2.override_kernel_check=true"
  ]
}

Shell脚本实现:

#!/bin/bash
CONFIG_FILE="/etc/docker/daemon.json"
# 备份原配置
cp $CONFIG_FILE ${CONFIG_FILE}.bak
# 写入新配置(使用临时文件避免残留)
cat > /tmp/daemon_temp.json << EOF
{
  "storage-driver": "overlay2",
  "storage-opts": ["overlay2.override_kernel_check=true"],
  "exec-opts": ["native.cgroupdriver=systemd"]
}
EOF
mv /tmp/daemon_temp.json $CONFIG_FILE
systemctl restart docker
方案B:配置Containerd驱动
#!/bin/bash
# 修改containerd默认存储驱动为overlayfs
sed -i 's/storage_driver = ".*"/storage_driver = "overlayfs"/' /etc/containerd/config.toml
systemctl restart containerd

自动化切换存储驱动脚本(完整版)

#!/bin/bash
# 脚本名称:switch_storage_driver.sh
# 功能:批量检测并切换容器存储驱动
set -euo pipefail
LOG_FILE="/var/log/storage_driver_switch.log"
BACKUP_DIR="/etc/container_driver_backup_$(date +%Y%m%d%H%M)"
TARGET_DRIVER="${1:-overlay2}"  # 默认切换为overlay2
# 颜色输出函数
log_info() { echo -e "\033[36m[INFO]\033[0m $(date) - $*" | tee -a $LOG_FILE; }
log_warn() { echo -e "\033[33m[WARN]\033[0m $(date) - $*" | tee -a $LOG_FILE; }
log_err()  { echo -e "\033[31m[ERROR]\033[0m $(date) - $*" | tee -a $LOG_FILE; }
# 检查当前运行时
check_driver() {
    if command -v docker &> /dev/null; then
        CURRENT_DRIVER=$(docker info --format '{{.Driver}}' 2>/dev/null || echo "unknown")
        RUNTIME="docker"
    elif command -v containerd &> /dev/null; then
        CURRENT_DRIVER=$(containerd config dump 2>/dev/null | grep -i storage | head -1 | awk '{print $3}' || echo "unknown")
        RUNTIME="containerd"
    else
        log_err "未检测到容器运行时(Docker/Containerd)"
        exit 1
    fi
    log_info "当前运行时: ${RUNTIME}, 当前驱动: ${CURRENT_DRIVER}"
}
# 备份配置
backup_config() {
    mkdir -p $BACKUP_DIR
    case $RUNTIME in
        docker)
            cp -r /etc/docker $BACKUP_DIR/
            cp -r /var/lib/docker $BACKUP_DIR/ 2>/dev/null || true
            ;;
        containerd)
            cp /etc/containerd/config.toml $BACKUP_DIR/
            ;;
    esac
    log_info "配置已备份至: $BACKUP_DIR"
}
# 切换驱动
switch_driver() {
    case $RUNTIME in
        docker)
            # 验证内核支持
            if ! lsmod | grep -q overlay; then
                modprobe overlay || log_warn "overlay内核模块未加载"
            fi
            cat > /etc/docker/daemon.json << EOF
{
  "storage-driver": "${TARGET_DRIVER}",
  "storage-opts": ["${TARGET_DRIVER}.override_kernel_check=true"],
  "exec-opts": ["native.cgroupdriver=systemd"]
}
EOF
            systemctl daemon-reload
            systemctl restart docker
            ;;
        containerd)
            sed -i "s/storage_driver = .*/storage_driver = \"${TARGET_DRIVER}\"/" /etc/containerd/config.toml
            systemctl restart containerd
            ;;
    esac
    sleep 3
    log_info "驱动切换至: ${TARGET_DRIVER}"
}
# 验证结果
verify_switch() {
    if [ "$RUNTIME" == "docker" ]; then
        NEW_DRIVER=$(docker info --format '{{.Driver}}')
    else
        NEW_DRIVER=$(crictl info | grep storageDriver | awk '{print $2}')
    fi
    if [ "$NEW_DRIVER" == "$TARGET_DRIVER" ]; then
        log_info "✅ 驱动切换成功!当前:$NEW_DRIVER"
    else
        log_err "❌ 驱动切换失败,当前仍为:$NEW_DRIVER"
        log_warn "如需回滚,请执行:rm -rf /etc/docker && cp -r ${BACKUP_DIR}/docker /etc/"
        exit 2
    fi
}
# 执行流程
check_driver
backup_config
switch_driver
verify_switch

使用方法

chmod +x switch_storage_driver.sh
sudo ./switch_storage_driver.sh overlay2  # 切换为overlay2

常见问题与问答

Q1:为什么改完驱动后容器无法启动?

典型原因

  1. 容器镜像缓存与驱动不兼容:执行 docker system prune -a 清理缓存(注意会删除所有非运行容器)。
  2. 内核模块未加载:运行 modprobe overlay 加载OverlayFS模块。
  3. 配置文件权限异常:确保 /etc/docker/daemon.json 属主为root,权限644。

诊断脚本

#!/bin/bash
# 驱动切换后问题排查脚本
echo "=== 1. 内核模块检查 ==="
lsmod | grep -E "overlay|dm_thin"
echo "=== 2. 配置文件解析 ==="
cat /etc/docker/daemon.json | python3 -m json.tool 2>/dev/null || echo "JSON格式错误"
echo "=== 3. 日志检查 ==="
journalctl -u docker --since "5 min ago" | grep -i error | head -10

Q2:不同Linux发行版对驱动支持有何差异?

发行版 默认驱动 注意事项
Ubuntu 20.04+ Overlay2 直接支持,无需额外配置
CentOS 7.x Device Mapper (loopback) 必须手动切换至Overlay2或配置direct-lvm
Debian 11+ Overlay2 与Docker官方一致
RHEL 8+ Overlay2 需确保容器运行时版本≥20.10
Tumbleweed Btrfs 可同时支持Overlay2

关键提示

  • CentOS 7使用Overlay2需安装 container-selinux 最新版。
  • 低版本内核(如3.10)不支持Overlay2,需升级内核或使用Device Mapper。

Q3:如何通过脚本批量配置多节点?

解决方案:使用Ansible或自定义SSH批量推送脚本:

#!/bin/bash
# 批量切换脚本示例
NODES=("192.168.1.101" "192.168.1.102" "192.168.1.103")
USER="root"
for node in "${NODES[@]}"; do
    echo "=== 处理节点: $node ==="
    scp switch_storage_driver.sh ${USER}@${node}:/tmp/
    ssh ${USER}@${node} "bash /tmp/switch_storage_driver.sh overlay2"
    ssh ${USER}@${node} "systemctl status docker --no-pager | head -5"
done

注意事项

  • 批量操作前需确认所有节点容器运行时版本一致。
  • 建议分批次执行(每次3-5个节点),避免大规模中断。

总结与调优建议

核心要点

  1. 配置优先级:生产环境始终选择Overlay2,除非有明确的文件系统特性需求。
  2. 备份至上:任何驱动修改前,备份 /etc/docker/var/lib/docker(注意数据量大的节点)。
  3. 自动化脚本:将上述脚本纳入CI/CD流水线,实现环境一致性。
  4. 监控预警:切换后关注 docker info 中的 WARNING: No swap limit support 等提示。

未来演进

  • CSI驱动:Kubernetes 1.28+已原生支持容器存储接口(CSI),可绕过容器运行时驱动限制。
  • PASST模式:新版本Docker支持绕过存储层直接访问块设备,适合高性能IO场景。

最终建议:在测试环境充分验证脚本逻辑后,再推广至生产环境。 环境差异可能导致脚本需要细微调整(如systemd服务名、配置文件路径),请根据实际检查调整。

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