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>
This commit is contained in:
335
docker/export-images.sh
Executable file
335
docker/export-images.sh
Executable file
@@ -0,0 +1,335 @@
|
||||
#!/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"
|
||||
Reference in New Issue
Block a user