Jenkins安装与项目自动构建

1.安装Jenkins Jenkins安装手册 https://www.jenkins.io/doc/book/installing/ Jenkins官方的下载地址: https://www.jenkins.io/download/ 找到合适的版本,使用wget命&#

1.安装Jenkins

Jenkins安装手册

https://www.jenkins.io/doc/book/installing/

Jenkins官方的下载地址:

https://www.jenkins.io/download/

找到合适的版本,使用wget命令进行下载

wget https://get.jenkins.io/war-stable/2.452.3/jenkins.war

启动jenkis

java -jar jenkins.war

可能出现端口被占用的情况(因为Jenkins默认端口是8080,很容易冲突)

java.net.BindException: Address already in use
        at java.base/sun.nio.ch.Net.bind0(Native Method)
        at java.base/sun.nio.ch.Net.bind(Net.java:565)
        at java.base/sun.nio.ch.ServerSocketChannelImpl.netBind(ServerSocketChannelImpl.java:344)
        at java.base/sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:301)
        at org.eclipse.jetty.server.ServerConnector.openAcceptChannel(ServerConnector.java:339)
Caused: java.io.IOException: Failed to bind to 0.0.0.0/0.0.0.0:8080
        at org.eclipse.jetty.server.ServerConnector.openAcceptChannel(ServerConnector.java:344)
        at org.eclipse.jetty.server.ServerConnector.open(ServerConnector.java:304)
        at org.eclipse.jetty.server.Server.lambda$doStart$0(Server.java:402)
        at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
        at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
        at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
        at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024)
        at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
        at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
        at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
        at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
        at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
        at org.eclipse.jetty.server.Server.doStart(Server.java:398)
        at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:93)
        at winstone.Launcher.<init>(Launcher.java:205)
Caused: java.io.IOException: Failed to start Jetty
        at winstone.Launcher.<init>(Launcher.java:209)
        at winstone.Launcher.main(Launcher.java:496)
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
        at java.base/java.lang.reflect.Method.invoke(Method.java:580)
        at executable.Main.main(Main.java:351)

可以加上端口号配置(Jenkins安装手册有讲到)

java -jar jenkins.war --httpPort=9090

# 直接后台运行
nohup java -jar jenkins.war --httpPort=9090 & > info.log

指定端口号之后启动日志如下:

Running from: /home/ubuntu/jenkins/jenkins.war
webroot: /home/ubuntu/.jenkins/war
2024-07-19 09:45:55.111+0000 [id=1]     INFO    winstone.Logger#logInternal: Beginning extraction from war file
2024-07-19 09:45:55.228+0000 [id=1]     WARNING o.e.j.s.handler.ContextHandler#setContextPath: Empty contextPath
2024-07-19 09:45:55.294+0000 [id=1]     INFO    org.eclipse.jetty.server.Server#doStart: jetty-10.0.20; built: 2024-01-29T20:46:45.278Z; git: 3a745c71c23682146f262b99f4ddc4c1bc41630c; jvm 21.0.3+9-Ubuntu-1ubuntu122.04.1
2024-07-19 09:45:55.797+0000 [id=1]     INFO    o.e.j.w.StandardDescriptorProcessor#visitServlet: NO JSP Support for /, did not find org.eclipse.jetty.jsp.JettyJspServlet
2024-07-19 09:45:55.857+0000 [id=1]     INFO    o.e.j.s.s.DefaultSessionIdManager#doStart: Session workerName=node0
2024-07-19 09:45:56.604+0000 [id=1]     INFO    hudson.WebAppMain#contextInitialized: Jenkins home directory: /home/ubuntu/.jenkins found at: $user.home/.jenkins
2024-07-19 09:45:56.985+0000 [id=1]     INFO    o.e.j.s.handler.ContextHandler#doStart: Started w.@13006998{Jenkins v2.452.3,/,file:///home/ubuntu/.jenkins/war/,AVAILABLE}{/home/ubuntu/.jenkins/war}
2024-07-19 09:45:56.999+0000 [id=1]     INFO    o.e.j.server.AbstractConnector#doStart: Started ServerConnector@45afc369{HTTP/1.1, (http/1.1)}{0.0.0.0:9090}
2024-07-19 09:45:57.031+0000 [id=1]     INFO    org.eclipse.jetty.server.Server#doStart: Started Server@f0f2775{STARTING}[10.0.20,sto=0] @2674ms
2024-07-19 09:45:57.032+0000 [id=32]    INFO    winstone.Logger#logInternal: Winstone Servlet Engine running: controlPort=disabled
2024-07-19 09:45:57.305+0000 [id=40]    INFO    jenkins.InitReactorRunner$1#onAttained: Started initialization
2024-07-19 09:45:57.314+0000 [id=39]    INFO    jenkins.InitReactorRunner$1#onAttained: Listed all plugins
2024-07-19 09:45:58.456+0000 [id=40]    INFO    jenkins.InitReactorRunner$1#onAttained: Prepared all plugins
2024-07-19 09:45:58.461+0000 [id=39]    INFO    jenkins.InitReactorRunner$1#onAttained: Started all plugins
2024-07-19 09:45:58.474+0000 [id=39]    INFO    jenkins.InitReactorRunner$1#onAttained: Augmented all extensions
2024-07-19 09:45:58.728+0000 [id=38]    INFO    jenkins.InitReactorRunner$1#onAttained: System config loaded
2024-07-19 09:45:58.729+0000 [id=38]    INFO    jenkins.InitReactorRunner$1#onAttained: System config adapted
2024-07-19 09:45:58.729+0000 [id=38]    INFO    jenkins.InitReactorRunner$1#onAttained: Loaded all jobs
2024-07-19 09:45:58.731+0000 [id=38]    INFO    jenkins.InitReactorRunner$1#onAttained: Configuration for all jobs updated
2024-07-19 09:45:58.811+0000 [id=54]    INFO    hudson.util.Retrier#start: Attempt #1 to do the action check updates server
2024-07-19 09:45:59.394+0000 [id=38]    INFO    jenkins.install.SetupWizard#init: 

*************************************************************
*************************************************************
*************************************************************

Jenkins initial setup is required. An admin user has been created and a password generated.
Please use the following password to proceed to installation:

.................

This may also be found at: /home/ubuntu/.jenkins/secrets/initialAdminPassword

*************************************************************
*************************************************************
*************************************************************

使用HTTP访问9090端口可以进入Jenkins页面,进入页面可以查看~/.jenkins/secrets/initialAdminPassword当中的密码,进行手动登录。

http://localhost:9090

身份验证完成之后,进入到插件安装的环节,选择需要的插件进行安装。

2.Jenkis构建项目的插件安装&配置

2.1 Publish Over SSH安装和配置

这个插件的作用是支持使用SSH的方式将文件推送到目标机器上完成部署,比如我在当前机器上编译出来一个Jar包,希望推送到目标服务器上并使用java -jar完成服务的部署。

安装完成插件之后,需要配置远程服务器信息,配置路径如下:“Dashboard”-“系统管理”-“System”,搜索并找到Publish Over SSH对应的配置。

首先配置密码:需要从PassphraseKey,和Path To key三个选择一个进行配置。

  • 如果是简单的密码登录的方式(用户名+密码的方式),在passphrase当中配置具体的密码就行。
  • 如果是基于RSA密钥的方式,则需要将公钥上传到服务器、私钥填入Jenkins。
    • 公钥需要从本地拷贝放到放到远程目标服务器的~/.ssh/authorized_keys文件当中,格式参考ssh-rsa ...... {username}@{email}@qq.com
    • Path to key处填入私钥的文件路径,也可以在Key处填入私钥的内容,格式参考:-----BEGIN OPENSSH PRIVATE KEY----- ..... -----END OPENSSH PRIVATE KEY-----

jenkins-publish-over-ssh-1.png

接着在下面的SSH Server当中配置远程的机器的hostname,username,以及部署时对于文件需要推送到目标服务器的基础路径。

jenkins-publish-over-ssh-2.pic.jpg

在填写完成SSH相关的信息之后,可以点击右下角的Test Configuration去测试配置是否成功,能否连接上配置的目标服务器。

2.2 Jenkins环境JDK安装

运行Java应用必须是用到JDK,因此需要配置JDK才能正常执行。JDK的配置路径如下:“Dashboard"-“系统管理”-"全局工具配置”。

jenkins-java-install.png

在这里安装JDK有以下几种方式:

  • 可以手动指定一下JAVA_HOME的路径(如果本地已经有安装JDK);
  • 可以从java.sun.com下载/压缩包的方式自动安装JDK(本机没安装JDK的情况)。

2.3 Jenkins环境Gradle/Maven安装

Maven和Gradle的安装类似,配置路径:“Dashboard”-“系统管理”-“全局工具配置”,在这里选择需要安装的Maven版本/Gradle版本即可。

jenkins-gradle-install.png

3.在Jenkins当中基于自动化部署项目

3.1基于Gradle构建一个Java项目并部署到远程服务器

正常的部署一个Gradle工程的步骤如下:

  • 1.从Git仓库拉取代码。
  • 2.使用Gradle构建并输出jar包。
  • 3.使用java -jar运行生成的jar包。

在Jenkins当中,构建一个Gradle工程的步骤也完全一致,只是使用了Jenkins配置的方式实现。

(1) Jenkins新任务创建

在Jenkins首页选择"新建任务"

jenkins-main-menu.png

进入到下面的项目创建页面,给项目起个名,选择"构建一个自由风格的软件项目"这个项目类型,也可以根据需要选择别的项目类型。

jenkins-new-task.png

(2) 配置Git仓库地址用于代码拉取

接着是配置Git地址,配置的是需要从哪个地方去拉去代码完成构建。

  • Respository URL:填写远程Git仓库的地址。
  • Credential:填写登录远程Git仓库的账号密码。
  • Branches to build:填写要去进行构建的代码分支。

jenkins-build-project-git.png

(3) 利用构建工具(Maven/Gradle)完成项目的构建并输出产物

在拉取到项目之后,需要利用构建工具(Maven/Gradle)完成项目的构建,并输出最终的产物(构件,Archive)。

Build Steps处选择"增加构建步骤",我们这里使用Gradle进行项目的构建,因此这里选择"Invoke Gradle script"。

jenkins-build-step.png

在选择Invoke Gradle script之后,进入到下面的页面,我们选择Gradle执行的任务,比如我们平时执行构建使用的命令是./gradlew build,那么我们在这里就填入任务名称build

jenkins-build-project-gradle.png

上面的构建步骤,对于Java应用来说,最终会输出一个Jar包,接下来,我们需要配置,将这个Jar包上传到哪里?这个Jar包如何进行部署到目标服务器?

(4) 配置部署目标服务器并配置服务启动命令

在"构建后操作"当中,选择Send build artifacts over SSH,执行下面步骤。

  • 在Name处配置好我们之前安装Publish over SSH时配置的目标服务器的机器名称。
  • SourceFiles:配置我们需要上传到目标文件的文件的内容,如下的配置代表需要将工程目录下的build/libs/*.jar全部上传到目标服务器。
  • 在Remote directory当中选择上传到目标服务器的地址,会以Publish over SSH配置的地址作为基地址,我们这里配置的是相当于地址nativeproject,最终的上传路径会是/home/ubuntu/project/nativeproject
  • Exec command:在将Jar包上传到目标服务器之后,需要指定的应用启动命令,我们这里使用java -jar的方式去启动SpringBoot项目。

jenkins-build-project-remotessh.png

3.2 基于Jenkins Pipeline构建项目

在创建Jenkins任务时,选择流水线任务,可以通过指定Jenkinsfile的方式去构建项目。

Jenkinsfile基于Groovy语法进行脚本的编写,可以实现更加灵活的项目构建逻辑。

参考如下:

pipeline {
    agent any
    
    // 配置环境变量, 后面可以根据env.xxx去获取到环境变量信息
    environment {
        SERVER_NAME = 'tencentyun'
        REMOTE_DIR = 'nativeproject2'
        JAR_NAME = "build/libs/*.jar"
        GIT_URL = 'git@github.com:wanna280/nativeproject.git'
        GIT_BRANCH = 'main'
    }

    stages {
        // Git拉取代码
        stage("Checkout") {
            steps {
                git branch: '${GIT_BRANCH}', url: '${GIT_URL}'
            }
        }
        // 使用Shell去执行Gradle编译Jar包, 会放到build/libs/目录下
        stage('Build') {
            steps {
                sh './gradlew clean build'
            }
        }
        // 上传文件并启动服务器
        stage('Upload And Start Server') {
            steps {
                sshPublisher(publishers: [
                    sshPublisherDesc(
                        configName: "${env.SERVER_NAME}",   // 部署的目标服务器的名字, 需要和Publish Over SSH配置的名称对应
                        transfers: [
                            sshTransfer(
                                sourceFiles: 'build/libs/*.jar',    // 需要传输到远程的文件
                                remoteDirectory: "${env.REMOTE_DIR}",  // 上传到远程服务器路径, 会以Publish Over SSH配置的路径作为基础路径, 这里填相对路径
                                removePrefix: '',     // 上传到服务器之后, 需要把文件去除的前缀, 比如文件是build/libs/xx.jar, 配置"build/libs/",那么上传到服务器之后路径就会去掉前缀
                                execCommand: """  
                                pwd
                                cd project/nativeproject2/
                                pwd
                                nohup java -jar ${env.JAR_NAME} --server.port=9190 > info.log 2>&1 &
                                """   // 上传到远程服务器之后, 需要执行的命令, 需要注意的是: 当前路径是Home目录
                            )
                        ],
                        verbose: true
                    )
                ])
            }
        }
    }

    
    post {
        success {
            echo 'Deployment succeeded!'
        }
        failure {
            echo 'Deployment failed!'
        }
    }
}

我们定义三个阶段(Stage):

  • Checkout用于通过Git拉取代码。
  • Build用于执行Shell脚本,从而执行Gradle命令完成Gradle项目的构建。
  • Upload And Start Server,在Gradle构建完成项目之后会输出Jar包到build/libs/目录下,在这个阶段当中我们通过SSH的方式将生成的Jar包上传到目标服务器当中,并通过execCommand去配置部署目标服务器要执行的Shell脚本。
Comment