You've already forked DataMate
- 在 build-offline.sh 脚本中添加 --pull=false 参数并改进错误处理 - 为 Makefile.offline.mk 中的各个服务构建任务添加 --pull=false 参数 - 新增 build-offline-classic.sh 脚本,提供不使用 BuildKit 的传统构建方式 - 新增 build-offline-v2.sh 脚本,提供增强版 BuildKit 离线构建功能 - 新增 diagnose.sh 脚本,用于诊断离线构建环境状态 - 在 Makefile 中添加 offline-build-classic 和 offline-diagnose
250 lines
7.4 KiB
Bash
250 lines
7.4 KiB
Bash
#!/bin/bash
|
|
# BuildKit 离线构建脚本 v2 - 增强版
|
|
# Usage: ./build-offline-v2.sh [cache-dir] [version]
|
|
|
|
set -e
|
|
|
|
CACHE_DIR="${1:-./build-cache}"
|
|
VERSION="${2:-latest}"
|
|
BUILDKIT_CACHE_DIR="$CACHE_DIR/buildkit"
|
|
IMAGES_DIR="$CACHE_DIR/images"
|
|
RESOURCES_DIR="$CACHE_DIR/resources"
|
|
|
|
# 颜色输出
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m' # No Color
|
|
|
|
log_info() {
|
|
echo -e "${GREEN}[INFO]${NC} $1"
|
|
}
|
|
|
|
log_warn() {
|
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1"
|
|
}
|
|
|
|
# 检查缓存目录
|
|
if [ ! -d "$CACHE_DIR" ]; then
|
|
log_error "缓存目录 $CACHE_DIR 不存在"
|
|
log_info "请先解压缓存包: tar -xzf build-cache-*.tar.gz"
|
|
exit 1
|
|
fi
|
|
|
|
# 确保 buildx 构建器存在(使用 docker 驱动以支持本地镜像)
|
|
setup_buildx() {
|
|
log_info "设置 BuildKit 构建器..."
|
|
|
|
# 删除旧的构建器
|
|
if docker buildx inspect offline-builder > /dev/null 2>&1; then
|
|
docker buildx rm offline-builder 2>/dev/null || true
|
|
fi
|
|
|
|
# 创建新的构建器,使用 docker 驱动(支持本地镜像,不需要推送到 registry)
|
|
docker buildx create --name offline-builder \
|
|
--driver docker-container \
|
|
--driver-opt image=moby/buildkit:buildx-stable-1 \
|
|
--use
|
|
|
|
log_info "BuildKit 构建器创建完成"
|
|
}
|
|
|
|
# 加载基础镜像
|
|
load_base_images() {
|
|
log_info "加载基础镜像..."
|
|
|
|
if [ ! -f "$IMAGES_DIR/base-images.tar" ]; then
|
|
log_warn "基础镜像文件不存在: $IMAGES_DIR/base-images.tar"
|
|
log_info "检查本地是否存在所需镜像..."
|
|
|
|
# 检查关键镜像是否存在
|
|
required_images=(
|
|
"maven:3-eclipse-temurin-21"
|
|
"eclipse-temurin:21-jdk"
|
|
"mysql:8"
|
|
"node:20-alpine"
|
|
"nginx:1.29"
|
|
)
|
|
|
|
for img in "${required_images[@]}"; do
|
|
if ! docker inspect "$img" > /dev/null 2>&1; then
|
|
log_error "缺少基础镜像: $img"
|
|
log_info "请确保基础镜像已加载: docker load -i base-images.tar"
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
log_info "本地基础镜像检查通过"
|
|
return
|
|
fi
|
|
|
|
log_info "从 $IMAGES_DIR/base-images.tar 加载基础镜像..."
|
|
docker load -i "$IMAGES_DIR/base-images.tar"
|
|
log_info "基础镜像加载完成"
|
|
}
|
|
|
|
# 验证镜像是否存在
|
|
verify_image() {
|
|
local image_name=$1
|
|
if docker inspect "$image_name" > /dev/null 2>&1; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# 离线构建函数
|
|
offline_build() {
|
|
local service_name=$1
|
|
local image_name=$2
|
|
local dockerfile=$3
|
|
local cache_file="$BUILDKIT_CACHE_DIR/${service_name}-cache"
|
|
|
|
log_info "----------------------------------------"
|
|
log_info "构建 [$service_name] -> $image_name:$VERSION"
|
|
log_info "----------------------------------------"
|
|
|
|
if [ ! -d "$cache_file" ]; then
|
|
log_warn "$service_name 的缓存不存在,跳过..."
|
|
return 0
|
|
fi
|
|
|
|
# 获取 Dockerfile 中的基础镜像
|
|
local base_images
|
|
base_images=$(grep -E '^FROM' "$dockerfile" | awk '{print $2}' | sort -u)
|
|
|
|
log_info "检查基础镜像..."
|
|
for base_img in $base_images; do
|
|
# 跳过多阶段构建中的 AS 别名
|
|
base_img=$(echo "$base_img" | cut -d':' -f1-2 | sed 's/AS.*//i' | tr -d ' ')
|
|
|
|
if [ -z "$base_img" ] || [[ "$base_img" == *"AS"* ]]; then
|
|
continue
|
|
fi
|
|
|
|
if verify_image "$base_img"; then
|
|
log_info " ✓ $base_img"
|
|
else
|
|
log_warn " ✗ $base_img (未找到)"
|
|
# 尝试从 base-images.tar 中加载
|
|
if [ -f "$IMAGES_DIR/base-images.tar" ]; then
|
|
log_info " 尝试从 tar 包加载..."
|
|
docker load -i "$IMAGES_DIR/base-images.tar" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# 执行离线构建
|
|
log_info "开始构建..."
|
|
|
|
# 构建参数
|
|
local build_args=()
|
|
|
|
# 为需要外部资源的服务添加 build-arg
|
|
case "$service_name" in
|
|
runtime|deer-flow-backend|deer-flow-frontend)
|
|
if [ -d "$RESOURCES_DIR" ]; then
|
|
build_args+=("--build-arg" "RESOURCES_DIR=$RESOURCES_DIR")
|
|
fi
|
|
;;
|
|
backend-python)
|
|
if [ -d "$RESOURCES_DIR" ]; then
|
|
build_args+=("--build-arg" "RESOURCES_DIR=$RESOURCES_DIR")
|
|
build_args+=("--build-arg" "DATAX_LOCAL_PATH=$RESOURCES_DIR/DataX")
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
# 执行构建
|
|
if docker buildx build \
|
|
--builder offline-builder \
|
|
--cache-from "type=local,src=$cache_file" \
|
|
--pull=false \
|
|
--output "type=docker" \
|
|
"${build_args[@]}" \
|
|
-f "$dockerfile" \
|
|
-t "$image_name:$VERSION" \
|
|
. 2>&1; then
|
|
|
|
log_info "✓ $service_name 构建成功"
|
|
return 0
|
|
else
|
|
log_error "✗ $service_name 构建失败"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# 主流程
|
|
main() {
|
|
log_info "======================================"
|
|
log_info "BuildKit 离线构建"
|
|
log_info "======================================"
|
|
log_info "缓存目录: $CACHE_DIR"
|
|
log_info "版本: $VERSION"
|
|
|
|
# 步骤 1: 设置构建器
|
|
setup_buildx
|
|
|
|
# 步骤 2: 加载基础镜像
|
|
load_base_images
|
|
|
|
# 步骤 3: 定义服务列表
|
|
declare -A SERVICES=(
|
|
["database"]="datamate-database:scripts/images/database/Dockerfile"
|
|
["gateway"]="datamate-gateway:scripts/images/gateway/Dockerfile"
|
|
["backend"]="datamate-backend:scripts/images/backend/Dockerfile"
|
|
["frontend"]="datamate-frontend:scripts/images/frontend/Dockerfile"
|
|
["runtime"]="datamate-runtime:scripts/images/runtime/Dockerfile"
|
|
["backend-python"]="datamate-backend-python:scripts/images/backend-python/Dockerfile"
|
|
["deer-flow-backend"]="deer-flow-backend:scripts/images/deer-flow-backend/Dockerfile"
|
|
["deer-flow-frontend"]="deer-flow-frontend:scripts/images/deer-flow-frontend/Dockerfile"
|
|
["mineru"]="datamate-mineru:scripts/images/mineru/Dockerfile"
|
|
)
|
|
|
|
# 步骤 4: 批量构建
|
|
log_info ""
|
|
log_info "======================================"
|
|
log_info "开始批量构建"
|
|
log_info "======================================"
|
|
|
|
local failed=()
|
|
local succeeded=()
|
|
|
|
for service_name in "${!SERVICES[@]}"; do
|
|
IFS=':' read -r image_name dockerfile <<< "${SERVICES[$service_name]}"
|
|
|
|
if offline_build "$service_name" "$image_name" "$dockerfile"; then
|
|
succeeded+=("$service_name")
|
|
else
|
|
failed+=("$service_name")
|
|
fi
|
|
echo ""
|
|
done
|
|
|
|
# 步骤 5: 汇总结果
|
|
log_info "======================================"
|
|
log_info "构建完成"
|
|
log_info "======================================"
|
|
|
|
if [ ${#succeeded[@]} -gt 0 ]; then
|
|
log_info "成功 (${#succeeded[@]}): ${succeeded[*]}"
|
|
fi
|
|
|
|
if [ ${#failed[@]} -gt 0 ]; then
|
|
log_error "失败 (${#failed[@]}): ${failed[*]}"
|
|
exit 1
|
|
else
|
|
log_info "✓ 所有服务构建成功!"
|
|
echo ""
|
|
log_info "镜像列表:"
|
|
docker images --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}" | grep -E "(datamate-|deer-flow-)" || true
|
|
fi
|
|
}
|
|
|
|
# 执行主流程
|
|
main "$@"
|