头图

导读: 网易云信的 gitlab 服务器搭建在外网,Jenkins 服务器搭建在内网,因此 gitlab 没办法直接把 webhook 发送给Jenkins,而 pipeline 的搭建采用第三方 relay 转发的方式,但是这个 relay 经常“罢工”。本文根据网易云信的落地实践,详细介绍了如何借助 Gitlab-ci 替代 webhook 触发 Jenkins job。

文|邹李勇 网易云信资深 C++ 开发/DevOps 工程师

背景

Gitlab 如何触发 Jenkins job?

我们 push 代码或提交 merge request 的时候,gitlab 会发送一个webhook(单纯地理解成一个 http 请求或 restful api 即可)给 Jenkins,请求执行某个 job。

困境

网易云信使用的 gitlab 服务由杭研维护,服务器搭建在外网。而我们的  Jenkins 服务器搭建在内网,这意味着 gitlab 没办法直接把 webhook 发送给Jenkins,而我们的 pipeline 在搭建之初采用了第三方 relay 转发的方式,把 gitlab 的 webhook 转发给 Jenkins。虽然把 webhook 从 gitlab 发送到 Jenkins 的目的达到了,但是这个免费的第三方 relay 可能经常“罢工”。

借助 Gitlab-ci 替代 webhook 触发 Jenkins job

gitlab-ci

简单来讲,gitlab-ci 是 gitlab 自带的特性,通过源码根目录下的 .gitlab-ci.yml 文件配置。在代码  push 或 merge request 的时候自动运行。

以下是官方的解释:

GitLab Auto DevOps is a collection of pre-configured features and integrations that work together to support your software delivery process.

GitLab CI/CD can automatically build, test, deploy, and monitor your applications by using Auto DevOps.

To use GitLab CI/CD, you need:

  • Application code hosted in a Git repository.
  • A file called .gitlab-ci.yml in the root of your repository, which contains the CI/CD configuration.

官方的 you need 中其实还少了一点 —— GitLab runner,运行 GitLab-ci job 的地方,也就是说 .gitlab-ci.yml 中定义的所有工作都会分发到这里。

在我们这个实践中,我们会在 GitLab runner 中搭建一套 python 环境和 python-jenkins 模块,然后通过 python 脚本触发 Jenkins job,最后把这个触发 Jenkins job 的 python 脚本部署到 gitlab-ci 中。

因此整个实践分成3个部分。

  1. 搭建 Gitlab runner
  2. 通过 python-jenkins 触发 Jenkins job
  3. 使用 gitlab-ci 在 push 代码和 merge request 时调用 python 脚本

搭建 gitlab runner

安装 gitlab runner 客户端

官方: GitLab Runner can be installed and used on GNU/Linux, macOS, FreeBSD, and Windows.

因此选择任意一台长期在线的打包机即可。笔者推荐优先选择 GNU/Linux,通常 linux 上搭建常驻服务要比其他系统更方便,MacOS 次之,最后是 Windows。

以下是 GNU/Linux 平台的安装方法的搬运:

  • Download

    To download the appropriate package for your system:

  • Find the latest file name and options athttps://gitlab-runner-downloa...
  • Choose a version and download a binary, as described in the documentation for downloading any other tagged releases for bleeding edge GitLab Runner releases.

For example, for Debian or Ubuntu:

# Replace ${arch} with any of the supported architectures, e.g. amd64, arm, arm64# A full list of architectures can be found here https://gitlab-runner-downloads.s3.amazonaws.com/latest/index.htmlcurl -LJO "https://gitlab-runner-downloads.s3.amazonaws.com/latest/deb/gitlab-runner_${arch}.deb"

For example, for CentOS or Red Hat Enterprise Linux:

​​​​​​​

# Replace ${arch} with any of the supported architectures, e.g. amd64, arm, arm64# A full list of architectures can be found here https://gitlab-runner-downloads.s3.amazonaws.com/latest/index.htmlcurl -LJO "https://gitlab-runner-downloads.s3.amazonaws.com/latest/rpm/gitlab-runner_${arch}.rpm"
  • Install

    • Install the package for your system as follows.

    For example, for Debian or Ubuntu:

dpkg -i gitlab-runner_<arch>.deb

For example, for CentOS or Red Hat Enterprise Linux:

rpm -i gitlab-runner_<arch>.rpm

其他平台的安装方案请点击官方教程传送门:

https://docs.gitlab.com/runne...

通常作为一个 trigger 代理,任务开销很小,我们可以把 /etc/gitlab-runner/config.toml 配置里的 concurrent 可以改得大一些,以支持更高的并发量。

注册 gitlab runner

  • 注意

    • 搭建 gitlab runner 每台 runner 只需要执行一次
    • 注册 gitlab runner 每个 git 仓库每台 runner 都需要单独注册

<!---->

  • 准备工作

    - 先登入 gitlab,进入对应的 git 仓库(project)

    - 展开左边侧边栏最下面 Settings -> CI/CD。点击页面上 Runners 栏右边的 Expand,页面往下滚动一点可看到大概如下,注意红框中的内容。

  • 注册

    以官方 GNU/Linux 为例,其他平台高度雷同。

    1. Run the following command:

sudo gitlab-runner register

2. Enter your GitLab instance URL.

上图红框中 Register the runner with this URL 下面的内容。

3. Enter the token you obtained to register the runner.

上图红框中 And this registration token 下面内容。

4. Enter a description for the runner. You can change this value later in the GitLab user interface.

在 gitlab 中显示的 runner 描述,该实践中我们把他当名字用,叫 scapegoat-01。

5. Enter the tags associated with the runner, separated by commas. You can change this value later in the GitLab user interface.

tag 相当于Jenkins中的 label, 用于 runner 分类。该实践中输入 scapegoat。

6. Provide the runner executor. For most use cases, enter docker.

这里我们选 shell, window 可选 powershell。

其他平台命令相同,官方传送门:

https://docs.gitlab.com/runne...

注册成功后可在红框下面 Available specific runners 里看到我们刚刚注册的 runner,类似下图。其中:

  • scapegoat-01 是 runner 的 description
  • scapegoat 对应 runner 的 tag,tag 可以不只一个

通过 python-jenkins 触发 Jenkins job

环境安装

  • 在 gitlab runn er 里安装 python

    Linux 发行版中一般自带,Debian 系列可以使用以下命令,其他平台可以 Google 查看。

sudo apt install python或sudo apt install python3
  • 安装 pip

    python3 通常自带,python 需要自己安装。

sudo apt install python-pip
  • 安装 python-jenkins
python -m pip install python-jenkinsor python3 -m pip install python-jenkins
  • 获取 gitlab access token

    登入 jenkins,点击菜单栏右边的用户图标。

    在弹出的左侧边栏点击 Configure。

    在 APT Token 栏点击 Add new token,输入 token 名,点击 Generate。

如下红框中的内容就是新生成的 token,在下面 python 脚本中要用。

编写 python 脚本

#!/usr/bin/python# file name: Jenkins-Compile-trigger.py
import jenkinsimport requestsimport timeimport sysimport os
jenkins_url = "http://yunxin-jenkins.netease.im:8080"# gitlab的登录账号jenkins_user = "zouliyong" #获取gitlab access token章节获取到的tokenjenkins_token = "xxxxxxxxxxxxxxxxx" #Jenkins job namejob_name = "Lava-CI"#从gitlab-ci中获取Jenkins job的参数,按需修改job_parameters = {                      "_GitlabSourceBranch"    : os.getenv("CI_MERGE_REQUEST_SOURCE_BRANCH_NAME"),                    "_GitlabTargetBranch"    : os.getenv("CI_MERGE_REQUEST_TARGET_BRANCH_NAME"),                    "_GitlabMergeRequestLastCommit"  : os.getenv("CI_COMMIT_SHA"),                    "_GitlabSourceRepoHomepage"  : os.getenv("CI_PROJECT_URL"),                    "_GitlabMergeRequestIid"  : os.getenv("CI_MERGE_REQUEST_IID"),                    "_GitlabPipelineId"  : os.getenv("CI_PIPELINE_ID"),                    "_GitlabPipelineUrl" : os.getenv("CI_PIPELINE_URL"),                    "_GitlabJobId"  : os.getenv("CI_JOB_ID"),                    "_GitlabUserName"  : os.getenv("GITLAB_USER_NAME"),                    "_GitlabUserEmail"   : os.getenv("GITLAB_USER_EMAIL")}
build_number = 0build_info = {"building" : False}                 print("jenkins job name: ", job_name)print("jenkins job parameters: ", job_parameters)
#连接Jenkins服务server = jenkins.Jenkins(jenkins_url, username= jenkins_user, password= jenkins_token)
#获取Jenkins job最后一次build的build numberdef last_build_number(server, job):    last_build = server.get_job_info(job)['lastBuild']    return 1 if None == last_build else last_build['number']

last_build = server.get_job_info(job_name)['lastBuild']next_build_number = last_build_number(server, job_name) + 1
#触发Jenkinsjobqueue_id = server.build_job(job_name, parameters=job_parameters)print("Jenkins build is waiting for running [queue id = %d] ..." % queue_id)sys.stdout.flush()
#到这里,Jenkins job已经被放到执行队列里了,#只是触发不用等待Jenkins job结束的话,python脚本可以到此为止
#等待Jenkins job开始执行while True:    if next_build_number <= last_build_number(server, job_name):        try:            build_info = server.get_build_info(job_name, next_build_number)        except requests.exceptions.RequestException as e:            print(e)            server = jenkins.Jenkins(jenkins_url, username= jenkins_user, password= jenkins_token)            build_info = server.get_build_info(job_name, next_build_number)        if queue_id == build_info["queueId"]:            build_number = next_build_number            print("build number: %d" % build_number)            break        next_build_number = next_build_number + 1    time.sleep(0.1)
print("Jenkins build is running [build number = %d] ..." % build_number)print("Jenkins job URL: %s/job/%s/%d/display/redirect" % (jenkins_url, job_name, build_number))sys.stdout.flush()
#到这里Jenkins job已经被正式调度,并开始执行
#等待Jenkins job执行结束while build_info["building"]:    try:        build_info = server.get_build_info(job_name, build_number)    except requests.exceptions.RequestException as e:        print(e)        server = jenkins.Jenkins(jenkins_url, username= jenkins_user, password= jenkins_token)        build_info = server.get_build_info(job_name, build_number)    time.sleep(1)
# 获取执行结果result = server.get_build_info(job_name, build_number)["result"]print("jenkins build result: %s" % result)assert("SUCCESS" == result)

运行如上脚本即可触发对应的 Jenkins job,

python Jenkins-Compile-trigger.py

使用 gitlab-ci 在 push 代码和 merge request 时调用 python 脚本 编写 .gitlab-ci.yml 文件。

#file name: .gitlab-ci.yml
stages:    - build
# 在代码push的时候触发Compilation:    stage: build    tags:        - scapegoat # runner tag 参照上面注册gitlab runner章节    script:        - python Jenkins-Compile-trigger.py
# 只在merge request的时候触发CI:    stage: build    tags:        - scapegoat    script:        # 这个脚本可以根据情况参照Jenkins-Compile-trigger.py自行修改        - python Jenkins-CI-trigger.py     only:        - merge_requests

把如上 .gitlab-ci.yml 和触发用 Jenkins job 的 python 一起放在源码根目录下,同代码一起上传仓库。通过 gitlab-ci 触发 Jenkins job 的功能就实现了。

作者介绍

邹李勇,网易云信资深 C++ 开发/DevOps 工程师。Linux 平台 RTC SDK 开发,负责 DevOps 系统开发和运维。


网易数智
619 声望140 粉丝

欢迎关注网易云信 GitHub: