DevOps流水線設(shè)計(jì)的最佳實(shí)踐
談到到DevOps,持續(xù)交付流水線是繞不開的一個(gè)話題,相對(duì)于其他實(shí)踐,通過流水線來(lái)實(shí)現(xiàn)快速高質(zhì)量的交付價(jià)值是相對(duì)能快速見效的,特別對(duì)于開發(fā)測(cè)試人員,能夠獲得實(shí)實(shí)在在的收益。很多文章介紹流水線,不管是jenkins,gitlab-ci, 流水線,還是drone, github action 流水線, 文章都很多,但是不管什么工具,流水線設(shè)計(jì)的思路是一致的。于此同時(shí),在實(shí)踐過程中,發(fā)現(xiàn)大家對(duì)流水像有些誤區(qū),不是一大堆流水線,就是一個(gè)流水線調(diào)一個(gè)超級(jí)復(fù)雜的腳本,各種硬編碼和環(huán)境依賴,所以希望通過這篇文章能夠給大家分享自己對(duì)于Pipeline流水線的設(shè)計(jì)心得體會(huì)。
概念
- 持續(xù)集成 (Continuous Integration,CI)
持續(xù)集成(CI)是在源代碼變更后自動(dòng)檢測(cè)、拉取、構(gòu)建和(在大多數(shù)情況下)進(jìn)行單元測(cè)試的過程
對(duì)項(xiàng)目而言,持續(xù)集成(CI)的目標(biāo)是確保開發(fā)人員新提交的變更是好的,不會(huì)發(fā)生break build; 并且最終的主干分支一直處于可發(fā)布的狀態(tài),
對(duì)于開發(fā)人員而言,要求他們必須頻繁地向主干提交代碼,相應(yīng)也可以即時(shí)得到問題的反饋。實(shí)時(shí)獲取到相關(guān)錯(cuò)誤的信息,以便快速地定位與解決問題
顯然這個(gè)過程可以大大地提高開發(fā)人員以及整個(gè)IT團(tuán)隊(duì)的工作效率,避免陷入好幾天得不到好的“部署產(chǎn)出”,影響后續(xù)的測(cè)試和交付。

- 持續(xù)交付 (Continuous Delivery,CD)
持續(xù)交付在持續(xù)集成的基礎(chǔ)上,將集成后的代碼部署到更貼近真實(shí)運(yùn)行環(huán)境的「預(yù)發(fā)布環(huán)境」(production-like environments)中。交付給質(zhì)量團(tuán)隊(duì)或者用戶,以供評(píng)審。如果評(píng)審?fù)ㄟ^,代碼就進(jìn)入生產(chǎn)階段 持續(xù)交付并不是指軟件每一個(gè)改動(dòng)都要盡快部署到產(chǎn)品環(huán)境中,它指的是任何的代碼修改都可以在任何時(shí)候?qū)崟r(shí)部署。
強(qiáng)調(diào): 1、手動(dòng)部署 2、有部署的能力,但不一定部署

- 持續(xù)部署 (Continuous Deployment, CD)
代碼通過評(píng)審之后,自動(dòng)部署到生產(chǎn)環(huán)境中。持續(xù)部署是持續(xù)交付的最高階段。
強(qiáng)調(diào) 1、持續(xù)部署是自動(dòng)的 2、持續(xù)部署是持續(xù)交付的最高階段 3、持續(xù)交付表示的是一種能力,持續(xù)部署則是一種方式

流水線的編排設(shè)計(jì)
參考: https://docs.gitlab.com/ee/ci/pipelines/pipeline_architectures.html
這里非常推薦以版本控制系統(tǒng)為源的構(gòu)建流水線設(shè)計(jì),從每一位開發(fā)人員提交代碼即可對(duì)當(dāng)前提交代碼進(jìn)行檢查編譯構(gòu)建,盡快將錯(cuò)誤反饋給每位提交人員。

對(duì)于DevOps流水線,主要是由各類任務(wù)串聯(lián)起來(lái),而對(duì)于任務(wù)本身又分為兩張類型,一種是自動(dòng)化任務(wù),一種是人工執(zhí)行任務(wù)。具體如下:
- 自動(dòng)化任務(wù):包括了代碼靜態(tài)檢查,構(gòu)建,打包,部署,單元測(cè)試,環(huán)境遷移,自定義腳本運(yùn)行等。
- 人工任務(wù):人工任務(wù)主要包括了檢查審核,打標(biāo)簽基線,組件包制作等類似工作。
而通常我們看到的流水線基本都由上述兩類任務(wù)組合編排而成,一個(gè)流水線可以是完全自動(dòng)化執(zhí)行,也可以中間加入了人工干預(yù)節(jié)點(diǎn),在人工干預(yù)處理后再繼續(xù)朝下執(zhí)行。比如流水線中到了測(cè)試部署完成后,可以到測(cè)試環(huán)境人工驗(yàn)證環(huán)節(jié),只有人工驗(yàn)證通過再流轉(zhuǎn)到遷移發(fā)布到生產(chǎn)環(huán)境動(dòng)作任務(wù)。
DevOps流水線實(shí)際上和我們?cè)瓉?lái)經(jīng)常談到的持續(xù)集成最佳實(shí)踐是相當(dāng)類似的,較大的一個(gè)差異點(diǎn)就在于引入了容器化技術(shù)來(lái)實(shí)現(xiàn)自動(dòng)化部署和應(yīng)用托管。至于在DevOps實(shí)踐中,是否必須馬上將項(xiàng)目切換到微服務(wù)架構(gòu)框架模式,反而不是必須得。
在整個(gè)DevOps流水線中,我們實(shí)際上強(qiáng)調(diào)個(gè)一個(gè)關(guān)鍵點(diǎn)在于“一套Docker鏡像文件+多套環(huán)境配置+多套構(gòu)建版本標(biāo)簽”做法。以確保我們最終構(gòu)建和測(cè)試通過的版本就是我們部署到生產(chǎn)環(huán)境的版本。
構(gòu)建操作只有一次,而后面到測(cè)試環(huán)境,到UAT環(huán)境,到生產(chǎn)環(huán)境,都屬于是鏡像的環(huán)境遷移和部署。而不涉及到需要再次重新打包的問題。這個(gè)是持續(xù)集成,也是DevOps的基本要求。
流水線任務(wù)的標(biāo)準(zhǔn)化/原子化
今天談DevOps流水線編排,主要是對(duì)流水線編排本身的靈活性進(jìn)一步思考。
- 構(gòu)建操作:構(gòu)建我們通常采用Maven進(jìn)行自動(dòng)化構(gòu)建,構(gòu)建完成輸出一個(gè)或多個(gè)Jar包或War包。
注意常規(guī)方式下構(gòu)建完執(zhí)行進(jìn)行部署操作,部署操作一般就是將構(gòu)建的結(jié)果拷貝到我們的測(cè)試環(huán)境服務(wù)器,同時(shí)對(duì)初始化腳本進(jìn)行啟動(dòng)等。而在DevOps下,該操作會(huì)變成兩個(gè)操作,即一個(gè)打包,一個(gè)部署。打包是將構(gòu)建完成的內(nèi)容制作為鏡像,部署是將鏡像部署到具體的資源池和指定集群。
- 打包鏡像操作:實(shí)際上即基于構(gòu)建完成的部署包來(lái)生成鏡像。該操作一般首先基于一個(gè)基礎(chǔ)鏡像文件基礎(chǔ)上進(jìn)行,在基礎(chǔ)鏡像文件上拷貝和寫入具體的部署包文件,同時(shí)在啟動(dòng)相應(yīng)的初始化腳本。
那么首先要考慮構(gòu)建操作和打包操作如何松耦合開,打包操作簡(jiǎn)單來(lái)就是就是一個(gè)鏡像制作,需要的是構(gòu)建操作產(chǎn)生的輸出。我們可以對(duì)其輸出和需要拷貝的內(nèi)容在構(gòu)建的時(shí)候進(jìn)行約定。而打包任務(wù)則是一個(gè)標(biāo)準(zhǔn)化的鏡像制作任務(wù),我們需要考慮的僅僅是基于
1)基于哪個(gè)基礎(chǔ)鏡像
2)中間件容器默認(rèn)目錄設(shè)置
3)初始化啟動(dòng)命令。
即在實(shí)際的打包任務(wù)設(shè)計(jì)的時(shí)候,我們不會(huì)指定具體的部署包和部署文件,這個(gè)完全由編排的時(shí)候由上游輸入。
- 部署操作:部署操作相當(dāng)更加簡(jiǎn)單,重點(diǎn)就是將鏡像部署到哪個(gè)資源池,哪個(gè)集群節(jié)點(diǎn),初始化的節(jié)點(diǎn)配置等。具體部署哪個(gè)鏡像不要指定,而是由上游任務(wù)節(jié)點(diǎn)輸入。
任務(wù)節(jié)點(diǎn)間松耦合設(shè)計(jì)的意義
這種松耦合設(shè)計(jì)才能夠使流水線編排更加靈活。比如我們?cè)谶M(jìn)行了構(gòu)建打包后,我們希望同時(shí)講打包內(nèi)容部署到開發(fā)環(huán)境和測(cè)試環(huán)境。那么則是打包動(dòng)作完成后需要對(duì)接兩個(gè)應(yīng)用部署任務(wù)。這兩個(gè)部署任務(wù)都依托上面的打包結(jié)果進(jìn)行自動(dòng)化部署,可以并行進(jìn)行。
對(duì)于測(cè)試環(huán)境部署完成后,我們需要進(jìn)行測(cè)試人員手工驗(yàn)證測(cè)試,如果測(cè)試通過,我們打標(biāo)簽后希望能夠直接發(fā)布到UAT環(huán)境。而這種操作我們也希望在一個(gè)流水線來(lái)設(shè)計(jì)和完成。這樣我們更加容易在持續(xù)集成看板上看到整個(gè)版本構(gòu)建和遷移的完整過程。如果這是在一個(gè)大流水線里面,那么對(duì)于UAT環(huán)境部署任務(wù)就需要一直去追溯流水線上的最近的一個(gè)打包任務(wù)節(jié)點(diǎn),同時(shí)取該任務(wù)節(jié)點(diǎn)產(chǎn)生的輸出來(lái)進(jìn)行相應(yīng)的環(huán)境部署操作。
在談DevOps的時(shí)候,一個(gè)重點(diǎn)就是和QA/QC的協(xié)同,因此在流水線編排的時(shí)候一定要考慮各類測(cè)試節(jié)點(diǎn),包括靜態(tài)代碼檢查,自動(dòng)化的單元測(cè)試,人工的測(cè)試驗(yàn)證。同時(shí)最好基于持續(xù)集成實(shí)踐,能夠?qū)y(cè)試過程和整個(gè)自動(dòng)化構(gòu)建過程緊密結(jié)合起來(lái)。
簡(jiǎn)單來(lái)說(shuō),測(cè)試人員發(fā)現(xiàn)build1.0.0001版本4個(gè)bug并提交,那么在下次自動(dòng)化構(gòu)建完成并單元測(cè)試通過后,測(cè)試人員能夠很清楚的看到哪些Bug已經(jīng)修改并可以在新構(gòu)建的版本進(jìn)行驗(yàn)證。只有這樣才能夠形成閉環(huán),整個(gè)流水線作業(yè)才能夠更好的發(fā)揮協(xié)同作用。
流水線中蘊(yùn)含的工程實(shí)踐
流水線除了任務(wù)步驟的編排,更重要的核心是最佳工程實(shí)踐的體現(xiàn)。過去傳統(tǒng)的思維,自動(dòng)化就是寫個(gè)shell/python腳本批量執(zhí)行,在DevOps/微服務(wù)時(shí)代,這一招太out了,每種工程實(shí)踐的背后都有需要解決的問題,通過在流水線設(shè)計(jì)中注入最佳的工程實(shí)踐,可以讓流水線的價(jià)值最大化,也讓流水線更高級(jí)不是嘛。
- 版本控制 - 解決的問題:需求和代碼的關(guān)系,版本變化的跟蹤
- 最優(yōu)的分支策略 - 解決的問題:版本發(fā)布和團(tuán)隊(duì)協(xié)作,某些情況會(huì)和環(huán)境有關(guān)系
- 代碼靜態(tài)掃描 ** - 解決的問題: 開發(fā)規(guī)范和安全的問題**
- 80%以上的單元測(cè)試覆蓋率 ** - 解決的問題:代碼功能質(zhì)量的問題,讓測(cè)試左移**
- 漏洞(Vulnerability)掃描 - 解決的問題:部署環(huán)境/產(chǎn)品安全的問題
- 開源工具掃描 ** - 解決的問題:解決供應(yīng)鏈安全問題,別忘了log4j**
- 制品(Artifact)版本控制 - 解決的問題:制品的版本控制,制品的晉級(jí),某些情況下環(huán)境的回滾
- 環(huán)境自動(dòng)創(chuàng)建 - 解決的問題:解決的是構(gòu)建/部署環(huán)境一致性的問題,開發(fā)測(cè)的好好的,測(cè)試一驗(yàn)證怎么不行啊,容器化/云原生讓這個(gè)問題更好的解決
- 不可變服務(wù)器(Immutable Server )- 解決的問題: 可能不好理解,打個(gè)比方如果如果你的服務(wù)器掛了,或者某次配置更改了服務(wù)就起不來(lái)了,使用不可變基礎(chǔ)設(shè)施的主要好處是部署的簡(jiǎn)單性、可靠性和一致性,服務(wù)器可以隨時(shí)替換上線
- 集成測(cè)試
- 性能測(cè)試
- 每次提交都觸發(fā):構(gòu)建、部署和自動(dòng)化測(cè)試 ** - 解決的問題:快速失敗,避免下游時(shí)間的浪費(fèi)**
- 自動(dòng)化變更請(qǐng)求 ** - 解決問題:某些場(chǎng)景下通過狀態(tài)變更觸發(fā)某些動(dòng)作**
- 零停機(jī)發(fā)布 - 解決的問題:滾動(dòng)/藍(lán)綠/灰度發(fā)布等,用戶無(wú)感知
- 功能開關(guān) - 解決的問題: 主干開發(fā)中,如果某個(gè)功能沒開放完,就通過on/off某個(gè)特性來(lái)讓穩(wěn)定的功能上線;還有一個(gè)場(chǎng)景,比如某些面對(duì)消費(fèi)者的廣告網(wǎng)站,想看看自己某個(gè)功能客戶是否細(xì)化,通過功能開關(guān)看看市場(chǎng)反饋,一般和A/B測(cè)試配合
基于場(chǎng)景設(shè)計(jì)流水線
是否需要一條完整的流水線?流水線是越多越好,還是越少越好?
建議按照?qǐng)鼍皝?lái)設(shè)計(jì),一條流水線通吃所有流程是不現(xiàn)實(shí)的,搞了好多流水線(比如一個(gè)構(gòu)建就一個(gè)流水線,一個(gè)復(fù)制操作就一個(gè)流水線)這些都是不可取的,維護(hù)成本巨大,得不償失。

流水線按照?qǐng)鼍胺诸惾缦拢?/strong>
- 端到端自動(dòng)化流水線
- 需求、代碼構(gòu)建、測(cè)試、部署環(huán)境內(nèi)嵌自動(dòng)化能力,每次提交都觸發(fā)完整流水線
- 提交階段流水線(個(gè)人級(jí))、
- 驗(yàn)收階段流水線(團(tuán)隊(duì)級(jí))、
- 部署階段流水線(部署/發(fā)布)
- 流水線自動(dòng)化觸發(fā),遞次自動(dòng)化(制品)晉級(jí);
- 流水線任務(wù)按需串行、并行、特殊場(chǎng)景下跳過執(zhí)行
- 必要環(huán)節(jié)人工干預(yù), e.g. 在手工測(cè)試、正式發(fā)布等環(huán)節(jié)導(dǎo)入手工確認(rèn)環(huán)節(jié),流水線牽引流動(dòng)

1)提交流水線
過程如下:
- 提交即構(gòu)建
- 編譯單測(cè)打包代碼質(zhì)量檢查
- 構(gòu)建錯(cuò)誤第一時(shí)間通知提交人
以Jenkins實(shí)現(xiàn)為例,
通過webhook觸發(fā)CI構(gòu)建,首先配置Jenkins項(xiàng)目
- 使用generic webhook方式觸發(fā)項(xiàng)目構(gòu)建
- 配置構(gòu)建觸發(fā)器參數(shù)(獲取gitlab返回的數(shù)據(jù),比如分支、用戶等信息)
- 配置構(gòu)建觸發(fā)器中的token(確保唯一,建議可以用項(xiàng)目名稱)
- 配置觸發(fā)器中的請(qǐng)求過濾(merge_request,opend)

其次是Gitlab的配置
- 項(xiàng)目-》集成-》新建webhook
- 填寫webhook地址?token=projectName
- MergeRequest操作觸發(fā)

剩下的就是編寫Jenkinsfile了,下面列出幾個(gè)關(guān)鍵點(diǎn)
1.獲取gitlab數(shù)據(jù)中的分支名稱,作為本次構(gòu)建的分支名稱。
2.獲取gitlab數(shù)據(jù)中的用戶郵箱,作為構(gòu)建失敗后通知對(duì)象。
2)MR流水線
過程如下:
- codereview
- 配置分支保護(hù)
- 創(chuàng)建合并請(qǐng)求對(duì)將代碼審查結(jié)果在評(píng)論區(qū)展現(xiàn)
- 由assignUser合并代碼
合并流水線設(shè)計(jì):合并流水線的步驟其實(shí)跟提交流水線很類似,但是在代碼質(zhì)量檢查的步驟中嚴(yán)格要求檢查質(zhì)量閾的狀態(tài),當(dāng)質(zhì)量閾狀態(tài)為錯(cuò)誤的時(shí)候,需要立即失敗并通知發(fā)起人。
第一次設(shè)計(jì)
- 開發(fā)人員創(chuàng)建MR并指定AssignUser。
- CI工具開始對(duì)MR中的源分支進(jìn)行編譯構(gòu)建打包代碼檢查。
- 構(gòu)建成功(代碼質(zhì)量沒問題)在MR頁(yè)面評(píng)論提示信息。
- 構(gòu)建失敗在MR頁(yè)面評(píng)論失敗信息
第二次設(shè)計(jì)(借助GitlabCI)- 優(yōu)化點(diǎn):加入MR構(gòu)建失敗攔截,成功自動(dòng)合并
- 項(xiàng)目配置當(dāng)流水線成功時(shí)才能merge。
- 開發(fā)人員創(chuàng)建MR并指定AssignUser。
- Jenkins開始對(duì)MR中的源分支的最后一次commit狀態(tài)改為running。
- 然后進(jìn)行編譯構(gòu)建打包代碼檢查。
- 構(gòu)建成功,更新最后一次commit的狀態(tài)為 success。
- 構(gòu)建失敗,更新最后一次commit的狀態(tài)為faild。
3)SQL發(fā)布流水線

除了代碼有版本,其實(shí)SQL也有“版本的”,SQL腳本的版本對(duì)于產(chǎn)品的升級(jí)回滾至關(guān)重要。
一般對(duì)SQL的集成,會(huì)包含如下要素
- 構(gòu)建環(huán)節(jié),對(duì)SQL語(yǔ)法進(jìn)行檢查,避免打進(jìn)包里語(yǔ)法是錯(cuò)的;某些情況下,多個(gè)開發(fā)會(huì)寫不同的增量腳本,最后發(fā)布時(shí)候需要做腳本的合并
- SQL腳本的版本,某些情況下產(chǎn)品自身要用表來(lái)記錄自身業(yè)務(wù)腳本的版本,通過產(chǎn)品版本來(lái)判斷某些腳本是否應(yīng)該被執(zhí)行。
當(dāng)然,也有其他數(shù)據(jù)庫(kù)版本管理工具,比如 flyway 和 liquibase;
- Flyway是獨(dú)立于數(shù)據(jù)庫(kù)的應(yīng)用、管理并跟蹤數(shù)據(jù)庫(kù)變更的數(shù)據(jù)庫(kù)版本管理工具。用通俗的話講,F(xiàn)lyway可以像Git管理不同人的代碼那樣,管理不同人的sql腳本,從而做到數(shù)據(jù)庫(kù)同步。
- liquibase 只是在功能上和Flyway有差異
不管怎么樣,它們底層的原理都是用另外的表記錄SQL腳本的版本,升級(jí)更新是比較版本差異,來(lái)決定是否執(zhí)行。
python自帶的model模塊 python manage.py makemigrations 同樣在做類似 的事情
數(shù)據(jù)庫(kù)版本管理

流水線的關(guān)鍵元素
不管你用什么CI/CD平臺(tái),開源的Jenkins, GitLab CI, Teckton, Drone,還是商用的Azure,阿里云效等,不管是代碼化,還是可視化,流水線包含的元素基本都差不多,下面通過不同的示例來(lái)說(shuō)明這些元素的作用和含義。
參考:
- https://docs.gitlab.com/ee/ci/pipelines/
- https://learn.microsoft.com/en-us/azure/devops/pipelines/get-started/key-pipelines-concepts?view=azure-devops
- https://www.jenkins.io/doc/book/pipeline/
Agent&Runner(執(zhí)行代理)
image: "registry.example.com/my/image:latest" #gitlab-ci
pool:
vmImage: ubuntu-latest #auzure
agent { label 'linux' } //jenkins
agent {
docker {
image 'maven:3-alpine'
label 'Ubuntu'
args '-v /root/.m2:/root/.m2'
}
}
Parameter(參數(shù)變量)
- **流水線級(jí)別參數(shù) **(全局參數(shù)),范圍限于整個(gè)流水線運(yùn)行時(shí),可被整個(gè)流水線其他任務(wù)使用
- 內(nèi)置全局參數(shù) - 一般稱為built-in(預(yù)定義) variable, 有的平臺(tái)成為環(huán)境變量
export CI_JOB_ID="50"
export CI_COMMIT_SHA="1ecfd275763eff1d6b4844ea3168962458c9f27a"
export CI_COMMIT_SHORT_SHA="1ecfd275"
export CI_COMMIT_REF_NAME="main"
export CI_REPOSITORY_URL="https://gitlab-ci-token:[masked]@example.com/gitlab-org/gitlab-foss.git"
1. BUILD_ID : 當(dāng)前build的id
2. BUILD_NUM : 當(dāng)前build的在pipeline中的build num
3. PIPELINE_NAME : pipeline 名稱
4. PIPELINE_ID: pipeline Id
5. GROUP: pipeline 所屬的group 名稱
6. TRIGGER_USER: 觸發(fā)build的user(event觸發(fā)的為觸發(fā)gitlab event的user)
7. STAGE_NAME: 當(dāng)前運(yùn)行的stage的名稱
8. STAGE_DISPLAY_NAME : 當(dāng)前運(yùn)行的stage的顯示名稱
9. PIPELINE_URL : pipeline在ui中的網(wǎng)頁(yè)的鏈接
10. BUILD_URL: build 在ui的網(wǎng)頁(yè)鏈接
11. WORKSPACE: 當(dāng)前stage運(yùn)行的工作目錄,通常用作拼接絕對(duì)路徑
- 非內(nèi)置全局參數(shù)
environment {
HARBOR_ACCESS_KEY = credentials('harbor-userpwd-pair')
SERVER_ACCESS_KEY = credentials('deploy-userpwd-pair')
GITLAB_API_TOKEN = credentials('gitlab_api_token_secret')
}
- 外部參數(shù) - 一般作為運(yùn)行時(shí)參數(shù)
variables:
TEST_SUITE:
description: "The test suite that will run. Valid options are: 'default', 'short', 'full'."
value: "default"
DEPLOY_ENVIRONMENT:
description: "Select the deployment target. Valid options are: 'canary', 'staging', 'production', or a stable branch of your choice."
parameters([
separator(name: "PROJECT_PARAMETERS", sectionHeader: "Project Parameters"),
string(name: 'PROJECT_NAME', defaultValue: 'vue-app', description: '項(xiàng)目名稱') ,
string(name: 'GIT_URL', defaultValue: 'git@git.xxxx.com.cn:devopsing/vuejs-docker.git', description: 'Git倉(cāng)庫(kù)URL') ,
])
- 步驟任務(wù)參數(shù) (局部參數(shù)) - 一般作為某個(gè)插件任務(wù)的輸入?yún)?shù),也可以使用上個(gè)任務(wù)的輸出作為參數(shù),范圍僅限于該任務(wù)內(nèi)
- 加密變量 - 對(duì)特殊變量進(jìn)行加密處理
secrets:
DATABASE_PASSWORD:
vault: production/db/password@ops # translates to secret `ops/data/production/db`, field `password`
Step(步驟)
參考: https://docs.drone.io/pipeline/overview/
---
kind: pipeline
type: docker
name: default
steps:
- name: backend
image: golang
commands:
- go build
- go test
- name: frontend
image: node
commands:
- npm install
- npm run test
...
Stage(階段)
一般用于對(duì)多個(gè)任務(wù)(step)進(jìn)行分組歸類,便于管理
stage('Pull code') {
steps {
echo 'Pull code...'
script {
git branch: '${Branch_Or_Tags}', credentialsId: 'gitlab-private-key', url: 'git@git.xxxx.com.cn:xxxx/platform-frontend.git'
}
}
}
Trigger(觸發(fā)器)
trigger:
- master
- releases/*
trigger_pipeline:
stage: deploy
script:
- 'curl --fail --request POST --form token=$MY_TRIGGER_TOKEN --form ref=main "https://gitlab.example.com/api/v4/projects/123456/trigger/pipeline"'
rules:
- if: $CI_COMMIT_TAG
environment: production
trigger:
event:
- promote
target:
- production
trigger:
type: cron
cron: '*/5 * * * *' #每5分鐘執(zhí)行一次
制品歸檔&緩存 (artifacts&cache)
一般用于CI制品的歸檔,以及CI構(gòu)建的緩存
archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
job:
artifacts:
name: "$CI_JOB_NAME"
paths:
- binaries/
cache: &global_cache
key: $CI_COMMIT_REF_SLUG
paths:
- node_modules/
- public/
- vendor/
policy: pull-push
集成憑證(Credentials)
參考:
- http://www.rzrgm.cn/FLY_DREAM/p/13888423.html
- https://www.jenkins.io/doc/book/pipeline/jenkinsfile/#handling-credentials
主要用于CI/CD流水線對(duì)接外部工具,通過token/pwd/private key等方式連接外部服務(wù)。一般需要在界面做些提前配置,生成token 或者憑證ID,將ID在CI/CD yaml 或jenkinsfile中使用
withCredentials([usernamePassword(credentialsId: 'amazon', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {
// available as an env variable, but will be masked if you try to print it out any which way
// note: single quotes prevent Groovy interpolation; expansion is by Bourne Shell, which is what you want
sh 'echo $PASSWORD'
// also available as a Groovy variable
echo USERNAME
// or inside double quotes for string interpolation
echo "username is $USERNAME"
}
Service(服務(wù))
該元素應(yīng)用于一些復(fù)雜的場(chǎng)景,比如需要一種外部(公共)服務(wù)為流水線提供某種輸入或者結(jié)果。
您可以將相互依賴的服務(wù)用于復(fù)雜的作業(yè),例如端到端測(cè)試,其中外部API需要與自己的數(shù)據(jù)庫(kù)通信。
例如,對(duì)于使用API的前端應(yīng)用程序的端到端測(cè)試,并且API需要數(shù)據(jù)庫(kù):
end-to-end-tests:
image: node:latest
services:
- name: selenium/standalone-firefox:${FIREFOX_VERSION}
alias: firefox
- name: registry.gitlab.com/organization/private-api:latest
alias: backend-api
- postgres:14.3
variables:
FF_NETWORK_PER_BUILD: 1
POSTGRES_PASSWORD: supersecretpassword
BACKEND_POSTGRES_HOST: postgres
script:
- npm install
- npm test
模板(Template)
參考: https://docs.drone.io/template/yaml/
某些平臺(tái)會(huì)使用“模板“的概念,其實(shí)就是復(fù)用的思想,通過加載固定模板實(shí)現(xiàn)一些快捷動(dòng)作
kind: template
load: plugin.yaml
data:
name: name
image: image
commands: commands
kind: pipeline
type: docker
name: default
steps:
- name: {{ .input.name }}
image: {{ .input.image }}
commands:
- {{ .input.commands }}
執(zhí)行邏輯控制
參考: https://docs.gitlab.com/ee/ci/pipelines/pipeline_architectures.html

stage('run-parallel') {
steps {
parallel(
a: {
echo "task 1"
},
b: {
echo "task 2"
}
)
}
}
stage('Build') {
when {
environment name: 'ACTION_TYPE', value: 'CI&CD'
}
steps {
buildDocker("vue")
}
}

stages:
- build
- test
- deploy
image: alpine
build_a:
stage: build
script:
- echo "This job builds something quickly."
build_b:
stage: build
script:
- echo "This job builds something else slowly."
test_a:
stage: test
needs: [build_a]
script:
- echo "This test job will start as soon as build_a finishes."
- echo "It will not wait for build_b, or other jobs in the build stage, to finish."
test_b:
stage: test
needs: [build_b]
script:
- echo "This test job will start as soon as build_b finishes."
- echo "It will not wait for other jobs in the build stage to finish."
deploy_a:
stage: deploy
needs: [test_a]
script:
- echo "Since build_a and test_a run quickly, this deploy job can run much earlier."
- echo "It does not need to wait for build_b or test_b."
environment: production
deploy_b:
stage: deploy
needs: [test_b]
script:
- echo "Since build_b and test_b run slowly, this deploy job will run much later."
environment: production
門禁審批
pipeline {
agent any
stages {
stage('Example') {
input {
message "Should we continue?"
ok "Yes, we should."
submitter "alice,bob"
parameters {
string(name: 'PERSON', defaultValue: 'Mr Jenkins', description: 'Who should I say hello to?')
}
}
steps {
echo "Hello, ${PERSON}, nice to meet you."
}
}
}
}
pool:
vmImage: ubuntu-latest
jobs:
- job: waitForValidation
displayName: Wait for external validation
pool: server
timeoutInMinutes: 4320 # job times out in 3 days
steps:
- task: ManualValidation@0
timeoutInMinutes: 1440 # task times out in 1 day
inputs:
notifyUsers: |
someone@example.com
instructions: 'Please validate the build configuration and resume'
onTimeout: 'resume'
部署流水線分步驟實(shí)施
說(shuō)了這么多,如果從0開始寫流水線呢,可以按照下面的步驟,從“點(diǎn)”到“線”結(jié)合業(yè)務(wù)需要串起來(lái),適合自己團(tuán)隊(duì)協(xié)作開發(fā)節(jié)奏的流水線才是最好的。
- 對(duì)價(jià)值流進(jìn)行建模并創(chuàng)建簡(jiǎn)單的可工作流程
- 將構(gòu)建和部署流程自動(dòng)化
- 將單元測(cè)試和代碼分析自動(dòng)化
- 將驗(yàn)收測(cè)試自動(dòng)化
- 將發(fā)布自動(dòng)化

注意的事項(xiàng)
開始寫流水線需要注意一下幾個(gè)方面,請(qǐng)考慮進(jìn)去
- 確定變量 - 哪些是你每次構(gòu)建或者部署需要變化的,比如構(gòu)建參數(shù),代碼地址,分支名稱,安裝版本,部署機(jī)器IP等,控制變化的,這樣保證任務(wù)的可復(fù)制性,不要寫很多hardcode進(jìn)去
- 變量/命名的規(guī)范化,不要為了一時(shí)之快,最后換個(gè)機(jī)器/換個(gè)項(xiàng)目,流水線就不能玩了,還要再改
- 如果可以,最好是封裝標(biāo)準(zhǔn)動(dòng)作成為插件,甚至做成自研平臺(tái)服務(wù)化,讓更多團(tuán)隊(duì)受益
- 如果你還在用手動(dòng)的方式配置流水線,請(qǐng)盡快切換到代碼方式,不管是jenkinsfile,還是yaml , 一切皆代碼 也是DevOps提倡的。


流水線案例
案例-1

案例-2


浙公網(wǎng)安備 33010602011771號(hào)