#!/usr/bin/env bash
# ==============================================================================
# 脚本名称: docker_rescue_data.sh
# 功能描述: 自动利用数据卷继承机制提取 Exited 容器内部数据 (纯只读提取)
# 使用方式: ./docker_rescue_data.sh <故障容器名或ID> <容器内要提取的数据卷路径>
# ==============================================================================
set -o pipefail
TARGET_CONTAINER=$1
TARGET_PATH=$2
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
PLAIN='\033[0m'
# 这里修复了渲染乱码 \vert{}\vert{} 为 ||
if [ -z "$TARGET_CONTAINER" ] || [ -z "$TARGET_PATH" ]; then
echo -e "${RED}用法: $0 <故障容器名或ID> <容器内部的数据目录, 如 /var/lib/mysql>${PLAIN}"
exit 1
fi
echo -e "${BLUE}================================================================${PLAIN}"
echo -e "${BLUE} 容器 Exited 离线数据自动提取脚本 ${PLAIN}"
echo -e "${BLUE}================================================================${PLAIN}"
# 1. 检查容器是否存在
if ! docker ps -a --format '{{.Names}}' | grep -q -x "$TARGET_CONTAINER"; then
# 尝试按 ID 匹配
if ! docker ps -a --format '{{.ID}}' | grep -q "^${TARGET_CONTAINER}"; then
echo -e "${RED}[FATAL] 未找到名为 $TARGET_CONTAINER 的容器,请确认名称或是否已被删除。${PLAIN}"
exit 1
fi
fi
CONTAINER_STATE=$(docker inspect --format '{{.State.Status}}' "$TARGET_CONTAINER")
echo -e "${YELLOW}[1/3] 锁定目标容器: ${TARGET_CONTAINER} (当前状态: ${CONTAINER_STATE})${PLAIN}"
# 2. 准备宿主机接收目录
HOST_BACKUP_DIR="$(pwd)/rescue_export_${TARGET_CONTAINER}_$(date +%Y%m%d%H%M)"
mkdir -p "$HOST_BACKUP_DIR"
echo -e "${YELLOW}[2/3] 创建宿主机数据接收目录: ${HOST_BACKUP_DIR}${PLAIN}"
# 3. 启动临时容器执行底层打包拷贝
echo -e "${YELLOW}[3/3] 启动临时 Alpine 容器进行数据卷挂载与归档提取...${PLAIN}"
echo -e " -> 目标路径: ${TARGET_PATH}"
# 使用 tar 打包避免大量小文件导致 I/O 阻塞,同时保留属主权限 (-p)
docker run --rm \
--volumes-from "$TARGET_CONTAINER" \
-v "${HOST_BACKUP_DIR}:/export" \
alpine \
sh -c "if [ -e '${TARGET_PATH}' ]; then tar -czpf /export/rescue_data.tar.gz -C \$(dirname '${TARGET_PATH}') \$(basename '${TARGET_PATH}'); echo 'SUCCESS'; else echo 'NOT_FOUND'; fi" > /tmp/rescue_cmd.out 2>&1
RESULT=$(tail -n 1 /tmp/rescue_cmd.out)
echo -e "${BLUE}----------------------------------------------------------------${PLAIN}"
if [ "$RESULT" == "SUCCESS" ]; then
echo -e "${GREEN}[OK] 数据提取成功。${PLAIN}"
echo -e "数据已打包保存至: ${GREEN}${HOST_BACKUP_DIR}/rescue_data.tar.gz${PLAIN}"
ls -lh "${HOST_BACKUP_DIR}/rescue_data.tar.gz"
else
echo -e "${RED}[ERROR] 数据卷继承提取未找到目标路径 ${TARGET_PATH}(该路径可能未声明为 Volume,而处于容器可写层)。${PLAIN}"
echo -e "${YELLOW}[INFO] 正在自动降级使用 docker cp 从容器可写层提取...${PLAIN}"
if docker cp "${TARGET_CONTAINER}:${TARGET_PATH}" "${HOST_BACKUP_DIR}/"; then
echo -e "${GREEN}[OK] docker cp 降级提取成功,数据已保存至: ${HOST_BACKUP_DIR}${PLAIN}"
ls -lh "${HOST_BACKUP_DIR}"
else
echo -e "${RED}[FATAL] 提取失败,请确认容器内是否存在路径 ${TARGET_PATH}。${PLAIN}"
rm -rf "$HOST_BACKUP_DIR"
fi
fi
echo -e "${BLUE}================================================================${PLAIN}"