#!/bin/bash # ═══════════════════════════════════════════════════════════════════════════════ # Hawkbit Monolith + UI 本地启动脚本(替代 docker-compose) # ═══════════════════════════════════════════════════════════════════════════════ # # 两种模式: # h2 H2 内嵌数据库(零依赖,开发测试用) # postgres PostgreSQL + RabbitMQ(模拟生产环境) # # 用法: # ./start-hawkbit.sh h2 # H2 模式,端口 9090/9091 # ./start-hawkbit.sh postgres # PostgreSQL 模式,端口 9090/9091 # ./start-hawkbit.sh stop # 停止所有 # ./start-hawkbit.sh status # 查看状态 # ./start-hawkbit.sh dbinit # 仅执行数据库初始化(postgres 模式) # ./start-hawkbit.sh help # 帮助 # # 对应 docker-compose 文件: # h2 → docker-compose-monolith-with-ui-postgres.yml (但用 H2) # postgres → docker-compose-monolith-dbinit-with-ui-postgres.yml # ═══════════════════════════════════════════════════════════════════════════════ set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" HAWKBIT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" LOG_DIR="$SCRIPT_DIR/logs" PID_DIR="$SCRIPT_DIR/pid" # ── 配置 ───────────────────────────────────────────────────────────────────── MONOLITH_JAR="$HAWKBIT_ROOT/hawkbit-monolith/hawkbit-update-server/target/hawkbit-update-server-0-SNAPSHOT.jar" UI_JAR="$HAWKBIT_ROOT/hawkbit-ui/target/hawkbit-ui.jar" DBINIT_JAR="$HOME/.m2/repository/org/eclipse/hawkbit/hawkbit-repository-jpa-init/0-SNAPSHOT/hawkbit-repository-jpa-init-0-SNAPSHOT.jar" # 端口 MONOLITH_PORT=9090 UI_PORT=9091 # PostgreSQL 配置(postgres 模式) PG_HOST=localhost PG_PORT=5432 PG_DB=hawkbit PG_USER=postgres PG_PASSWORD=hawkbit # RabbitMQ 配置(postgres 模式) RABBIT_HOST=localhost RABBIT_PORT=5672 # H2 配置 H2_DB_DIR="$SCRIPT_DIR/h2data" # JVM 参数 MONOLITH_XMS=512m MONOLITH_XMX=1024m UI_XMS=256m UI_XMX=512m # ── 颜色 ───────────────────────────────────────────────────────────────────── RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m' info() { echo -e "${GREEN}[INFO]${NC} $*"; } warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } error() { echo -e "${RED}[ERROR]${NC} $*"; } step() { echo -e "\n${BLUE}═══ $* ═══${NC}"; } # ── 初始化 ─────────────────────────────────────────────────────────────────── init() { mkdir -p "$LOG_DIR" "$PID_DIR" } # ── 清理 ───────────────────────────────────────────────────────────────────── cleanup_pid() { local pid_file="$1" local name="$2" if [ -f "$pid_file" ]; then local pid=$(cat "$pid_file") if kill -0 "$pid" 2>/dev/null; then info "停止 $name (PID: $pid)" kill "$pid" 2>/dev/null || true sleep 2 kill -9 "$pid" 2>/dev/null || true fi rm -f "$pid_file" fi } # ── 停止 ───────────────────────────────────────────────────────────────────── do_stop() { step "停止 Hawkbit 服务" cleanup_pid "$PID_DIR/hawkbit-monolith.pid" "hawkbit-monolith" cleanup_pid "$PID_DIR/hawkbit-ui.pid" "hawkbit-ui" if [ -f "$PID_DIR/postgres.pid" ]; then local pg_pid=$(cat "$PID_DIR/postgres.pid") if kill -0 "$pg_pid" 2>/dev/null; then info "停止 PostgreSQL (PID: $pg_pid)" pg_ctl -D "$SCRIPT_DIR/pgdata" stop 2>/dev/null || kill "$pg_pid" fi rm -f "$PID_DIR/postgres.pid" fi if [ -f "$PID_DIR/rabbitmq.pid" ]; then local rb_pid=$(cat "$PID_DIR/rabbitmq.pid") if kill -0 "$rb_pid" 2>/dev/null; then info "停止 RabbitMQ (PID: $rb_pid)" kill "$rb_pid" 2>/dev/null || true fi rm -f "$PID_DIR/rabbitmq.pid" fi info "全部已停止" } # ── 状态 ───────────────────────────────────────────────────────────────────── do_status() { echo "Hawkbit 服务状态:" echo "─────────────────" for svc in "hawkbit-monolith:${MONOLITH_PORT}:$PID_DIR/hawkbit-monolith.pid" \ "hawkbit-ui:${UI_PORT}:$PID_DIR/hawkbit-ui.pid"; do IFS=':' read -r name port pid_file <<< "$svc" if [ -f "$pid_file" ] && kill -0 "$(cat "$pid_file")" 2>/dev/null; then echo -e " ${GREEN}●${NC} $name (port $port, PID $(cat "$pid_file"))" else echo -e " ${RED}○${NC} $name (port $port)" fi done echo "" if [ -f "$PID_DIR/postgres.pid" ] && kill -0 "$(cat "$PID_DIR/postgres.pid")" 2>/dev/null; then echo -e " ${GREEN}●${NC} PostgreSQL (port $PG_PORT)" else echo -e " ${YELLOW}○${NC} PostgreSQL (not managed by this script)" fi } # ── 等待端口 ───────────────────────────────────────────────────────────────── wait_for_port() { local host=$1 port=$2 timeout=${3:-30} label=${4:-"$host:$port"} local waited=0 while ! (echo >"/dev/tcp/$host/$port") 2>/dev/null; do sleep 1 waited=$((waited + 1)) if [ $waited -ge $timeout ]; then error "$label 启动超时(${timeout}s)" return 1 fi done info "$label 就绪" } # ── H2 模式 ────────────────────────────────────────────────────────────────── start_h2() { step "H2 模式启动" if [ -f "$PID_DIR/hawkbit-monolith.pid" ] && kill -0 "$(cat "$PID_DIR/hawkbit-monolith.pid")" 2>/dev/null; then warn "hawkbit-monolith 已在运行" else mkdir -p "$H2_DB_DIR" info "启动 hawkbit-update-server (H2, 端口 $MONOLITH_PORT)..." nohup java \ -Xms${MONOLITH_XMS} -Xmx${MONOLITH_XMX} \ -jar "$MONOLITH_JAR" \ --server.port=$MONOLITH_PORT \ --hawkbit.dmf.rabbitmq.enabled=false \ --hawkbit.server.ddi.security.authentication.anonymous.enabled=true \ --hawkbit.server.ui.demo.user=admin \ --hawkbit.server.ui.demo.password=admin \ --spring.datasource.url="jdbc:h2:file:$H2_DB_DIR/hawkbit;DB_CLOSE_DELAY=-1" \ >> "$LOG_DIR/hawkbit-monolith.log" 2>&1 & echo $! > "$PID_DIR/hawkbit-monolith.pid" fi start_ui } # ── PostgreSQL 模式 ────────────────────────────────────────────────────────── start_postgres() { step "PostgreSQL 模式启动" # ── 检查 PostgreSQL ──────────────────────────────────────────────── if ! command -v pg_isready &>/dev/null; then error "PostgreSQL 未安装。请先执行: sudo apt install postgresql-16 rabbitmq-server" echo "" echo "或者用 H2 模式: $0 h2" exit 1 fi # 确保 PostgreSQL 运行 if ! pg_isready -h $PG_HOST -p $PG_PORT &>/dev/null; then info "启动 PostgreSQL..." sudo pg_ctlcluster 16 main start 2>/dev/null || \ sudo service postgresql start 2>/dev/null || { error "无法启动 PostgreSQL" exit 1 } fi info "PostgreSQL 已就绪" # ── 创建数据库和用户 ────────────────────────────────────────────── sudo -u postgres psql -tc "SELECT 1 FROM pg_roles WHERE rolname='$PG_USER'" 2>/dev/null | grep -q 1 || { info "创建数据库用户 $PG_USER" sudo -u postgres psql -c "CREATE USER $PG_USER WITH PASSWORD '$PG_PASSWORD';" 2>/dev/null sudo -u postgres psql -c "ALTER USER $PG_USER CREATEDB;" 2>/dev/null } sudo -u postgres psql -tc "SELECT 1 FROM pg_database WHERE datname='$PG_DB'" 2>/dev/null | grep -q 1 || { info "创建数据库 $PG_DB" sudo -u postgres psql -c "CREATE DATABASE $PG_DB OWNER $PG_USER;" 2>/dev/null } # ── RabbitMQ ─────────────────────────────────────────────────────── if ! rabbitmqctl status &>/dev/null 2>&1; then info "启动 RabbitMQ..." sudo service rabbitmq-server start 2>/dev/null || { warn "RabbitMQ 启动失败,禁用 DMF" RABBIT_DISABLED=true } fi [ -z "$RABBIT_DISABLED" ] && info "RabbitMQ 已就绪" # ── DB Init (Flyway 迁移) ────────────────────────────────────────── if [ -f "$DBINIT_JAR" ]; then step "数据库初始化 (Flyway 迁移)" info "执行 Hawkbit DB Init..." java \ -Xms256m -Xmx512m \ -classpath "$DBINIT_JAR" \ -Dspring.datasource.url="jdbc:postgresql://${PG_HOST}:${PG_PORT}/${PG_DB}" \ -Dspring.datasource.username="$PG_USER" \ -Dspring.datasource.password="$PG_PASSWORD" \ org.eclipse.hawkbit.repository.jpa.init.HawkbitFlywayDbInit \ migrate 2>&1 | tee "$LOG_DIR/dbinit.log" if [ ${PIPESTATUS[0]} -ne 0 ]; then error "DB 初始化失败,查看日志: $LOG_DIR/dbinit.log" exit 1 fi info "DB 初始化完成" else warn "DB Init JAR 未找到: $DBINIT_JAR" warn "跳过 DB 初始化,由 Monolith 内嵌 Flyway 执行" fi # ── 启动 Monolith ───────────────────────────────────────────────── info "启动 hawkbit-update-server (PostgreSQL, 端口 $MONOLITH_PORT)..." local rabbit_args="" if [ "$RABBIT_DISABLED" = true ]; then rabbit_args="--hawkbit.dmf.rabbitmq.enabled=false" else rabbit_args="--spring.rabbitmq.host=$RABBIT_HOST" fi nohup java \ -Xms${MONOLITH_XMS} -Xmx${MONOLITH_XMX} \ -Dspring.profiles.active=postgresql \ -jar "$MONOLITH_JAR" \ --server.port=$MONOLITH_PORT \ --spring.datasource.url="jdbc:postgresql://${PG_HOST}:${PG_PORT}/${PG_DB}" \ --spring.datasource.username="$PG_USER" \ --spring.datasource.password="$PG_PASSWORD" \ $rabbit_args \ --hawkbit.server.ddi.security.authentication.anonymous.enabled=true \ --hawkbit.server.ui.demo.user=admin \ --hawkbit.server.ui.demo.password=admin \ >> "$LOG_DIR/hawkbit-monolith.log" 2>&1 & echo $! > "$PID_DIR/hawkbit-monolith.pid" wait_for_port localhost $MONOLITH_PORT 60 "hawkbit-monolith" start_ui } # ── 启动 UI ────────────────────────────────────────────────────────────────── start_ui() { if [ -f "$PID_DIR/hawkbit-ui.pid" ] && kill -0 "$(cat "$PID_DIR/hawkbit-ui.pid")" 2>/dev/null; then warn "hawkbit-ui 已在运行" else info "启动 hawkbit-ui (端口 $UI_PORT)..." nohup java \ -Xms${UI_XMS} -Xmx${UI_XMX} \ -jar "$UI_JAR" \ --server.port=$UI_PORT \ --hawkbit.server.mgmturl="http://127.0.0.1:${MONOLITH_PORT}" \ >> "$LOG_DIR/hawkbit-ui.log" 2>&1 & echo $! > "$PID_DIR/hawkbit-ui.pid" fi wait_for_port localhost $UI_PORT 30 "hawkbit-ui" step "启动完成" echo "" echo " ┌─────────────────────────────────────────────────────┐" echo " │ Hawkbit Management API : http://localhost:${MONOLITH_PORT}/rest/v1 │" echo " │ Hawkbit DDI API : http://localhost:${MONOLITH_PORT}/DEFAULT/controller/v1 │" echo " │ Hawkbit UI : http://localhost:${UI_PORT} │" echo " │ Admin 账号 : admin / admin │" echo " ├─────────────────────────────────────────────────────┤" echo " │ 日志目录 : $LOG_DIR" echo " │ PID 目录 : $PID_DIR" echo " │ 停止 : $0 stop" echo " │ 状态 : $0 status" echo " └─────────────────────────────────────────────────────┘" } # ── 仅 DB 初始化 ───────────────────────────────────────────────────────────── do_dbinit() { step "数据库初始化" if ! pg_isready -h $PG_HOST -p $PG_PORT &>/dev/null; then error "PostgreSQL 未运行" exit 1 fi if [ ! -f "$DBINIT_JAR" ]; then error "DB Init JAR 未找到: $DBINIT_JAR" exit 1 fi java \ -Xms256m -Xmx512m \ -classpath "$DBINIT_JAR" \ -Dspring.datasource.url="jdbc:postgresql://${PG_HOST}:${PG_PORT}/${PG_DB}" \ -Dspring.datasource.username="$PG_USER" \ -Dspring.datasource.password="$PG_PASSWORD" \ org.eclipse.hawkbit.repository.jpa.init.HawkbitFlywayDbInit \ migrate info "DB 初始化完成" } # ── 帮助 ───────────────────────────────────────────────────────────────────── do_help() { cat << 'EOF' ╔══════════════════════════════════════════════════════════════════╗ ║ Hawkbit Monolith + UI 本地启动脚本 ║ ╚══════════════════════════════════════════════════════════════════╝ 用法: start-hawkbit.sh 命令: h2 H2 内嵌数据库模式(零依赖,开发测试推荐) postgres PostgreSQL 模式(需先: sudo apt install postgresql-16 rabbitmq-server) stop 停止所有服务 status 查看服务状态 dbinit 仅执行 DB 初始化(postgres 模式) help 显示此帮助 示例: ./start-hawkbit.sh h2 # 快速启动,用 H2 数据库 ./start-hawkbit.sh postgres # 生产模式,用 PostgreSQL ./start-hawkbit.sh stop # 停止 ./start-hawkbit.sh status # 查看 对应 Docker Compose: h2 ≈ docker-compose-monolith-with-ui-postgres.yml (但用 H2) postgres ≈ docker-compose-monolith-dbinit-with-ui-postgres.yml 区别: H2 PostgreSQL DB 文件存储 独立服务 安装 零依赖 sudo apt install postgresql-16 数据路径 h2data/ PostgreSQL pgdata/ 适用场景 开发/测试 生产模拟 数据持久化 是 是 H2 模式启动后: MGMT API: http://localhost:9090/rest/v1 DDI API: http://localhost:9090/DEFAULT/controller/v1 UI: http://localhost:9091 Admin: admin / admin EOF } # ═══════════════════════════════════════════════════════════════════════════════ # Main # ═══════════════════════════════════════════════════════════════════════════════ init case "${1:-help}" in h2) start_h2 ;; postgres) start_postgres ;; stop) do_stop ;; status) do_status ;; dbinit) do_dbinit ;; help|--help|-h) do_help ;; *) error "未知命令: $1" do_help exit 1 ;; esac