為了保證安全性多數的持續整合系統都會部署在公司內部的區域網中,這樣如果程式碼部署在 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 建立隧道的方式簡單便捷的解決。