#!/usr/bin/env bash # ===================================================================== # 使用 Podman 部署实验室预约平台 # # 用法: # ./scripts/podman-deploy.sh up # 构建镜像并启动(默认) # ./scripts/podman-deploy.sh down # 停止并删除容器(保留数据库数据卷) # ./scripts/podman-deploy.sh down-v # 停止并删除容器与数据卷(重建演示数据) # ./scripts/podman-deploy.sh status # 查看容器状态 # ./scripts/podman-deploy.sh logs # 跟随查看日志 # # 说明:Podman 的 compose 实现(podman-compose)在容器名/镜像名解析上与 # Docker Compose 存在差异,常见报错为: # Error: short-name "lab-reservation_backend" did not resolve to an alias ... # 因此本脚本先显式构建 lab-backend:latest / lab-frontend:latest, # 再交给 compose 启动,确保 compose 直接使用本地镜像而不会尝试拉取。 # ===================================================================== set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" cd "${PROJECT_DIR}" # ---------- 选择可用的 compose 命令 ---------- if podman compose version >/dev/null 2>&1; then COMPOSE=(podman compose) elif command -v podman-compose >/dev/null 2>&1; then COMPOSE=(podman-compose) else echo "错误:未找到 podman compose / podman-compose,请先安装其一" >&2 exit 1 fi # ---------- 读取 .env 中的端口配置(不 source,避免值中含空格被当作命令执行) ---------- read_env() { [ -f .env ] && grep -E "^$1=" .env 2>/dev/null | tail -n 1 | cut -d= -f2- | tr -d '"' || true } WEB_PORT="${WEB_PORT:-$(read_env WEB_PORT)}" BACKEND_PORT="${BACKEND_PORT:-$(read_env BACKEND_PORT)}" WEB_PORT="${WEB_PORT:-8088}" BACKEND_PORT="${BACKEND_PORT:-8080}" # ---------- 构建镜像 ---------- build_images() { echo "==> 构建后端镜像 lab-backend:latest(首次构建需拉取 Maven 依赖)" podman build -t lab-backend:latest . echo "==> 构建前端镜像 lab-frontend:latest" podman build -t lab-frontend:latest ./frontend } # ---------- 等待 HTTP 服务就绪 ---------- wait_ready() { local name="$1" url="$2" tries=60 code for _ in $(seq 1 "${tries}"); do code="$(curl -s -o /dev/null -w '%{http_code}' "${url}" || true)" if [ -n "${code}" ] && [ "${code}" != "000" ]; then echo " ${name} 就绪:${url}(HTTP ${code})" return 0 fi sleep 2 done echo " 警告:${name} 未在预期时间内就绪,请执行 '$0 logs' 查看日志" >&2 return 1 } ACTION="${1:-up}" case "${ACTION}" in up) build_images echo "==> 启动容器" "${COMPOSE[@]}" up -d echo "==> 等待服务就绪" # 后端返回 401 也代表进程已起来 wait_ready "后端" "http://localhost:${BACKEND_PORT}/api/labs" || true wait_ready "前端" "http://localhost:${WEB_PORT}/" || true echo echo "部署完成:" echo " 前端地址:http://localhost:${WEB_PORT}" echo " 后端地址:http://localhost:${BACKEND_PORT}" echo " 演示账号:admin/admin123、teacher1/123456、student1/123456" echo "${COMPOSE[@]}" ps || podman ps --filter "name=lab-" ;; build) build_images ;; down) "${COMPOSE[@]}" down ;; down-v) "${COMPOSE[@]}" down -v ;; status) "${COMPOSE[@]}" ps || podman ps --filter "name=lab-" ;; logs) "${COMPOSE[@]}" logs -f --tail=100 ;; *) echo "用法:$0 [up|build|down|down-v|status|logs]" >&2 exit 1 ;; esac