为了保证安全性多数的持续集成系统都会部署在公司内部的局域网中,这样如果代码部署在 Bitbucket 等环境中就只能通过轮询的方式来触发 Build。那么有没有办法通过 Bitbucket 的 Webhooks 功能在开发人员提交代码时触发 Build 呢?答案是肯定的,并且有很多种实现方式。
本文笔者将介绍一种比较简单的实现方式来实现由 Bitbucket 的 Webhooks 触发内网 Jenkins 中的 Build。其结构如下:
实现本方案的条件是需要在外网有一台可以访问的主机,通过 SSH 的端口转发技术(准确的说是"远程端口转发")把外网主机监听到的请求转发给内网中的 Jenkins 服务器。下面我们一起来看看详细的实现步骤。
添加用来触发 Build 的用户
一般情况下我们会添加一个用户专门用来控制自动触发 Build。在 Manage Jenkins -> Manage Users 界面中点击 "Create User":
输入用户信息并创建用户,笔者创建的用户名称为 autobuilder。
配置用户的权限
对于刚才创建的 autobuilder 用户,只有先赋予一定的权限,它才能够系统中的 Build。
进入 Manage Jenkins -> Configure Global Security 配置界面,在 Authorization 的配置项中选择 "Matrix-based security",然后把 autobuilder 用户添进来:
并赋予 Overall - Read,Job - Build,Job - Read,Job - Workspace 四种权限。
当然我们可能会使用不同的 Authorization 方式,其实只要保证赋予 autobuilder 用户合适的权限就可以了。
打开 Build 的 job trigger 配置
必须在 Project 中进行配置才能开启远程触发 Build 的功能。进入 Project 的配置界面,在 "Build Triggers" 下面选中 "Trigger builds remotely":
如上图所示,你必须输入一个 Authentication Token。这主要是为了唯一的标识当前的 Project 并保证一定的安全性。你可以从 https://randomkeygen.com/ 轻松的生成一个合格的 Authentication Token。
获得触发 Build 的 URL
万事具备,接下来让我们构建可以在远程触发 Build 的 URL。
在前面的截图中,第二个红框中的内容就是一个可以触发 Build 的 URL 模板。接下来我们用 Jenkins 服务器地址替换模板中的 JENKINS_URL,用实际的 Authentication Token 替换模板中的 TOKEN_NAME。得到的 URL 为:
http://localhost:8080/jenkins/job/elephant/build?token=QTfICY6LwGvgG5jhy8EzMD6C9dEdk8fS
现在在这个 URL 还不能触发 Build,因为还缺少认证信息。
使用 autobuilder 用户登录到 jenkins 服务器中,点击右上角的用户名称进入用户信息界面,然后点击 "Configure" 进入配置界面。点击 API Token 小节中的 "Show API Token" 按钮:
获得上面的认证信息后就可以拼出完整的 URL 了。
http://autobuilder:52f4dec5458db692ba0d97a4079ae186@localhost:8080/jenkins/job/elephant/build?token=QTfICY6LwGvgG5jhy8EzMD6C9dEdk8fS
通过 curl 在 Jenkins 服务器运行的主机上触发 Build 试试:
$ curl http://autobuilder:52f4dec5458db692ba0d97a4079ae186@localhost:8080/job/elephant/build?token=QTfICY6LwGvgG5jhy8EzMD6C9dEdk8fS
接下来用我们准备的外网主机域名替换本地的主机名,笔者准备的外网主机域名为 xxxengine.eastasia.cloudapp.azure.com,端口号为 10055。所以用 xxxengine.eastasia.cloudapp.azure.com:10055 替换 localhost:8080,最终的 URL 为:
http://autobuilder:52f4dec5458db692ba0d97a4079ae186@xxxengine.eastasia.cloudapp.azure.com:10055/job/elephant/build?token=QTfICY6LwGvgG5jhy8EzMD6C9dEdk8fS
配置远程端口转发
远程端口转发是 SSH 端口转发的一种,如果你还不熟悉 SSH 端口转发技术,请先移步《SSH 端口转发》。
假设我们在 Azure 上有一台运行 Ubuntu 16.04 Server 的虚拟主机,主机的域名为 xxxengine.eastasia.cloudapp.azure.com,允许外部访问的端口为 22 和 10055:
其中 22 端口是 SSH 服务器用来建立 SSH 连接用的,10055 端口用来监听外部触发 Jenkins Build 的请求。
先登录主机 xxxengine.eastasia.cloudapp.azure.com, 在 SSH 服务器的配置文件 /etc/ssh/sshd_config 中添加一行:
GatewayPorts yes
保存并重启 SSH 服务器。
下面我们建立一条从内网中 Jenkins 服务器到外网 xxxengine.eastasia.cloudapp.azure.com 主机的隧道。在内网中运行 Jenkins 服务器的主机上执行下面的命令:
$ ssh -fN -R 10055:localhost:8080 nick@xxxengine.eastasia.cloudapp.azure.com
R 选项指明端口转发的方式为远程端口转发,10055 为主机 xxxengine.eastasia.cloudapp.azure.com 需要监听的端口号。localhost:8080 是 Jenkins 服务器监听的本机端口。nick@xxxengine.eastasia.cloudapp.azure.com 表示通过用户 nick 建立到主机 xxxengine.eastasia.cloudapp.azure.com 的 SSH 连接。选项 fN 则让该远程转发以后台方式运行。
建立好端口转发后让我们再次登录到主机 xxxengine.eastasia.cloudapp.azure.com 上,执行 ss -tunl 命令查看端口的监听情况:
看,主机已经能够监听任何来源发送到 10055 端口的请求了。
配置 Bitbucket Webhooks
在 Bitbucket 中打开你的项目,选择 "Settings -> Webhooks" 进入 Webhooks 的配置界面。添加一个新的 webhook 配置,并且把我们在前面创建的最终版的 URL 设置其 URL:
直接保存就可以了!
检查结果
向 Bitbucket 代码库中提交代码,Bitbucket 在收到代码推送后会使用我们在 webhook 中设置的 URL 发送 http 请求。结果内网中的 Jenkins 服务器收到请求触发一个新的 Build。
整个过程中的数据流向为:
Bitbucket 向 xxxengine.eastasia.cloudapp.azure.com 的 10055 端口发送请求;SSH 服务器监听 10055 端口并且把接收到的请求通过 SSH 隧道发送到内网中 Jenkins 服务器上 localhost 的 8080 端口;Jenkins 服务器监听 localhsot 的 8080 端口,并且处理收到的请求。
总结
我们在没有对现有系统做太多改变的情况下,仅仅使用 SSH 在内网与外网之间建立了一条隧道,就实现了外网对内容 Jenkins 的访问。
其实日常工作中还有很多类似的用例,都可以通过 SSH 建立隧道的方式简单便捷的解决。