Files
smart-admin/build-docker-bundle.sh
2025-12-13 23:00:09 +08:00

280 lines
7.7 KiB
Bash
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.

#!/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" <<EOF
# PostgreSQL (pgvector)
POSTGRES_DB=${POSTGRES_DB}
POSTGRES_USER=${POSTGRES_USER}
POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
POSTGRES_PORT=${POSTGRES_PORT}
# Redis
REDIS_PASSWORD=${REDIS_PASSWORD}
REDIS_PORT=${REDIS_PORT}
# 应用
APP_PORT=${APP_PORT}
EOF
cat > "${INIT_DIR}/001-create-vector-extension.sql" <<'EOF'
CREATE EXTENSION IF NOT EXISTS vector;
EOF
cat > "${OUTPUT_DIR}/docker-compose.yml" <<EOF
version: "3.8"
services:
pgvector:
image: ${PGVECTOR_IMAGE}
container_name: ${APP_NAME}-pgvector
restart: unless-stopped
environment:
POSTGRES_DB: \${POSTGRES_DB}
POSTGRES_USER: \${POSTGRES_USER}
POSTGRES_PASSWORD: \${POSTGRES_PASSWORD}
PGDATA: /var/lib/postgresql/data/pgdata
ports:
- "\${POSTGRES_PORT}:5432"
volumes:
- pgvector_data:/var/lib/postgresql/data
- ./pgvector-init:/docker-entrypoint-initdb.d
healthcheck:
test: ["CMD-SHELL", "pg_isready -U \${POSTGRES_USER} -d \${POSTGRES_DB}"]
interval: 20s
timeout: 5s
retries: 5
redis:
image: ${REDIS_IMAGE}
container_name: ${APP_NAME}-redis
restart: unless-stopped
command: ["redis-server", "--appendonly", "yes", "--requirepass", "\${REDIS_PASSWORD}"]
ports:
- "\${REDIS_PORT}:6379"
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "-a", "\${REDIS_PASSWORD}", "ping"]
interval: 20s
timeout: 5s
retries: 5
app:
image: ${APP_IMAGE}
container_name: ${APP_NAME}-app
restart: unless-stopped
depends_on:
pgvector:
condition: service_healthy
redis:
condition: service_healthy
ports:
- "\${APP_PORT}:8000"
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://pgvector:5432/\${POSTGRES_DB}
SPRING_DATASOURCE_USERNAME: \${POSTGRES_USER}
SPRING_DATASOURCE_PASSWORD: \${POSTGRES_PASSWORD}
SPRING_DATA_REDIS_HOST: redis
SPRING_DATA_REDIS_PORT: 6379
SPRING_DATA_REDIS_PASSWORD: \${REDIS_PASSWORD}
SERVER_PORT: 8000
volumes:
- ./config/application.yml:/app/config/application.yml:ro
- app_uploads:/app/uploads
- app_extracts:/app/extracts
- app_logs:/app/logs
volumes:
pgvector_data:
redis_data:
app_uploads:
app_extracts:
app_logs:
EOF
cat > "${OUTPUT_DIR}/README.md" <<EOF
# Docker 离线部署包(${APP_NAME} ${VERSION}
## 包含内容
- 应用镜像:${APP_IMAGE}
- pgvector 数据库镜像:${PGVECTOR_IMAGE}
- Redis 缓存镜像:${REDIS_IMAGE}
- docker-compose.yml / .env / config / pgvector-init 目录
## 使用步骤
1. 将整个目录拷贝到目标服务器并进入该目录:
\`\`\`
cd $(basename "${OUTPUT_DIR}")
\`\`\`
2. 导入离线镜像:
\`\`\`
docker load -i images/app-${VERSION}.tar
docker load -i images/pgvector.tar
docker load -i images/redis.tar
\`\`\`
3. 根据需要修改 \`.env\`、\`config/application.yml\`。
4. 启动:
\`\`\`
docker compose up -d
\`\`\`
5. 查看状态:
\`\`\`
docker compose ps
docker compose logs -f app
\`\`\`
缺省账号密码可通过执行脚本前设置环境变量覆盖,如:
\`\`\`
POSTGRES_PASSWORD=StrongPass REDIS_PASSWORD=Secret ./build-docker-bundle.sh
\`\`\`
EOF
print_step "保存离线镜像到 ${IMAGES_DIR}"
docker save "${APP_IMAGE}" -o "${IMAGES_DIR}/app-${VERSION}.tar"
docker save "${PGVECTOR_IMAGE}" -o "${IMAGES_DIR}/pgvector.tar"
docker save "${REDIS_IMAGE}" -o "${IMAGES_DIR}/redis.tar"
print_step "打包完成"
print_info "输出目录:${OUTPUT_DIR}"
print_info "镜像文件:${IMAGES_DIR}"
print_info "可根据需要编辑 ${OUTPUT_DIR}/.env 与 ${CONFIG_DIR}/application.yml 后,执行 docker compose up -d 启动。"