Caicloud基于Docker技術的CI&CD實踐
摘要
Docker技術應用廣泛,可以將軟件與其依賴環境打包起來,以鏡像方式交付,讓軟件運行在“標準環境”中。這一特性可以應用到持續集成中,實現原生支持容器云平臺持續交付。本文將概述CI&CD基本工作流程,軟件整體框架以及實現原理。
背景概述
持續集成是一種軟件開發實踐,即團隊成員經常集成他們的工作,每次集成都需要通過自動化的構建,包括編譯、發布、自動化測試來驗證,從而盡早的發現集成錯誤。持續交付是指頻繁地將軟件新版本交付質量團隊或者用戶以供評審,如果評審通過,發布到生產環境。
Docker是一款基于LXC的容器引擎,自2013年開源以來,因為其易用性、高可移植性在開源社區非常火熱。Docker將軟件與其依賴環境打包起來,以鏡像方式交付,讓軟件運行在“標準環境”中,這非常符合云計算的要求。各大IT巨頭紛紛跟進,基于Docker容器技術創業公司也如雨后春筍,Docker創造了一個嶄新的容器云行業。
Docker技術應用廣泛。如,利用其隔離特性,為開發、測試提供一個輕量級獨立沙盒環境進行集成測試。加速本地開發和構建流程,使其更加高效輕量化,開發人員可以構建、運行并分享容器,輕松提交到測試環境中,并最終進入生產環境。Caicloud Circle正是基于Docker這一特性打造的一款容器原生持續集成持續交付Saas產品。
CICD基本流程
Circle提供豐富的rest API供web應用調用;
通過API建立VCS與Circle服務關聯后,VCS上的commit等動作會觸發Circle構建;
VCS Provider組件從VCS拉取代碼庫中源碼;
基于源碼中配置文件CI配置啟動需要的CI微服務容器進行集成測試;
集成測試通過后,進入Pre Build階段在指定編譯環境容器中編譯可執行文件;
Build階段中將可執行文件拷貝到指定運行環境容器中,打成鏡像推送到Registry中;
Post build階段會做一些鏡像發布后的關聯操作,比如推送運行依賴的靜態資源文件到CDN中;
鏡像發布后可以自動部署應用到Caicloud、K8s、Mesos、Swarm等Paas平臺;
構建過程日志可以通過LogServer拉取;
構建結束構建結果可以發郵件通知用戶。
整體框架
Circle整體框架如下圖所示。Circle運行在容器中,與kafka-zookeeper容器集群通信推送拉取構建過程日志,基礎數據信息存儲在mongo數據庫容器集群中。在Circle內部:API Swagger組件提供在線Circle API說明幫助文檔;API Server組件接收用戶http請求,生成異步待處理事件記入Async Event Manager組件的事件隊列中,并應答用戶請求;Async Event Manager組件在檢測到新的待處理事件創建后,根據事件具體操作類型調用CI&CD組件提供的工具函數組裝拼接成工作流水線,檢測到事件完成后更新數據庫中文檔記錄;在容器外運行一組Docker Daemon隊列,各工作流水線獨立使用一個Docker Daemon,以實現用戶和事件隔離;CD&CD組件會從Docker Daemon Queue中調度取出一個空閑Docker Daemon執行流水線任務,過程日志推送到Kafka指定的topic中;Log Server組件向用戶提供一個獲取日志的Websocket服務器,從Kafka拉取實時日志推送給用戶。
Circle也可以多節點分布式部署,部署圖如下所示。每一個立方體代表一個節點,使用Haproxy反向代理實現負載均衡和SSL數據加密,分發API請求到各Circle節點中。
實現原理
在web頁面通過OAUTH用戶授權拉取用戶VCS版本庫列表,選擇建立與指定版本庫相關聯的服務。如果VCS使用的git還能調用git API建立webhook,當版本庫產生commit、tag、pull request等事件時調用Circle API觸發CI&CD。
Circle CI&CD的具體各步驟操作定義在代碼庫caicloud.yml文件中。分為integration、pre_build、build、post_build、deploy五段。Circle從代碼庫中拉取文件后,解析caicloud.yml文件,依配置執行具體操作。
integration段執行集成測試,yaml文件中定義了編譯可執行文件使用的語言鏡像名,運行使用的環境變量,啟動指令(可以運行一些測試腳本、測試應用),以及集成測試依賴的微服務容器配置。首先使用Docker remote API調用分配的Docker Daemon啟動依賴的微服務容器;然后啟動集成容器編譯可以文件,執行命令行,完成后容器退出,返回命令行執行結果碼標示集成結果。
prebuild段執行編譯工作,可以使用Dockerfile也可以使用yaml的K-V值。Yaml文件中可以定義構建使用的Dockerfile路徑文件名,prebuild容器啟動的基礎鏡像、環境變量、啟動命令行以及編輯結束后,需要輸出的可執行文件夾或文件名,如果Dockerfile和容器配置同時被定義,優先使用Dockerfile。CI&CD首先解析指定的Dockerfile或者yaml文件內容,獲取容器啟動配置,依配置調用容器,執行命令行編譯可執行文件后退出容器,如果編譯成功,使用Docker copy API拷出指定的輸出文件。
在build段中基于指定的Dockerfile構建發布鏡像。在該Dockerfile中可以添加prebuild段中輸出的可執行文件到發布環境中。Prebuild和build的分步操作實現了軟件編譯環境與軟件運行環境的隔離。構建完成后push鏡像到指定的鏡像倉庫中。
Post build段可以定義一些鏡像發布后的關聯操作。比如用戶可以制作一個包含程序運行依賴的必要靜態資源的鏡像,在Post build階段以該鏡像啟動容器,并通過執行命令行推送到CDN中。
發布完成后進入部署階段。用戶在web界面預先配置好部署方案,指定集群分區應用和容器名,Circle會調用集群提供的應用部署API,將構建好的鏡像部署應用中,并查詢部署狀態,如果失敗回滾到上一個成功鏡像。
一些亮點feature
a) 多Docker Daemon構建實現用戶事件隔離
作為服務器不會在同一時刻僅處理單一請求,在同一節點上同時運行的事件任務可能會相互影響。Circle采用多Docker Daemon隔離實現用戶事件隔離。在節點上運行一組Docker Daemon隊列,調度分配給單一事件任務使用,使用完成后清理殘留容器和鏡像,確保構建環境整潔。隊列元素個數有限,當沒有空閑Docker Daemon時事件任務進入排隊等待狀態,等待超過2小時,事件任務超時失敗。當用戶需要取消構建時,僅需kill正在執行當前構建任務的Docker Daemon,然后重啟一個新的Docker Daemon加入到空閑隊列中。
b) 微服務多模塊聯合發布
微服務正在博客、社交媒體討論組和會議演講中獲得越來越多的關注。微服務架構是一種特定的軟件應用程序設計方式——將大型軟件拆分為多個獨立可部署服務組合而成的套件方案。為了管理微服務多模塊間的依賴管理,聯合集成發布多個模塊,Circle實現了聯合發布功能。
首先建立多個與單一模塊代碼庫代碼關聯的服務;然后通過UI拖拽方式設置多個服務間的樹形依賴關系,Circle將樹形關系轉化成線性發布序列存儲。當用戶點擊一鍵聯合發布時,Circle將同時啟動多條CI&CD流水線對多個模塊分別進行集成測試和構建(Integration+Prebuild+Build+Post Build操作),所有模塊構建都完成后依存儲線性發布序列依次部署到容器集群應用中(Deploy操作)。
c) 鏡像安全掃描
常見的文件分析方法有兩種:靜態分析和動態分析。我們采用的是靜態分析,檢閱鏡像的文件系統。漏洞是從Linux操作系統的通用漏洞披露(CVE)數據庫獲取。
今后工作展望
a) 運維
目前Circle實際部署采用的多節點分布式部署方式,各節點上使用Docker compose運行各容器,升級運維都比較麻煩,需要SSH連接到各節點上命令行操作。我們計劃近期將Circle部署到Caicloud CLaas技術棧,運用Kubernetes強大的運維功能提高Circle的生產效率。可能會對現有框架做些改造,目前思路如下:
每個立方體表示一個pod。使用nignx做反向代理,TLS加密;Circle-Master中API-server組件向web提供API服務,Log-server組件提供實時日志服務,當調用API構建,會到新建一個構建任務發送給Worker manager組件,Worker manager記錄任務信息到etcd中,并新建一個Circle-Worker pod執行任務;Circle-Worker中有之前的CI&CD組件,依次啟動容器執行Integration、Prebuild、Build、Post Build和Deploy,中間過程日志推送到Kafka,任務狀態同步到etcd,任務結束后pod退出;各組件需要持久化的信息寫入mongo。
b) 并發
CI&CD任務需要占用大量的系統資源,Circle服務器資源有限,如何才能支持大量并發的構建任務?我們的思路是可以讓用戶添加自有工作節點到Circle集群中,由Circle來調度CI&CD任務管理邏輯,由用戶自有工作節點來承載執行任務的運算負荷。將CI&CD流水線部分拆分出來,基于Docker in Docker鏡像打包制作成Circle-Worker鏡像。用戶在自有機器上安裝運行Docker后,將Docker remote API地址以及機器資源配置告知Circle,Circle驗證機器有效性后將該機器拉入集群中。當該用戶有需要執行的CI&CD任務時,調度用戶節點運行Circle-Worker容器從Circle獲取任務信息并執行,完成后返回結果。
QA
Q:感謝分享,請問你們選用circle的而不是jenkins原因嗎?個人感覺jenkins還是比較強大的,尤其是它豐富的插件
A:我們目的是研發一套基于容器云原生的CI&CD平臺,以便于更好支持今后對接容器集群部署應用和應用管理。
Q:您好!感謝分享,受益匪淺,想請問教:用戶在自有機器上安裝運行Docker后,將Docker remote API地址以及機器資源配置告知Circle,Circle驗證機器有效性后將該機器拉入集群中。這里既然已經手動安裝運行了docker還需要做哪些來驗證機器有效性?
A:還需要做哪些來驗證機器有效性:比如驗證docker daemon是否正常運行,安全證書等。
Q:感謝分享,由于CI會涉及到不同的(開發,測試等等)環境,那么對于不同環境的自動編譯和部署可能處理機制不同,可以通過同一個yml去定義和識別么
A:沒錯,整個CI會涉及不同的環境,正是因為環境的問題,也是我們采用容器技術原因之一。在不同的環境上部署同一個容器鏡像,確實也會有不同的環境特殊配置,比方說數據庫地址,這個時候需要外部來解決配置管理的問題,我們整個容器解決方案中包含配置中心,以環境變量和文件掛載的方式來解決此類問題。
Q:請問:如果使用環境鏡像進行編譯,那么最終編譯后的鏡像大小會不會因為有大量的依賴包而很大?
A:不是很清楚您指的環境鏡像是什么,最終編譯生成的鏡像大小基本與應用使用的編程語言、依賴的中間件和本地其它依賴庫或文件。
Q:問下代碼包是放到鏡像里還是放到宿主機上,如何解決鏡像分發慢的問題?
A:代碼下載到宿主機再掛載到容器中編譯可執行文件,完成后從容器中拷出可執行文件,打包到發布環境鏡像中。我們在宿主機上有運行一個proxy registry用于加速拉取鏡像。宿主機拉取過的歷史鏡像會緩存在本地proxy registry,下次拉取時先從本地拉取。
來自:http://dockone.io/article/1704