問題
有時候,在本地提交完代碼,接着需要將代碼部署到測試壞境。
一般部署過程都需要自己登錄到某個部署平臺,手動去觸發。(不包括有些可能直接push完代碼就自動觸發部署了)。
雖然這樣手動觸發操作很簡單,但每次都打開網頁,找項目,去操作,也不免有些麻煩。
思考
能不能在提交完代碼,就接着在命令行,完成部署呢?
簡單實現
這裏以 jenkins 爲例,說說我的處理過程。
1、打開 jenkins 中你要遠程部署的項目界面,點擊配置。
2、在配置中找到“構建觸發器”欄目,在“身份驗證令牌”中填入一段字符串。下面的小字是訪問鏈接的拼寫規則,原來遠程構建觸發器是通過 HTTP 協議訪問鏈接實現的,接下來說說拼寫規則。記得先將這步的修改應用,保存。
3、完成了這些,我們來說觸發鏈接的拼寫規則:
參數說明:
JENKINS_URL
: jenkins 的域名或ip + 端口號
PEOJECT_NAME
:項目名稱
TOKEN_NAME
:上一步配置的字符串
- 不帶參數:
JENKINS_URL/job/PROJECT_NAME/build?token=TOKEN_NAME
- 帶參數:
JENKINS_URL/job/PROJECT_NAME/buildWithParameters?token=TOKEN_NAME&KEY=value
4、拿着第三步拼好的鏈接,直接在瀏覽器中訪問。
如果返回 201 狀態碼,恭喜已經成功觸發部署了。
如果返回 403 狀態碼,那麼可能你的 jenkin 開啓了 CSRF 保護。
需要回到主界面 -> 系統管理 -> 全局安全配置 -> 找到 CSRF Protection 關掉就好了。
通過上面的簡單操作,已經可以實現遠程觸發構建了,但是如果需要在命令行訪問的話,我們還需要寫一個簡單的 shell 腳本。
# 你的 jenkins 用戶名
user="USERNAME"
# 你的 jenkins 密碼
password="PASSWORD"
curl -u $user:$password 上面拼接好的URL
保存上面的腳本文件到自己的項目裏,在 .gitignore 忽略掉,比如我保存到我的項目下並命名爲 .publish
接下來測試下好不好用,在項目目錄下,直接執行 sh .publish
,然後回到 jenkins 項目界面,發現項目已經在部署了。
優化
經過上面的處理,已經完成用命令行遠程觸發構建。但是有一個小缺憾,就是觸發部署後,沒有狀態返回。這個我怎麼知道部署成功了沒有?
我們先整理思路。
因爲觸發部署的時候並沒有返回什麼可利用的信息,所以要想拿到部署過程中的狀態返回,必須輪詢 /api/json
接口,這個接口會返回部署的相關信息。
接口格式:
JENKINS_URL/job/PROJECT_NAME/api/json
接下來我們分析這個接口返回的信息,看看有什麼可以利用的?
分析後發現,想要做顯示進度肯定沒戲了。但我們發現有這幾個字段可以利用,來實現簡單的狀態判斷。
這幾個字段下都會自帶一個數字 id。
lastBuild // 最後一次構建
lastCompletedBuild // 最後一次完成的構建
lastSuccessfulBuild // 最後一次成功的構建
在構建開始 lastBuild 會生成一個新 id,這個時候 lastCompletedBuild 的 id 還是上次構建的 id。等 lastCompletedBuild 的 id 變成新 id,標誌着構建完成。這個時候再對比 lastBuild 和 lastSuccessfulBuild 是否一樣,來判斷構建是否成功。
有了思路,我們來用代碼實現。
由於部署信息接口返回的都是 json 或者 xml 格式,用 bash 解析可能需要安裝其他東西。所以接下來用 node.js 實現。
這裏爲了簡單,我還用 curl 來模擬登錄。除此之外,也可以在 jenkins 中設置自己用戶的 token (網上有教程)或者使用 node.js 模擬登錄。
/**
* 觸發部署腳本
* 需要在 node 環境下運行
*/
const exec = require('child_process').exec
// 獲取命令行最後一個參數
const arg = process.argv.pop()
// jenkins 的登錄賬號密碼
const user = 'USER_NAME'
const password = 'PASSWORD'
// 訪問地址
const url = 'JENKINS_URL/job/'
// 環境配置,這裏的 key 對應輸入的參數
const envArr = {
test: 'PROJECT_NAME',
pre: 'PROJECT_NAME'
}
// 默認部署測試環境
let env = envArr[arg] ? envArr[arg] : envArr['test']
// 構建命令
const build = `curl -u ${user}:${password} ${url +
env}/buildWithParameters?token=XXX&KEY=value`
// 獲取部署信息命令
const info = `curl -u ${user}:${password} ${url + env}/api/json`
function sleep() {
return new Promise(resolve => {
setTimeout(resolve, 1000)
})
}
// 開始構建
function start(cmd) {
return new Promise((resolve, reject) => {
exec(cmd, (err, stdout, stderr) => {
if (err) reject(err)
else resolve(stdout)
})
})
}
// 獲取部署信息
function getInfo(cmd) {
return new Promise((resolve, reject) => {
exec(cmd, (err, stdout, stderr) => {
if (err) reject(err)
else resolve(stdout)
})
})
}
void (async () => {
await start(build)
let data = JSON.parse(await getInfo(info))
var lastId = data.lastBuild.number
var comId = data.lastCompletedBuild.number
var sucID = data.lastSuccessfulBuild.number
console.log('開始部署')
// 判斷是否部署完成
while (lastId !== comId) {
console.log(`#${lastId} ${env} 部署中,請稍等...`)
// 休息後請求
await sleep()
let data = JSON.parse(await getInfo(info))
lastId = data.lastBuild.number
comId = data.lastCompletedBuild.number
sucID = data.lastSuccessfulBuild.number
}
// 判斷是否部署成功
if (sucID === lastId) {
console.log('部署成功!')
} else {
console.log('部署失敗!')
}
})()
對於上面的代碼進行自己業務參數的更改,命名爲 .publish
就可以實現命令行發佈了。
node .publish test // 測試環境部署
node .publish pre // 預發佈環境部署
最後看下最終的運行效果,部署過程中,每行開頭會顯示這次部署的id,中間(馬賽克)是項目名稱:
好了,上面就是關於 jenkins 遠程觸發構建的嘗試,後面可能還會優化。因爲對各方面的知識都是剛接觸,可能有些地方思路有侷限性。希望有大牛看到可以提出寶貴的意見。謝謝!
end~