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

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://<jenkins-url>/user/<userid>/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}☆**",
                "- 上线结果:**👉☆${currentBuild.currentResult}☆👈**",
                "- 上线发起:**${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 ~

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