perf: 并行构建 + Maven 依赖缓存优化
主要改进: 1. 并行构建(最大 2 个服务同时构建) 2. Maven 依赖基础镜像(aiot-deps) 3. 服务构建基于依赖镜像,跳过依赖下载 4. 部署也支持并行 预期效果:后续构建时间从 25分钟 降至 8-12分钟
This commit is contained in:
353
Jenkinsfile
vendored
353
Jenkinsfile
vendored
@@ -1,52 +1,35 @@
|
||||
// ============================================
|
||||
// AIOT Platform - Jenkins Pipeline
|
||||
// 智能增量构建 + Docker 容器化部署
|
||||
// 并行构建 + Maven 依赖缓存优化
|
||||
// ============================================
|
||||
|
||||
pipeline {
|
||||
agent any
|
||||
|
||||
options {
|
||||
// 保留最近 10 次构建
|
||||
buildDiscarder(logRotator(numToKeepStr: '10'))
|
||||
// 禁止并发构建
|
||||
disableConcurrentBuilds()
|
||||
// 超时设置
|
||||
timeout(time: 60, unit: 'MINUTES')
|
||||
timestamps()
|
||||
}
|
||||
|
||||
environment {
|
||||
// Gitea 仓库配置
|
||||
GIT_REPO = 'http://172.17.16.14:3000/XW-AIOT/aiot-platform-cloud.git'
|
||||
|
||||
// Docker Registry 配置
|
||||
REGISTRY = 'localhost:5000'
|
||||
|
||||
// Maven 配置
|
||||
MAVEN_OPTS = '-Xmx2048m -Dmaven.repo.local=.m2/repository'
|
||||
|
||||
// 镜像标签
|
||||
IMAGE_TAG = "${env.BRANCH_NAME}-${env.BUILD_NUMBER}-${env.GIT_COMMIT?.take(8)}"
|
||||
|
||||
// 服务列表(核心服务 - 不包括开发中的 ops-server)
|
||||
DEPS_IMAGE = "${REGISTRY}/aiot-deps:latest"
|
||||
CORE_SERVICES = 'viewsh-gateway,viewsh-module-system-server,viewsh-module-infra-server,viewsh-module-iot-server,viewsh-module-iot-gateway'
|
||||
DEPLOY_HOST = '172.19.0.1'
|
||||
DEPLOY_PATH = '/opt/aiot-platform-cloud'
|
||||
MAX_PARALLEL = 2 // 最大并行构建数
|
||||
}
|
||||
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
script {
|
||||
echo "=== 检出代码 ==="
|
||||
checkout scm
|
||||
|
||||
// 获取 Git 提交信息
|
||||
env.GIT_COMMIT_MSG = sh(
|
||||
script: 'git log -1 --pretty=%B',
|
||||
returnStdout: true
|
||||
).trim()
|
||||
|
||||
echo "Commit: ${env.GIT_COMMIT}"
|
||||
echo "Message: ${env.GIT_COMMIT_MSG}"
|
||||
env.GIT_COMMIT_MSG = sh(script: 'git log -1 --pretty=%B', returnStdout: true).trim()
|
||||
echo "📦 Commit: ${env.GIT_COMMIT?.take(8)} - ${env.GIT_COMMIT_MSG}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,117 +37,68 @@ pipeline {
|
||||
stage('Detect Changes') {
|
||||
steps {
|
||||
script {
|
||||
echo "=== 检测变更的服务 ==="
|
||||
env.SERVICES_TO_BUILD = detectChangedServices()
|
||||
env.DEPS_CHANGED = checkDepsChanged()
|
||||
|
||||
// 获取变更的文件列表
|
||||
def changedFiles = sh(
|
||||
script: '''
|
||||
# 获取上一次成功构建的提交
|
||||
PREV_COMMIT=$(git rev-parse HEAD~1 2>/dev/null || echo "")
|
||||
|
||||
if [ -z "$PREV_COMMIT" ]; then
|
||||
# 首次构建或只有一个提交,构建所有核心服务
|
||||
echo "all"
|
||||
else
|
||||
# 检测变更的文件
|
||||
git diff --name-only $PREV_COMMIT HEAD
|
||||
fi
|
||||
''',
|
||||
returnStdout: true
|
||||
).trim()
|
||||
|
||||
echo "Changed files:\n${changedFiles}"
|
||||
|
||||
// 分析需要构建的服务
|
||||
def servicesToBuild = []
|
||||
|
||||
if (changedFiles == 'all' || changedFiles.isEmpty()) {
|
||||
// 首次构建或强制全量构建
|
||||
echo "首次构建或无法检测变更,构建所有核心服务"
|
||||
servicesToBuild = CORE_SERVICES.split(',')
|
||||
} else {
|
||||
// 检测每个服务是否有变更
|
||||
CORE_SERVICES.split(',').each { service ->
|
||||
def modulePath = service.replace('-server', '').replace('viewsh-module-', 'viewsh-module-').replace('viewsh-', '')
|
||||
|
||||
if (changedFiles.contains(modulePath) ||
|
||||
changedFiles.contains('pom.xml') ||
|
||||
changedFiles.contains('viewsh-framework') ||
|
||||
changedFiles.contains('viewsh-dependencies') ||
|
||||
changedFiles.contains('Jenkinsfile') ||
|
||||
changedFiles.contains('docker/')) {
|
||||
servicesToBuild.add(service)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有检测到变更,但有代码提交,构建所有服务
|
||||
if (servicesToBuild.isEmpty() && !changedFiles.isEmpty()) {
|
||||
echo "检测到代码变更但未匹配到具体服务,构建所有服务"
|
||||
servicesToBuild = CORE_SERVICES.split(',')
|
||||
}
|
||||
}
|
||||
|
||||
env.SERVICES_TO_BUILD = servicesToBuild.join(',')
|
||||
echo "Services to build: ${env.SERVICES_TO_BUILD}"
|
||||
|
||||
if (servicesToBuild.isEmpty()) {
|
||||
echo "No services need to be built. Skipping build."
|
||||
if (env.SERVICES_TO_BUILD.isEmpty()) {
|
||||
echo "⏭️ No changes detected, skipping build"
|
||||
currentBuild.result = 'SUCCESS'
|
||||
error("No changes detected, skipping build")
|
||||
error("No changes")
|
||||
}
|
||||
echo "🔄 Services to build: ${env.SERVICES_TO_BUILD}"
|
||||
echo "📦 Deps changed: ${env.DEPS_CHANGED}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('Docker Build & Push') {
|
||||
stage('Build Dependencies Image') {
|
||||
when {
|
||||
expression { env.SERVICES_TO_BUILD != '' }
|
||||
expression {
|
||||
env.DEPS_CHANGED == 'true' || !depsImageExists()
|
||||
}
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
echo "=== Docker 镜像构建与推送 ==="
|
||||
echo "使用 Docker 多阶段构建(包含 Maven 编译)"
|
||||
echo "📦 Building dependencies base image..."
|
||||
|
||||
def services = env.SERVICES_TO_BUILD.split(',')
|
||||
sh """
|
||||
docker build \
|
||||
-f docker/Dockerfile.deps \
|
||||
-t ${DEPS_IMAGE} \
|
||||
.
|
||||
|
||||
docker push ${DEPS_IMAGE}
|
||||
"""
|
||||
|
||||
// 串行构建,避免内存溢出
|
||||
services.each { service ->
|
||||
def modulePath = getModulePath(service)
|
||||
def jarName = service
|
||||
echo "✅ Dependencies image built and pushed"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('Build Services') {
|
||||
when { expression { env.SERVICES_TO_BUILD != '' } }
|
||||
steps {
|
||||
script {
|
||||
def services = env.SERVICES_TO_BUILD.split(',') as List
|
||||
def batchSize = MAX_PARALLEL.toInteger()
|
||||
|
||||
echo "🔨 Building ${services.size()} services with parallelism=${batchSize}"
|
||||
|
||||
// 分批并行构建
|
||||
services.collate(batchSize).each { batch ->
|
||||
echo "📦 Building batch: ${batch.join(', ')}"
|
||||
|
||||
echo "========================================="
|
||||
echo "构建服务: ${service}"
|
||||
echo "模块路径: ${modulePath}"
|
||||
echo "========================================="
|
||||
|
||||
try {
|
||||
// Docker 多阶段构建(包含 Maven 编译和镜像打包)
|
||||
sh """
|
||||
docker build \
|
||||
-f docker/Dockerfile.template \
|
||||
--build-arg MODULE_NAME=${modulePath} \
|
||||
--build-arg JAR_NAME=${jarName} \
|
||||
--build-arg SKIP_TESTS=true \
|
||||
-t ${REGISTRY}/${service}:${IMAGE_TAG} \
|
||||
-t ${REGISTRY}/${service}:latest \
|
||||
.
|
||||
"""
|
||||
|
||||
echo "推送镜像: ${service}"
|
||||
sh """
|
||||
docker push ${REGISTRY}/${service}:${IMAGE_TAG}
|
||||
docker push ${REGISTRY}/${service}:latest
|
||||
"""
|
||||
|
||||
echo "✓ ${service} 构建成功"
|
||||
} catch (Exception e) {
|
||||
echo "✗ ${service} 构建失败: ${e.message}"
|
||||
throw e
|
||||
def parallelBuilds = [:]
|
||||
batch.each { service ->
|
||||
parallelBuilds[service] = {
|
||||
buildAndPush(service)
|
||||
}
|
||||
}
|
||||
|
||||
parallel parallelBuilds
|
||||
}
|
||||
|
||||
// 清理悬空镜像
|
||||
echo "清理悬空镜像..."
|
||||
// 清理
|
||||
sh "docker image prune -f || true"
|
||||
}
|
||||
}
|
||||
@@ -172,46 +106,26 @@ pipeline {
|
||||
|
||||
stage('Deploy') {
|
||||
when {
|
||||
expression { env.SERVICES_TO_BUILD != '' && env.BRANCH_NAME == 'master' }
|
||||
allOf {
|
||||
expression { env.SERVICES_TO_BUILD != '' }
|
||||
branch 'master'
|
||||
}
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
echo "=== 部署到生产环境 ==="
|
||||
|
||||
def services = env.SERVICES_TO_BUILD.split(',')
|
||||
def services = env.SERVICES_TO_BUILD.split(',') as List
|
||||
|
||||
// 部署也可以并行
|
||||
def parallelDeploys = [:]
|
||||
services.each { service ->
|
||||
echo "Deploying ${service}..."
|
||||
|
||||
// 诊断信息
|
||||
sh """
|
||||
echo "=== 诊断信息 ==="
|
||||
echo "当前用户: \$(whoami)"
|
||||
echo "当前目录: \$(pwd)"
|
||||
echo "测试 SSH 连接..."
|
||||
ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 root@172.19.0.1 'echo "SSH 连接成功"; hostname; pwd' || echo "SSH 连接失败"
|
||||
"""
|
||||
|
||||
// 通过 SSH 在宿主机上执行部署命令
|
||||
// Jenkins 容器网络: 1panel-network, Gateway: 172.19.0.1
|
||||
sh """
|
||||
ssh -o StrictHostKeyChecking=no root@172.19.0.1 '
|
||||
cd /opt/aiot-platform-cloud || { echo "目录不存在,创建中..."; mkdir -p /opt/aiot-platform-cloud; exit 1; }
|
||||
docker compose -f docker-compose.core.yml pull ${service}
|
||||
docker compose -f docker-compose.core.yml up -d ${service}
|
||||
'
|
||||
"""
|
||||
|
||||
// 等待服务健康检查
|
||||
echo "Waiting for ${service} to be healthy..."
|
||||
sh """
|
||||
ssh -o StrictHostKeyChecking=no root@172.19.0.1 '
|
||||
timeout 120 sh -c "until docker inspect --format=\\"{{.State.Health.Status}}\\" aiot-${service} 2>/dev/null | grep -q healthy; do sleep 5; done" || true
|
||||
'
|
||||
"""
|
||||
parallelDeploys[service] = {
|
||||
deployService(service)
|
||||
}
|
||||
}
|
||||
|
||||
echo "Deployment completed!"
|
||||
parallel parallelDeploys
|
||||
|
||||
echo "🚀 Deployment completed!"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -219,23 +133,18 @@ pipeline {
|
||||
|
||||
post {
|
||||
success {
|
||||
echo "=== 构建成功 ==="
|
||||
echo "Built services: ${env.SERVICES_TO_BUILD}"
|
||||
echo "Image tag: ${IMAGE_TAG}"
|
||||
echo """
|
||||
✅ 构建成功
|
||||
📦 Services: ${env.SERVICES_TO_BUILD}
|
||||
🏷️ Tag: ${IMAGE_TAG}
|
||||
"""
|
||||
}
|
||||
|
||||
failure {
|
||||
echo "=== 构建失败 ==="
|
||||
echo "Please check the logs for details."
|
||||
echo "❌ 构建失败,请检查日志"
|
||||
}
|
||||
|
||||
always {
|
||||
// 清理工作空间(可选)
|
||||
// cleanWs()
|
||||
|
||||
// 显示磁盘使用情况
|
||||
sh 'df -h'
|
||||
sh 'docker system df'
|
||||
sh 'df -h | grep -E "/$|/var" || true'
|
||||
sh 'docker system df || true'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -244,16 +153,118 @@ pipeline {
|
||||
// 辅助函数
|
||||
// ============================================
|
||||
|
||||
def detectChangedServices() {
|
||||
def changedFiles = sh(
|
||||
script: '''
|
||||
PREV=$(git rev-parse HEAD~1 2>/dev/null || echo "")
|
||||
[ -z "$PREV" ] && echo "all" || git diff --name-only $PREV HEAD
|
||||
''',
|
||||
returnStdout: true
|
||||
).trim()
|
||||
|
||||
if (changedFiles == 'all' || changedFiles.isEmpty()) {
|
||||
return env.CORE_SERVICES
|
||||
}
|
||||
|
||||
def triggerAll = ['pom.xml', 'viewsh-framework', 'viewsh-dependencies', 'Jenkinsfile', 'docker/']
|
||||
if (triggerAll.any { changedFiles.contains(it) }) {
|
||||
return env.CORE_SERVICES
|
||||
}
|
||||
|
||||
def services = []
|
||||
env.CORE_SERVICES.split(',').each { service ->
|
||||
def path = getModulePath(service).split('/')[0]
|
||||
if (changedFiles.contains(path)) {
|
||||
services.add(service)
|
||||
}
|
||||
}
|
||||
|
||||
return services.isEmpty() ? env.CORE_SERVICES : services.join(',')
|
||||
}
|
||||
|
||||
def checkDepsChanged() {
|
||||
def changedFiles = sh(
|
||||
script: '''
|
||||
PREV=$(git rev-parse HEAD~1 2>/dev/null || echo "")
|
||||
[ -z "$PREV" ] && echo "all" || git diff --name-only $PREV HEAD
|
||||
''',
|
||||
returnStdout: true
|
||||
).trim()
|
||||
|
||||
// 如果 pom.xml 或基础模块变化,需要重建依赖镜像
|
||||
def depsFiles = ['pom.xml', 'viewsh-dependencies', 'viewsh-framework']
|
||||
|
||||
if (changedFiles == 'all') {
|
||||
return 'true'
|
||||
}
|
||||
|
||||
return depsFiles.any { changedFiles.contains(it) } ? 'true' : 'false'
|
||||
}
|
||||
|
||||
def depsImageExists() {
|
||||
def result = sh(
|
||||
script: "docker manifest inspect ${env.DEPS_IMAGE} > /dev/null 2>&1",
|
||||
returnStatus: true
|
||||
)
|
||||
return result == 0
|
||||
}
|
||||
|
||||
def buildAndPush(String service) {
|
||||
def modulePath = getModulePath(service)
|
||||
|
||||
echo "🔨 Building ${service}..."
|
||||
|
||||
sh """
|
||||
docker build \
|
||||
-f docker/Dockerfile.service \
|
||||
--build-arg DEPS_IMAGE=${DEPS_IMAGE} \
|
||||
--build-arg MODULE_NAME=${modulePath} \
|
||||
--build-arg JAR_NAME=${service} \
|
||||
--build-arg SKIP_TESTS=true \
|
||||
-t ${REGISTRY}/${service}:${IMAGE_TAG} \
|
||||
-t ${REGISTRY}/${service}:latest \
|
||||
.
|
||||
|
||||
docker push ${REGISTRY}/${service}:${IMAGE_TAG}
|
||||
docker push ${REGISTRY}/${service}:latest
|
||||
"""
|
||||
|
||||
echo "✅ ${service} built and pushed"
|
||||
}
|
||||
|
||||
def deployService(String service) {
|
||||
echo "🚀 Deploying ${service}..."
|
||||
|
||||
sh """
|
||||
ssh -o StrictHostKeyChecking=no root@${DEPLOY_HOST} '
|
||||
cd ${DEPLOY_PATH}
|
||||
docker compose -f docker-compose.core.yml pull ${service}
|
||||
docker compose -f docker-compose.core.yml up -d ${service}
|
||||
'
|
||||
"""
|
||||
|
||||
// 健康检查
|
||||
sh """
|
||||
ssh -o StrictHostKeyChecking=no root@${DEPLOY_HOST} '
|
||||
for i in 1 2 3 4 5 6 7 8 9 10 11 12; do
|
||||
STATUS=\$(docker inspect --format="{{.State.Health.Status}}" aiot-${service} 2>/dev/null || echo "starting")
|
||||
[ "\$STATUS" = "healthy" ] && echo "${service} is healthy" && exit 0
|
||||
sleep 10
|
||||
done
|
||||
echo "${service} health check timeout"
|
||||
'
|
||||
"""
|
||||
|
||||
echo "✅ ${service} deployed"
|
||||
}
|
||||
|
||||
def getModulePath(String service) {
|
||||
// 根据服务名获取 Maven 模块路径
|
||||
def moduleMap = [
|
||||
def map = [
|
||||
'viewsh-gateway': 'viewsh-gateway',
|
||||
'viewsh-module-system-server': 'viewsh-module-system/viewsh-module-system-server',
|
||||
'viewsh-module-infra-server': 'viewsh-module-infra/viewsh-module-infra-server',
|
||||
'viewsh-module-iot-server': 'viewsh-module-iot/viewsh-module-iot-server',
|
||||
'viewsh-module-iot-gateway': 'viewsh-module-iot/viewsh-module-iot-gateway'
|
||||
// viewsh-module-ops-server 暂未合并到 master,待开发完成后添加
|
||||
]
|
||||
|
||||
return moduleMap[service] ?: service
|
||||
return map[service] ?: service
|
||||
}
|
||||
|
||||
38
docker/Dockerfile.deps
Normal file
38
docker/Dockerfile.deps
Normal file
@@ -0,0 +1,38 @@
|
||||
# ============================================
|
||||
# Maven 依赖基础镜像
|
||||
# 预下载所有依赖,供服务构建时复用
|
||||
# ============================================
|
||||
|
||||
FROM eclipse-temurin:17-jdk-alpine
|
||||
|
||||
# 安装 Maven
|
||||
RUN apk add --no-cache maven
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
# 复制所有 pom 文件
|
||||
COPY pom.xml .
|
||||
COPY viewsh-dependencies/pom.xml viewsh-dependencies/
|
||||
COPY viewsh-framework/pom.xml viewsh-framework/
|
||||
COPY viewsh-gateway/pom.xml viewsh-gateway/
|
||||
COPY viewsh-server/pom.xml viewsh-server/
|
||||
COPY viewsh-module-system/pom.xml viewsh-module-system/
|
||||
COPY viewsh-module-system/viewsh-module-system-api/pom.xml viewsh-module-system/viewsh-module-system-api/
|
||||
COPY viewsh-module-system/viewsh-module-system-server/pom.xml viewsh-module-system/viewsh-module-system-server/
|
||||
COPY viewsh-module-infra/pom.xml viewsh-module-infra/
|
||||
COPY viewsh-module-infra/viewsh-module-infra-api/pom.xml viewsh-module-infra/viewsh-module-infra-api/
|
||||
COPY viewsh-module-infra/viewsh-module-infra-server/pom.xml viewsh-module-infra/viewsh-module-infra-server/
|
||||
COPY viewsh-module-iot/pom.xml viewsh-module-iot/
|
||||
COPY viewsh-module-iot/viewsh-module-iot-core/pom.xml viewsh-module-iot/viewsh-module-iot-core/
|
||||
COPY viewsh-module-iot/viewsh-module-iot-api/pom.xml viewsh-module-iot/viewsh-module-iot-api/
|
||||
COPY viewsh-module-iot/viewsh-module-iot-server/pom.xml viewsh-module-iot/viewsh-module-iot-server/
|
||||
COPY viewsh-module-iot/viewsh-module-iot-gateway/pom.xml viewsh-module-iot/viewsh-module-iot-gateway/
|
||||
|
||||
# 下载所有依赖到本地仓库
|
||||
RUN mvn dependency:go-offline -B || true
|
||||
|
||||
# 复制源代码
|
||||
COPY . .
|
||||
|
||||
# 预编译 framework 和 dependencies(所有服务共享)
|
||||
RUN mvn install -pl viewsh-dependencies,viewsh-framework -am -DskipTests -B -q || true
|
||||
69
docker/Dockerfile.service
Normal file
69
docker/Dockerfile.service
Normal file
@@ -0,0 +1,69 @@
|
||||
# ============================================
|
||||
# 服务构建 Dockerfile
|
||||
# 基于依赖基础镜像,快速编译服务
|
||||
# ============================================
|
||||
|
||||
# 构建参数
|
||||
ARG DEPS_IMAGE=aiot-deps:latest
|
||||
|
||||
# ============ 构建阶段 ============
|
||||
FROM ${DEPS_IMAGE} AS builder
|
||||
|
||||
ARG MODULE_NAME
|
||||
ARG JAR_NAME
|
||||
ARG SKIP_TESTS=true
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
# 复制最新源代码(覆盖基础镜像中的代码)
|
||||
COPY . .
|
||||
|
||||
# 编译指定模块(依赖已经在基础镜像中准备好)
|
||||
RUN mvn package -pl ${MODULE_NAME} -am -DskipTests=${SKIP_TESTS} -B -q -o || \
|
||||
mvn package -pl ${MODULE_NAME} -am -DskipTests=${SKIP_TESTS} -B -q
|
||||
|
||||
# ============ 运行阶段 ============
|
||||
FROM eclipse-temurin:17-jre-alpine
|
||||
|
||||
# 安装必要工具
|
||||
RUN apk add --no-cache wget curl
|
||||
|
||||
# 构建参数
|
||||
ARG MODULE_NAME
|
||||
ARG JAR_NAME
|
||||
ARG APP_PORT=48080
|
||||
|
||||
# 元数据标签
|
||||
LABEL maintainer="XW-AIOT Team"
|
||||
LABEL service="${MODULE_NAME}"
|
||||
|
||||
# 创建非 root 用户
|
||||
RUN addgroup -g 1000 appuser && \
|
||||
adduser -D -u 1000 -G appuser appuser
|
||||
|
||||
# 创建应用目录
|
||||
RUN mkdir -p /app/logs /app/config && \
|
||||
chown -R appuser:appuser /app
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 从构建阶段复制 JAR 文件
|
||||
COPY --from=builder --chown=appuser:appuser /build/${MODULE_NAME}/target/${JAR_NAME}.jar app.jar
|
||||
|
||||
# 切换到非 root 用户
|
||||
USER appuser
|
||||
|
||||
# 环境变量
|
||||
ENV TZ=Asia/Shanghai \
|
||||
JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseG1GC -XX:MaxGCPauseMillis=200" \
|
||||
SPRING_PROFILES_ACTIVE=prod
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE ${APP_PORT}
|
||||
|
||||
# 健康检查
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
|
||||
CMD wget --no-verbose --tries=1 --spider http://localhost:${APP_PORT}/actuator/health || exit 1
|
||||
|
||||
# 启动应用
|
||||
ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar app.jar"]
|
||||
Reference in New Issue
Block a user