Jenkins 流水線多種使用場景詳解(Jenkinsfile,多環境部署,多分支部署)
theme: smartblue
Jenkins是目前大多數中小公司使用的CI、CD工具,其中Jenkins的任務又分普通任務和流水線任務,普通任務的構建和部署在我之前的一篇文章中寫過使用教程# 基於 Docker 安裝 Jenkins,並配置使用 Jenkins 打包 Node 前後端服務部署到遠端伺服器,但其中流水線任務可實現我們更復雜的需求也更自由,不過上手難度也稍微高點。
一、安裝Jenkins
推薦使用 Docker 來安裝Jenkins,更方便後期的遷移部署等,具體安裝步驟可參考
# 基於 Docker 安裝 Jenkins,並配置使用 Jenkins 打包 Node 前後端服務部署到遠端伺服器
二、普通流水線
這裡我將演示使用流水線來部署一個前端專案,其他專案也同樣是這幾個步驟
首先建立一個流水線任務
在流水線配置這有兩種方式,第一種是直接把流水線指令碼寫在配置文字框這,第二種是把指令碼寫在專案根目錄下,用
Jenkinsfile
檔案來寫入,圖下面可以看到有個流水線語法
的按鈕,是可以把具體操作用視覺化的方式生成指令碼。
我們在文字框這寫入以下指令碼內容:
``` pipeline { agent any
stages {
stage('Build') {
steps {
nodejs('node16') {
sh '''
if hash pnpm 2>/dev/null;
then
echo "pnpm"
else
npm i pnpm -g --registry http://registry.npmmirror.com/
fi
pnpm i
pnpm run build
'''
}
echo '構建完成'
}
}
stage('Zip') {
steps {
sh '''
tar -zcvf ${JOB_BASE_NAME}.tgz ./dist/*
rm -rf ./dist/*
mv ${JOB_BASE_NAME}.tgz ./dist
'''
echo '打包完成'
}
}
stage('Deploy') {
steps {
sshPublisher(publishers: [sshPublisherDesc(configName: 'tencent', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'echo \'部署完成\'', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
}
} ```
Stages
: 這個欄位下分了幾個單獨的stage
,會從上至下依次執行stage
,如果是剛才說的第一種方式,應該還會比上面多個拉取程式碼的階段。具體拉取程式碼的語法可以用上面的流水線語法
頁面視覺化生成。
stage('Build')
: 程式碼構建階段,這裡因為是前端專案,用到了node
來構建,需要安裝NodeJS
外掛,然後去全域性工具配置裡安裝一下具體的node
版本及設定下別名。
stage('Zip')
: 壓縮階段,因為我們前端程式碼部署只需要部署dist目錄
,把這個目錄tgz壓縮
一下發到目標伺服器。
stage('Deploy')
: 部署階段,需要安裝一個Publish Over SSH
外掛,然後通過上面的流水線語法
去視覺化配置部署到伺服器的配置,最後把生成的指令碼貼上到這就行。
到這我們構建及部署程式碼到伺服器的基本配置就完成了,大部分專案其實發版流程就是這幾步,下面還有幾種流水線進階用法。
三、多環境部署流水線
有時候我們會遇到多環境部署的情況,如開發壞境,生產環境等,大概就是我們通過在流水線新增一個部署壞境的引數來控制,在每次構建前選擇一下部署的壞境,具體指令碼如下:
``` pipeline { agent any
parameters {
choice(
description: '你需要哪個機器進行部署?',
name: 'deploy_hostname',
choices: ['tencent', 'dev01', 'tencent、dev01']
)
}
stages {
stage('Build') {
steps {
nodejs('node16') {
sh '''
if hash pnpm 2>/dev/null;
then
echo "pnpm"
else
npm i pnpm -g --registry http://registry.npmmirror.com/
fi
pnpm i
pnpm run build
'''
}
echo '構建完成'
}
}
stage('Zip') {
steps {
sh '''
tar -zcvf ${JOB_BASE_NAME}.tgz ./dist/*
rm -rf ./dist/*
mv ${JOB_BASE_NAME}.tgz ./dist
'''
}
}
stage('Deploy to tencent'){
when {
expression { deploy_hostname == 'tencent' }
}
steps{
sshPublisher(publishers: [sshPublisherDesc(configName: 'tencent', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'echo \'部署完成\'', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
stage('Deploy to dev01'){
when {
expression { deploy_hostname == 'dev01' }
}
steps{
sshPublisher(publishers: [sshPublisherDesc(configName: 'dev01', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'echo \'部署完成\'', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
stage('Deploy to tencent、dev01'){
when {
expression { deploy_hostname == 'tencent、dev01' }
}
steps{
sshPublisher(publishers: [sshPublisherDesc(configName: 'tencent', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'echo \'部署完成\'', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
sshPublisher(publishers: [sshPublisherDesc(configName: 'dev01', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'echo \'部署完成\'', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
}
} ```
可以看到除了我們之前配置的agent
和stages
還多了一個parameters
的引數配置,添加了一個deploy_hostname
的選擇引數,有三個值'tencent', 'dev01', 'tencent、dev01'
在具體的stage
裡面也多了when
的配置,就是根據我們選擇的部署環境引數來執行相應壞境的部署流程,當when
裡面的條件不滿足時,流水線會跳過裡面的steps
四、多分支流水線
還有種情況是專案多分支的情況下,每個分支可能對應的部署壞境,或者執行條件不一樣,就會用到Jenkins的多分支流水線
在新建Jenkins任務時選擇多分支流水線
在分支源裡配置對應的git專案地址
和認證憑據
,儲存後他會自動掃描專案裡面的分支,我們需要在每個分支下建立一個Jenkinsfile
檔案,把我們的指令碼寫在這個檔案裡
具體的構建部署指令碼可參考之前的普通流水線,如果需要WebHook自動觸發的可參考下面指令碼
``` pipeline { agent any
triggers {
GenericTrigger (
causeString: 'Triggered',
genericVariables: [[key: 'ref', value: '$.ref']],
printContributedVariables: true,
printPostContent: true,
token: 'test01'
)
}
stages {
stage('Build') {
steps {
nodejs('node16') {
sh '''
if hash pnpm 2>/dev/null;
then
echo "pnpm"
else
npm i pnpm -g --registry http://registry.npmmirror.com/
fi
pnpm i
pnpm run build
'''
}
echo '構建完成'
}
}
stage('Zip') {
steps {
sh '''
tar -zcvf ${JOB_BASE_NAME}.tgz ./dist/*
rm -rf ./dist/*
mv ${JOB_BASE_NAME}.tgz ./dist
'''
}
}
stage('Deploy to tencent'){
when {
branch 'master'
}
steps{
sshPublisher(publishers: [sshPublisherDesc(configName: 'tencent', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'echo \'部署完成\'', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
stage('Deploy to dev01'){
when {
branch 'dev'
}
steps{
sshPublisher(publishers: [sshPublisherDesc(configName: 'dev01', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'echo \'部署完成\'', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
}
} ```
可以看到上面添加了一個triggers
配置,這個需要安裝Generic Webhook Trigger
外掛,然後按上面那樣配置,其中的token配置可以隨意改動,就是我們最終觸發hooks的url最後面的引數。
配置完成後,就可以通知http://JENKINS_URL/generic-webhook-trigger/invoke?token=test01
來觸發我們的hook,一般我們需要在gitlab的Webhooks進行配置觸發hooks
到此Jenkins多種流水線的配置介紹就完成了,具體細節有不瞭解的小夥伴可在下面評論區留言。關於流水線語法每個配置的詳解可參考# Jenkinsfile宣告式語法詳解