Jenkins

Jenkins

安装

安装Java 8 ( JRE 或者 JDK 都可以)、清华镜像站
Jenkins和java版本关系

Supported Java versions for the LTS release line are:

2.361.1 (September 2022) and newer
Java 11 or Java 17

2.346.1 (June 2022) and newer
Java 8, Java 11, or Java 17

2.164.1 (March 2019) and newer
Java 8 or Java 11

2.60.1 (June 2017) and newer
Java 8

1.625.1 (October 2015) and newer
Java 7

war包启动

java -jar jenkins.war #默认端口8080
java -jar jenkins.war --httpPort=8080
java -jar jenkins.war --httpPort=8080 --prefix=/jenkins  # 指定URL路径

## 输出截取如下
Jenkins initial setup is required. An admin user has been created and a password generated.
Please use the following password to proceed to installation:

c1f0833ce31d41f4ad2b015867acbe8c

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

tomcat启动

1.安装tomcat
安装Tomcat,然后进入tomcat的webapp目录下,直接将jenkins.war放进来

2.设置Jekins环境变量设置
vi /etc/profile

在最后面加上这一句
export JENKINS_HOME=/data/program/tomcat/webapps/Jenkins(jnekins的安装路径)

. /etc/profile #使配置文件生效

3.修改tomcat端口号
#vi /usr/local/apache-tomcat/conf/server.xml
<Connector port="8083" protocol="HTTP/1.1"connectionTimeout="20000"                    redirectPort="8443" />

保证端口号不冲突
4. 重启tomcat,进入tomcat的安装目录下的bin目录

./startup.sh

tomcat会解压war包,生成一个jenkins文件夹,而且会在root目录下生成一个.jenkins的文件夹

WEB

http://localhost:8080
http://localhost:8080/jenkins

可以直接关闭新手入门界面。

安装插件

Manage Jenkins - Manage Plugins - Available 进行插件安装,搜索"local" - 勾选下面两个插件 - 下载安装并重启。

Locale                                  # 安装这个才会有`Locale - Default Language`配置项
Localization: Chinese (Simplified)      # 中文,安装完此插件,界面就是中文了

Pipeline                                # 流水线
matrix-project                          # Multi-Configuration Projects 中文叫多配置项目

Git                                     # Git仓库
GitLab                                  # GitLab仓库

可在 系统管理 - 系统配置 - Locale - Default Language配置语言 - 填写: zh_CN

对于无法安装的Plugin,如"trilead-api",可在清华大学镜像站手动下载,在Plugin Manager- Advanced页面进行上传安装。

相关依赖

git

yum install -y git

【系统管理】-【管理凭据】-【Jenkins】-【全局凭据】-【添加凭据】-将git账号填在这里

Docekr

远程关闭重启

只需要在访问jenkins服务器的网址url地址后加上对应指令即可。

# 1、关闭Jenkins
http://localhost:8080/exit
# 2、重启Jenkies
http://localhost:8080/restart
# 3、重新加载配置信息
http://localhost:8080/reload

参数化构建

新建一个自由风格的job,然后勾选“参数化构建过程 This project is parameterized”,点击“添加参数”,常用的类型有:字符参数、文本参数、选项参数。保存。之后进入该project,点击Build with Parameters即可开始任务。

Pipeline

Declarative Pipeline / 声明性Pipeline

在声明式Pipeline中的基本语句和表达式遵循Groovy的语法;

流水线顶层必须pipeline{};

agent any可以在任何可用的节点上执行pipeline;

stages包含一系列一个或多个stage 指令, 建议 stages 至少包含一个 stage 指令用于连续交付过程的每个离散部分,比如构建, 测试, 和部署;

steps是每个阶段中要执行的每个步骤。

// Jenkinsfile (Declarative Pipeline)
pipeline {
    agent any 
    stages {
        stage('Build') { 
            steps {
                // 
            }
        }
        stage('Test') { 
            steps {
                // 
            }
        }
        stage('Deploy') { 
            steps {
                // 
            }
        }
    }
}

Scripted Pipeline / 脚本化Pipeline

脚本化流水线, 与声明式一样的是, 是建立在底层流水线的子系统上的。与声明式不同的是, 脚本化流水线实际上是由 Groovy构建的通用 DSL 。 Groovy 语言提供的大部分功能都可以用于脚本化流水线的用户。

脚本化pipeline 顶层是node{};

支持stage;

可以直接使用groovy语言进行编码。

// Jenkinsfile (Scripted Pipeline)
node {  
    stage('Build') { 
        // 
    }
    stage('Test') { 
        // 
    }
    stage('Deploy') { 
        // 
    }
}

示例

// Jenkinsfile (Scripted Pipeline)
node { 
    def ENV = 'test'
    def REPO =   'xxx'
    def IMAGE_PATH = "${ENV}/${REPO}"

    stage ('checkout'){ 
        sh """
            pwd
            ls
            docker login --username=100025468706 ccr.ccs.tencentyun.com/clrepo --password=cL@bRep0s1to2y
        """
        git branch: 'master', credentialsId: 'xsio', url: 'https://gitlab.cd.xsio.cn/cd_devops/gitlab-runner-helloworld2.git'
        commitId = sh returnStdout: true, script: 'git rev-parse HEAD'
        commitId = commitId.trim()

        // commitId = sh(returnStdout: true, script: 'git rev-parse HEAD').trim()

        IMAGE = "${env.REGISTRY_SNAPSHOT}/${IMAGE_PATH}:${commitId}"
    }

    stage('Build') { 
        // 
    }
    stage('build frontend') {
        sh '''
            export HOME=/opt/hudson
            . ${HOME}/.bashrc
            npm i --unsafe-perm
            npm run build
        '''
    }
    stage ('build backend'){
        sh '''
            export HOME=/opt/hudson
            . ${HOME}/.bashrc
            # mvn -B -DskipTests clean package
            chmod +x ./gradlew
            ./gradlew clean
            ./gradlew bootRepackage
            '''
        }
    stage('Test') { 
        echo 'fasttest for test'
    }
    stage ('package'){
        sh """
            export HOME=/opt/hudson
            docker build -t ${IMAGE} .
            docker push ${IMAGE}
            docker rmi ${IMAGE} || echo 
        """
        }
    stage ('deploy'){
        // 调用创建独立的使用参数化构建的job进行发布
        build job: "deploycdk8s/master", parameters: [string(name: 'IMAGE_TAG', value: commitId), string(name: 'SERVICE_NAMES', value: REPO), string(name: 'ENV', value: ENV)]
        }
}

二者的选择

那么为什么两种实现pipeline的方式呢?我们可以简单的了解一下其发展的历史。Jenkins是使用Java实现的,所以在很早的时候就引入了groovy作为DSL,管理员可以使用groovy来实现一些自动化和高级的管理功能。因为groovy引擎已经集成到Jenkins,所以在pipeline一开始很自然就使用groovy来编写Jenkinsfile。但是groovy毕竟是一种语言,对于没有太多编程经验的小白学习成本有些高,这个时候声明式的pipeline就出现了,主要是为了降低入门的难度,二者主要区别如下:

声明式pipeline,官方鼓励声明式编程模型,比较适合没有编程经验的初学者。

脚本式pipeline,是基于groovy的DSL语言实现的,为Jenkins用户提供了大量的灵活性性和可扩展性,如果脚本中有大量的逻辑处理则推荐使用。

个人总结二者的主要区别有两点:

  1. 脚本式pipeline只支持stage,像stages 、steps、options更细致的阶段划分则不支持;

2.当我们需要在脚本中写复杂逻辑的时候,通过脚本式pipeline可以方便的编写脚本,例如需要加入循环的逻辑,使用脚本式pipeline

内置的环境变量

全局变量可以在搭建好的jenkins服务上查看,访问地址:http://jenkins访问地址/pipeline-syntax/globals#env

使用环境变量
Jenkins 流水线通过全局变量 env 提供环境变量,它在 Jenkinsfile 文件的任何地方都可以使用。Jenkins 流水线中可访问的完整的环境变量列表记录在 ``${YOUR_JENKINS_URL}/pipeline-syntax/globals#env``,并且包括:

BUILD_ID
当前构建的 ID,与 Jenkins 版本 1.597+ 中创建的构建号 BUILD_NUMBER 是完全相同的。

BUILD_NUMBER
当前构建号,比如 “153”。

BUILD_TAG
字符串 ``jenkins-${JOB_NAME}-${BUILD_NUMBER}``。可以放到源代码、jar 等文件中便于识别。

BUILD_URL
可以定位此次构建结果的 URL(比如 http://buildserver/jenkins/job/MyJobName/17/ )

EXECUTOR_NUMBER
用于识别执行当前构建的执行者的唯一编号(在同一台机器的所有执行者中)。这个就是你在“构建执行状态”中看到的编号,只不过编号从 0 开始,而不是 1。

JAVA_HOME
如果你的任务配置了使用特定的一个 JDK,那么这个变量就被设置为此 JDK 的 JAVA_HOME。当设置了此变量时,PATH 也将包括 JAVA_HOME 的 bin 子目录。

JENKINS_URL
Jenkins 服务器的完整 URL,比如 https://example.com:port/jenkins/ (注意:只有在“系统设置”中设置了 Jenkins URL 才可用)。

JOB_NAME
本次构建的项目名称,如 “foo” 或 “foo/bar”。

NODE_NAME
运行本次构建的节点名称。对于 master 节点则为 “master”。

WORKSPACE
workspace 的绝对路径。

其他功能示例

pipeline {
    agent any
    parameters {
        // 填写字符串
        string(name: "cdp_version", defaultValue: "", description: "")
        //下拉选择,默认将 choices 列表中的第一个值作为默认值。
        choice(name: 'cdp_profiles', choices: ['small', 'medium', 'large'], description: 'Esxi Server profiles.')
    }
    environment {
        CDP_VERSION   = "$params.cdp_version"
        CDP_PROFILES  = "$params.cdp_profiles"
    }

    stages {
        stage("Test Job") {
            steps{
                script {
                    sh """
                        // 引用变量
                        echo "Deploying ${params.cdp_version} version"
                        export cdp_profiles=${CDP_PROFILES}
                    """
                }
            }
        }
        // 调用其他 Job,无返回值
        stage('Trigger Another Job') {
            steps {
                build job: 'boot-auto-test-rlx',
                    parameters: [string(name: 'cdp_version', value: "${env.CDP_VERSION}"),
                                 string(name: 'cdp_profiles', value: "${env.CDP_PROFILES}")],
                    wait: false
            }
        }
        // 调用其他 Job,带返回值
        stage('Trigger Another Job And Return') {
            steps {
                script {
                    def buildResult = build job: 'boot-auto-test-rlx', 
                                           parameters: [string(name: 'cdp_version', value: "${env.CDP_VERSION}"),
                                                        string(name: 'cdp_profiles', value: "${env.CDP_PROFILES}")],
                                           wait: false
                    // 输出跳转链接到指定的构建ID
                    echo "Triggered build URL: ${buildResult.getAbsoluteUrl()}"
                }
            }
        }
    }
}

Remote access API

CURL

wiki

个人尝试了下,该方式是通过命令行直接调curl去发POST请求的方式来触发job的构建。对于用openid管理的Jenkins,需要带上参数--user USER:PASSWORD,其中的USER和PASSWORD不是你的openID登录的账号密码,而是登录后显示在Jenkins中的User Id和API Token,它们的的查看方式如下:

用openID登录jenkins —> 点击右上角的用户名,进入用户个人页面 —> 点击左边的设置,打开设置页面 —> API Token,Show Api Token...

如果需要参数化构建job,则要加上--data-urlencode json='{"parameter": [{"name":"param_name1","value":"param_value1"}, {"name":"param_name2","value":"param_value2"}]}'

显然,这种方式比较繁琐,很容易出现因格式不正确导致触发任务失败,而且这种方式不能帮助我们获取更多的关于job的信息以便于我们后续对job的状态进行跟踪。

#定义远程的jenkins master server的url,以及port
JENKINS_URL='http://jenkins.xx.xxx.cn/'

#定义用户的User Id 和 API Token,获取方式同上文
USER='xxx'
TOKEN='xxxx'

#获取job名为job_name的job的相关信息
JOB_NAME='build_wechat-plugin'

#获取job名为job_name的job的lastBuild的buildNumber
build_number=$(curl -s --user ${USER}:${TOKEN} ${JENKINS_URL}/job/${JOB_NAME}/lastBuild/buildNumber)
echo "lastBuildNumber: ${build_number}"

#获取job名为job_name的job的某次构建的执行结果状态

#判断job名为job_name的job的某次构建是否还在构建中

#获取job名为job_name的job的某次构建的Console Output
job_console_output=$(curl -s --user ${USER}:${TOKEN} ${JENKINS_URL}/job/${JOB_NAME}/lastBuild/consoleFull)
job_console_output=$(curl -s --user ${USER}:${TOKEN} ${JENKINS_URL}/job/${JOB_NAME}/${build_number}/consoleFull)
#echo "${job_console_output}"

echo "${job_console_output}" |grep "docker push"

# 获取构建控制台输出,用这个,比consoleFull少了没用的HTML信息。
curl -s --user ${USER}:${TOKEN} ${JENKINS_URL}/job/${JOB_NAME}/lastBuild/logText/progressiveText

#获取最近一次成功的build的时间
job_timestamp=$(curl -s --user ${USER}:${TOKEN} ${JENKINS_URL}/job/${JOB_NAME}/lastBuild/buildTimestamp)
#echo ${job_timestamp}
#结果为: 4/1/22 4:30 PM   转换: date -d "4/1/22 4:30 PM" +"%Y-%m-%d %H:%M:%S"
job_timestamp=$(date -d "${job_timestamp}" +"%Y-%m-%d %H:%M:%S")
echo ${job_timestamp}
#结果为: 2022-04-01 16:30.00

#获取最近一次成功的build的描述,不确定是否有用
curl -s --user ${USER}:${TOKEN} ${JENKINS_URL}/job/${JOB_NAME}/lastBuild/description

#获取最近一次成功的build的num
curl -s --user ${USER}:${TOKEN} ${JENKINS_URL}/job/${JOB_NAME}/lastSuccessfulBuild/buildNumber

#获取最近一次稳定的build的num
curl -s --user ${USER}:${TOKEN} ${JENKINS_URL}/job/${JOB_NAME}/lastStableBuild/buildNumber

#获取最近一次失败的build的num
curl -s --user ${USER}:${TOKEN} ${JENKINS_URL}/job/${JOB_NAME}/lastFailedBuild/buildNumber

#查询job名为job_name的job的lastBuild的状态 json 格式
curl -s --user ${USER}:${TOKEN} ${JENKINS_URL}/job/${JOB_NAME}/lastBuild/api/json
curl -s --user ${USER}:${TOKEN} ${JENKINS_URL}/job/${JOB_NAME}/lastBuild/api/json | sed 's/,/\n/g'
curl -s --user ${USER}:${TOKEN} ${JENKINS_URL}/job/${JOB_NAME}/lastBuild/api/json | sed 's/,/\n/g' | sed -n 's/"result":"\([^"]*\)"/\1/p'
curl -s --user ${USER}:${TOKEN} ${JENKINS_URL}/job/${JOB_NAME}/lastBuild/api/json | sed 's/,/\n/g' | sed -n 's/"timestamp":\([^,]*\)/\1/p' | awk 'NR==1 {print $0}'
#curl -s --user ${USER}:${TOKEN} ${JENKINS_URL}/job/${JOB_NAME}/lastBuild/api/xml
curl -s --user ${USER}:${TOKEN} ${JENKINS_URL}/job/${JOB_NAME}/${build_number}/api/json
#curl -s --user ${USER}:${TOKEN} ${JENKINS_URL}/job/${JOB_NAME}/${build_number}/api/xml

curl -s --user ${USER}:${TOKEN} ${JENKINS_URL}/job/${JOB_NAME}/buildHistory/ajax

Python-Jenkins

Python-Jenkins官网
Python-Jenkins Doc

#pip install python-jenkins
import jenkins

import re

#定义远程的jenkins master server的url,以及port
jenkins_server_url='http://jenkins.xx.xxx.cn/'

#定义用户的User Id 和 API Token,获取方式同上文
user_id='xxx'
api_token='xxxx'

#实例化jenkins对象,连接远程的jenkins master server
server=jenkins.Jenkins(jenkins_server_url, username=user_id, password=api_token)

#构建job名为job_name的job(不带构建参数)
#server.build_job(job_name)

#String参数化构建job名为job_name的job, 参数param_dict为字典形式,如:param_dict= {"param1":“value1”, “param2”:“value2”}
#server.build_job(job_name, parameters=param_dict)

#获取job名为job_name的job的相关信息
job_name = 'build_wechatplugin'
job_result = server.get_job_info(job_name)
print(job_result)

#获取job名为job_name的job的最后次构建号
build_number = server.get_job_info(job_name)['lastBuild']['number']
print(build_number)

#获取job名为job_name的job的某次构建的执行结果状态
job_status = server.get_build_info(job_name,build_number)['result']
print(job_status)

#判断job名为job_name的job的某次构建是否还在构建中
is_building = server.get_build_info(job_name,build_number)['building']
print(is_building)

#获取job名为job_name的job的某次构建的Console Output
job_console_output = server.get_build_console_output(job_name,build_number)
#print(job_console_output)

pattern = re.compile('docker push .*?\n',re.S)

result = re.search(pattern, job_console_output)
print(result.group(0).strip())