Files
par/.drone.yml
mediabot-pt b24ec27905 perf: 优化 CI Docker 镜像构建速度
- 新增 CI 精简 Dockerfile,直接打包 drone 预编译 jar,避免在 Docker 内重复编译(省去 npm ci + mvn 全量编译 ~15min)
- build-docker-image 步骤改用精简 Dockerfile,去掉无效的 docker pull 缓存尝试
- maven-build 步骤优化:动态检测 CPU 核数设置并行度 -T 1C/2C、增加 JVM 内存、添加 --fail-at-end
- .dockerignore 优化:CI Dockerfile 仅需 par-api/target/*.jar,排除所有源码/pom/前端文件
- par-api/Dockerfile 标注为独立多阶段构建版本(本地使用)

参考 mediabot 项目的多阶段构建思路,但将编译前置到 drone host 缓存中,Docker 构建秒级完成
2026-06-29 12:53:33 +08:00

413 lines
14 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
kind: pipeline
type: docker
name: par-build
volumes:
- name: maven-cache
host:
path: /tmp/drone-maven-cache
- name: npm-cache
host:
path: /tmp/drone-npm-cache
- name: dockersock
host:
path: /var/run/docker.sock
trigger:
event: [push]
branch: [dev, test, main]
exclude:
commit:
- ^\[skip ci\]
- ^\[ci skip\]
platform:
os: linux
arch: amd64
workspace:
path: /drone/src
environment:
# 仓库配置
LOCAL_HARBOR_REG: "192.168.31.253"
LOCAL_HARBOR_PROJ: "yescent"
# 镜像/容器通用
IMAGE_NAME: "${DRONE_REPO_NAME}"
CONTAINER_NAME: "par-server"
# 部署配置
SERVER_PORT: "8080"
APP_PORT: "9012"
TZ: "Asia/Shanghai"
steps:
# ===================== 调试信息 =====================
- name: print-drone-vars
image: 192.168.31.253/library/alpine:latest
commands:
- echo "===== ALL DRONE ENV VARIABLES ====="
- env | grep -i drone | sort
# ===================== 准备 Maven 缓存 =====================
- name: prepare-cache
image: 192.168.31.253/library/maven-java21:3.9.9-alpine
volumes:
- name: maven-cache
path: /host-cache
commands:
- |
echo "正在准备Maven缓存目录..."
mkdir -p /host-cache/repository
if [ "$(ls -A /host-cache/repository 2>/dev/null)" ]; then
CACHE_SIZE=$(du -sh /host-cache/repository | cut -f1)
echo "检测到现有缓存,大小: $CACHE_SIZE"
echo "CACHE_EXISTS=true" > /drone/src/.cache_info
else
echo "未发现现有缓存,将初始化新缓存。"
echo "CACHE_EXISTS=false" > /drone/src/.cache_info
fi
# ===================== 前端构建(npm) =====================
- name: frontend-build
image: 192.168.31.253/library/node:22-alpine
volumes:
- name: npm-cache
path: /root/.npm
environment:
NPM_CONFIG_REGISTRY: "https://registry.npmmirror.com"
commands:
- |
echo "====== 开始前端构建 ======"
npm config set registry https://registry.npmmirror.com
cd web
npm ci --prefer-offline --ignore-scripts 2>/dev/null || npm install --prefer-offline --ignore-scripts
# 确保 rollup 原生绑定可用
if [ -f node_modules/@rollup/rollup-linux-x64-musl/package.json ]; then
node -e "require('@rollup/rollup-linux-x64-musl/package.json')" 2>/dev/null || npm install @rollup/rollup-linux-x64-musl --ignore-scripts --force
fi
npm run build
echo "前端构建完成,产物列表:"
ls -la dist/
echo "====== 前端构建结束 ======"
# ===================== Maven 构建(含前端静态资源) =====================
- name: maven-build
image: 192.168.31.253/library/maven-java21:3.9.9-alpine
pull: if-not-exists
environment:
MAVEN_OPTS: "-Dmaven.repo.local=/root/.m2/repository -Xms512m -Xmx1024m -Djava.awt.headless=true -Dmaven.compiler.forceJavacCompilerUse=true"
volumes:
- name: maven-cache
path: /root/.m2
commands:
- |
set -e
echo "====== 开始 Maven 构建 ======"
# 配置阿里云 Maven 镜像加速
mkdir -p /root/.m2
cat > /root/.m2/settings.xml << 'EOF'
<settings>
<mirrors>
<mirror>
<id>aliyunmaven</id>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
</settings>
EOF
echo "已配置阿里云 Maven 镜像"
# 将前端构建产物复制到 static 目录
FRONTEND_DIST="/drone/src/web/dist"
STATIC_DIR="/drone/src/par-api/src/main/resources/static"
if [ -d "$FRONTEND_DIST" ]; then
mkdir -p "$STATIC_DIR"
cp -r "$FRONTEND_DIST"/* "$STATIC_DIR"/
echo "前端产物已复制到 $STATIC_DIR"
ls -la "$STATIC_DIR"
else
echo "警告:未找到前端构建产物 $FRONTEND_DIST,将仅构建后端"
fi
# 生成版本标签
RELEASE_VERSION="v1.0.0"
BUILD_VERSION="v1.0.0.${DRONE_BUILD_NUMBER}"
echo "RELEASE_VERSION=$RELEASE_VERSION" > /drone/src/.version.env
echo "BUILD_VERSION=$BUILD_VERSION" >> /drone/src/.version.env
echo "BUILD_DATE=$(TZ=Asia/Shanghai date +%Y%m%d)" >> /drone/src/.version.env
echo "发布版本: $RELEASE_VERSION, 构建版本: $BUILD_VERSION"
# 检测可用 CPU 核数,动态设置 Maven 并行度
CPU_CORES=$(nproc 2>/dev/null || echo 4)
echo "检测到 $CPU_CORES 个 CPU 核心"
if [ "$CPU_CORES" -ge 4 ]; then
MVN_THREADS="2C"
else
MVN_THREADS="1C"
fi
echo "Maven 并行度: -T $MVN_THREADS"
# Maven 打包(使用阿里云镜像 + 多线程并行编译 + 跳过测试 + 离线优先)
mvn -B clean package \
-s /root/.m2/settings.xml \
-DskipTests \
-Dmaven.test.skip=true \
-T "$MVN_THREADS" \
--no-transfer-progress \
--fail-at-end
JAR_FILE=$(ls par-api/target/*.jar | head -1)
echo "构建成功!产物: $JAR_FILE ($(du -sh $JAR_FILE | cut -f1))"
depends_on:
- prepare-cache
- frontend-build
# ===================== Docker 镜像构建 =====================
# 参考 mediabot 优化:Drone 已在前序步骤完成编译(frontend-build + maven-build),
# 此处使用 CI 精简 Dockerfile,仅将预构建 jar 打包进 JRE 镜像,无需在 Docker 内重新编译。
- name: build-docker-image
image: 192.168.31.253/library/docker:cli
volumes:
- name: dockersock
path: /var/run/docker.sock
environment:
DOCKER_BUILDKIT: "1"
commands:
- |
echo "====== 开始构建 Docker 镜像 ======"
source /drone/src/.version.env
# main 分支用干净版本号,test 带 pipeline 号
if [ "$DRONE_BRANCH" = "main" ]; then
APP_VERSION="$RELEASE_VERSION"
else
APP_VERSION="$BUILD_VERSION"
fi
echo "版本: $APP_VERSION"
# 验证 jar 产物存在
JAR_FILE=$(ls par-api/target/*.jar 2>/dev/null | head -1)
if [ -z "$JAR_FILE" ]; then
echo "错误:未找到 jar 产物,maven-build 可能失败"
exit 1
fi
echo "jar 产物: $JAR_FILE ($(du -sh $JAR_FILE | cut -f1))"
# 使用 CI 精简 Dockerfile:仅 COPY jar + 设置运行环境,秒级完成
docker build \
--build-arg BUILDKIT_INLINE_CACHE=1 \
--build-arg VERSION="$APP_VERSION" \
--build-arg BUILD_TIME="$(TZ=Asia/Shanghai date '+%Y-%m-%d %H:%M')" \
-t "$IMAGE_NAME:$BUILD_VERSION" \
-t "$IMAGE_NAME:latest" \
-f Dockerfile \
.
echo "Docker 镜像构建完成。"
docker images | grep "$IMAGE_NAME"
depends_on:
- maven-build
when:
branch: [test, main]
# ===================== Test:推本地 Harbor =====================
- name: push-to-harbor
image: 192.168.31.253/library/docker:cli
network_mode: host
volumes:
- name: dockersock
path: /var/run/docker.sock
environment:
LOCAL_HARBOR_REG: "192.168.31.253"
LOCAL_HARBOR_PROJ: "yescent"
LOCAL_REGISTRY_USER:
from_secret: local_registry_user
LOCAL_REGISTRY_PASSWORD:
from_secret: local_registry_password
commands:
- |
echo "====== 推送镜像到本地 Harbor ======"
source /drone/src/.version.env
LOCAL_IMAGE_FULL="$LOCAL_HARBOR_REG/$LOCAL_HARBOR_PROJ/$IMAGE_NAME"
LOCAL_IMAGE_TAG="$LOCAL_IMAGE_FULL:$BUILD_VERSION"
LOCAL_IMAGE_LATEST="$LOCAL_IMAGE_FULL:latest"
docker tag "$IMAGE_NAME:$BUILD_VERSION" "$LOCAL_IMAGE_TAG"
docker tag "$IMAGE_NAME:latest" "$LOCAL_IMAGE_LATEST"
echo "$LOCAL_REGISTRY_PASSWORD" | docker login -u "$LOCAL_REGISTRY_USER" --password-stdin $LOCAL_HARBOR_REG
docker push "$LOCAL_IMAGE_TAG"
docker push "$LOCAL_IMAGE_LATEST"
docker rmi "$LOCAL_IMAGE_TAG" "$LOCAL_IMAGE_LATEST" || true
echo "====== Harbor 推送完成 ======"
depends_on:
- build-docker-image
when:
status: success
branch: [test]
# ===================== Test:部署到测试服务器 =====================
- name: deploy-test
image: 192.168.31.253/library/alpine:latest
environment:
DEPLOY_IMAGE: "192.168.31.253/yescent/${DRONE_REPO_NAME}"
APP_PORT: "9012"
SSH_USER:
from_secret: test_ssh_user
SSH_HOST:
from_secret: test_ssh_host
SSH_KEY:
from_secret: test_ssh_key
commands:
- |
echo "====== 部署测试环境 ======"
source /drone/src/.version.env
# 安装新版 openssh-client
apk add --no-cache openssh-client
mkdir -p ~/.ssh
chmod 700 ~/.ssh
# 使用 printf 确保换行符正确写入
printf '%s\n' "$SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
# 如果私钥是 OpenSSH 新格式,尝试转换为 PEM 格式
ssh-keygen -p -m PEM -N "" -f ~/.ssh/id_rsa 2>/dev/null || true
export IMG="$DEPLOY_IMAGE:$BUILD_VERSION"
ssh -o StrictHostKeyChecking=no -o IdentitiesOnly=yes -i ~/.ssh/id_rsa $SSH_USER@$SSH_HOST "
docker pull $IMG
docker stop par-server 2>/dev/null || true
docker rm -f par-server 2>/dev/null || true
docker run -d --name par-server --restart always \\
--env-file /data/par/db.env \\
-p \${APP_PORT:-9012}:8080 \\
-v /data/par/logs:/app/logs \\
-v /data/par/static:/app/static \\
$IMG
sleep 10
curl -s http://localhost:\${APP_PORT:-9012}/health && echo OK || echo FAIL
"
echo "====== 部署完成 ======"
depends_on:
- push-to-harbor
when:
status: success
branch: [test]
# ===================== Main:推本地 Harbor 并部署 =====================
- name: push-to-harbor-main
image: 192.168.31.253/library/docker:cli
network_mode: host
volumes:
- name: dockersock
path: /var/run/docker.sock
environment:
LOCAL_HARBOR_REG: "192.168.31.253"
LOCAL_HARBOR_PROJ: "yescent"
LOCAL_REGISTRY_USER:
from_secret: local_registry_user
LOCAL_REGISTRY_PASSWORD:
from_secret: local_registry_password
commands:
- |
echo "====== 推送镜像到本地 Harbor (main) ======"
source /drone/src/.version.env
LOCAL_IMAGE_FULL="$LOCAL_HARBOR_REG/$LOCAL_HARBOR_PROJ/$IMAGE_NAME"
LOCAL_IMAGE_TAG="$LOCAL_IMAGE_FULL:$BUILD_VERSION"
LOCAL_IMAGE_LATEST="$LOCAL_IMAGE_FULL:latest"
docker tag "$IMAGE_NAME:$BUILD_VERSION" "$LOCAL_IMAGE_TAG"
docker tag "$IMAGE_NAME:latest" "$LOCAL_IMAGE_LATEST"
echo "$LOCAL_REGISTRY_PASSWORD" | docker login -u "$LOCAL_REGISTRY_USER" --password-stdin $LOCAL_HARBOR_REG
docker push "$LOCAL_IMAGE_TAG"
docker push "$LOCAL_IMAGE_LATEST"
docker rmi "$LOCAL_IMAGE_TAG" "$LOCAL_IMAGE_LATEST" || true
echo "====== Harbor 推送完成 ======"
depends_on:
- build-docker-image
when:
status: success
branch: [main]
- name: deploy-main
image: 192.168.31.253/library/alpine:latest
environment:
DEPLOY_IMAGE: "192.168.31.253/yescent/${DRONE_REPO_NAME}"
APP_PORT: "9012"
SSH_USER:
from_secret: prod_ssh_user
SSH_HOST:
from_secret: prod_ssh_host
SSH_KEY:
from_secret: prod_ssh_key
commands:
- |
echo "====== 部署生产环境 ======"
source /drone/src/.version.env
# 安装新版 openssh-client
apk add --no-cache openssh-client
mkdir -p ~/.ssh
chmod 700 ~/.ssh
# 使用 printf 确保换行符正确写入
printf '%s\n' "$SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
# 如果私钥是 OpenSSH 新格式,尝试转换为 PEM 格式
ssh-keygen -p -m PEM -N "" -f ~/.ssh/id_rsa 2>/dev/null || true
export IMG="$DEPLOY_IMAGE:$BUILD_VERSION"
ssh -o StrictHostKeyChecking=no -o IdentitiesOnly=yes -i ~/.ssh/id_rsa $SSH_USER@$SSH_HOST "
docker pull $IMG
docker stop par-server 2>/dev/null || true
docker rm -f par-server 2>/dev/null || true
docker run -d --name par-server --restart always \\
--env-file /data/par/db.env \\
-p \${APP_PORT:-9012}:8080 \\
-v /data/par/logs:/app/logs \\
-v /data/par/static:/app/static \\
$IMG
sleep 10
curl -s http://localhost:\${APP_PORT:-9012}/health && echo OK || echo FAIL
"
echo "====== 部署完成 ======"
depends_on:
- push-to-harbor-main
when:
status: success
branch: [main]
# ===================== 构建报告 =====================
- name: cache-report
image: 192.168.31.253/library/alpine:latest
volumes:
- name: maven-cache
path: /host-cache
commands:
- |
echo "====== 构建流程结束 ======"
if [ -d "/host-cache/repository" ]; then
CACHE_SIZE=$(du -sh /host-cache/repository 2>/dev/null | cut -f1 || echo "计算失败")
echo "Maven缓存大小: $CACHE_SIZE"
fi
source /drone/src/.version.env 2>/dev/null
if [ -n "$BUILD_VERSION" ]; then
echo "构建版本: $BUILD_VERSION"
fi
echo "=============================="
when:
status: [success, failure]
depends_on:
- maven-build