为什么:用户下午要部署,但全仓没有任何部署产物;且生产漏配密钥会静默使用代码里的 dev 默认值,
属上线阻塞项(04-部署架构 / 12-上线检查清单)。
- 配置外置(三件):DB_URL、JWT 双密钥(**发现 app-secret 此前没外置,一直用 dev 默认值**)、
MEDIA_SIGNATURE_SECRET、MEDIA_SIGNATURE_REQUIRED、CORS_ALLOWED_ORIGINS、MEDIA_ROOT_DIR
- 新增三份 application-prod.yml:签名强制开启、CORS 收紧、媒资指向挂载点、日志级别
- 新增 ProdSecretGuard(@Profile("prod")):密钥缺失/仍是 dev 默认值/不足 32 位、签名未开、
DB_URL 或 CORS 为空 → 一次列全并终止启动(exit 1)
- 新增 /healthz 轻量健康端点:查明 /actuator/health 的"超时"是旧 jar 未重启所致,项目本就
没有 actuator 依赖,故不引新依赖;同时把 /healthz 加进安全白名单
- Docker 化:多阶段 Dockerfile(非 root、tini、依赖层缓存、ffmpeg 只给 worker)、.dockerignore、
自包含 docker-compose.yml(含 PostgreSQL 18 + Redis + 命名卷 + 按序初始化 sql + healthcheck)
- tools/smoke.sh(失败非 0 退出)+ DEPLOY.md(Compose 主线 / 裸 jar 兜底 / 回滚 / 故障速查)
- 实测:mvn test 83 条全过;两入口 package 通过;prod 缺密钥退出码 1 且报错明确、配齐后正常启动;
冒烟脚本全 PASS;本机无 Docker,镜像与 compose 未经构建验证(已在回执如实标注)
73 lines
3.1 KiB
Docker
73 lines
3.1 KiB
Docker
# Author: fruit.com.cn
|
||
#
|
||
# 多阶段构建:maven 构建 fat jar -> JRE 运行(非 root、tini 做 1 号进程)。
|
||
# 三个可运行件共用一份 Dockerfile,用 MODULE 构建参数区分。
|
||
#
|
||
# 构建(在 FruitServer 目录下执行):
|
||
# docker build -t fruitshorts/app-api:latest --build-arg MODULE=fruitshorts-app-api .
|
||
# docker build -t fruitshorts/admin-api:latest --build-arg MODULE=fruitshorts-admin-api .
|
||
# docker build -t fruitshorts/transcode-worker:latest --build-arg MODULE=fruitshorts-transcode-worker \
|
||
# --build-arg INSTALL_FFMPEG=true .
|
||
#
|
||
# 注意:本机当前没有 Docker(见 DEPLOY.md),这份 Dockerfile 未经本机构建验证。
|
||
|
||
# ---------- 1. 构建阶段 ----------
|
||
FROM maven:3.9-eclipse-temurin-21 AS build
|
||
WORKDIR /build
|
||
|
||
# 先只拷各模块 pom 再拉依赖:依赖层可复用,改业务代码不会重新下载依赖
|
||
COPY pom.xml .
|
||
COPY fruitshorts-common/pom.xml fruitshorts-common/
|
||
COPY fruitshorts-framework/pom.xml fruitshorts-framework/
|
||
COPY fruitshorts-module-system/pom.xml fruitshorts-module-system/
|
||
COPY fruitshorts-module-content/pom.xml fruitshorts-module-content/
|
||
COPY fruitshorts-module-media/pom.xml fruitshorts-module-media/
|
||
COPY fruitshorts-module-user/pom.xml fruitshorts-module-user/
|
||
COPY fruitshorts-module-playback/pom.xml fruitshorts-module-playback/
|
||
COPY fruitshorts-module-interaction/pom.xml fruitshorts-module-interaction/
|
||
COPY fruitshorts-module-commerce/pom.xml fruitshorts-module-commerce/
|
||
COPY fruitshorts-module-creator/pom.xml fruitshorts-module-creator/
|
||
COPY fruitshorts-admin-api/pom.xml fruitshorts-admin-api/
|
||
COPY fruitshorts-app-api/pom.xml fruitshorts-app-api/
|
||
COPY fruitshorts-transcode-worker/pom.xml fruitshorts-transcode-worker/
|
||
RUN mvn -B -q -DskipTests dependency:go-offline || true
|
||
|
||
COPY . .
|
||
ARG MODULE=fruitshorts-app-api
|
||
RUN mvn -B -DskipTests -pl ${MODULE} -am package \
|
||
&& cp ${MODULE}/target/*.jar /build/app.jar
|
||
|
||
# ---------- 2. 运行阶段 ----------
|
||
FROM eclipse-temurin:21-jre
|
||
|
||
# ffmpeg 只有转码 worker 需要,用构建参数控制,避免把 80MB 装进两个 API 镜像
|
||
ARG INSTALL_FFMPEG=false
|
||
RUN apt-get update \
|
||
&& apt-get install -y --no-install-recommends tini curl \
|
||
&& if [ "$INSTALL_FFMPEG" = "true" ]; then \
|
||
apt-get install -y --no-install-recommends ffmpeg; \
|
||
fi \
|
||
&& rm -rf /var/lib/apt/lists/*
|
||
|
||
# 非 root 运行;/data/uploads 预留给媒资挂载卷
|
||
RUN groupadd -r fruitshorts \
|
||
&& useradd -r -g fruitshorts -d /app -s /sbin/nologin fruitshorts \
|
||
&& mkdir -p /app /data/uploads \
|
||
&& chown -R fruitshorts:fruitshorts /app /data/uploads
|
||
|
||
WORKDIR /app
|
||
COPY --from=build --chown=fruitshorts:fruitshorts /build/app.jar /app/app.jar
|
||
|
||
USER fruitshorts
|
||
|
||
ENV JAVA_OPTS="-XX:MaxRAMPercentage=75 -XX:+UseG1GC -Duser.timezone=Asia/Shanghai" \
|
||
SPRING_PROFILES_ACTIVE=prod \
|
||
MEDIA_ROOT_DIR=/data/uploads
|
||
|
||
# 健康检查打 /healthz(不依赖 actuator,见 HealthController)
|
||
HEALTHCHECK --interval=30s --timeout=5s --start-period=40s --retries=3 \
|
||
CMD curl -fsS http://127.0.0.1:${SERVER_PORT:-8080}/healthz || exit 1
|
||
|
||
ENTRYPOINT ["/usr/bin/tini", "--"]
|
||
CMD ["sh", "-c", "exec java $JAVA_OPTS -jar /app/app.jar"]
|