#!/usr/bin/env bash # # 构建离线 Docker 交付包:包含应用镜像、pgvector(PostgreSQL)、Redis 及统一的 docker-compose # 使用方式: # chmod +x build-docker-bundle.sh # ./build-docker-bundle.sh # 使用默认版本号(时间戳) # VERSION=v1.2.3 ./build-docker-bundle.sh # 指定版本号 set -euo pipefail ROOT_DIR="$(cd "$(dirname "$0")" && pwd)" TIMESTAMP="$(date +%Y%m%d_%H%M%S)" APP_NAME="gdyd-zhpb-zgf" VERSION="${VERSION:-${TIMESTAMP}}" OUTPUT_DIR="${ROOT_DIR}/docker-bundle-${VERSION}" IMAGES_DIR="${OUTPUT_DIR}/images" CONFIG_DIR="${OUTPUT_DIR}/config" INIT_DIR="${OUTPUT_DIR}/pgvector-init" APP_IMAGE="${APP_NAME}-app:${VERSION}" PGVECTOR_IMAGE="${PGVECTOR_IMAGE:-pgvector/pgvector:pg16-trixie}" REDIS_IMAGE="${REDIS_IMAGE:-redis:7-alpine}" # 默认账号密码,可在执行脚本前通过环境变量覆盖 POSTGRES_DB="${POSTGRES_DB:-vector_db}" POSTGRES_USER="${POSTGRES_USER:-vectoruser}" POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-vectorpass}" POSTGRES_PORT="${POSTGRES_PORT:-5433}" REDIS_PASSWORD="${REDIS_PASSWORD:-TNh8jiy8WpEweGLZ}" REDIS_PORT="${REDIS_PORT:-26739}" APP_PORT="${APP_PORT:-8000}" print_step() { printf '\n\033[1;34m==> %s\033[0m\n' "$1" } print_info() { printf ' %s\n' "$1" } abort() { printf '\033[1;31m[错误]\033[0m %s\n' "$1" >&2 exit 1 } check_command() { command -v "$1" >/dev/null 2>&1 || abort "未找到命令:$1,请先安装。" } APT_UPDATED=0 SUDO_CMD="" if command -v sudo >/dev/null 2>&1 && [[ ${EUID:-0} -ne 0 ]]; then SUDO_CMD="sudo" fi update_apt_once() { if [[ ${APT_UPDATED} -eq 0 ]]; then print_step "更新 apt 软件源" ${SUDO_CMD} env DEBIAN_FRONTEND=noninteractive apt-get update -y APT_UPDATED=1 fi } install_with_apt() { local package="$1" local friendly="$2" if ! command -v apt-get >/dev/null 2>&1; then abort "缺少 ${friendly},但当前环境不支持自动安装(未检测到 apt-get)。" fi update_apt_once print_step "安装 ${friendly}" ${SUDO_CMD} env DEBIAN_FRONTEND=noninteractive apt-get install -y "${package}" } ensure_maven() { if ! command -v mvn >/dev/null 2>&1; then install_with_apt "maven" "Maven" fi } ensure_java21() { local major="" if command -v java >/dev/null 2>&1; then local version_line version_line="$(java -version 2>&1 | head -n 1 | cut -d'"' -f2)" major="${version_line%%.*}" if [[ "${major}" == "1" ]]; then major="$(echo "${version_line}" | cut -d'.' -f2)" fi if [[ -n "${major}" && "${major}" -ge 21 ]]; then return fi print_info "检测到 Java 版本 ${major:-unknown},低于 21,准备自动安装 OpenJDK 21。" else print_info "未检测到 Java,准备自动安装 OpenJDK 21。" fi install_with_apt "openjdk-21-jdk" "OpenJDK 21" } ensure_clean_dir() { if [[ -d "$1" ]]; then rm -rf "$1" fi mkdir -p "$1" } ensure_java21 ensure_maven check_command docker print_step "编译 Spring Boot 应用" mvn -q -U -DskipTests clean package JAR_FILE="$(find "${ROOT_DIR}/target" -maxdepth 1 -type f -name "*.jar" ! -name "*-sources.jar" ! -name "*-javadoc.jar" | head -n 1)" [[ -n "${JAR_FILE}" ]] || abort "未找到可用的 JAR 文件,请检查 Maven 构建结果。" print_info "找到 JAR:${JAR_FILE}" print_step "构建应用镜像 ${APP_IMAGE}" docker build \ --build-arg VERSION="${VERSION}" \ --build-arg BUILD_TIME="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \ -t "${APP_IMAGE}" \ "${ROOT_DIR}" print_step "拉取依赖镜像" docker pull "${PGVECTOR_IMAGE}" docker pull "${REDIS_IMAGE}" print_step "生成离线交付包目录 ${OUTPUT_DIR}" ensure_clean_dir "${OUTPUT_DIR}" mkdir -p "${IMAGES_DIR}" "${CONFIG_DIR}" "${INIT_DIR}" if [[ -f "${ROOT_DIR}/src/main/resources/application.yml" ]]; then cp "${ROOT_DIR}/src/main/resources/application.yml" "${CONFIG_DIR}/application.yml" print_info "已复制默认 application.yml 到 ${CONFIG_DIR}/application.yml" else print_info "未找到 src/main/resources/application.yml,可手动放置到 ${CONFIG_DIR}/application.yml" fi cat > "${OUTPUT_DIR}/.env" < "${INIT_DIR}/001-create-vector-extension.sql" <<'EOF' CREATE EXTENSION IF NOT EXISTS vector; EOF cat > "${OUTPUT_DIR}/docker-compose.yml" < "${OUTPUT_DIR}/README.md" <