Shell脚本如何配置容器停止前清理

wen 实用脚本 1

本文目录导读:

Shell脚本如何配置容器停止前清理

  1. 使用 trap 捕获信号
  2. Docker 容器的优雅停止
  3. Kubernetes Pod 的 PreStop Hook
  4. 完整的实用案例
  5. 调试和验证
  6. 注意事项

在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>

注意事项

  1. 信号转发:确保容器内的进程能正确接收和转发信号
  2. 超时控制:设置合理的清理超时时间(Docker默认10秒)
  3. 幂等性:清理操作应该可以多次执行而不会出错
  4. 日志记录:记录清理操作的详细日志便于调试
  5. 资源释放:确保所有文件描述符、网络连接等资源都被释放

这样配置后,无论容器是正常退出还是收到停止信号,都能执行预定义的清理操作,确保容器优雅地停止。

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