docs(spec): 补充九、部署与发布章节(deploy.md 需求落地)
- TOC 添加第九章入口 - 九.1 Maven 构建:移除 fat JAR,添加 maven-jar-plugin + maven-dependency-plugin + maven-assembly-plugin - 九.2 分发包结构:bin/etc/libs/logs 四级目录 - 九.3 start.sh:Docker 用 exec 前台、VM 用 nohup 后台 - 九.4 logback.xml:INFO 级别,60 MB 滚动,30 天保留 - 九.5 Dockerfile 更新:多阶段构建,复制 etc/ 配置并调用 start.sh - 九.6 log.debug → log.info:11 文件 21 处,附批量替换命令 - 八 合规清单新增 #12-14:包结构、start.sh Docker 兼容、日志级别
This commit is contained in:
@@ -45,6 +45,13 @@
|
|||||||
- [7.4 状态机越界拒绝测试](#74-状态机越界拒绝测试)
|
- [7.4 状态机越界拒绝测试](#74-状态机越界拒绝测试)
|
||||||
- [7.5 多租户隔离测试](#75-多租户隔离测试)
|
- [7.5 多租户隔离测试](#75-多租户隔离测试)
|
||||||
- [八、宪章合规检查清单](#八宪章合规检查清单)
|
- [八、宪章合规检查清单](#八宪章合规检查清单)
|
||||||
|
- [九、部署与发布](#九部署与发布)
|
||||||
|
- [9.1 Maven 构建配置变更](#91-maven-构建配置变更)
|
||||||
|
- [9.2 分发包结构](#92-分发包结构)
|
||||||
|
- [9.3 start.sh 启动脚本](#93-startsh-启动脚本)
|
||||||
|
- [9.4 logback.xml 配置](#94-logbackxml-配置)
|
||||||
|
- [9.5 Dockerfile 更新](#95-dockerfile-更新)
|
||||||
|
- [9.6 日志级别规范(log.debug → log.info)](#96-日志级别规范logdebug--loginfo)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -1478,12 +1485,14 @@ volumes:
|
|||||||
|
|
||||||
**Dockerfile(backend):**
|
**Dockerfile(backend):**
|
||||||
|
|
||||||
|
> 完整的多阶段构建 Dockerfile 见 [9.5 Dockerfile 更新](#95-dockerfile-更新)。
|
||||||
|
> 以下为旧版占位,已被新版替代(使用薄 jar + start.sh 方式)。
|
||||||
|
|
||||||
```dockerfile
|
```dockerfile
|
||||||
FROM eclipse-temurin:17-jre-alpine
|
# 已废弃——使用 fat JAR 方式,不符合新部署规范
|
||||||
WORKDIR /app
|
# FROM eclipse-temurin:17-jre-alpine
|
||||||
COPY target/label-backend-*.jar app.jar
|
# COPY target/label-backend-*.jar app.jar
|
||||||
EXPOSE 8080
|
# ENTRYPOINT ["java", "-jar", "app.jar"]
|
||||||
ENTRYPOINT ["java", "-jar", "app.jar"]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
[↑ 返回目录](#目录)
|
[↑ 返回目录](#目录)
|
||||||
@@ -1599,5 +1608,336 @@ PR 合并前评审人**必须**逐条核对以下清单:
|
|||||||
| 9 | 只追加审计日志、AOP 切面、审计失败不回滚业务 | `AuditAspect`、`@OperationLog` | `sys_operation_log` 无 UPDATE/DELETE;审计异常仅 error 日志 |
|
| 9 | 只追加审计日志、AOP 切面、审计失败不回滚业务 | `AuditAspect`、`@OperationLog` | `sys_operation_log` 无 UPDATE/DELETE;审计异常仅 error 日志 |
|
||||||
| 10 | RESTful URL、统一响应格式、分页必须 | `Result<T>`、各 Controller | 无动词路径;无裸 POJO 返回;所有列表接口有分页参数 |
|
| 10 | RESTful URL、统一响应格式、分页必须 | `Result<T>`、各 Controller | 无动词路径;无裸 POJO 返回;所有列表接口有分页参数 |
|
||||||
| 11 | YAGNI:业务在 Service,Controller 只处理 HTTP | 包结构 | Controller 无业务判断逻辑;无未使用的抽象层 |
|
| 11 | YAGNI:业务在 Service,Controller 只处理 HTTP | 包结构 | Controller 无业务判断逻辑;无未使用的抽象层 |
|
||||||
|
| 12 | 部署包结构规范(bin/etc/libs/logs 四级) | `pom.xml`、`distribution.xml` | fat JAR 已移除;assembly 产出 zip + tar.gz;libs/ 含薄 jar + 所有依赖 |
|
||||||
|
| 13 | start.sh 启动可执行、Docker 兼容 | `src/main/scripts/start.sh` | Docker 内 exec 前台运行;VM 内 nohup 后台运行;日志写入 logs/ |
|
||||||
|
| 14 | 日志级别统一 INFO、无 log.debug 残留 | 全体 Service 类 | `grep -r "log\.debug"` 返回 0 结果;logback.xml root level=INFO |
|
||||||
|
|
||||||
|
[↑ 返回目录](#目录)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 九、部署与发布
|
||||||
|
|
||||||
|
### 9.1 Maven 构建配置变更
|
||||||
|
|
||||||
|
**目标**:用薄 jar(thin jar)替代 Spring Boot fat JAR,由 Assembly Plugin 组装可分发压缩包。
|
||||||
|
|
||||||
|
**移除** `spring-boot-maven-plugin`(fat JAR),**替换为**以下三个插件:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
|
||||||
|
<!-- 1. 薄 jar:仅打包编译后的 class 文件,Manifest 声明 Main-Class -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${project.build.directory}/libs</outputDirectory>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<mainClass>com.label.LabelBackendApplication</mainClass>
|
||||||
|
<addClasspath>false</addClasspath>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<!-- 2. 将所有运行时依赖复制到 target/libs/ -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>copy-dependencies</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals><goal>copy-dependencies</goal></goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${project.build.directory}/libs</outputDirectory>
|
||||||
|
<excludeScope>test</excludeScope>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<!-- 3. 组装分发包(zip + tar.gz) -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>create-distribution</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals><goal>single</goal></goals>
|
||||||
|
<configuration>
|
||||||
|
<descriptors>
|
||||||
|
<descriptor>src/main/assembly/distribution.xml</descriptor>
|
||||||
|
</descriptors>
|
||||||
|
<finalName>${project.artifactId}-${project.version}</finalName>
|
||||||
|
<appendAssemblyId>false</appendAssemblyId>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Assembly 描述符** `src/main/assembly/distribution.xml`:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0
|
||||||
|
https://maven.apache.org/xsd/assembly-2.1.0.xsd">
|
||||||
|
<id>dist</id>
|
||||||
|
<formats>
|
||||||
|
<format>zip</format>
|
||||||
|
<format>tar.gz</format>
|
||||||
|
</formats>
|
||||||
|
<includeBaseDirectory>true</includeBaseDirectory>
|
||||||
|
|
||||||
|
<!-- bin/start.sh(可执行权限) -->
|
||||||
|
<files>
|
||||||
|
<file>
|
||||||
|
<source>src/main/scripts/start.sh</source>
|
||||||
|
<outputDirectory>bin</outputDirectory>
|
||||||
|
<fileMode>0755</fileMode>
|
||||||
|
</file>
|
||||||
|
</files>
|
||||||
|
|
||||||
|
<!-- etc/ 配置文件 -->
|
||||||
|
<fileSets>
|
||||||
|
<fileSet>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
<outputDirectory>etc</outputDirectory>
|
||||||
|
<includes>
|
||||||
|
<include>application.yml</include>
|
||||||
|
<include>logback.xml</include>
|
||||||
|
</includes>
|
||||||
|
</fileSet>
|
||||||
|
<!-- logs/ 空目录占位(确保解压后目录存在) -->
|
||||||
|
<fileSet>
|
||||||
|
<directory>src/main/assembly/empty-logs</directory>
|
||||||
|
<outputDirectory>logs</outputDirectory>
|
||||||
|
</fileSet>
|
||||||
|
</fileSets>
|
||||||
|
|
||||||
|
<!-- libs/:薄 jar(project artifact)+ 所有运行时依赖 -->
|
||||||
|
<dependencySets>
|
||||||
|
<dependencySet>
|
||||||
|
<outputDirectory>libs</outputDirectory>
|
||||||
|
<useProjectArtifact>true</useProjectArtifact>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependencySet>
|
||||||
|
</dependencySets>
|
||||||
|
</assembly>
|
||||||
|
```
|
||||||
|
|
||||||
|
> **注意**:需在项目根创建空目录占位文件 `src/main/assembly/empty-logs/.gitkeep`,
|
||||||
|
> 否则 Assembly Plugin 会因源目录不存在而报错。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 9.2 分发包结构
|
||||||
|
|
||||||
|
`mvn clean package` 后在 `target/` 生成:
|
||||||
|
|
||||||
|
```
|
||||||
|
target/
|
||||||
|
├── libs/
|
||||||
|
│ ├── label-backend-1.0.0-SNAPSHOT.jar ← 薄 jar(仅 class)
|
||||||
|
│ ├── spring-boot-starter-web-3.2.5.jar
|
||||||
|
│ ├── mybatis-plus-boot-starter-3.5.9.jar
|
||||||
|
│ └── ... (全部运行时依赖)
|
||||||
|
├── label-backend-1.0.0-SNAPSHOT.zip
|
||||||
|
└── label-backend-1.0.0-SNAPSHOT.tar.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
压缩包解压后的内部结构:
|
||||||
|
|
||||||
|
```
|
||||||
|
label-backend-1.0.0-SNAPSHOT/
|
||||||
|
├── bin/
|
||||||
|
│ └── start.sh # 启动脚本(0755)
|
||||||
|
├── etc/
|
||||||
|
│ ├── application.yml # Spring 配置(生产环境按需修改)
|
||||||
|
│ └── logback.xml # 日志配置(INFO + 60 MB 滚动)
|
||||||
|
├── libs/
|
||||||
|
│ ├── label-backend-1.0.0-SNAPSHOT.jar
|
||||||
|
│ └── ... (所有依赖 jar)
|
||||||
|
└── logs/ # 空目录,运行时写入日志
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 9.3 start.sh 启动脚本
|
||||||
|
|
||||||
|
新增文件:`src/main/scripts/start.sh`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# label-backend 启动脚本
|
||||||
|
# - Docker 环境(检测 /.dockerenv):exec 前台运行,保持容器进程存活
|
||||||
|
# - 裸机 / VM:nohup 后台运行,日志追加至 logs/startup.log
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
BASEDIR=$(cd "$(dirname "$0")/.." && pwd)
|
||||||
|
LIBDIR="$BASEDIR/libs"
|
||||||
|
CONFDIR="$BASEDIR/etc"
|
||||||
|
LOGDIR="$BASEDIR/logs"
|
||||||
|
|
||||||
|
mkdir -p "$LOGDIR"
|
||||||
|
|
||||||
|
JVM_OPTS="${JVM_OPTS:--Xms512m -Xmx1024m}"
|
||||||
|
MAIN_CLASS="com.label.LabelBackendApplication"
|
||||||
|
JAVA_ARGS="$JVM_OPTS \
|
||||||
|
-Dspring.config.location=file:$CONFDIR/application.yml \
|
||||||
|
-Dlogging.config=file:$CONFDIR/logback.xml \
|
||||||
|
-cp $LIBDIR/*"
|
||||||
|
|
||||||
|
if [ -f /.dockerenv ]; then
|
||||||
|
# Docker 容器:exec 替换当前进程,PID=1 接管信号
|
||||||
|
exec java $JAVA_ARGS $MAIN_CLASS
|
||||||
|
else
|
||||||
|
# 裸机 / VM:nohup 后台运行
|
||||||
|
nohup java $JAVA_ARGS $MAIN_CLASS >> "$LOGDIR/startup.log" 2>&1 &
|
||||||
|
echo "label-backend started, PID=$!"
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 9.4 logback.xml 配置
|
||||||
|
|
||||||
|
新增文件:`src/main/resources/logback.xml`
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<configuration scan="true" scanPeriod="60 seconds">
|
||||||
|
|
||||||
|
<property name="LOG_PATH" value="${LOG_PATH:-logs}"/>
|
||||||
|
<property name="APP_NAME" value="label-backend"/>
|
||||||
|
<property name="LOG_PATTERN"
|
||||||
|
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
|
||||||
|
|
||||||
|
<!-- 控制台输出(Docker 日志采集依赖 stdout) -->
|
||||||
|
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder charset="UTF-8">
|
||||||
|
<pattern>${LOG_PATTERN}</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- 滚动文件:60 MB / 个,按日分组,保留 30 天,总上限 3 GB -->
|
||||||
|
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<file>${LOG_PATH}/${APP_NAME}.log</file>
|
||||||
|
<encoder charset="UTF-8">
|
||||||
|
<pattern>${LOG_PATTERN}</pattern>
|
||||||
|
</encoder>
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||||
|
<fileNamePattern>${LOG_PATH}/${APP_NAME}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
||||||
|
<maxFileSize>60MB</maxFileSize>
|
||||||
|
<maxHistory>30</maxHistory>
|
||||||
|
<totalSizeCap>3GB</totalSizeCap>
|
||||||
|
</rollingPolicy>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<root level="INFO">
|
||||||
|
<appender-ref ref="CONSOLE"/>
|
||||||
|
<appender-ref ref="FILE"/>
|
||||||
|
</root>
|
||||||
|
|
||||||
|
</configuration>
|
||||||
|
```
|
||||||
|
|
||||||
|
> **说明**:`LOG_PATH` 可通过环境变量或 JVM 参数覆盖,默认指向相对于工作目录的 `logs/`。
|
||||||
|
> Docker Compose 中如需持久化日志,挂载 `/app/logs` 卷并设置 `LOG_PATH=/app/logs`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 9.5 Dockerfile 更新
|
||||||
|
|
||||||
|
替换 `Dockerfile` 全文:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# 构建阶段:Maven + JDK 17 编译,生成薄 jar 及依赖
|
||||||
|
FROM maven:3.9-eclipse-temurin-17-alpine AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 优先复制 pom.xml 利用 Docker 层缓存(依赖不变时跳过 go-offline)
|
||||||
|
COPY pom.xml .
|
||||||
|
RUN mvn dependency:go-offline -q
|
||||||
|
|
||||||
|
COPY src ./src
|
||||||
|
RUN mvn clean package -DskipTests -q
|
||||||
|
|
||||||
|
# 运行阶段:仅含 JRE 的精简镜像
|
||||||
|
FROM eclipse-temurin:17-jre-alpine
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 复制部署结构:bin/ libs/ etc/
|
||||||
|
COPY --from=builder /app/src/main/scripts/start.sh bin/start.sh
|
||||||
|
COPY --from=builder /app/target/libs/ libs/
|
||||||
|
COPY --from=builder /app/src/main/resources/application.yml etc/application.yml
|
||||||
|
COPY --from=builder /app/src/main/resources/logback.xml etc/logback.xml
|
||||||
|
|
||||||
|
RUN mkdir -p logs && chmod +x bin/start.sh
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# start.sh 检测到 /.dockerenv 后自动以 exec 前台方式运行
|
||||||
|
ENTRYPOINT ["bin/start.sh"]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Docker Compose 中配置日志路径持久化(可选):**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
backend:
|
||||||
|
build: ./label_backend
|
||||||
|
volumes:
|
||||||
|
- ./logs/backend:/app/logs
|
||||||
|
environment:
|
||||||
|
LOG_PATH: /app/logs
|
||||||
|
# ... 其他环境变量保持不变
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 9.6 日志级别规范(log.debug → log.info)
|
||||||
|
|
||||||
|
代码约定:**所有业务 Service 的正常流程日志使用 `log.info`**,`log.debug` 仅保留在需要
|
||||||
|
开发期详细追踪的工具类(当前项目无此场景,全部改为 `log.info`)。
|
||||||
|
|
||||||
|
**需修改的文件(共 11 个,21 处):**
|
||||||
|
|
||||||
|
| 文件 | `log.debug` 数量 |
|
||||||
|
|------|-----------------|
|
||||||
|
| `module/video/service/VideoProcessService.java` | 5 |
|
||||||
|
| `module/source/service/SourceService.java` | 2 |
|
||||||
|
| `module/user/service/UserService.java` | 3 |
|
||||||
|
| `module/user/service/AuthService.java` | 2 |
|
||||||
|
| `module/config/service/SysConfigService.java` | 2 |
|
||||||
|
| `module/annotation/service/ExtractionApprovedEventListener.java` | 2 |
|
||||||
|
| `module/task/service/TaskService.java` | 1 |
|
||||||
|
| `module/annotation/service/QaService.java` | 1 |
|
||||||
|
| `module/export/service/FinetuneService.java` | 1 |
|
||||||
|
| `module/task/service/TaskClaimService.java` | 1 |
|
||||||
|
| `module/export/service/ExportService.java` | 1 |
|
||||||
|
|
||||||
|
**执行方式**(实现阶段批量替换):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 验证变更范围
|
||||||
|
grep -rn "log\.debug" src/main/java
|
||||||
|
|
||||||
|
# 批量替换
|
||||||
|
find src/main/java -name "*.java" \
|
||||||
|
-exec sed -i 's/log\.debug(/log.info(/g' {} +
|
||||||
|
|
||||||
|
# 确认清零
|
||||||
|
grep -r "log\.debug" src/main/java && echo "FAIL" || echo "PASS"
|
||||||
|
```
|
||||||
|
|
||||||
[↑ 返回目录](#目录)
|
[↑ 返回目录](#目录)
|
||||||
|
|||||||
Reference in New Issue
Block a user