提前声明:下文均使用Gradle进行项目的管理,没有使用Maven进行项目的管理。

1. 准备工作

注意:

  • 1.GraalVM编译需要gcc,在编译打包之前需要在Jenkins编译机器上先安装gcc。
  • 2.GraalVM编译需要libz.a,因此需要在Jenkins编译机器上先安装zlib
  • 3.GraalVM打包成为本地的二进制文件,因此它没有跨平台特性,如果想要在Linux上部署服务,就必须在Linux机器上进行打包
  • 4.GraalVM打包必须使用GraalVM专属的JDK进行编译,不能使用普通的JDK进行打包,可以从Oracle官网https://www.oracle.com/cn/java/technologies/downloads/#java24进行GraalVM的下载。

安装GCC:

sudo apt install gcc

安装zlib:

# ubuntu
sudo apt-get install zlib1g-dev
# centos
sudo yum install zlib-devel
# homebrew
brew install zlib

2.自定义GraalVM编译参数

Jenkins的编译Shell脚本如下,注意必须使用GraalVM的JDK进行编译和打包,使用普通的JDK无法进行GraalVM的编译打包。具体可以从Oracle官网https://www.oracle.com/cn/java/technologies/downloads/#java24上进行下载。

JAVA_HOME=/home/wanna/graalvm-jdk21
./gradlew clean nativeCompile -x test

对应的是Jenkins的BuildSteps这部分的配置。

image-cz6b.png

类型选择执行Shell。

image-mt8e.png

3.部署命令

编译之后,上传到目标机器上的启动脚本:

lsof -t -i:8080 | xargs -r kill -9 && cd /home/wanna/datasync/schedule-datasync/ && chmod a+x datasync-server && nohup ./datasync-server > info.log 2>&1 &

对应的是Jenkins中的"构建后操作"。

image-ivmo.png

注意:

    1. Source files决定我们要上传的文件列表,GraalVM的Gradle插件打包出来的产物放在build/native/nativeCompile/目录下,我们需要将build/native/nativeCompile/*上传到服务器上。
    1. 为了上传到远程服务器后,目录中的build/native/nativeCompile丢掉,在Remove prefix中,把这部分去掉,这样上传到目标服务器后目录就短一些。
    1. Remote directory中需要配置上传到服务器的哪个文件夹下,我这里填写的是schedule-datasync,代表我希望打包的二进制文件产物上传到这个文件夹下(注意Publish Over SSH插件本地配置了登录时的默认工作路径,两者路径拼接之后才是真正的目录)。

类型选择"Send build artifcats over SSH"(注意需要先安装"Publish Over SSH"插件)

image-xya4.png

4. 出现问题介绍

4.1 重复部署8080端口会出现端口冲突

使用如下的命令,找出来8080端口所占用的进程PID,使用kill命令进行杀掉。

lsof -t -i:8080 | xargs -r kill -9

4.2 上传的二进制文件没有x执行权限

解决方式:在部署命令中给二进制可执行文件先加上+x的权限再进行执行。

chmod a+x datasync-server

4.3 CPU指令集不兼容

出现问题详细如下:

The current machine does not support all of the following CPU features that are required by the image: [CX8, CMOV, FXSR, MMX, SSE, SSE2, SSE3, SSSE3, SSE4_1, SSE4_2, POPCNT, LZCNT, AVX, AVX2, BMI1, BMI2, FMA].
Please rebuild the executable with an appropriate setting of the -march option.

这种情况,说明GraalVM编译时,用到了一些高级的指令集,但是当前的CPU并不支持。

此时我们需要修改Gradle的配置,对GraalVMExtension进行配置,新增一个-march=x86-64参数,使用通过的x86-64结构,可以兼容大多数的服务器。

configure<GraalVMExtension> {
    binaries {
        named("main") {
            buildArgs.add("-march=x86-64")
        }
    }
}

修改后,重新进行提交代码,并触发Jenkins部署。当去服务器上看到如下的SpringBoot启动日志,说明启动成功(/home/wanna/datasync/schedule-datasync/datasync-server可以看到,用的是二进制的包进行的启动,而不是用的jar包进行的启动)。

2025-03-30T18:49:59.542Z  INFO 1845 --- [datasync-server] [           main] c.w.p.d.DatasyncServerApplicationKt      : Starting AOT-processed DatasyncServerApplicationKt using Java 21.0.6 with PID 1845 (/home/wanna/datasync/schedule-datasync/datasync-server started by wanna in /home/wanna/datasync/schedule-datasync)

5. 基于Jenkinsfile利用Pipeline完成项目的自动化部署

5.1 项目中新增Jenkinsfile文件

在项目下新增Jenkinsfile文件,其中内容如下:

pipeline {
    agent any
    
    environment {
        // Gitea 仓库信息
        GITEA_REPO = 'https://gitea.wanna1314y.top/wanna/datasync-server.git'
        GITEA_CREDENTIALS = 'gitea-username-password'
        GIT_BRANCH = 'main'

        // 部署的目标服务器的名字
        SERVER_NAME = 'datasync-server'
    }

    tools {
        // 使用 Jenkins 配置的 Maven
        maven 'maven'
        // 使用 Jenkins 配置的 JDK
        jdk 'graalvm-jdk21'
    }

    stages {
        stage('Checkout') {
            steps {
                // 使用凭证拉取代码
                git credentialsId: "${GITEA_CREDENTIALS}", 
                    url: "${GITEA_REPO}",
                    branch: "${GIT_BRANCH}"
            }
        }

        stage('Gradle Build') {
            steps {
                // 执行 Maven 构建
                sh """
                    JAVA_HOME=/home/wanna/graalvm-jdk21
                    ./gradlew clean nativeCompile -x test
                """
            }
        }

        // 上传文件并启动服务器
        stage('Upload And Start Server') {
            steps {
                sshPublisher(publishers: [
                    sshPublisherDesc(
                        configName: "${SERVER_NAME}",   // 部署的目标服务器的名字, 需要和Publish Over SSH配置的名称对应
                        transfers: [
                            sshTransfer(
                                sourceFiles: 'build/native/nativeCompile/*',    // 需要传输到远程的文件
                                remoteDirectory: "schedule-datasync",  // 上传到远程服务器路径, 会以Publish Over SSH配置的路径作为基础路径, 这里填相对路径
                                removePrefix: 'build/native/nativeCompile',     // 上传到服务器之后, 需要把文件去除的前缀, 比如文件是build/libs/xx.jar, 配置"build/libs/",那么上传到服务器之后路径就会去掉前缀

                                // 上传到远程服务器之后, 需要执行的命令, 需要注意的是: 当前路径是Home目录
                                execCommand: """
                                    lsof -t -i:8080 | xargs -r kill -9
                                    cd /home/wanna/datasync/schedule-datasync/
                                    chmod a+x datasync-server
                                    nohup ./datasync-server >> info.log 2>&1 &
                                """
                            )
                        ],
                        verbose: true
                    )
                ])
            }
        }
    }

    post {
        success {
            // 构建成功后的操作
            echo 'Build and deployment successful!'
        }
        failure {
            // 构建失败后的操作
            echo 'Build or deployment failed!'
        }
        always {
            // 清理工作空间
            cleanWs()
        }
    }
} 

5.2 在Jenkins上新建项目

在Jenkins中,新建任务,选择"流水线":

image-b2gf.png

选择"Pipeline scripte from SCM",代表从代码管理仓库中,选择Jenkinsfile进行项目构建。

接入填入Git仓库地址,和账号密码(Credentials)。

image-1c5c.png

在下面选择Git仓库中的代码分支,和Jenkinsfile的文件路径,从而实现让Jenkins自动从Git仓库拉取Jenkinsfile,进行项目的编译和部署。

image-gcmx.png