Jenkins workflowLibs库的使(妙)用

本文采用更为大白话的形式进行记录,实际应用在生产环境超过半年,现在整理出来分享一下

1、重温概念

开篇介绍,要写的当然还是一些文字性内容,不管是官方原文或书籍描述,都要花心思去理解,然后顺便表达一下我自己的理解。

  • 可信共享库和不可信共享库

Jenkins 的共享库形式,分为可信共享库和不可信共享库两种。

可信库可以调用/使用 Java 中的任何方法、 JenkinsAPIJenkins 插件、 Groovy 语言等。由于可信库在它们可以调用和使用的内容方面具有如此广泛的优势,所以管理可在其中添加和更改代码的权限就非常重要了。因此对可信库进行更新应该需要适当级别的源码版本控制访问和验证。出于同样的原因,可能会造成任何损害的代码应该始终被包含在受监督的可信库中。

不可信库代码是被调用和使用限制的代码,调用先前列出的方法类型不允许使用相同的自由度,而且它不能像可信代码那样访问更大的内部对象集合。

  • 内部库和外部库

共享库的另一个不同之处是,托管源码控制仓库的位置,不管是在 Jenkins 实例内部还是在外部源码控制系统中。

在大多数情况下内部被视为一种更偏向传统的选项,但出于完整性考虑,此处也包含了相关描述。

2、需求引入

本文标题提到的 使(妙)用 ,就是指上面概念中提到的 内部库 ,这个概念网上(千篇一律)很少能找到。是的,我这里将仔细分析出我的应用场景,以便于表达我的迫切需求(这也可能是很多共享库用户的需求)。

在大多数共享库使用场景下,我们都是将共享库的代码提交到公共的 git 仓库服务商,例如 githubgitee ,或者是提交到企业内部搭建的 git 服务端,例如 gitlab 。为了能够正常使用这个外部的共享库, Jenkins 与之的一大要求就是网络能够连通,保障能通过 ssh 或者 http 协议进行通信。

但往往很多场景下,我们的 Jenkins 不能连通外网,甚至连必须的插件也需要离线下载后安装或者临时通过网络代理安装。如果网络不能连通,是不是就意味着我们的共享库不能用了呢?一开始我也这样以为,想要用到共享库方法的解决办法(未必最佳)可能有如下

  • 方法一

    拆分共享库的方法,单独集成到 pipeline 流水线脚本中,但这样会导致每个 pipeline 的长度回归到使用共享库之前那样冗长和重复

  • 方法二

    再在 Jenkins 所在的网络环境中搭建一个轻量的 git 服务端,例如 git init 创建一个,但无法可视化,且不安全,存在于服务器的仅仅是一个目录

以上方法虽然能实现我们想要使用共享库方法,但都不太友好。

再来聊一下内部库,在 Jenkins 2.0 时代,包含着一个 内部库 ,可用于存储内部库或测试目的。内部库默认有一个特定的名称 workflowLibs ,这其实就是一个内置于 Jenkins 内部的 Git 仓库。那既然这个库内置了,存在于哪里呢?我们来看一下 Jenkins 的安装主目录,我这里的 Jenkins 基于 k8s 安装,并且做了数据卷的持久化,进入对应的 pv 下查看,其中的主要目录如下所示

$ tree -L 1 kube-system-jenkins-data-pvc-6866fcaf-b742-40e0-afa8-64b93e75ef52
kube-system-jenkins-data-pvc-6866fcaf-b742-40e0-afa8-64b93e75ef52/
├── backup
├── jobs
├── log
├── logs
├── nodes
├── pipeline-config-history
├── plugins
├── secrets
├── updates
├── userContent
├── users
├── war
├── workflow-libs  # 内部库
└── workspace

内部库名为 workflow-libs ,这个内部库其实就是一个已经初始化且存在于 Jenkins 内部的 Git 仓库。

下面记录如何使用 workflowLibs 内部库。

3、开通workflowLibs

workflowLibs 内部库和普通的 git 仓库一样,可以通过 ssh 访问或 http 访问,本文记录的是通过 ssh 协议访问,另外一种方式类似。

由于 Jenkins 部署在 k8s 中,因此我们只能通过 ingress 或者在内网环境下通过 nodePort 方式访问。在 k8s 中部署的 Jenkins 默认暴露的是 8080 端口,这个端口用于提供 http 访问。同时我这里使用到了基于 k8s 动态的 slave 模式动态构建以及管理外部的普通 agent ,相互通信需要再打开一个端口,端口号可以在 Jenkins 的系统配置—>全局安全配置—>代理中指定。

3.1 开通ssh端口

这里需要再为 Jenkins 开放一个 ssh 协议的端口,用于共享库的开发者和 workflowLibs 内部库通信,由于不想暴露给外部,我这里还是通过 nodePort 方式去访问, Jenkins Service 声明如下

apiVersion: v1
kind: Service
metadata:
  name: jenkins
  namespace: kube-system
spec:
  type: NodePort
  selector:
    name: jenkins
  ports:
  - name: http
    port: 80
    targetPort: 8080
    protocol: TCP
    nodePort: 30009
  - name: agent
    port: 35555
    targetPort: 35555
    protocol: TCP
    nodePort: 35555
  - name: jenkinsssh  # Jenkins ssh
    port: 22222
    targetPort: 22222
    protocol: TCP
    nodePort: 32222

重新应用资源清单

$ kubectl apply -f jenkins-svc.yaml

登录到 Jenkins ,系统配置—>全局安全配置—> SSH Server ,设置 ssh 端口,这个端口与上面 Service 中指定的端口对应

3.2 配置密钥

http:///user//configure 页面的 SSH 公钥字段中添加用户的 SSH 公钥,这个公钥在我们能够和 Jenkins Server 进行 ssh 通信的机器上生成即可,生成后在当前用户的设置界面下进行添加

3.3 初始化克隆workflowLibs库

上面的操作完成后,就可以在共享库代码的开发机器( ssh 客户端)上进行克隆了

➜  ~ git clone ssh://ssgeek@192.168.12.82:32222/workflowLibs.git
Cloning into 'workflowLibs'...
warning: You appear to have cloned an empty repository.
➜  ~ cd workflowLibs
➜  workflowLibs git checkout -b master
Switched to a new branch 'master'

3.4 配置使用workflowLibs库

要提到的是,与配置其他的共享库不一样,内部库在使用前并不需要在 Jenkins 上单独配置使用共享库 workflowLibs 内部库,内部库只要提交代码,就能直接在 pipeline 中导入和使用。

4、自定义使用workflowLibs库

这里以我生产使用的 workflowLibs 库中的其中一个方法为例,即上线通知,我把它称为“上线小喇叭”。

4.1 方法定义

使用这个功能时,我关注到 Jenkins 最新的钉钉插件已经更新,这次的上线通知借助了此插件,如果是不想通过插件,更为灵活的自定义钉钉通知,可以参考我之前的文章 JenkinsShareLibrary实践之自定义通知器 ,两种方式具体选择哪个,根据自己的需求合理定制即可。

我把关于钉钉插件和共享库使用的方法命名为 dingtalk.groovy ,为了减少在 pipeline 中的引用操作,将这个文件放在了共享库目录的全局方法目录中,对 pipeline 来说,直接调用。

整个 workflowLibs 库的目录结构如下

[root@dev workflowLibs]# tree -L 2
.
├── README.md
└── vars
    └── dingtalk.groovy

1 directory, 2 files

方法如下,其中的消息内容需要按照 插件说明 编写固定的格式

/* dingtalk.groovy
   ##################################################
   # Created by SSgeek                              #
   #                                                #
   # A Part of the Project jenkins workflowLibs     #
   # https://www.ssgeek.com                         #
   ##################################################
*/

// post步骤
def dingTalk(BasicEnv,ImageTag,ReleaseMess){
    wrap([$class: 'BuildUser']){
        dingTalk (
            // 机器人 id
            robot: 'eb5e68bb-1ad7-4638-8008-9d0d1072d319',
            // 消息类型
            type: 'ACTION_CARD',
            // 需要 @ 的手机号码
            at:[],
            // 是否 @ 全部
            atAll: true,
            // 消息标题
            title: '生产上线小喇叭',
            // 消息内容主体
            text: [
                "[${env.JOB_NAME}](${env.JOB_URL})\n",
                '---',
                "- 任务编号:[${env.BUILD_ID}](${env.BUILD_URL})",
                "- 上线版本:**${ImageTag}**",
                "- 上线环境:**${BasicEnv}**",
                // "- 上线结果:**☆${currentBuild.currentResult}☆**",
                "- 上线结果:**:point_right:☆${currentBuild.currentResult}☆:point_left:**",
                "- 上线发起:**${env.BUILD_USER}**",
                "- 上线描述:**${ReleaseMess}**",
                "- 持续时间:${currentBuild.durationString}\n",
            ],
            // 点击消息跳转的 URL
            messageUrl: '',
            // 图片 URL
            picUrl:'',
            // 单个按钮的方案,设置此项和 singleUrl 后 btns 无效
            // singleTitle:'',
            // singleUrl:'',
            // 自定义按钮组
            btns: [
                [
                    title: '发布日志',
                    actionUrl: "${env.BUILD_URL}/console"
                ],
                [
                    title: '控制台',
                    actionUrl: "https://jenkins.ssgeek.com"
                ]
            ],
            // 按钮的排列方式:horizotal水平排列H、vertical垂直排列V
            btnLayout: 'H',
            // 是否隐藏发消息者头像
            hideAvatar: true
        )
    }
}

4.2 插件配置

插件需要在 Jenkins 中配置钉钉机器人的 hook 地址,配置完成会生成一个 id ,和上面方法中的 robot 对应

4.3 使用

pipeline 中使用,部分内容如下

post{
  always{
    script{
      dingtalk.dingTalk(BasicEnv,ImageTag,ReleaseMess)
    }
  }
}

效果这里就不贴图啦,查看钉钉插件的文档即可。

5、总结

本文通过 workflowLibs 库的灵活使用,实现了在无外网共享库环境下,独立的 Jenkins 使用共享库的需求

希望能帮助到大家,See you ~

部分描述内容参考自 官方文档