Shell脚本如何配置容器终止宽限期

wen 实用脚本 1

本文目录导读:

Shell脚本如何配置容器终止宽限期

  1. Docker/Kubernetes 环境下的配置
  2. Shell脚本处理退出信号
  3. 高级宽限期管理脚本
  4. Docker 容器内的专门脚本
  5. 配置示例:不同环境的设置
  6. 测试宽限期配置
  7. 调试和监控脚本
  8. 最佳实践建议

在Shell脚本中配置容器终止宽限期(Graceful Shutdown),主要有以下几种方式:

Docker/Kubernetes 环境下的配置

Docker run 命令

# 使用 --stop-grace-period 参数(单位:秒)
docker run --stop-grace-period=30 -d nginx
# 或者使用时间格式
docker run --stop-grace-period=1m30s -d nginx

Docker Compose

version: '3.8'
services:
  myapp:
    image: myapp:latest
    stop_grace_period: 30s  # 设置30秒宽限期
    # 或者
    stop_grace_period: 1m30s

Kubernetes Pod

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  terminationGracePeriodSeconds: 60  # 60秒宽限期
  containers:
  - name: myapp
    image: myapp:latest

Shell脚本处理退出信号

#!/bin/bash
# 设置宽限期(秒)
GRACE_PERIOD=30
# 捕获退出信号
cleanup() {
    echo "接收到终止信号,开始优雅退出..."
    # 执行清理操作
    echo "保存数据..."
    # sync 或其他保存操作
    echo "关闭连接..."
    # 关闭数据库连接、网络连接等
    echo "正在等待后台任务完成..."
    # 等待子进程结束
    wait
    echo "优雅退出完成"
    exit 0
}
# 注册信号处理函数
trap cleanup SIGTERM SIGINT SIGHUP
# 模拟主程序运行
echo "应用启动,PID: $$"
echo "宽限期: $GRACE_PERIOD 秒"
# 主循环
while true; do
    echo "应用运行中..."
    sleep 5
done

高级宽限期管理脚本

#!/bin/bash
# 配置参数
GRACE_PERIOD=60
CHECK_INTERVAL=2
PID_FILE="/var/run/app.pid"
# 状态变量
CLEANUP_COMPLETE=false
# 智能清理函数
smart_cleanup() {
    local start_time=$(date +%s)
    local current_time=$start_time
    local elapsed_time=0
    echo "[$(date)] 开始优雅退出,宽限期: ${GRACE_PERIOD}s"
    # 阶段1: 停止新请求(5秒)
    echo "阶段1: 停止接受新请求..."
    sleep 5
    elapsed_time=$(( $(date +%s) - start_time ))
    echo "已用时: ${elapsed_time}s"
    # 阶段2: 完成正在处理的请求
    if [ $elapsed_time -lt $GRACE_PERIOD ]; then
        remaining=$(( GRACE_PERIOD - elapsed_time ))
        echo "阶段2: 等待当前请求完成(最多等待 ${remaining}s)..."
        # 检查是否有活跃连接
        while [ $elapsed_time -lt $GRACE_PERIOD ]; do
            if check_active_connections; then
                echo "仍有活跃连接,等待中..."
                sleep $CHECK_INTERVAL
            else
                echo "所有连接已关闭"
                break
            fi
            elapsed_time=$(( $(date +%s) - start_time ))
        done
    fi
    # 阶段3: 保存状态和清理
    if [ $elapsed_time -lt $GRACE_PERIOD ]; then
        echo "阶段3: 保存应用状态..."
        save_state
        echo "清理临时文件..."
        cleanup_temp_files
        echo "关闭日志..."
        close_logs
    fi
    CLEANUP_COMPLETE=true
    echo "[$(date)] 优雅退出完成(用时: ${elapsed_time}s)"
}
# 检查活跃连接
check_active_connections() {
    # 根据实际应用实现
    # 示例:检查网络连接
    local connections=$(ss -tn | grep -c "ESTAB")
    [ $connections -gt 0 ]
    return $?
}
# 保存状态
save_state() {
    # 模拟状态保存
    echo "保存应用状态到文件..."
    # 实际保存逻辑
}
# 清理文件
cleanup_temp_files() {
    echo "清理 /tmp/app_* 文件"
    rm -f /tmp/app_* 2>/dev/null
}
# 关闭日志
close_logs() {
    echo "关闭日志文件句柄..."
    # 实际日志清理逻辑
}
# 主信号处理
handle_signal() {
    smart_cleanup
    if [ "$CLEANUP_COMPLETE" = true ]; then
        echo "正在关闭进程..."
        kill -TERM $$ 2>/dev/null
        exit 0
    else
        echo "宽限期不足,强制关闭"
        exit 1
    fi
}
trap handle_signal SIGTERM SIGINT
# 写入PID
echo $$ > $PID_FILE
# 主程序逻辑
echo "应用启动,PID: $$"
echo "宽限期配置: ${GRACE_PERIOD}s"
# 模拟运行
while true; do
    # 应用主逻辑
    echo "处理请求中... ($(date))"
    sleep 10
done

Docker 容器内的专门脚本

# Dockerfile
FROM alpine:latest
# 安装 tini 作为 init 进程
RUN apk add --no-cache tini
# 复制启动脚本
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
# 使用 tini 作为入口
ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/docker-entrypoint.sh"]
#!/bin/sh
# docker-entrypoint.sh
# 宽限期配置(可以通过环境变量覆盖)
TERMINATION_GRACE_PERIOD=${TERMINATION_GRACE_PERIOD:-30}
# 信号处理
cleanup() {
    echo "Docker容器接收到SIGTERM信号"
    echo "开始${TERMINATION_GRACE_PERIOD}秒的优雅关闭..."
    # 通知应用停止
    kill -TERM $child 2>/dev/null
    # 等待子进程或超时
    for i in $(seq 1 $TERMINATION_GRACE_PERIOD); do
        if ! kill -0 $child 2>/dev/null; then
            echo "子进程已优雅退出"
            break
        fi
        sleep 1
    done
    # 如果超时,强制终止
    if kill -0 $child 2>/dev/null; then
        echo "宽限期结束,强制终止进程"
        kill -KILL $child 2>/dev/null
    fi
    exit 0
}
trap cleanup SIGTERM SIGINT
# 启动主应用
exec /usr/local/bin/your-app &
child=$!
# 等待子进程
wait $child

配置示例:不同环境的设置

生产环境脚本

#!/bin/bash
# 生产环境配置
GRACE_PERIOD=${PROD_GRACE_PERIOD:-60}
# 检查应用状态
check_app_healthy() {
    # 健康检查逻辑
    curl -f http://localhost:8080/health
}
# 优雅关闭
graceful_shutdown() {
    local start_time=$(date +%s)
    echo "开始生产环境优雅关闭..."
    # 1. 从负载均衡器移除
    echo "从负载均衡器移除..."
    kubectl label pod $(hostname) routeable=false
    # 2. 等待现有请求完成
    while [ $(($(date +%s) - start_time)) -lt $GRACE_PERIOD ]; do
        if ! check_app_healthy; then
            echo "应用健康检查失败,准备关闭"
            break
        fi
        echo "等待请求完成..."
        sleep 2
    done
    # 3. 执行最终清理
    cleanup_resources
    echo "生产环境优雅关闭完成"
}
trap graceful_shutdown SIGTERM
# 启动应用
echo "生产环境应用启动..."
while true; do
    sleep 10
done

测试宽限期配置

#!/bin/bash
# 测试脚本 - 验证容器宽限期
test_graceful_shutdown() {
    container_name="test-graceful-$RANDOM"
    # 创建测试容器
    docker run -d --name $container_name \
        --stop-grace-period=30 \
        alpine sh -c "trap 'echo 接收到信号; sleep 10; exit 0' SIGTERM; while true; do sleep 1; done"
    # 测试1: 正常关闭
    echo "测试1: Docker正常关闭"
    time docker stop $container_name
    # 测试2: 强制关闭(超出宽限期)
    echo "测试2: Docker强制关闭"
    container_name2="test-graceful-force-$RANDOM"
    docker run -d --name $container_name2 \
        --stop-grace-period=5 \
        alpine sh -c "trap 'echo 接收到信号; sleep 30; exit 0' SIGTERM; while true; do sleep 1; done"
    time docker container kill $container_name2
    # 清理
    docker rm -f $container_name $container_name2
    echo "测试完成"
}
test_graceful_shutdown

调试和监控脚本

#!/bin/bash
# 监控容器关闭过程
monitor_shutdown() {
    local container=$1
    local pid=$(docker inspect -f '{{.State.Pid}}' $container)
    echo "监控容器: $container (PID: $pid)"
    echo "开始时间: $(date)"
    # 监控进程状态变化
    while docker ps -q --filter "name=$container" | grep -q .; do
        local status=$(docker inspect -f '{{.State.Status}}' $container)
        local exit_code=$(docker inspect -f '{{.State.ExitCode}}' $container)
        echo "状态: $status, 退出码: $exit_code, 时间: $(date)"
        # 检查超时
        if [ "$status" = "exited" ]; then
            break
        fi
        sleep 1
    done
    echo "关闭过程完成"
    echo "结束时间: $(date)"
}
# 使用示例
monitor_shutdown "myapp-container"

最佳实践建议

  1. 合理的宽限期设置

    • Web应用:30-60秒
    • 数据库:60-120秒
    • 批处理:根据任务大小调整
  2. 信号处理

    • 处理 SIGTERM(15)信号
    • 避免使用 SIGKILL(9)
  3. 监控和日志

    • 记录关闭过程
    • 设置告警
  4. 测试验证

    • 定期测试优雅关闭
    • 验证数据完整性

这些脚本和配置可以根据具体应用需求进行调整,重要的是确保关闭过程中的数据完整性和服务稳定性。

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