Shell脚本如何配置容器补丁更新

wen 实用脚本 1

本文目录导读:

Shell脚本如何配置容器补丁更新

  1. 基础容器更新脚本(适用于测试环境)
  2. 无停机更新脚本(蓝绿部署)
  3. 生产环境滚动更新脚本
  4. 使用Docker Compose的更新脚本
  5. 自动化更新脚本(带通知和监控)
  6. 使用建议
  7. 定时更新脚本(cron示例)

配置容器补丁更新的Shell脚本需要考虑容器运行时的安全性、最小化停机时间和回滚能力,以下是几种常见场景的配置方案:

基础容器更新脚本(适用于测试环境)

#!/bin/bash
# 容器补丁更新脚本 - 基础版
set -euo pipefail
# 配置变量
CONTAINER_NAME="my-app"
IMAGE_NAME="my-registry/my-app"
BACKUP_DIR="/backup/containers"
LOG_FILE="/var/log/container-update.log"
# 日志函数
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
# 创建备份
backup_container() {
    log "开始备份容器 $CONTAINER_NAME"
    mkdir -p "$BACKUP_DIR"
    # 导出当前容器配置
    docker inspect "$CONTAINER_NAME" > "$BACKUP_DIR/${CONTAINER_NAME}_config_$(date +%Y%m%d_%H%M%S).json"
    # 如果容器正在运行,创建镜像备份
    if docker ps -q -f name="$CONTAINER_NAME" > /dev/null 2>&1; then
        docker commit "$CONTAINER_NAME" "${IMAGE_NAME}:backup-$(date +%Y%m%d_%H%M%S)"
        log "容器镜像备份完成"
    fi
}
# 拉取最新镜像
pull_update() {
    log "拉取最新镜像: $IMAGE_NAME:latest"
    docker pull "$IMAGE_NAME:latest"
    if [ $? -eq 0 ]; then
        log "镜像拉取成功"
    else
        log "错误: 镜像拉取失败"
        exit 1
    fi
}
# 执行更新
perform_update() {
    log "停止旧容器: $CONTAINER_NAME"
    docker stop "$CONTAINER_NAME" || true
    docker rm "$CONTAINER_NAME" || true
    log "启动新容器"
    docker run -d \
        --name "$CONTAINER_NAME" \
        --restart unless-stopped \
        -p 8080:80 \
        "$IMAGE_NAME:latest"
    # 检查容器是否成功启动
    sleep 5
    if docker ps -q -f name="$CONTAINER_NAME" > /dev/null 2>&1; then
        log "容器更新成功"
        return 0
    else
        log "错误: 容器启动失败"
        return 1
    fi
}
# 健康检查
health_check() {
    log "执行健康检查..."
    # 替换为实际健康检查命令
    curl -f http://localhost:8080/health || return 1
    # 检查容器日志是否有错误
    docker logs --tail 50 "$CONTAINER_NAME" | grep -i "error" && return 1
    return 0
}
# 回滚函数
rollback() {
    log "执行回滚操作..."
    local backup_image=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep "${IMAGE_NAME}:backup" | sort | tail -1)
    if [ -n "$backup_image" ]; then
        docker stop "$CONTAINER_NAME" || true
        docker rm "$CONTAINER_NAME" || true
        docker run -d \
            --name "$CONTAINER_NAME" \
            --restart unless-stopped \
            -p 8080:80 \
            "$backup_image"
        log "回滚完成,使用镜像: $backup_image"
    else
        log "没有找到可回滚的备份镜像"
        exit 1
    fi
}
# 主执行流程
main() {
    log "开始容器补丁更新流程"
    backup_container
    pull_update
    if perform_update; then
        if health_check; then
            log "更新成功完成"
        else
            log "健康检查失败,执行回滚"
            rollback
        fi
    else
        log "更新失败,执行回滚"
        rollback
    fi
}
# 执行
main

无停机更新脚本(蓝绿部署)

#!/bin/bash
# 蓝绿部署更新脚本
set -euo pipefail
# 配置变量
APP_NAME="my-app"
IMAGE="$1"  # 从参数获取新镜像
BLUE_PORT=8081
GREEN_PORT=8082
ACTIVE_COLOR_FILE="/tmp/active_color.txt"
# 获取当前活跃环境
get_active_color() {
    if [ -f "$ACTIVE_COLOR_FILE" ]; then
        cat "$ACTIVE_COLOR_FILE"
    else
        echo "blue"
    fi
}
# 切换负载均衡
switch_traffic() {
    local new_color=$1
    local new_port
    if [ "$new_color" = "blue" ]; then
        new_port=$BLUE_PORT
    else
        new_port=$GREEN_PORT
    fi
    # 更新负载均衡配置(示例使用Nginx)
    cat > /etc/nginx/conf.d/${APP_NAME}.conf << EOF
upstream ${APP_NAME} {
    server 127.0.0.1:${new_port};
}
server {
    listen 80;
    server_name ${APP_NAME}.example.com;
    location / {
        proxy_pass http://${APP_NAME};
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
    }
}
EOF
    nginx -s reload
    echo "$new_color" > "$ACTIVE_COLOR_FILE"
    echo "已切换到 $new_color 环境"
}
# 部署新版本到备用环境
deploy_staging() {
    local active_color=$(get_active_color)
    local staging_color
    local staging_port
    # 确定备用环境
    if [ "$active_color" = "blue" ]; then
        staging_color="green"
        staging_port=$GREEN_PORT
    else
        staging_color="blue"
        staging_port=$BLUE_PORT
    fi
    echo "部署到 $staging_color 环境"
    # 启动新版本容器
    docker run -d \
        --name "${APP_NAME}-${staging_color}" \
        -p ${staging_port}:80 \
        "$IMAGE"
    # 等待容器就绪
    sleep 10
    # 验证新版本
    if curl -f http://localhost:${staging_port}/health; then
        echo "新版本验证成功"
        switch_traffic "$staging_color"
        # 停止旧版本
        docker stop "${APP_NAME}-${active_color}" || true
        docker rm "${APP_NAME}-${active_color}" || true
    else
        echo "新版本验证失败,保持当前环境"
        docker stop "${APP_NAME}-${staging_color}" || true
        docker rm "${APP_NAME}-${staging_color}" || true
        exit 1
    fi
}
# 执行部署
deploy_staging

生产环境滚动更新脚本

#!/bin/bash
# 生产环境滚动更新脚本
set -euo pipefail
# 配置
CONTAINER_PREFIX="web-app"
INSTANCE_COUNT=3
IMAGE_NAME="$1"
ROLLBACK_IMAGE="${IMAGE_NAME}:previous"
# 滚动更新函数
rolling_update() {
    local success_count=0
    for i in $(seq 1 $INSTANCE_COUNT); do
        local container_name="${CONTAINER_PREFIX}-${i}"
        echo "更新容器 $container_name"
        # 优雅停止
        docker exec "$container_name" /usr/sbin/nginx -s quit || true
        sleep 2
        # 启动新容器
        docker run -d \
            --name "${container_name}_new" \
            --network app-network \
            "$IMAGE_NAME"
        # 等待就绪
        sleep 5
        # 健康检查
        if docker exec "${container_name}_new" curl -f http://localhost/health; then
            # 切换流量
            docker network disconnect app-network "$container_name" || true
            docker network connect app-network "${container_name}_new"
            # 停止旧容器
            docker stop "$container_name" || true
            docker rm "$container_name" || true
            # 重命名新容器
            docker rename "${container_name}_new" "$container_name"
            ((success_count++))
            echo "容器 $container_name 更新成功"
        else
            echo "容器 ${container_name}_new 健康检查失败"
            docker stop "${container_name}_new" || true
            docker rm "${container_name}_new" || true
            return 1
        fi
    done
    echo "滚动更新完成,成功更新 $success_count/$INSTANCE_COUNT 个实例"
}
# 执行滚动更新
rolling_update

使用Docker Compose的更新脚本

#!/bin/bash
# Docker Compose更新脚本
set -euo pipefail
COMPOSE_FILE="docker-compose.yml"
PROJECT_NAME="my-project"
BACKUP_COMPOSE="docker-compose.backup.yml"
# 备份当前配置
backup_compose() {
    echo "备份当前Docker Compose配置"
    cp "$COMPOSE_FILE" "$BACKUP_COMPOSE"
    # 保存当前镜像标签
    docker-compose images > "images_backup_$(date +%Y%m%d).txt"
}
# 更新服务
update_services() {
    echo "拉取最新镜像"
    docker-compose pull
    echo "重建并启动服务(零停机)"
    docker-compose up -d --no-deps --build service1 service2
    echo "等待服务就绪..."
    sleep 10
    # 健康检查
    for service in service1 service2; do
        if docker-compose ps "$service" | grep -q "Up"; then
            echo "$service 运行正常"
        else
            echo "$service 启动失败,开始回滚"
            rollback
            exit 1
        fi
    done
}
# 回滚函数
rollback() {
    echo "执行回滚..."
    if [ -f "$BACKUP_COMPOSE" ]; then
        cp "$BACKUP_COMPOSE" "$COMPOSE_FILE"
        docker-compose down
        docker-compose up -d
        echo "回滚完成"
    else
        echo "没有可用的备份配置"
        exit 1
    fi
}
# 执行更新
backup_compose
update_services

自动化更新脚本(带通知和监控)

#!/bin/bash
# 完整的自动化更新脚本
set -euo pipefail
# 配置
NOTIFY_EMAIL="admin@example.com"
SLACK_WEBHOOK="https://hooks.slack.com/services/xxx"
PROMETHEUS_URL="http://prometheus:9090"
# 通知函数
notify() {
    local message=$1
    local level=${2:-INFO}
    # 邮件通知
    echo "$message" | mail -s "[$level] 容器更新通知" "$NOTIFY_EMAIL"
    # Slack通知
    curl -X POST -H "Content-Type: application/json" \
        -d "{\"text\": \"[$level] $message\"}" \
        "$SLACK_WEBHOOK" 2>/dev/null || true
}
# 监控指标检查
check_metrics() {
    local query="avg(rate(container_cpu_usage_seconds_total[5m]))"
    local result=$(curl -s "$PROMETHEUS_URL/api/v1/query?query=$query" | jq '.data.result[0].value[1]')
    if (( $(echo "$result > 0.8" | bc -l) )); then
        notify "CPU使用率过高: $result" "WARNING"
        return 1
    fi
    return 0
}
# 预检查
pre_check() {
    echo "执行更新前检查..."
    # 检查磁盘空间
    local disk_usage=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//')
    if [ "$disk_usage" -gt 80 ]; then
        notify "磁盘使用率超过80%: $disk_usage%" "WARNING"
    fi
    # 检查内存
    local mem_usage=$(free | grep Mem | awk '{print $3/$2 * 100.0}' | cut -d. -f1)
    if [ "$mem_usage" -gt 80 ]; then
        notify "内存使用率超过80%: $mem_usage%" "WARNING"
    fi
    # 检查当前容器状态
    if ! docker ps --format "{{.Names}}" | grep -q "my-app"; then
        notify "容器 my-app 不在运行状态" "ERROR"
        exit 1
    fi
    echo "预检查通过"
}
# 后检查
post_check() {
    echo "执行更新后检查..."
    # 检查关键指标
    check_metrics || true
    # 检查API响应
    local response_time=$(curl -o /dev/null -s -w "%{time_total}" http://localhost:8080/api/health)
    if (( $(echo "$response_time > 2.0" | bc -l) )); then
        notify "API响应时间过高: ${response_time}s" "WARNING"
    fi
    echo "后检查完成"
}
# 主流程
main() {
    notify "开始容器更新流程" "INFO"
    pre_check
    # 执行更新(调用之前的更新函数)
    if perform_update; then
        post_check
        notify "容器更新成功完成" "INFO"
    else
        notify "容器更新失败,开始回滚" "ERROR"
        rollback
    fi
}
# 执行主流程
main

使用建议

  1. 选择合适的更新策略

    • 测试环境:使用基础更新脚本
    • 生产环境:使用蓝绿部署或滚动更新
    • 高可用要求:使用滚动更新,逐个替换容器
  2. 安全考虑

    # 使用TLS验证镜像签名
    export DOCKER_CONTENT_TRUST=1
    # 使用只读文件系统
    docker run --read-only ...
    # 限制容器资源
    docker run --memory="512m" --cpus="0.5" ...
  3. 错误处理

    # 设置超时
    timeout 120 docker-compose pull
    # 幂等性操作
    docker stop container_name 2>/dev/null || true
    docker rm container_name 2>/dev/null || true
  4. 监控和日志

    # 发送更新事件到监控系统
    curl -X POST "http://monitoring:8086/write?db=deployments" \
        --data-binary "container_update,app=my-app,status=success value=1"
    # 集中日志收集
    exec > >(tee -a /var/log/container-update.log | logger -t container-update)

定时更新脚本(cron示例)

# crontab -e
# 每周日凌晨3点执行更新
0 3 * * 0 /usr/local/bin/container-update.sh >> /var/log/container-update.cron.log 2>&1
# 每天检查更新
0 0 * * * /usr/local/bin/check-updates.sh

选择哪种方案取决于你的具体需求:测试环境用简单方案,生产环境建议使用蓝绿部署或滚动更新,配合完整的监控和通知机制。

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