- Jenkins 容器在 1panel-network 网络中 - 网关地址是 172.19.0.1 而不是 172.17.0.1 - Jenkins 负责指挥,实际部署在宿主机执行
251 lines
10 KiB
Groovy
251 lines
10 KiB
Groovy
// ============================================
|
||
// AIOT Platform - Jenkins Pipeline
|
||
// 智能增量构建 + Docker 容器化部署
|
||
// ============================================
|
||
|
||
pipeline {
|
||
agent any
|
||
|
||
options {
|
||
// 保留最近 10 次构建
|
||
buildDiscarder(logRotator(numToKeepStr: '10'))
|
||
// 禁止并发构建
|
||
disableConcurrentBuilds()
|
||
// 超时设置
|
||
timeout(time: 60, unit: 'MINUTES')
|
||
}
|
||
|
||
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)
|
||
CORE_SERVICES = 'viewsh-gateway,viewsh-module-system-server,viewsh-module-infra-server,viewsh-module-iot-server,viewsh-module-iot-gateway'
|
||
}
|
||
|
||
stages {
|
||
stage('Checkout') {
|
||
steps {
|
||
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}"
|
||
}
|
||
}
|
||
}
|
||
|
||
stage('Detect Changes') {
|
||
steps {
|
||
script {
|
||
echo "=== 检测变更的服务 ==="
|
||
|
||
// 获取变更的文件列表
|
||
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."
|
||
currentBuild.result = 'SUCCESS'
|
||
error("No changes detected, skipping build")
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
stage('Docker Build & Push') {
|
||
when {
|
||
expression { env.SERVICES_TO_BUILD != '' }
|
||
}
|
||
steps {
|
||
script {
|
||
echo "=== Docker 镜像构建与推送 ==="
|
||
echo "使用 Docker 多阶段构建(包含 Maven 编译)"
|
||
|
||
def services = env.SERVICES_TO_BUILD.split(',')
|
||
|
||
// 串行构建,避免内存溢出
|
||
services.each { service ->
|
||
def modulePath = getModulePath(service)
|
||
def jarName = service
|
||
|
||
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
|
||
}
|
||
}
|
||
|
||
// 清理悬空镜像
|
||
echo "清理悬空镜像..."
|
||
sh "docker image prune -f || true"
|
||
}
|
||
}
|
||
}
|
||
|
||
stage('Deploy') {
|
||
when {
|
||
expression { env.SERVICES_TO_BUILD != '' && env.BRANCH_NAME == 'master' }
|
||
}
|
||
steps {
|
||
script {
|
||
echo "=== 部署到生产环境 ==="
|
||
|
||
def services = env.SERVICES_TO_BUILD.split(',')
|
||
|
||
services.each { service ->
|
||
echo "Deploying ${service}..."
|
||
|
||
// 通过 SSH 在宿主机上执行部署命令
|
||
// Jenkins 容器网络: 1panel-network, Gateway: 172.19.0.1
|
||
sh """
|
||
ssh -o StrictHostKeyChecking=no root@172.19.0.1 '
|
||
cd /opt/aiot-platform-cloud
|
||
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
|
||
'
|
||
"""
|
||
}
|
||
|
||
echo "Deployment completed!"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
post {
|
||
success {
|
||
echo "=== 构建成功 ==="
|
||
echo "Built services: ${env.SERVICES_TO_BUILD}"
|
||
echo "Image tag: ${IMAGE_TAG}"
|
||
}
|
||
|
||
failure {
|
||
echo "=== 构建失败 ==="
|
||
echo "Please check the logs for details."
|
||
}
|
||
|
||
always {
|
||
// 清理工作空间(可选)
|
||
// cleanWs()
|
||
|
||
// 显示磁盘使用情况
|
||
sh 'df -h'
|
||
sh 'docker system df'
|
||
}
|
||
}
|
||
}
|
||
|
||
// ============================================
|
||
// 辅助函数
|
||
// ============================================
|
||
|
||
def getModulePath(String service) {
|
||
// 根据服务名获取 Maven 模块路径
|
||
def moduleMap = [
|
||
'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
|
||
}
|