Files
KnowledgeBase/docker/export-images.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

335 lines
8.7 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# Docker镜像导出脚本
# 用于将构建好的Docker镜像导出为tar文件便于离线部署
set -e
# 脚本配置
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
EXPORT_DIR="${PROJECT_ROOT}/docker-images"
LOG_FILE="${EXPORT_DIR}/export.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 显示此帮助信息
-o, --output DIR 指定导出目录 (默认: ./docker-images)
-c, --compress 启用压缩 (使用gzip)
-v, --verify 导出后验证镜像完整性
--custom-images 导出自定义镜像列表 (用逗号分隔)
--skip-build 跳过镜像构建,直接导出现有镜像
示例:
$0 # 导出所有镜像
$0 -c -v # 导出并压缩,验证完整性
$0 -o /tmp/images --compress # 导出到指定目录并压缩
$0 --custom-images "mysql:8.0,redis:7-alpine" # 导出指定镜像
EOF
}
# 默认配置
COMPRESS=false
VERIFY=false
SKIP_BUILD=false
CUSTOM_IMAGES=""
# 解析命令行参数
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
show_help
exit 0
;;
-o|--output)
EXPORT_DIR="$2"
shift 2
;;
-c|--compress)
COMPRESS=true
shift
;;
-v|--verify)
VERIFY=true
shift
;;
--custom-images)
CUSTOM_IMAGES="$2"
shift 2
;;
--skip-build)
SKIP_BUILD=true
shift
;;
*)
log_error "未知参数: $1"
show_help
exit 1
;;
esac
done
# 创建导出目录
mkdir -p "$EXPORT_DIR"
mkdir -p "$(dirname "$LOG_FILE")"
log "开始Docker镜像导出过程..."
log "导出目录: $EXPORT_DIR"
# 检查Docker是否运行
if ! docker info >/dev/null 2>&1; then
log_error "Docker未运行或无法访问"
exit 1
fi
# 定义需要导出的镜像列表
if [[ -n "$CUSTOM_IMAGES" ]]; then
IFS=',' read -ra IMAGES <<< "$CUSTOM_IMAGES"
else
IMAGES=(
"knowledge-base-app:latest"
"mysql:8.0"
"redis:7-alpine"
"getmeili/meilisearch:v1.5"
)
fi
# 构建自定义镜像(如果需要)
if [[ "$SKIP_BUILD" == false ]]; then
log "检查是否需要构建自定义镜像..."
# 检查knowledge-base-app镜像是否存在
if ! docker image inspect knowledge-base-app:latest >/dev/null 2>&1; then
log "构建knowledge-base-app镜像..."
cd "$PROJECT_ROOT"
if docker build --platform linux/amd64 -t knowledge-base-app:latest -f Dockerfile --target production .; then
log_success "knowledge-base-app镜像构建成功"
else
log_error "knowledge-base-app镜像构建失败"
exit 1
fi
else
log_success "knowledge-base-app镜像已存在"
fi
fi
# 拉取外部镜像
log "拉取外部镜像..."
for image in "mysql:8.0" "redis:7-alpine" "getmeili/meilisearch:v1.5"; do
if [[ " ${IMAGES[@]} " =~ " ${image} " ]]; then
log "拉取镜像: $image"
if docker pull --platform linux/amd64 "$image"; then
log_success "镜像 $image 拉取成功"
else
log_warning "镜像 $image 拉取失败,将尝试使用本地镜像"
fi
fi
done
# 导出镜像
log "开始导出镜像..."
EXPORTED_FILES=()
for image in "${IMAGES[@]}"; do
log "导出镜像: $image"
# 检查镜像是否存在
if ! docker image inspect "$image" >/dev/null 2>&1; then
log_error "镜像 $image 不存在,跳过导出"
continue
fi
# 生成文件名(替换特殊字符)
filename=$(echo "$image" | sed 's/[\/:]/_/g')
output_file="${EXPORT_DIR}/${filename}.tar"
# 导出镜像
if docker save -o "$output_file" "$image"; then
log_success "镜像 $image 导出成功: $output_file"
EXPORTED_FILES+=("$output_file")
# 显示文件大小
size=$(du -h "$output_file" | cut -f1)
log "文件大小: $size"
else
log_error "镜像 $image 导出失败"
continue
fi
# 压缩文件(如果启用)
if [[ "$COMPRESS" == true ]]; then
log "压缩文件: $output_file"
if gzip "$output_file"; then
compressed_file="${output_file}.gz"
log_success "文件压缩成功: $compressed_file"
# 更新文件列表
EXPORTED_FILES=("${EXPORTED_FILES[@]/$output_file}")
EXPORTED_FILES+=("$compressed_file")
# 显示压缩后大小
compressed_size=$(du -h "$compressed_file" | cut -f1)
original_size=$(du -h "$output_file" 2>/dev/null | cut -f1 || echo "N/A")
log "压缩后大小: $compressed_size (原始: $original_size)"
else
log_error "文件压缩失败"
fi
fi
done
# 验证导出的镜像(如果启用)
if [[ "$VERIFY" == true ]]; then
log "验证导出的镜像..."
for file in "${EXPORTED_FILES[@]}"; do
if [[ -f "$file" ]]; then
log "验证文件: $file"
# 检查文件完整性
if [[ "$file" == *.gz ]]; then
# 验证gzip文件
if gzip -t "$file"; then
log_success "压缩文件完整性验证通过"
else
log_error "压缩文件完整性验证失败"
fi
else
# 验证tar文件
if tar -tf "$file" >/dev/null 2>&1; then
log_success "tar文件完整性验证通过"
else
log_error "tar文件完整性验证失败"
fi
fi
fi
done
fi
# 生成镜像清单
manifest_file="${EXPORT_DIR}/images-manifest.txt"
log "生成镜像清单: $manifest_file"
cat > "$manifest_file" << EOF
# Docker镜像导出清单
# 生成时间: $(date)
# 导出目录: $EXPORT_DIR
# 压缩: $COMPRESS
# 验证: $VERIFY
EOF
for file in "${EXPORTED_FILES[@]}"; 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
# 生成导入脚本
import_script="${EXPORT_DIR}/import-images.sh"
log "生成导入脚本: $import_script"
cat > "$import_script" << 'EOF'
#!/bin/bash
# Docker镜像导入脚本
# 自动生成用于导入导出的Docker镜像
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo "开始导入Docker镜像..."
# 检查Docker是否运行
if ! docker info >/dev/null 2>&1; then
echo "错误: Docker未运行或无法访问"
exit 1
fi
# 导入所有tar文件
for file in "$SCRIPT_DIR"/*.tar*; do
if [[ -f "$file" ]]; then
echo "导入镜像: $(basename "$file")"
if [[ "$file" == *.gz ]]; then
# 解压并导入
if gunzip -c "$file" | docker load; then
echo "✓ 镜像导入成功"
else
echo "✗ 镜像导入失败"
fi
else
# 直接导入
if docker load -i "$file"; then
echo "✓ 镜像导入成功"
else
echo "✗ 镜像导入失败"
fi
fi
fi
done
echo "镜像导入完成"
echo "可用镜像列表:"
docker images
EOF
chmod +x "$import_script"
# 显示总结
log_success "镜像导出完成!"
log "导出的文件:"
for file in "${EXPORTED_FILES[@]}"; do
if [[ -f "$file" ]]; then
size=$(du -h "$file" | cut -f1)
log " - $(basename "$file") ($size)"
fi
done
log "生成的文件:"
log " - images-manifest.txt (镜像清单)"
log " - import-images.sh (导入脚本)"
total_size=$(du -sh "$EXPORT_DIR" | cut -f1)
log "总大小: $total_size"
log_success "所有文件已保存到: $EXPORT_DIR"