Files
KnowledgeBase/docker/compress-and-verify.sh
lizhuoran 3c206e9e06 feat: 新增 Docker 部署支持、Swoole/Octane 集成及相关优化
- 添加 Dockerfile 与多套 docker-compose 配置(开发/生产环境)
- 集成 Laravel Octane (Swoole) 提升性能
- 新增健康检查、监控脚本及部署文档
- 新增 Docker 镜像离线导入包(MySQL/Redis/Meilisearch)
- 优化文档转换、预览服务及队列任务
- 添加 CreateAdminUser 命令与路由健康检查接口
- 新增 Swoole 队列兼容性测试套件

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 15:51:19 +08:00

399 lines
11 KiB
Bash
Executable File

#!/bin/bash
# Docker镜像压缩和完整性检查脚本
# 用于压缩导出的镜像文件并验证完整性
set -e
# 脚本配置
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
DEFAULT_INPUT_DIR="${PROJECT_ROOT}/docker-images"
LOG_FILE="${DEFAULT_INPUT_DIR}/compress-verify.log"
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 日志函数
log() {
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" | tee -a "$LOG_FILE"
}
log_success() {
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] ✓${NC} $1" | tee -a "$LOG_FILE"
}
log_warning() {
echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] ⚠${NC} $1" | tee -a "$LOG_FILE"
}
log_error() {
echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ✗${NC} $1" | tee -a "$LOG_FILE"
}
# 显示帮助信息
show_help() {
cat << EOF
Docker镜像压缩和完整性检查脚本
用法: $0 [选项] [输入目录]
选项:
-h, --help 显示此帮助信息
-c, --compress-level N 压缩级别 (1-9, 默认: 6)
-k, --keep-original 保留原始文件
-v, --verify-only 仅验证,不压缩
-u, --uncompress 解压缩文件
--parallel N 并行处理数量 (默认: 2)
参数:
输入目录 包含Docker镜像tar文件的目录 (默认: ./docker-images)
示例:
$0 # 压缩默认目录中的所有tar文件
$0 -c 9 -k /path/to/images # 最高压缩级别,保留原文件
$0 --verify-only # 仅验证现有文件完整性
$0 --uncompress # 解压缩所有.gz文件
EOF
}
# 默认配置
COMPRESS_LEVEL=6
KEEP_ORIGINAL=false
VERIFY_ONLY=false
UNCOMPRESS=false
PARALLEL_JOBS=2
INPUT_DIR="$DEFAULT_INPUT_DIR"
# 解析命令行参数
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
show_help
exit 0
;;
-c|--compress-level)
COMPRESS_LEVEL="$2"
if [[ ! "$COMPRESS_LEVEL" =~ ^[1-9]$ ]]; then
log_error "压缩级别必须是1-9之间的数字"
exit 1
fi
shift 2
;;
-k|--keep-original)
KEEP_ORIGINAL=true
shift
;;
-v|--verify-only)
VERIFY_ONLY=true
shift
;;
-u|--uncompress)
UNCOMPRESS=true
shift
;;
--parallel)
PARALLEL_JOBS="$2"
if [[ ! "$PARALLEL_JOBS" =~ ^[1-9][0-9]*$ ]]; then
log_error "并行任务数必须是正整数"
exit 1
fi
shift 2
;;
-*)
log_error "未知参数: $1"
show_help
exit 1
;;
*)
INPUT_DIR="$1"
shift
;;
esac
done
# 检查输入目录
if [[ ! -d "$INPUT_DIR" ]]; then
log_error "输入目录不存在: $INPUT_DIR"
exit 1
fi
# 创建日志目录
mkdir -p "$(dirname "$LOG_FILE")"
log "开始镜像压缩和完整性检查..."
log "输入目录: $INPUT_DIR"
log "压缩级别: $COMPRESS_LEVEL"
log "保留原文件: $KEEP_ORIGINAL"
log "仅验证: $VERIFY_ONLY"
log "解压缩: $UNCOMPRESS"
log "并行任务: $PARALLEL_JOBS"
# 检查必要工具
check_tools() {
local tools=("gzip" "sha256sum" "tar")
for tool in "${tools[@]}"; do
if ! command -v "$tool" >/dev/null 2>&1; then
log_error "缺少必要工具: $tool"
exit 1
fi
done
}
check_tools
# 验证文件完整性
verify_file() {
local file="$1"
local filename=$(basename "$file")
log "验证文件: $filename"
if [[ "$file" == *.tar.gz ]]; then
# 验证gzip文件
if gzip -t "$file" 2>/dev/null; then
log_success "压缩文件完整性验证通过: $filename"
return 0
else
log_error "压缩文件完整性验证失败: $filename"
return 1
fi
elif [[ "$file" == *.tar ]]; then
# 验证tar文件
if tar -tf "$file" >/dev/null 2>&1; then
log_success "tar文件完整性验证通过: $filename"
return 0
else
log_error "tar文件完整性验证失败: $filename"
return 1
fi
else
log_warning "未知文件类型,跳过验证: $filename"
return 1
fi
}
# 压缩文件
compress_file() {
local file="$1"
local filename=$(basename "$file")
local compressed_file="${file}.gz"
log "压缩文件: $filename (级别: $COMPRESS_LEVEL)"
# 检查是否已经压缩
if [[ "$file" == *.gz ]]; then
log_warning "文件已经压缩,跳过: $filename"
return 0
fi
# 检查压缩文件是否已存在
if [[ -f "$compressed_file" ]]; then
log_warning "压缩文件已存在,跳过: ${filename}.gz"
return 0
fi
# 获取原始文件大小
local original_size=$(du -b "$file" | cut -f1)
local original_size_human=$(du -h "$file" | cut -f1)
# 压缩文件
if gzip -"$COMPRESS_LEVEL" -c "$file" > "$compressed_file"; then
# 获取压缩后文件大小
local compressed_size=$(du -b "$compressed_file" | cut -f1)
local compressed_size_human=$(du -h "$compressed_file" | cut -f1)
# 计算压缩比
local ratio=$(echo "scale=2; $compressed_size * 100 / $original_size" | bc -l 2>/dev/null || echo "N/A")
log_success "文件压缩成功: ${filename}.gz"
log "原始大小: $original_size_human"
log "压缩后大小: $compressed_size_human"
if [[ "$ratio" != "N/A" ]]; then
log "压缩比: ${ratio}%"
fi
# 验证压缩文件
if verify_file "$compressed_file"; then
# 删除原文件(如果不保留)
if [[ "$KEEP_ORIGINAL" == false ]]; then
rm "$file"
log "已删除原文件: $filename"
fi
return 0
else
log_error "压缩文件验证失败,删除压缩文件"
rm -f "$compressed_file"
return 1
fi
else
log_error "文件压缩失败: $filename"
return 1
fi
}
# 解压缩文件
uncompress_file() {
local file="$1"
local filename=$(basename "$file")
log "解压缩文件: $filename"
# 检查是否是压缩文件
if [[ "$file" != *.gz ]]; then
log_warning "文件未压缩,跳过: $filename"
return 0
fi
# 生成解压后的文件名
local uncompressed_file="${file%.gz}"
local uncompressed_filename=$(basename "$uncompressed_file")
# 检查解压文件是否已存在
if [[ -f "$uncompressed_file" ]]; then
log_warning "解压文件已存在,跳过: $uncompressed_filename"
return 0
fi
# 解压文件
if gunzip -c "$file" > "$uncompressed_file"; then
log_success "文件解压成功: $uncompressed_filename"
# 验证解压文件
if verify_file "$uncompressed_file"; then
# 删除压缩文件(如果不保留)
if [[ "$KEEP_ORIGINAL" == false ]]; then
rm "$file"
log "已删除压缩文件: $filename"
fi
return 0
else
log_error "解压文件验证失败,删除解压文件"
rm -f "$uncompressed_file"
return 1
fi
else
log_error "文件解压失败: $filename"
return 1
fi
}
# 处理单个文件
process_file() {
local file="$1"
if [[ "$VERIFY_ONLY" == true ]]; then
verify_file "$file"
elif [[ "$UNCOMPRESS" == true ]]; then
uncompress_file "$file"
else
compress_file "$file"
fi
}
# 查找需要处理的文件
if [[ "$UNCOMPRESS" == true ]]; then
FILES=($(find "$INPUT_DIR" -name "*.tar.gz" -type f))
log "找到 ${#FILES[@]} 个压缩文件"
elif [[ "$VERIFY_ONLY" == true ]]; then
FILES=($(find "$INPUT_DIR" -name "*.tar*" -type f))
log "找到 ${#FILES[@]} 个文件需要验证"
else
FILES=($(find "$INPUT_DIR" -name "*.tar" -type f))
log "找到 ${#FILES[@]} 个tar文件需要压缩"
fi
if [[ ${#FILES[@]} -eq 0 ]]; then
log_warning "没有找到需要处理的文件"
exit 0
fi
# 处理文件
PROCESSED=0
FAILED=0
TOTAL=${#FILES[@]}
# 使用并行处理
export -f process_file verify_file compress_file uncompress_file log log_success log_warning log_error
export COMPRESS_LEVEL KEEP_ORIGINAL VERIFY_ONLY UNCOMPRESS LOG_FILE
export RED GREEN YELLOW BLUE NC
printf '%s\n' "${FILES[@]}" | xargs -n 1 -P "$PARALLEL_JOBS" -I {} bash -c 'process_file "$@"' _ {}
# 统计结果
for file in "${FILES[@]}"; do
if [[ "$VERIFY_ONLY" == true ]]; then
if verify_file "$file" >/dev/null 2>&1; then
((PROCESSED++))
else
((FAILED++))
fi
elif [[ "$UNCOMPRESS" == true ]]; then
uncompressed_file="${file%.gz}"
if [[ -f "$uncompressed_file" ]] || [[ "$file" != *.gz ]]; then
((PROCESSED++))
else
((FAILED++))
fi
else
compressed_file="${file}.gz"
if [[ -f "$compressed_file" ]] || [[ "$file" == *.gz ]]; then
((PROCESSED++))
else
((FAILED++))
fi
fi
done
# 更新清单文件
if [[ "$VERIFY_ONLY" == false ]]; then
manifest_file="${INPUT_DIR}/images-manifest.txt"
if [[ -f "$manifest_file" ]]; then
log "更新镜像清单..."
# 备份原清单
cp "$manifest_file" "${manifest_file}.backup"
# 重新生成清单
cat > "$manifest_file" << EOF
# Docker镜像清单
# 更新时间: $(date)
# 处理目录: $INPUT_DIR
EOF
for file in "$INPUT_DIR"/*.tar*; do
if [[ -f "$file" ]]; then
filename=$(basename "$file")
size=$(du -h "$file" | cut -f1)
checksum=$(sha256sum "$file" | cut -d' ' -f1)
echo "文件: $filename" >> "$manifest_file"
echo "大小: $size" >> "$manifest_file"
echo "SHA256: $checksum" >> "$manifest_file"
echo "" >> "$manifest_file"
fi
done
log_success "镜像清单已更新"
fi
fi
# 显示总结
log_success "处理完成!"
log "总文件数: $TOTAL"
log "成功处理: $PROCESSED"
log "失败数量: $FAILED"
if [[ "$FAILED" -gt 0 ]]; then
log_warning "$FAILED 个文件处理失败,请检查日志"
exit 1
else
log_success "所有文件处理成功"
fi