Files
label_backend/docs/superpowers/plans/2026-04-09-deploy-optimization.md

16 KiB
Raw Blame History

Deploy Optimization Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 将 label_backend 从 Spring Boot fat JAR 方式改造为薄 jar + Maven Assembly 分发包,并统一日志级别为 INFO。

Architecture: 移除 spring-boot-maven-plugin,改用 maven-jar-plugin(薄 jar+ maven-dependency-plugin(复制依赖到 target/libs/+ maven-assembly-plugin(组装 zip/tar.gz。新增 start.shDocker/VM 双模式启动)和 logback.xml60 MB 滚动。Dockerfile 改为多阶段构建,从 target/libs/src/main/resources/ 复制构建产物。

Tech Stack: Maven 3.9, JDK 17, Spring Boot 3.2.5, maven-assembly-plugin 3.x, logback 1.4.x (Spring Boot 管理版本)


文件结构

操作 路径 说明
新建 src/main/resources/logback.xml INFO 级60 MB 滚动日志配置
新建 src/main/scripts/start.sh Docker/VM 双模式启动脚本
新建 src/main/assembly/distribution.xml Assembly 描述符
新建 src/main/assembly/empty-logs/.gitkeep logs/ 目录占位Assembly 引用)
修改 pom.xml 替换 spring-boot-maven-plugin
修改 Dockerfile 多阶段构建,复制 etc/ + libs/
批量修改 11 个 Service 类 log.debuglog.info21 处)

Task 1: 创建 logback.xml

Files:

  • Create: src/main/resources/logback.xml

  • Step 1: 创建 logback.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>
  • Step 2: 验证文件存在
ls src/main/resources/logback.xml

预期输出:src/main/resources/logback.xml

  • Step 3: Commit
git add src/main/resources/logback.xml
git commit -m "feat(deploy): 添加 logback.xmlINFO 级60 MB 滚动)"

Task 2: 创建 start.sh 启动脚本

Files:

  • Create: src/main/scripts/start.sh

  • Step 1: 创建目录并写入 start.sh

mkdir -p src/main/scripts

文件内容 src/main/scripts/start.sh

#!/bin/bash
# label-backend 启动脚本
# - Docker 环境(检测 /.dockerenvexec 前台运行,保持容器进程存活
# - 裸机 / VMnohup 后台运行,日志追加至 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
  # 裸机 / VMnohup 后台运行
  nohup java $JAVA_ARGS $MAIN_CLASS >> "$LOGDIR/startup.log" 2>&1 &
  echo "label-backend started, PID=$!"
fi
  • Step 2: 验证文件存在
ls src/main/scripts/start.sh

预期输出:src/main/scripts/start.sh

  • Step 3: Commit
git add src/main/scripts/start.sh
git commit -m "feat(deploy): 添加 start.shDocker exec / VM nohup 双模式)"

Task 3: 创建 Assembly 描述符和目录占位

Files:

  • Create: src/main/assembly/distribution.xml

  • Create: src/main/assembly/empty-logs/.gitkeep

  • Step 1: 创建 assembly 目录结构

mkdir -p src/main/assembly/empty-logs
touch src/main/assembly/empty-logs/.gitkeep
  • Step 2: 创建 distribution.xml

文件内容 src/main/assembly/distribution.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.sh0755 可执行) -->
  <files>
    <file>
      <source>src/main/scripts/start.sh</source>
      <outputDirectory>bin</outputDirectory>
      <fileMode>0755</fileMode>
    </file>
  </files>

  <fileSets>
    <!-- etc/application.yml + logback.xml -->
    <fileSet>
      <directory>src/main/resources</directory>
      <outputDirectory>etc</outputDirectory>
      <includes>
        <include>application.yml</include>
        <include>logback.xml</include>
      </includes>
    </fileSet>

    <!-- libs/:薄 jar + 所有运行时依赖 -->
    <fileSet>
      <directory>${project.build.directory}/libs</directory>
      <outputDirectory>libs</outputDirectory>
      <includes>
        <include>**/*.jar</include>
      </includes>
    </fileSet>

    <!-- logs/ 空目录占位 -->
    <fileSet>
      <directory>src/main/assembly/empty-logs</directory>
      <outputDirectory>logs</outputDirectory>
    </fileSet>
  </fileSets>

</assembly>
  • Step 3: 验证文件存在
ls src/main/assembly/distribution.xml src/main/assembly/empty-logs/.gitkeep

预期输出:两个文件路径均显示

  • Step 4: Commit
git add src/main/assembly/
git commit -m "feat(deploy): 添加 Assembly 描述符 distribution.xml"

Task 4: 更新 pom.xml替换 spring-boot-maven-plugin

Files:

  • Modify: pom.xml

  • Step 1: 替换 <build><plugins> 段落

pom.xml<build> 段(当前仅含 spring-boot-maven-plugin替换为

<build>
    <plugins>

        <!-- 薄 jar仅打包编译后的 class输出到 target/libs/ -->
        <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>

        <!-- 将所有运行时依赖复制到 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>
                        <includeScope>runtime</includeScope>
                    </configuration>
                </execution>
            </executions>
        </plugin>

        <!-- 组装分发包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>

即用上述内容完整替换 pom.xml 中现有的 <build>...</build> 块(原内容为含 spring-boot-maven-plugin 的单插件配置)。

  • Step 2: 验证语法并试构建
mvn validate -q

预期输出:无错误(只做 pom.xml 解析校验,不实际编译)

  • Step 3: Commit
git add pom.xml
git commit -m "feat(deploy): pom.xml 替换 fat JAR → 薄 jar + maven-dependency + maven-assembly"

Task 5: 更新 Dockerfile

Files:

  • Modify: Dockerfile

  • Step 1: 替换 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"]
  • Step 2: 验证 Dockerfile 语法
docker build --no-cache --dry-run . 2>/dev/null || echo "docker not available; syntax check skipped"

预期:无语法错误(或 docker 不可用时跳过)

  • Step 3: Commit
git add Dockerfile
git commit -m "feat(deploy): Dockerfile 改为多阶段构建(薄 jar + start.sh"

Task 6: 批量替换 log.debug → log.info11 文件21 处)

Files:

  • Modify: src/main/java/com/label/module/video/service/VideoProcessService.java (5 处)

  • Modify: src/main/java/com/label/module/source/service/SourceService.java (2 处)

  • Modify: src/main/java/com/label/module/user/service/UserService.java (3 处)

  • Modify: src/main/java/com/label/module/user/service/AuthService.java (2 处)

  • Modify: src/main/java/com/label/module/config/service/SysConfigService.java (2 处)

  • Modify: src/main/java/com/label/module/annotation/service/ExtractionApprovedEventListener.java (2 处)

  • Modify: src/main/java/com/label/module/task/service/TaskService.java (1 处)

  • Modify: src/main/java/com/label/module/annotation/service/QaService.java (1 处)

  • Modify: src/main/java/com/label/module/export/service/FinetuneService.java (1 处)

  • Modify: src/main/java/com/label/module/task/service/TaskClaimService.java (1 处)

  • Modify: src/main/java/com/label/module/export/service/ExportService.java (1 处)

  • Step 1: 批量替换

find src/main/java -name "*.java" \
  -exec sed -i 's/log\.debug(/log.info(/g' {} +
  • Step 2: 验证替换完整,无 log.debug 残留
grep -r "log\.debug" src/main/java && echo "FAIL: 仍有 log.debug 残留" || echo "PASS: 无 log.debug"

预期输出:PASS: 无 log.debug

  • Step 3: 验证替换数量正确(应有 21 处 log.info 新增)
git diff --stat src/main/java | tail -1

预期:显示 11 个文件21 处改动(21 insertions(+), 21 deletions(-)

  • Step 4: Commit
git add src/main/java/
git commit -m "refactor(log): log.debug 全量替换为 log.info11 文件21 处)"

Task 7: 全量构建验证

Files: 只读操作,不修改文件

  • Step 1: 执行完整构建(跳过测试)
mvn clean package -DskipTests

预期:BUILD SUCCESS,无 ERROR 输出

  • Step 2: 验证 target/libs/ 目录存在且包含 jar
ls target/libs/*.jar | head -5
ls target/libs/label-backend-1.0.0-SNAPSHOT.jar

预期:显示薄 jar 及多个依赖 jar

  • Step 3: 验证分发包已生成
ls target/label-backend-1.0.0-SNAPSHOT.zip
ls target/label-backend-1.0.0-SNAPSHOT.tar.gz

预期:两个文件均存在

  • Step 4: 验证分发包内部结构
# 使用 unzip 检查 zip 内容Windows Git Bash 或 Linux 均可用)
unzip -l target/label-backend-1.0.0-SNAPSHOT.zip | grep -E "bin/|etc/|libs/|logs/"

预期输出包含:

label-backend-1.0.0-SNAPSHOT/bin/start.sh
label-backend-1.0.0-SNAPSHOT/etc/application.yml
label-backend-1.0.0-SNAPSHOT/etc/logback.xml
label-backend-1.0.0-SNAPSHOT/libs/label-backend-1.0.0-SNAPSHOT.jar
label-backend-1.0.0-SNAPSHOT/logs/
  • Step 5: 验证 logback.xml 已包含在 etc/ 内
unzip -l target/label-backend-1.0.0-SNAPSHOT.zip | grep logback

预期:label-backend-1.0.0-SNAPSHOT/etc/logback.xml

  • Step 6: Commit 构建验证记录(如有必要则修正问题后提交)

若 Step 1-5 全部通过,无需额外提交。若发现问题(如缺少文件、路径错误),修正对应 Task 后重新执行本 Task。


自检

Spec 覆盖检查

deploy.md 需求 对应任务
1. 分发包结构 bin/etc/libs/logs Task 3 (distribution.xml) + Task 7 验证
2. start.sh nohup 后台启动 Task 2
3. logback.xml INFO + 60 MB 滚动 Task 1
4. logback.xml + application.yml 在 etc/ Task 3 (distribution.xml fileSets)
5. maven-jar-plugin + maven-dependency-plugin Task 4
6. Maven Assembly Plugin → zip/tar.gz Task 3 + Task 4
7. Dockerfile 复制 etc/ + 调用 start.sh Task 5
8. log.debug → log.info Task 6

所有 8 条需求均有对应任务。

类型一致性

  • MAIN_CLASS="com.label.LabelBackendApplication" — 与 src/main/java/com/label/LabelBackendApplication.java 一致
  • maven-jar-plugin 输出路径 target/libs/ — 与 Dockerfile COPY --from=builder /app/target/libs/ 一致
  • distribution.xml <directory>${project.build.directory}/libs</directory> — 与 maven-dependency-pluginoutputDirectory 一致

GSTACK REVIEW REPORT

Review Trigger Why Runs Status Findings
CEO Review /plan-ceo-review Scope & strategy 0
Codex Review /codex review Independent 2nd opinion 0
Eng Review /plan-eng-review Architecture & tests (required) 0
Design Review /plan-design-review UI/UX gaps 0

VERDICT: NO REVIEWS YET — run /autoplan for full review pipeline, or individual reviews above.