fix: podman can't deploy
This commit is contained in:
@@ -8,6 +8,7 @@ data/
|
|||||||
*.iml
|
*.iml
|
||||||
docs/
|
docs/
|
||||||
frontend/
|
frontend/
|
||||||
|
scripts/
|
||||||
README.md
|
README.md
|
||||||
Dockerfile
|
Dockerfile
|
||||||
docker-compose.yml
|
docker-compose.yml
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ LAB_JWT_SECRET=lab-reservation-docker-secret-key-change-me-2026
|
|||||||
JAVA_OPTS=-Xms256m -Xmx512m
|
JAVA_OPTS=-Xms256m -Xmx512m
|
||||||
|
|
||||||
# ---------- 端口映射(宿主机端口) ----------
|
# ---------- 端口映射(宿主机端口) ----------
|
||||||
# 前端 Nginx 端口,若本机 80 被占用可改为 8088
|
# 前端 Nginx 端口(默认 8088:rootless Podman 无法绑定 80 等特权端口)
|
||||||
WEB_PORT=80
|
WEB_PORT=8088
|
||||||
# 后端直连端口(调试用,前端经 Nginx 代理访问后端)
|
# 后端直连端口(调试用,前端经 Nginx 代理访问后端)
|
||||||
BACKEND_PORT=8080
|
BACKEND_PORT=8080
|
||||||
|
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -42,3 +42,5 @@ data/
|
|||||||
### 本地环境变量(保留示例文件) ###
|
### 本地环境变量(保留示例文件) ###
|
||||||
.env
|
.env
|
||||||
!.env.example
|
!.env.example
|
||||||
|
|
||||||
|
mariadb-data
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
# =====================================================================
|
# =====================================================================
|
||||||
|
|
||||||
# ---------- 构建阶段 ----------
|
# ---------- 构建阶段 ----------
|
||||||
FROM maven:3.9-eclipse-temurin-21 AS build
|
FROM docker.io/library/maven:3.9-eclipse-temurin-21 AS build
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
|
||||||
# MAVEN_MIRROR=false 时跳过阿里云镜像配置,使用默认中央仓库
|
# MAVEN_MIRROR=false 时跳过阿里云镜像配置,使用默认中央仓库
|
||||||
@@ -29,7 +29,7 @@ RUN mvn -B -q -Dmaven.test.skip=true package \
|
|||||||
&& cp target/*.jar /build/app.jar
|
&& cp target/*.jar /build/app.jar
|
||||||
|
|
||||||
# ---------- 运行阶段 ----------
|
# ---------- 运行阶段 ----------
|
||||||
FROM eclipse-temurin:21-jre
|
FROM docker.io/library/eclipse-temurin:21-jre
|
||||||
WORKDIR /opt/app
|
WORKDIR /opt/app
|
||||||
|
|
||||||
# 以非 root 用户运行
|
# 以非 root 用户运行
|
||||||
|
|||||||
48
README.md
48
README.md
@@ -48,7 +48,7 @@ pnpm build # 生产构建(vue-tsc 类型检查 + vite build)
|
|||||||
```bash
|
```bash
|
||||||
cp .env.example .env # 可选:按需修改端口/密码/JWT 密钥
|
cp .env.example .env # 可选:按需修改端口/密码/JWT 密钥
|
||||||
docker compose up -d --build # 构建并启动 mariadb + backend + frontend
|
docker compose up -d --build # 构建并启动 mariadb + backend + frontend
|
||||||
# 前端:http://localhost (Nginx 托管静态资源并反代 /api)
|
# 前端:http://localhost:8088 (Nginx 托管静态资源并反代 /api)
|
||||||
# 后端:http://localhost:8080
|
# 后端:http://localhost:8080
|
||||||
docker compose ps # 查看健康状态
|
docker compose ps # 查看健康状态
|
||||||
docker compose down # 停止容器,保留数据库数据卷
|
docker compose down # 停止容器,保留数据库数据卷
|
||||||
@@ -57,10 +57,53 @@ docker compose down -v # 停止并清空数据库(下次启动重建
|
|||||||
|
|
||||||
- 编排:`mariadb:11.4`(数据持久化到命名卷)+ 后端多阶段构建镜像(JRE 21,非 root 运行)+ Nginx 前端镜像
|
- 编排:`mariadb:11.4`(数据持久化到命名卷)+ 后端多阶段构建镜像(JRE 21,非 root 运行)+ Nginx 前端镜像
|
||||||
- 三个服务均配置健康检查,后端与前端会等待上游就绪后再启动
|
- 三个服务均配置健康检查,后端与前端会等待上游就绪后再启动
|
||||||
|
- 国内加速:后端构建自动使用**阿里云 Maven 镜像**(`docker/maven-settings.xml`),
|
||||||
|
前端使用 **npmmirror**(淘宝 npm 镜像);如需走官方源:
|
||||||
|
`docker build --build-arg MAVEN_MIRROR=false .`、`docker build --build-arg NPM_REGISTRY=https://registry.npmjs.org ./frontend`
|
||||||
- 可用环境变量(见 `.env.example`):`WEB_PORT`(默认 80)、`BACKEND_PORT`(默认 8080)、
|
- 可用环境变量(见 `.env.example`):`WEB_PORT`(默认 80)、`BACKEND_PORT`(默认 8080)、
|
||||||
`DB_NAME` / `DB_USER` / `DB_PASSWORD` / `DB_ROOT_PASSWORD`、`LAB_JWT_SECRET`、`JAVA_OPTS`
|
`DB_NAME` / `DB_USER` / `DB_PASSWORD` / `DB_ROOT_PASSWORD`、`LAB_JWT_SECRET`、`JAVA_OPTS`
|
||||||
|
|
||||||
### 4. 演示账号
|
### 4. Podman 部署
|
||||||
|
|
||||||
|
Podman 的 compose 实现与 Docker Compose 在「镜像名/容器名解析」上存在差异,
|
||||||
|
直接 `podman compose up -d` 可能出现以下报错:
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: short-name "lab-reservation_backend" did not resolve to an alias ...
|
||||||
|
Error: "lab-backend" is not a valid container, cannot be used as a dependency ...
|
||||||
|
```
|
||||||
|
|
||||||
|
原因是 compose 未先构建本地镜像,Podman 便把 `lab-reservation_backend` 当作待拉取的短名镜像,
|
||||||
|
而系统未配置 `unqualified-search-registries`。**先显式构建镜像即可解决**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/podman-deploy.sh up # 一键:构建镜像 -> 启动 -> 健康检查 -> 打印访问地址
|
||||||
|
./scripts/podman-deploy.sh down # 停止(保留数据库数据卷)
|
||||||
|
./scripts/podman-deploy.sh down-v # 停止并清空数据库
|
||||||
|
./scripts/podman-deploy.sh logs # 查看日志
|
||||||
|
```
|
||||||
|
|
||||||
|
手动等价步骤:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
podman build -t lab-backend:latest .
|
||||||
|
podman build -t lab-frontend:latest ./frontend
|
||||||
|
podman compose up -d # 或 podman-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Podman 注意事项:
|
||||||
|
|
||||||
|
- **端口**:前端默认映射到 `8088`(rootless Podman 无法绑定 80 等特权端口),
|
||||||
|
如需 80 可 `WEB_PORT=80 ./scripts/podman-deploy.sh up`,或将
|
||||||
|
`net.ipv4.ip_unprivileged_port_start=0` 写入 `/etc/sysctl.d/` 后 `sysctl --system`
|
||||||
|
- **短名镜像**:本项目所有基础镜像已写成全限定名(`docker.io/library/...`)。
|
||||||
|
若自行添加镜像,建议同样全限定,或在
|
||||||
|
`~/.config/containers/registries.conf` 配置 `unqualified-search-registries = ["docker.io"]`
|
||||||
|
- **旧版 podman-compose**:若提示不支持 `depends_on.condition`,请升级到 1.0.6+,
|
||||||
|
或先 `podman-compose up -d mariadb`,等数据库健康后再 `podman-compose up -d backend frontend`
|
||||||
|
- **SELinux(Fedora/RHEL)**:本项目只使用命名卷,无需额外 `:Z` 标记;如自定义 bind mount 请自行加 `:Z`
|
||||||
|
|
||||||
|
### 5. 演示账号
|
||||||
|
|
||||||
| 账号 | 密码 | 角色 | 典型能力 |
|
| 账号 | 密码 | 角色 | 典型能力 |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
@@ -140,6 +183,7 @@ mvn verify # 运行全部测试并执行 JaCoCo 覆盖率门禁
|
|||||||
├── Dockerfile # 后端多阶段构建镜像
|
├── Dockerfile # 后端多阶段构建镜像
|
||||||
├── docker-compose.yml # mariadb + backend + frontend 一键编排
|
├── docker-compose.yml # mariadb + backend + frontend 一键编排
|
||||||
├── .env.example # 部署环境变量示例(复制为 .env 使用)
|
├── .env.example # 部署环境变量示例(复制为 .env 使用)
|
||||||
|
├── scripts/podman-deploy.sh # Podman 一键部署脚本
|
||||||
└── docs/design.md # 需求分析与详细设计
|
└── docs/design.md # 需求分析与详细设计
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,17 @@
|
|||||||
# 实验室预约与资源管理平台 - 一键部署编排
|
# 实验室预约与资源管理平台 - 一键部署编排
|
||||||
#
|
#
|
||||||
# 启动:docker compose up -d --build
|
# 启动:docker compose up -d --build
|
||||||
# 访问:前端 http://localhost:${WEB_PORT:-80}
|
# 访问:前端 http://localhost:${WEB_PORT:-8088}
|
||||||
# 后端 http://localhost:${BACKEND_PORT:-8080}
|
# 后端 http://localhost:${BACKEND_PORT:-8080}
|
||||||
# 停止:docker compose down (保留数据库数据卷)
|
# 停止:docker compose down (保留数据库数据卷)
|
||||||
# docker compose down -v (同时删除数据库数据,首次初始化会重建演示数据)
|
# docker compose down -v (同时删除数据库数据,首次初始化会重建演示数据)
|
||||||
#
|
#
|
||||||
# 可通过环境变量覆盖:DB_NAME / DB_USER / DB_PASSWORD / DB_ROOT_PASSWORD /
|
# 可通过环境变量覆盖:DB_NAME / DB_USER / DB_PASSWORD / DB_ROOT_PASSWORD /
|
||||||
# LAB_JWT_SECRET / WEB_PORT / BACKEND_PORT
|
# LAB_JWT_SECRET / JAVA_OPTS / WEB_PORT / BACKEND_PORT
|
||||||
|
#
|
||||||
|
# Podman 部署请使用 scripts/podman-deploy.sh(会先构建镜像再启动),或手动执行:
|
||||||
|
# podman build -t lab-backend:latest . && podman build -t lab-frontend:latest ./frontend
|
||||||
|
# podman compose up -d
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
|
|
||||||
name: lab-reservation
|
name: lab-reservation
|
||||||
@@ -84,7 +88,7 @@ services:
|
|||||||
backend:
|
backend:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
ports:
|
ports:
|
||||||
- "${WEB_PORT:-80}:80"
|
- "${WEB_PORT:-8088}:80"
|
||||||
networks:
|
networks:
|
||||||
- lab-net
|
- lab-net
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
# =====================================================================
|
# =====================================================================
|
||||||
|
|
||||||
# ---------- 构建阶段 ----------
|
# ---------- 构建阶段 ----------
|
||||||
FROM node:24-alpine AS build
|
FROM docker.io/library/node:24-alpine AS build
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# 使用 npmmirror 加速 npm 全局安装与依赖拉取
|
# 使用 npmmirror 加速 npm 全局安装与依赖拉取
|
||||||
@@ -24,7 +24,7 @@ COPY . .
|
|||||||
RUN pnpm build
|
RUN pnpm build
|
||||||
|
|
||||||
# ---------- 运行阶段 ----------
|
# ---------- 运行阶段 ----------
|
||||||
FROM nginx:1.27-alpine
|
FROM docker.io/library/nginx:1.27-alpine
|
||||||
|
|
||||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
COPY --from=build /app/dist /usr/share/nginx/html
|
COPY --from=build /app/dist /usr/share/nginx/html
|
||||||
|
|||||||
103
scripts/podman-deploy.sh
Executable file
103
scripts/podman-deploy.sh
Executable file
@@ -0,0 +1,103 @@
|
|||||||
|
#!/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
|
||||||
Reference in New Issue
Block a user