DockOne微信分享(一零八):基於Jenkins和Kubernetes的CI工作流程

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
【編者的話】Jenkins作為最為流行的持續整合工具,在結合使用容器技術,Kubernetes叢集的基礎上,該如何發揮出新的能力,在應用微服務化的基礎上,提供更好的CI方式,值得我們每一個開發人員去持續不斷的摸索。本次分享主要介紹我司如何使用Jenkins Pipeline、Container和 Kubernetes Deployment的能力, 通過增加使用文本模版引擎, 擴充Kubernetes Config能力,完成公司產品開發CI工作流程的建立。

Jenkins和Kubernetes

Jenkins作為最流行的持續整合工具,有著豐富的使用者群、強大的擴充能力、豐富的外掛程式,是開發人員最為常見的CI工具。在Jenkins 加強其Pipeline功能後,更是可以通過豐富的step庫,實現各種複雜的流程。同時隨著Docker的流行,Jenkins內也增加了對Docker的支援,可以實現容器內流程的執行。

而Kubernetes隨著版本迭代的速度越來越快,在容器圈內的熱度也越來越高,同時每次版本發布,所新增的功能也不斷增加。做為當前主流的容器管理平台,其強大的能力無需在此多做介紹。

應用程式容器化和應用微服務化設計思考

容器化不意味著微服務化,傳統單體應用也可以容器化,但是很難享受到容器化後帶來的好處。微服務化也不是一定要容器化,應用拆解為微服務後,一樣可以不利用容器而是通過傳統的營運來完成系統構建和部署。當微服務化和容器化相結合之後,就能充分利用各方優勢,帶來了Auto Scaling,簡化部署,易於擴充,技術相容等優點。

我們在針對應用進行微服務化拆分的過程中,主要先考慮到的是功能點、控制對象、開發組的人員配置安排,產品路線圖規劃等。例如,針對現有開發組人員人數、分配和各自的技能熟練程度,就可以考慮到服務模組數量的控制,安排好服務模組開發小組;針對功能點和中遠期產品規劃,就可以將特定功能歸納到一個服務模組中,並在版本開發迭代的過程中,通過擴充這個服務模組的能力,來完成產品功能的開發,或者暫時將部分功能整合在一個模組中,隨著功能增加或反覆式開發法,再進行進一步的模組拆分或拆解。

對於模組開發的要求,由於使用了容器技術,對於開發語言或特定架構的選型,可以交給具體的模組開發人員。在團隊內,我們不做強制要求,但是做建議要求,避免出現過多的技術棧,導致後期的維護困難。

在我們團隊內,就只集中在兩種後端開發語言的使用,相應的架構或主要的開發庫,也都有相應而且明確的選擇。對於模組的API介面,使用REST,並且至少按照成熟度等級模型LEVEL2提供API。

容器環境下的編譯和單元測試

我們整個CI工作流程的驅動,都是由Jenkins完成,並且使用了Jenkins Pipeline。第一,Pipeline可以更好的組合job內的stage,重複利用模組間相同的部分,並且隨著開發複雜度的增加來逐步增加擴充stage,實現更多所需的功能;第二,將Pipeline Groovy指令碼來源設定為原始碼內,可以根據原始碼功能點來控制流程程,同時也完成了對指令碼的版本管理。

由於有容器這麼個工具,我們各個模組的編譯環境,單元測試環境,也都放到了容器中。各個模組均可以安裝自身模組的運行特性或環境要求,準備自身的編譯環境、單元測試環境、運行環境,因此,程式碼程式庫內會分別留存相應的Dockerfile,通過不同的Dockerfile完成不同環境鏡像的準備。同時,Jenkins現在也可以通過Docker Pipeline外掛程式,支援在容器內運行step,因此我們利用其功能完成的實際的編譯和測試流程是這樣的:
  1. 使用編譯環境的Dockerfile構建編譯環境鏡像。
  2. 使用編譯環境鏡像啟動容器並在容器內完成編譯,完成編譯的中間產物也暫存在Workspace中。
  3. 使用測試環境的Dockerfile構件單元測試環境鏡像。
  4. 使用單元測試環境鏡像啟動容器並在容器內運行單元測試,單元測試指令碼來源於程式碼程式庫,同時也使用到編譯時間產生的中間產物。
  5. 使用發布Dockerfile構建實際發布鏡像並上傳鏡像庫。


其中由於編譯環境和單元測試環境不是經常變更,也可以抽出編譯環境鏡像準備和單元測試環境鏡像準備兩個步驟放到獨立的CI job中去,需要時手工觸發即可。

服務部署和升級

對於CI流程,在完成編譯和打包後,需要做的就是服務啟動和測試了。我們利用的是Kubernetes Deployment和service,在每次CI流程完成編譯和打包後,通過拿到Build號,作為鏡像的tag,完成鏡像的上傳歸檔;同時利用tag,修改Kubernetes中已經建立的Deployment,利用Deployment的Rolling Update,完成升級。

對Kubernetes服務模版和服務配置的擴充

我們在實際使用Kubernetes Deployment 升級的方式進行服務部署的過程中,發現其中還是存在很多不方便的地方。例如:Kubernetes內的同名問題,Kubernetes Deployment升級時的鏡像tag變更問題,等等各處需要隨著CI流程可能存在變更的地方。例如在有相同名字的Deployment存在的情況下,後來的Deployment會無法建立,這導致如果想以啟動新的Deployment的方式來測試某個版本,需要修改名稱,對於與Deployment相關的service也一樣,在啟動新的命名後的Deployment,也需要啟動與其對應的service用於暴露服務。

對於Deployment升級所需的鏡像tag修改,需要每次隨著CI產生了新的鏡像tag而做變更,因而每次需要修改相應yaml檔案內的鏡像tag,修改為實際CI流程中產生的值,然後再使用升級功能完成服務升級。

針對這些問題,我們使用了一套文本模板引擎,部署或升級用的yaml檔案本身寫成為模板,可能有變化或者需要根據CI流程變化的位置,使用模板標識佔位,而具體的模板資料內容,則或者通過Jenkins的CI流程擷取,或者使用特定的設定檔讀取,或者從具體的輸入參數來擷取;

同時,模板資料內容,也會在實際部署時,做為Kubernetes的Configmap設定到系統中,因此資料內容也可以通過Kubernetes使用Configmap的方式來用到環境變數或啟動命令中。通過文本模板引擎,將模板和資料合併後,產生的yaml檔案,再作為後續Kubernetes操作所使用的內容。

通過利用這種方式,我們把需要部署的內容分離成了模板和配置;模板一般在服務架構,使用的鏡像名,啟動方式或配置參數沒有大的變化的情況下保持不變,而通過不同配置的靈活使用,完成服務升級或拉起新部署,完成不同資料存放區使用的指向,完成對各模組內部配置的修改。

通過利用這種方式,我們的可修改的內容,從Configmap本身只能覆蓋到的環境變數或啟動命令這塊,擴充到了啟動名稱、Label、鏡像等YAML檔案內的各個可填值處,以此來解決同名,鏡像修改,Label增加或變更等各種使用Kubernetes時碰到的問題。

自動化測試

在通過Jenkins拉動完成編譯打包和服務升級部署後,就可以拉動自動化測試了。測試架構我們選擇了使用Robotframework。測試指令碼通過Kubernetes service擷取到服務的具體暴露連接埠,然後再根據測試指令碼依次執行針對API的測試。

測試指令碼的來源,部分是從各模組程式碼程式庫中,由各模組開發人員提交的針對自身模組的API測試,部分是由測試人員完成撰寫提交的針對跨模組的測試。針對自動化測試這塊,我們的完成度並不是很高,僅僅是搭建起了基本的運行架構,能夠與整個流程對接上。

版本發布

由於開發的產品本身就是由若干鏡像構成,因此產品發布,可以歸結為鏡像的發布。在測試通過後,可以簡單的利用鏡像複製能力,將測試通過的相關鏡像的版本,通過鏡像庫間的複製,由開發測試所用的內部鏡像庫,複製到外部發布鏡像庫,就可以完成版本發布,同時可以通過複製時的tag控制,發布為指定的版本號碼。

總結

如上介紹,說明了我們在自身開發過程中建立CI流程的做法。對於整個流程的建立,我們並沒有太多需要特殊化處理的地方;對於各項工具的使用,也沒有太多突出之處;我們僅僅根據自身需求,建立了和開發過程適配的CI流。在此介紹我們的CI流程的建立,也是希望拋磚引玉,能從更多處獲得交流和向大家學習的機會。

Q&A

Q:關於Jenkins和Docker整合的幾個外掛程式可以分享一下嗎?

A:Docker Pipeline、Docker Plugin、docker-build-step這幾個外掛程式。
Q:請問有容雲的鏡像複製大體思路是什嗎?目前我們是測試、預發布、發布共用一個倉庫。通過輔助模組標籤實現的。

A:鏡像複製功能,簡單來說實現了不同項目間的鏡像複製,根據我們鏡像倉庫的設計,對不同階段(開發、測試、可上線)的鏡像以不同項目分類,基於鏡像複製功能即可快速實現不同階段的產品發布,也就是鏡像發布,此功能可下載AppHouse版本進行試用。
Q:貴公司的內部倉庫和外部倉庫鏡像是即時同步的嗎? 你們的設定檔是通過配置中心管理還是鏡像間的環境變數實現的?

A:不是即時同步的,而是通過了我們公司鏡像庫產品的鏡像複製能力實現的。目前開發流程中的產品回合組態是通過自身增強設定檔能力實現的,配置會用來修改應用部署的YAML檔案,也會產生為ConfigMap。
Q:有沒有一個簡單的sample,可以上手跟著練習一下?

A:基於Kubernetes的CICD產品即將發布,會提供對應的demo示範平台,請及時關注,謝謝!
Q:Kubernetes的YAML的部署檔案的模板怎麼去替換預留位置?舊版本的容器怎麼處理?

A:使用了自身開發的一套文本模板引擎,其實類似Web架構中的模板引擎,完成模板和配置的合并,通過使用配置中的key-value,替換掉模板中key的預留位置。另外由於是使用的Kubernetes deployment的rolling update,升級完成後舊版本的容器/pod會由Kubernetes自行刪除。
Q:傳統單體應用如何容器化改造,可否分階段實施?

A:可以的,容器化改造或微服務化改造有很多實施方法,例如逐漸重構拆解,或新增模組進行微服務化和容器化,或開發新的模組替代原有應用的功能點,等等。各團隊均可以選擇合適自身的流程來進行改造。
Q:資料庫可以容器化嗎?

A:可通過將資料卷掛入容器的方式將資料庫容器化,但是現在實際項目中還很少見。
Q:有這樣一個情境,兩個服務有依賴關係,服務A依賴於服務B,如何保證服務A和B的啟動順序的?

A:良好的設計是使得A服務啟動後自行完成對B服務的檢測發現和調用,而不是強依賴其啟動順序.
Q:Kubernetes的服務的模板和配置,這個模板怎麼來的,是使用者自己編排?還是自己事先準備好的?配置資料是怎麼儲存的?

A:因為當前模板和配置只用來啟動我們自身開發的應用,因此這個模板是我們自己為我們的應用準備的。配置資料以檔案的形式儲存,但同時在使用文本引擎做模板和配置合并時,也可以接受參數作為配置。
Q:什麼是CI和CD,這個搞不懂?

A:CI更多是偏嚮應用編譯,代碼檢查,單元測試等動作,CD是偏向於應用部署,運行流程。我們的開發過程在編譯打包完成後,實際也會將應用跑起來用於測試,也可以算是針對測試的CD。
Q:使用容器打包Jenkins流程的主要收益是什嗎?

A:由於不同程式對於編譯環境的依賴各有不同,原有使用Jenkins方法是在Jenkins node上完成環境準備,現在可以利用容器完成環境準備,對於Jenkins node的依賴可以進一步降低。同時環境變更也可以由開發人員自行控制。
Q:多編譯環境是用的不同鏡像嗎?如何處理Pipeline處理編譯環境的問題?

A:是的。由於我們本身產品開發各個模組有各個模組的開發語言和架構,因此各模組都要維護自身的編譯環境鏡像。使用Pipeline在進行編譯時間,是通過使用鏡像運行容器然後在容器內編譯的方式來使用編譯環境的。
Q:請問Jenkins也是部署在Docker裡面的嗎?如果Jenkins在Docker裡面怎麼樣在Docker裡面使用Docker執行CI?

A:是的,我們也在摸索將Jenkins本身放到容器中運行。在這種情況下,Jenkins容器內使用root許可權,掛載docker.sock和Docker資料目錄到容器中就可以了。
Q:使用Pipeline先構建編譯環境鏡像,再編譯,是否會導致整個流程需要很長時間?是否有最佳化方案?

A:編譯鏡像由於不會經常變動,因此這個鏡像的構建通常使用cache就能直接完成,另外我們也把編譯環境鏡像打包這個步驟抽出來單獨作為job執行了,這樣在實際編譯流程中就無需再進行編譯環境構建。
Q:Jenkins和Kubernetes的使用者是怎麼管理的?我的期望是使用者只能看到自己得資源,別的使用者是沒有許可權的。

A:我們本身只是使用這兩種工具,在開發環境中不對外,所有不存在使用者管理的問題。在我們公司正在開發的CICD產品中,對這塊有我們自身理解基礎上的設計。
Q:Jenkins的持續整合是怎麼實現的?比如不同的源碼倉庫的提交觸發,如GitHub、GitLab版本號碼怎麼控制的?

A:Jenkins的CI流程觸發可以有很多種,代碼提交觸發,定時觸發,手動觸發。版本號碼的控制也可以有很多方案,比如使用job的編號,使用Git的commit號,使用時間戳等等。
Q:容器化後發布也要通過Jenkins,感覺Docker的發布沒有Jenkins方便,除了容器化的可移植,還有什麼原因值得推進項目容器化?

A:應用程式容器化,其實更多的是看重應用在容器管理平台上運行起來後所獲得的能力,例如在Kubernetes上運行後的水平擴充,服務發現,滾動升級,等等。
Q:Kubernetes update需要制定新的鏡像才能做滾服更新(升級),如果只是更新了ConfigMap,有辦法做滾服更新嗎?

A:我們的CI流程完成後,各模組的鏡像tag會發生變化,我們利用具體產生的tag組建組態,然後部署的YAML檔案寫為模板,鏡像的具體tag會根據每次CI流程產生的配置不同而組合為不同的YAML檔案,然後使用組合後的yaml,即tag已經變更為最新版本的YAML檔案進行應用的滾動升級。
Q:Pipeline採用在鏡像裡構建的方案,是怎麼實現的?用Jenkins現成的外掛程式 or 用Groovy指令碼實現?

A:使用了Jenkins的Docker外掛程式,同時使用方式是將相應命令寫在Groovy指令碼裡,例如: stage('Build'){ docker.image('golang:1.7').inside { sh './script/build.sh' } }
以上內容根據2017年2月28日晚群分享內容整理。分享人 黃文俊,有容雲資深系統架構師。主要負責容器雲平台產品架構及設計,8年工作經驗,有著企業級儲存,雲端運算解決方案相關理解。關注於微服務設計思考,開發流程最佳化,Docker及Kubernetes技術在實際環境中的應用。 DockOne每周都會組織定向的技術分享,歡迎感興趣的同學加:liyingjiesz,進群參與,您有想聽的話題或者想分享的話題都可以給我們留言。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.