本文目录导读:

在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"
最佳实践建议
-
合理的宽限期设置
- Web应用:30-60秒
- 数据库:60-120秒
- 批处理:根据任务大小调整
-
信号处理
- 处理 SIGTERM(15)信号
- 避免使用 SIGKILL(9)
-
监控和日志
- 记录关闭过程
- 设置告警
-
测试验证
- 定期测试优雅关闭
- 验证数据完整性
这些脚本和配置可以根据具体应用需求进行调整,重要的是确保关闭过程中的数据完整性和服务稳定性。