概 述
互聯網軟件的開發和發布,已經形成了一套標准流程,最重要的組成部分就是持續集成(Continuous integration,簡稱 CI)。
在講解之前,先給大家普及幾個概念:
- 持續集成(Continuous integration,簡稱 CI):持續集成指的是,頻繁地(一天多次)將代碼集成到主干。持續集成的目的,就是讓產品可以快速迭代,同時還能保持高質量。它的核心措施是,代碼集成到主干之前,必須通過自動化測試。只要有一個測試用例失敗,就不能集成。
- 持續交付(Continuous delivery):指的是,頻繁地將軟件的新版本,交付給質量團隊或者用戶,以供評審。如果評審通過,代碼就進入生產階段。
持續交付可以看作持續集成的下一步。它強調的是,不管怎么更新,軟件是隨時隨地可以交付的。 - 持續部署(continuous deployment):是持續交付的下一步,指的是代碼通過評審以后,自動部署到生產環境。
持續部署的目標是,代碼在任何時刻都是可部署的,可以進入生產階段。
持續部署的前提是能自動化完成測試、構建、部署等步驟。它與持續交付的區別,可以參考下圖。
我們常說的CI/CD就是指上面的持續集成、持續交付和持續部署。
一般來說,一個完善的git代碼的分支模型,主要有以下幾個分支:
master:用於生產環境的分支,一般是用來產品上線或者發布使用
hotfix:用於對master分支上發現的緊急bug或者問題進行的修復分支,修復完畢后合並到master和develop分支
release:用於對產品的預上線的分支,即准備上線分支,即上線前的預發布分支,供內測及上線,一般master分支的代碼主要由release提交
develop:用於開發者產品開發的合並分支,即不同的coder開發完程序之后,進行代碼合並的分支
feature:feature分支即我們開發者個人的分支或者某個新增功能的分支,用於開發階段的分支
CI/CD流程
在了解了以上git分支模型的概念后,我們就來梳理一下CI/CD的整個基本流程
1、我們開發者每天在自己的feature分支上進行了代碼的編寫,並提交以后,需要將自己的代碼提交到develop分支上,此時需要持續集成(CI)來為我們的代碼進行編譯構建
即通過Jenkins服務器或者基於Jenkins的雲服務器進行代碼的拉取、編譯、單元測試甚至構建到開發測試服務器上。
這步的工作一般可以設置為develop有提交即可進行或者每晚固定時間進行一次,按需設置即可
同時這一步的構建成功或者失敗,尤其是失敗了,可以直接反饋到開發人員這里,進行及時修復,屬於第一道關卡
2、構建成功的代碼,即可以部署到生產環境進行運行,通過Jenkins服務器打包發布文件,並推送到相關服務器上進行運行,即持續部署(CD)
但是我個人理解,持續部署這個操作可以發生在我們開發過程的好幾個階段
2.1 當我們提交develop分支的代碼后,通過CI進行了持續集成,CI結束后,可通過Jenkins服務器推送到我們自己開發的測試服務器進行部署(CD)。
2.2 在開發服務器進行自測完畢,並解決了大部分bug之后,我們可以再CD到正式測試服務器上,供測試部門及三方測試單位進行評審和測試。
3、正式測試完成之后,我們可以將我們的產品進行打標簽,從develop分支上提交到release分支,提交到release的分支也可以再進行一輪CI/CD的流程,為生產環境的發布做准備,
同時也可以將release部署出來的版本供外界進行公測等。
4、當release版本的試運行及公測都基本穩定並按計划允許上線的時候,我們將release的分支提交到master分支上,再進行一輪CI/CD,將打包的產品推送並發布到正式環境服務器上,供線上使用。
綜上所述:CI/CD是一個持續的自動化集成和部署的一個過程,存在於我們的開發、測試、生產的每一個環節
以下是基於我們自身的實際需求,進行的一個簡單的CI的過程,基於coding平台的持續集成,進行從不同的倉庫拉取相關的代碼,進行dotnet的編譯,並將生成的dll提交到對應的代碼倉庫中。
首先在coding的項目中,有較好的幫助文檔可以幫助我們使用其功能,https://help.coding.net/docs/ci/intro.html
找到左側的持續集成->構建計划->創建構建計划,創建構建計划中有內置的相關一些模板,如圖
創建構建計划
基礎信息配置
我這塊選擇的是自定義構建過程,選擇后,進入coding的基礎信息配置,這里允許我們選擇相關的代碼倉庫來源,如coding、GitHub、GitLab、碼雲等,我這里用到的是coding
然后會選擇代碼倉庫,選擇了代碼倉庫以后,我們默認的git操作對象就是該倉庫,同時相應的觸發規則也是基於該代碼倉庫進行的,當然我們也可以拉取操作其他的倉庫代碼,詳情見下文。
配置來源里面中我們使用靜態jenkinsfile,這里還提供了一個選項是使用代碼倉庫中的jenkinsfile,意思就是我們可以從git倉庫里拉取代碼后使用里面的Jenkinsfile執行計划,存儲於代碼倉庫里的jenkinsfile有利於我們的維護和修改
節點池配置:coding進行CI的時候,默認會提供一台Linux Ubuntu的雲服務器,用來執行我們的相關命令及程序,所以這里的節點池配置就是雲主機的節點信息,我們選擇上海的主機即可。
流程配置
流程配置里,主要用來設計我們構建計划的整個流程,即CI流程,這步就是我們CI的核心部分,他支持圖形化編輯器和文本編輯器
圖形化編輯器就是我們在流程圖上點擊按鈕增減相應的流程、命令等。他默認內置了一部分常用的流程空間,我們點選即可。
文本編輯器就是我們通過寫腳本代碼,進行流程的處理。這個腳本,主要遵循jenkinsfile的語法,所以,只要是jenkinsfile可以運行的腳本,在這個文本編輯器里就可以執行,但是如果我們是使用的自己安裝的Jenkins的話,也支持yml文件來編寫Jenkins腳本,由於我們用的是coding的雲服務器,默認只支持jenkinsfile。
由於個人實在無法看懂圖形編輯器生成的代碼,所以最終采用了寫腳本的方式來處理,這樣自己掌握腳本的過程,便於控制和修改。
這里我們着重講一下一些關鍵的腳本語法
首先Jenkinsfile的本質,就是一個流水線的腳本,也就是pipeline,pipeline就類似於我們任何一個程序的main函數一樣,是一個入口。
整個pipeline里面的語法:
pipeline {
agent any
environment {
VAR1 = '123'
}
stages {
stage('檢出') {
steps {
// 寫相關的腳本
}
}
stage('編譯') {
steps {
// 寫相關的腳本
}
}
}
}
從上面可以看出來,pipeline是整個流程的入口,也叫做一個流水線,在一個流水線里面,可以有主機(agent)、環境變量(environment)、階段集(stages)、階段(stage)、步驟(steps)
主機(agent):表示我們用的雲主機是哪一個節點,一般來說,any即可,即任意一台主機就行
環境變量(environment):我們可以自定義一些環境變量,用於在腳本中使用,環境變量的使用根據放置的位置來決定其生命周期,coding中的環境變量的優先級從高到低,分別為:
-
Jenkinsfile 中的 withEnv
-
Jenkinsfile 中的 environment
-
構建計划(Job)中的啟動參數
-
構建計划(Job)設置中的環境變量
-
構建過程中系統內置的環境變量
詳情參見:https://help.coding.net/docs/ci/configuration/env.html
階段集(stages):階段集就像我們的List一樣,是一個集合或者數組,里面存放每個階段對象
階段(stage):階段就是我們定義的一個階段對象,在該階段內,我們主要處理哪些任務或者步驟,屬於一個業務分類
步驟(steps):步驟就是我們具體要做的事情,第一步、第二步、第三步。。。
步驟中主要編寫相關的腳本:
支持的腳本有:
Linux shell腳本,用法:sh "" 、sh ''、sh """ """,無論單引號、雙引號還是三引號,里面都寫相關的Linux命令
Groovy語法:Groovy是一種基於JVM(Java虛擬機)的敏捷開發語言,它結合了Python、Ruby和Smalltalk的許多強大的特性,Groovy 代碼能夠與 Java 代碼很好地結合,也能用於擴展現有代碼。由於其運行在 JVM 上的特性,Groovy也可以使用其他非Java語言編寫的庫。
Jenkins:Jenkins是一個開源軟件項目,是基於Java開發的一種持續集成工具,用於監控持續重復的工作,旨在提供一個開放易用的軟件平台,使軟件項目可以進行持續集成。
所以說,所以整個groovy、Jenkins都是Java大家庭的東西,為了Java而存在的,所以Jenkinsfile支持groovy的語法,也是很正常的了。
groovy語法的使用,我們可以寫在script中,示例如下
script {
if(!fileExists("${env.WORKSPACE}/tool/")){
sh "mkdir tool"
}
下面給大家介紹在Jenkinsfile中我用到的幾個功能語法
git代碼拉取:
dir()表示在某目錄下執行,類似於Linux的cd命令
dir ("${env.WORKSPACE}/xxxxPath"){
git branch:'master',
url:'https://e.coding.net/xxxx/xxxx.git',
credentialsId:"這個ID是在開發者管理里面錄入憑據以后生成的一個ID,他代替了我們的git賬號的用戶名密碼,更加安全"
}
判斷文件是否存在:
${env.WORKSPACE}表示系統環境變量,即當前工作區
if(!fileExists("${env.WORKSPACE}/tool/")){
sh "mkdir tool"
}
dotnet sdk的環境搭建:
由於默認的雲主機上是沒有dotnet環境的,或者coding的雲主機上提供的dotnet是2.2的sdk,不滿足於我們的需求,所以我們在首次通過wget從微軟官網下載下來dotnet5的sdk的tar.gz包,然后在雲主機上進行解壓,並指定好路徑,解壓之后,即可以使用dotnetsdk進行程序的編譯
script{
if(!fileExists("${env.WORKSPACE}/tool/dotnet5/")){
echo("${env.WORKSPACE}/tool/dotnet5/ not exists")
if(!fileExists("${env.WORKSPACE}/tool/dotnet-sdk-5.0.404-linux-x64.tar.gz")){
echo("${env.WORKSPACE}/tool/dotnet5/dotnet-sdk-5.0.404-linux-x64.tar.gz not exists")
sh """
dotnet --version
dotnet --list-sdks
cat /proc/version
mkdir dotnet5
pwd
ls
wget https://download.visualstudio.microsoft.com/download/pr/2c1eb8c8-ac05-4dc7-9bef-307b3e450e9d/75e85b3d1662f60afd69572fd5df6884/dotnet-sdk-5.0.404-linux-x64.tar.gz
"""
}
sh "tar -zxf dotnet-sdk-5.0.404-linux-x64.tar.gz -C dotnet5"
}
}
dotnet編譯程序
${env.WORKSPACE}/tool/dotnet5/dotnet build -p:Version=1.0.0.123 ./xxxx.csproj
git代碼提交
dir ("${env.WORKSPACE}/tm-lw6-bin/svr"){
withCredentials([usernamePassword(credentialsId: "${CREDENTIALSID}",
passwordVariable: 'GIT_PASSWORD',
usernameVariable: 'GIT_USERNAME')]) {
// 配置 Git 工具中倉庫認證的用戶名、密碼
sh 'git config --local credential.helper "!p() { echo username=\\$GIT_USERNAME; echo password=\\$GIT_PASSWORD; }; p"'
// 配置 git 變量 user.name 和 user.emai
sh "git config --global user.name ${USER_NAME}"
sh "git config --global user.email ${USER_EMAIL}"
// 將文件推送到 git 遠程倉庫
sh """
git add -A .
git commit -m "這里寫提交注釋"
git push -u origin master
"""
}
}
至此Jenkinsfile的部分,基本就這些,剩下的根據實際需求,查詢相關的語法進行編寫即可
觸發規則
觸發規則,即表示在什么情況下觸發我們的構建計划進行執行,最簡單的就是手動構建,我們通過點擊立即構建進行,當然,在觸發規則里提供了更豐富的構建規則
如果可以看到,我們可以選擇某一個分支被推到后觸發、標簽推送觸發、主分支推送觸發、自動填寫規則、合並請求符合相關規則時觸發,以及定時觸發等,這個我們按需設置即可
變量與緩存
變量與緩存,用於設置相應的環境變量,同時也可以設置緩存目錄
這里我們緩存了dotnet sdk的安裝路徑,這樣下次構建的時候,無需重新下載dotnet sdk,可以直接使用,提高了構建的效率
通知提醒
通知提醒里面可以設置我們構建成功或者失敗的時候,設置相應的人員進行通知。coding這里支持的比較局限吧,我看到其他的項目中有人的構建還進行釘釘消息的通知推送等。