利用 Github 網路鉤子實現自動化部署
GitHub 的網路鉤子(webhook)功能,可以很方便的實現自動化部署。本文記錄了使用 Node.js 的開發部署過程,當專案的 master 分支被推時,將在伺服器進行自動部署,完整程式碼見 GitHub
新增網路鉤子
-
在 GitHub 的相應專案首頁,點選右上角選單
Setting
, 點選左側選單Webhooks
,點選右上角按鈕Add webhook
-
設定
Payload URL
為接收事件的地址,Content type
建議選擇applicaiton/json
,Secret
可選填任意字串,Which events would you like to trigger this webhook?
Just the push event.
,勾選Active
,點選下方的Add webhook
按鈕
開發處理請求
接收請求
使用 Node.js 建立一個 http 伺服器,接收 POST 請求並處理其提交資料
const { createServer } = require('http'); const port = process.env.GITHUB_WEBHOOK_PORT || '3000'; const server = createServer((req, res) => { if('POST' === req.method){ let body = ''; req.on('data', chunk => { body += chunk.toString(); }); req.on('end', () => { }); } }) server.listen(port, () => { console.log(`Listening on ${port}`); });
如果需要更改預設埠 3000,可以先執行以下命令新增環境變數(NUMBER 為任意埠)
export GITHUB_WEBHOOK_PORT=NUMBER
解析 Body
在 req 的 end 事件處理器中,把字串 body 解析成物件
req.on('end', () => {
try{
body = JSON.parse(decodeURIComponent(body).replace(/^payload=/, ''));
}catch(e){
console.log(e)
}
如果 Content type
applicaiton/json
,只需要 body = JSON.parse(body)
即可,以上程式碼相容了 Content type
設定為 application/x-www-form-urlencoded
的情況
拉取更新
根據 body 的 push 負載,提取專案和分支資訊,如果是 master 分支,則執行進入對應專案,拉取分支的命令
if('object' === typeof body){
if('refs/heads/master' === body.ref){
const { exec } = require('child_process')
const command = `cd ../${body.repository.name} && git pull origin master`
exec(command, (error, stdout, stderr) => {
});
注意這裡的專案所在的目錄,與此應用所在的目錄,是在同一個父目錄下的,如果不是可以相應調整命令的進入路徑
驗證金鑰
以上步驟已經實現了自動拉取更新,不過存在安全性的問題,因為不僅僅 GitHub 可以傳送這樣的請求,所以最好設定 Secret 以進行安全驗證
const secret = process.env.GITHUB_WEBHOOK_SECRET || '';
...
req.on('end', () => {
if('' !== secret){
const { createHmac } = require('crypto');
let signature = createHmac('sha1', secret).update(body).digest('hex');
if(req.headers['x-hub-signature'] !== `sha1=${signature}`){
console.log('Signature Error');
res.statusCode = 403;
res.end();
return;
}
}
執行應用前,先執行以下命令增加金鑰變數(STRING 為任意字串)
export GITHUB_WEBHOOK_SECRET=STRING
-
設定了 Secret 後,GitHub 在傳送請求時,會在請求頭增加 x-hub-signature 為 sha1=SIGNATURE, 其中 SIGNATURE 為 Secret 對應的使用 sha1 演算法加密的 HMAC 的 16 進位制值
-
通過對 Secret 的檢驗,可以確保只有知道了 Secret,才能傳送正確的帶 x-hub-signature 頭的請求,否則將拒絕請求
-
以上程式碼相容了不設定 Secret 的情況,即如果沒有增加變數 GITHUB_WEBHOOK_SECRET,則按原有邏輯處理,不會進行檢驗
本地鉤子構建
如果專案在拉取更新後需要構建,那麼可以 command 變數後面加上構建命令,例如 && npm run build
,但是不同專案的構建命令有可能是不一樣的,而且有的專案的構建命令可能還比較複雜,這些情況下可以通過設定 git 的本地鉤子進行處理
cd /PATH/TO/PROJECT/.git/hooks
nano post-merge
#!/bin/sh
SHELL_SCRIPT
chmod +x post-merge
- 其中 /PATH/TO/PROJECT/ 為專案的目錄位置,SHELL_SCRIPT 可以為任意 Shell 指令碼
- 因為 git pull 是 git fetch 和 git merge 的組合,所以拉取更新會觸發 post-merge 鉤子
- 預設新增的檔案是沒有執行許可權的,所以需要通過
chmod
增加x
位
部署應用上線
應用部署上線需要實現持久化和自動化,即專案應該一直在執行,如果伺服器重啟,專案應該自動啟動
變數自動建立
/etc/profile.d/ 裡的變數建立指令碼會在伺服器重啟時自動執行,所以新增一個設定指令碼進去
nono /etc/profile.d/github-webhook.sh
export GITHUB_WEBHOOK_PORT=NUMBER
export GITHUB_WEBHOOK_SECRET=STRING
執行以下命令可以使變數建立馬上生效
source /etc/profile
pm2 執行應用
pm2 可以確保 Node 應用的持續執行,並可通過配置實現監控和熱更新等功能
npm install pm2 -g
pm2 start app.js --name github-webhook
重啟自動執行
pm2 還內建支援配置自啟動原有應用,通過以下命令實現
pm2 startup
pm2 save
pm2 startup
會建立並開啟開機自動執行的服務, pm2 save
會儲存當前的 pm2 執行應用,作為重啟後的恢復內容
總結
在基於 GitHub webhook 的自動化部署中,主要使用了以下技術:
- Node.js 的 http 和 crypto 模組
- Git 的 post-merge Shell 鉤子
- profile 的自動變數設定和 pm2 工具