本文目录导读:

在Shell脚本中配置容器停止前的清理操作,主要有以下几种方法:
使用 trap 捕获信号
#!/bin/bash
# 定义清理函数
cleanup() {
echo "正在执行清理操作..."
# 删除临时文件
rm -rf /tmp/app_*
# 关闭数据库连接
mysqladmin shutdown
# 清理缓存
sync
echo "清理完成"
}
# 捕获退出信号
trap cleanup EXIT
trap cleanup SIGTERM
trap cleanup SIGINT
trap cleanup SIGQUIT
# 主程序
echo "应用启动..."
# 你的应用代码
while true; do
sleep 1
done
Docker 容器的优雅停止
使用 docker stop 信号
#!/bin/bash
# 清理函数
cleanup() {
echo "收到停止信号,开始清理..."
# 停止后台服务
kill -TERM $child_pid 2>/dev/null
# 等待子进程结束
wait $child_pid
# 清理临时资源
rm -f /var/run/app.pid
rm -rf /tmp/app_data/*
echo "清理完成,容器退出"
exit 0
}
trap cleanup SIGTERM SIGINT
# 启动主进程
/usr/local/bin/app &
child_pid=$!
# 等待子进程
wait
配合 supervisord 或 s6-overlay
#!/bin/bash
# supervisord 退出时的清理脚本
cleanup_script() {
echo "Supervisor 正在停止服务..."
# 停止所有子服务
supervisorctl stop all
# 清理共享内存
ipcrm -a 2>/dev/null
# 清理临时文件
find /tmp -type f -mmin +5 -delete
echo "所有服务已停止并清理"
}
# 注册清理
trap cleanup_script EXIT
# 启动 supervisor
exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
Kubernetes Pod 的 PreStop Hook
在 Deployment 或 Pod 配置中使用:
apiVersion: v1
kind: Pod
metadata:
name: myapp
spec:
containers:
- name: myapp
image: myapp:latest
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- |
echo "PreStop Hook: 开始清理..."
# 清理数据库连接
/usr/bin/mysqladmin -u root shutdown
# 清理缓存
/usr/bin/redis-cli shutdown
# 清理临时文件
rm -rf /tmp/*
echo "PreStop Hook: 清理完成"
完整的实用案例
#!/bin/bash
# ================= 配置 =================
APP_NAME="myapp"
LOG_FILE="/var/log/${APP_NAME}.log"
PID_FILE="/var/run/${APP_NAME}.pid"
TEMP_DIR="/tmp/${APP_NAME}"
# ================= 清理函数 =================
cleanup() {
local exit_code=$?
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 开始清理操作..." >> "$LOG_FILE"
# 1. 保存当前状态
echo "保存应用状态..." >> "$LOG_FILE"
# 2. 停止子进程
if [ -f "$PID_FILE" ]; then
local pid=$(cat "$PID_FILE")
if kill -0 $pid 2>/dev/null; then
echo "停止主进程 (PID: $pid)..." >> "$LOG_FILE"
kill -TERM $pid
wait $pid 2>/dev/null
fi
rm -f "$PID_FILE"
fi
# 3. 清理临时文件
if [ -d "$TEMP_DIR" ]; then
echo "清理临时目录..." >> "$LOG_FILE"
rm -rf "$TEMP_DIR"
fi
# 4. 关闭网络连接
echo "关闭网络连接..." >> "$LOG_FILE"
# 清理端口转发
iptables -w -t nat -D PREROUTING -p tcp --dport 8080 -j REDIRECT --to-port 80 2>/dev/null
# 5. 清理共享内存和信号量
ipcrm -a 2>/dev/null
# 6. 刷新文件系统缓冲区
sync
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 清理完成,退出代码: $exit_code" >> "$LOG_FILE"
# 退出时清理日志文件句柄
exec 3>&-
exit $exit_code
}
# ================= 信号处理 =================
# 捕获所有退出信号
trap cleanup EXIT
trap cleanup SIGTERM
trap cleanup SIGINT
trap cleanup SIGQUIT
trap cleanup SIGHUP
# ================= 主程序 =================
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ${APP_NAME} 启动..." | tee -a "$LOG_FILE"
# 创建必要的目录
mkdir -p "$TEMP_DIR"
# 启动主应用
/usr/local/bin/${APP_NAME} &
PID=$!
echo $PID > "$PID_FILE"
echo "主进程 PID: $PID" >> "$LOG_FILE"
# 保持容器运行
wait $PID
调试和验证
# 测试清理脚本是否正常工作 docker exec <container_id> /scripts/cleanup.sh # 发送信号测试 docker kill -s SIGTERM <container_id> # 查看日志确认清理执行 docker logs <container_id> --tail 50 # 强制停止测试 docker stop --time=30 <container_id>
注意事项
- 信号转发:确保容器内的进程能正确接收和转发信号
- 超时控制:设置合理的清理超时时间(Docker默认10秒)
- 幂等性:清理操作应该可以多次执行而不会出错
- 日志记录:记录清理操作的详细日志便于调试
- 资源释放:确保所有文件描述符、网络连接等资源都被释放
这样配置后,无论容器是正常退出还是收到停止信号,都能执行预定义的清理操作,确保容器优雅地停止。