TencentHub的架構實現

IT技術精選文摘發表於2018-07-17

640?wx_fmt=gif

TencentHub是一個集Docker映象、二進位制檔案、helmcharts於一體的倉庫儲存服務。那麼這一架構技術是如何基於Kubernetes 快速實現workflow引擎的呢?今天將為大家分享《TencentHub技術架構與DevOps落地實踐揭祕》,讓我們開始吧!

大家好,這次主要是給大家分享TencentHub技術架構和DevOps相關的一些東西,這兩點和大家日常開發結合會比較緊密,比較接地氣一點。我今天會主要分享三部分,第一,簡單聊一下TencentHub這個產品和DevOps的關係,我們如何去思考建設一個TencentHub映象倉庫+DevOps引擎。第二部分講講TencentHub總體的一些東西和Docker映象儲存在騰訊雲上是如何實現的,最後是我們的workflow引擎:為了支撐DevOps工作流去設計產品以及一些具體設計/實現細節。

640?wx_fmt=jpeg

TencentHub與DevOps?

什麼是DevOps?

我相信很多人對DevOps已經比較熟悉了,這個概念從2009年大火起來到現在已經過去了接近十年的時間。直擊DevOps的核心理念,下面這句話是我最贊同的對DevOps的描述:以業務敏捷為中心,去構造適應快速釋出軟體的工具和文化。我們做DevOps,要去推崇DevOps,肯定不能忘掉我們的目標是快速交付軟體產品。在達到這個目標的過程當中,非常重要的落地就是我們需要工具去支撐DevOps,所以接下來分享TencentHub這樣一個以DevOps引擎為指導的工具。

640?wx_fmt=jpeg

什麼是TencentHub

TencentHub是什麼?TencentHub核心有兩部分,第一,它是一個多功能的儲存倉庫,包含了Docker映象儲存功能以及helmcharts這樣的儲存,還有一些構建產物的儲存等等。第二,TencentHub是一個DevOps引擎,通過我們自己設計的workflow引擎對工作流進行編排,去幫助大家建立自己的DevOps流程,比如說構建、測試、稽核、部署等等任務。當然,在前置環節,我們也將去對接騰訊雲提供的TGit程式碼託管服務,最終給開發者提供一套完整的雲上Devops工具解決方案。

640?wx_fmt=jpeg

TencentHub技術架構

TencentHub – 總體架構

我簡單介紹一下TencentHub的總體架構,TencentHubd 的映象倉庫儲存是基於COS實現,因為COS可靠性非常高,這非常方便地給我們的映象儲存帶來高可靠保證。TencentHub核心還有一個自研workflow引擎,可以使用YAML定義DevOps流程,讓我們的DevOps本身達到Programmable,這對Devops的管理是很重要的。在TencentHub的Devops引擎裡面,我們使用容器去實現外掛機制,用來封裝使用者自定義的DevOps任務,後面會詳細介紹。使用Kubernetes作為執行引擎,執行DevOps任務,目前是使用TKE來實現的。在Docker儲存倉庫裡,我們也會加入Docker映象漏洞的安全掃描,後面會做一個的簡單介紹。

640?wx_fmt=jpeg

映象儲存

Registry Token Authentication Specification

左邊這個圖分為兩大部分。上面是Registry,下面是StorageEngine。Registry主要包含了組織、團隊、成員的管理,以及一些許可權的控制,同時還包括倉庫Repository的管理,負責Docker映象、檔案、helmchart的儲存。我們所有儲存都是使用一個倉庫來管理,映象、檔案等都會放在某一個倉庫下面。在最上面,我們提供了Webhook,可以幫助使用者對接自己的系統,監聽倉庫或者DevOps流程發生的一些事情。在Registry裡面還有Component元件,負責DevOps任務的儲存。下面這部分StorageEngine,是我們去解決統一的倉庫儲存層的問題,它是一個我們提供出來給全球的騰訊雲的機房都能使用TencentHub服務的機制。

640?wx_fmt=jpeg

TencentHub倉庫中最重要的是Docker映象的儲存。除了公共的映象儲存之外,TencentHub還支援私有的映象儲存。私有映象儲存需要通過登入才能獲取或者上傳Docker映象。如何是想登入認證呢?這是Docker官方的Registry使用者認證的規範,它並沒有納入到OCI規範裡面,只是在官方有一個文件去說明流程是什麼樣的,Docker客戶端也是按這個流程去實現的,所以我們只需要在TencentHub後端按這個流程去實現自己的授權服務就可以了。

640?wx_fmt=png

這裡用一個時序圖簡單說一下它的流程。首先可以看到,Docker服務端首先會去訪問/v2/這樣的路徑,客戶端會根據當前是否有攜帶Token來返回一個授權的地址,我們目前返回的是hub.tencentyun.com/token這個地址,客戶端就會自動訪問/token的URL,帶上當前的賬戶以及申請的許可權範圍。客戶端申請Token完成之後,就會進入Docker映象拉取流程。

640?wx_fmt=jpeg

OCI Distribution Specification

640?wx_fmt=png

640?wx_fmt=jpeg

distribution RESTful API

這裡再簡單補充一下關於distribution 規範的一些API。前面兩個不說,主要是第三個,每個Docker映象的上傳都會訪問到/v2/倉庫名/manifests/tags|digest地址,在這個地址上,它支援去GET、PUT、DELETE,去實現Docker映象在遠端倉庫的拉取/上傳/刪除的功能。剛才所說的Layer、Config檔案,包括Menifests檔案,也可以通過V2這個路徑下面的name+blobs+digest去獲取。更詳細的規範,可以看一下OCI組織已經成為標準的說明,下面有個URL。

640?wx_fmt=jpeg

distribution 架構

640?wx_fmt=png

在許可權控制下面會有API協議的函式處理,一些主要的業務邏輯,都在這裡實現。最終到下面storage的實現,它提供了一套儲存的外掛機制,有一個標準的儲存介面,規定了檔案上傳、檔案移動等等介面,只要去實現就可以了。我們這裡也會實現GOS,是對COS的簡單封裝,後面會做一個GOS簡單介紹。

640?wx_fmt=jpeg

現在騰訊雲容器服務的倉庫是CCR,還沒有提到TencentHub上面來。CCR有兩個問題,第一是不同地域的映象是不通的,比如說我們在廣州上傳一個映象,想在矽谷拉取是拉取不到的,這是我們直接依賴了COS提供的分發能力,沒有去對它做封裝,COS是無法跨區域訪問的,所以會存在這個問題,這會帶來使用者體驗上的不一致,基於倉庫的觸發器、Docker映象構建功能,都會受到這個問題的影響,所有基於倉庫的服務都需要單獨又去部署一份,維護上也帶來了很大影響。所以我們就做了下面這一層。

640?wx_fmt=png

前面基於Docker倉庫關於映象儲存方面的分享,也是TencentHub映象倉庫裡面最複雜的部分,關於artifact和heml chart這樣的檔案儲存,我們就不做過多的實現,因為它的實現都類似。

640?wx_fmt=jpeg

workflow引擎設計與實現

如何設計一個通用的DevOps型解決方案?

我們為什麼要去做一個TencentHub的DevOps引擎呢?因為我們發現很多客戶在上雲的時候,平時遇到非常多重複性的操作工作,例如在騰訊雲UI上面做很多事情,沒辦法自動化或者騰訊雲的API還不夠好用等等各種問題。其實DevOps的自動化要求是很高的,我們不能加入大量的人工去操作,基於這個考慮,同時還要照顧到中長尾客戶的一些需求,我們最終決定要做一個DevOps的工具,去幫使用者來建立自己的DevOps流程。

640?wx_fmt=jpeg

這裡是我們考慮如何去做這個事情。第一點,要實現DevOps這樣一個工具,首先要把DevOps任務編排起來,所以我們需要做到一個能儘量覆蓋到盡多客戶DevOps需求的編排邏輯。第二,DevOps任務是千差萬別的,我們沒辦法去開發完所有的外掛,去適配每個客戶自己的需求,他們的部署策略,他們的單元測試、編譯等等。所以我們需要把這些任務交給客戶自己去完成,需要設計一個外掛機制,就是Component,後面會介紹。第三點,使用者的DevOps流程執行在TencentHub裡面,我們需要真正去執行它,但我們不想去發一個執行任務的叢集,我們只需要只需要做很簡單的任務排程,然後交給成熟的叢集管理元件完成。所以我們最終還是選擇kubernetes來做這個事情,也就是TKE,它為我們省去了很多運維工作,還有TKE的監控都可以複用。

640?wx_fmt=png

Component的設計我們後面再詳細介紹。

640?wx_fmt=jpeg

Workflow 生命週期

640?wx_fmt=png

workflow被觸發執行,就會在系統中把它置成一個pending狀態。因為每個客戶有配額限制,我們的scheduler會去檢查這個使用者的配置是否足夠,例如他如果有些比較長的workflow執行,我們會一直將它放在一個排程的狀態,當檢查通過之後,就會被投入到TKE當中去執行。但是它也不一定馬上會執行起來,因為TKE的資源也有可能會比較緊張,我們這裡提供的是公有云服務,所有人都會訪問,所以資源可能會臨時不夠用,也會處於pending。我們會有一個狀態的檢測服務,不停的輪巡job的狀態,如果發現部分它已經脫離了pending,進入了其它狀態型別值,我們就會把它標定為running狀態。如果一個stage被標記為是一個可暫停的stage,這個stage裡所有的job被執行完之後,我們會把它標記為已被暫停的狀態,這時候它需要等待外界的反饋,來決定這條workflow繼續如何走: 如果外界反饋說它要取消,workflow就會進入到整個流程的結束。

640?wx_fmt=jpeg

Job feature

首先,每個job都考慮成為一個類似於函式一樣,去處理輸入,在內部做一些自己的業務邏輯,最後通過我們定義的標準輸出,去輸出一些它處理完的資訊。第二,job可以從workflow全域性環境變數中去讀取資訊,做一些邏輯。這些環境變數不能修改,因為如果我們提出一個全域性可修改的變數儲存,它會導致整個component的除錯或者開發會非常麻煩。第三,每個component肯定需要和外界打交道,我們workflow裡面會去提供cache和artifact指令,讓使用者可以非常方便地把container裡面構建出或者執行的一些結果儲存到我們提供的外部儲存裡面。第四,workflow沒有辦法去迴圈執行,只是一個DAG構成的關係圖,然後一條一條向前執行。這是我們設計workflow的一些考慮,沒有去違背這些,才去做接下來的開發。

640?wx_fmt=jpeg

Job Storage -- Artifacts & Cache

Cache,是用來在不同的job之間共享和傳遞一些資料,它可以是資料夾,也可以是檔案。Artifacts是構建出的一些結果,可以儲存在TencentHub倉庫裡面,在倉庫裡面有介面,也有API,可以進行檢視或者拉取下來。Cache沒有去跨多個workflow例項。Cache的具體實現工程是對指定的檔案/檔案目錄進行壓縮,上傳到TencentHub的物件儲存裡面。當下面有一個Job依賴它的時候,又會在Component內部把它下載下來。整個實現就是在兩個hook中進行完成,兩個hook一個是Prestart,一個Poststop,後面會介紹我們如何去實現hook機制。

640?wx_fmt=jpeg

為什麼使用容器去做DevOps?

我們選擇了用容器來做DevOps,為什麼我們要選擇用容器來做這樣的外掛機制呢?也是考慮了很久,討論了很久,核心是基於下面四個考慮。

640?wx_fmt=png

640?wx_fmt=jpeg

Component像一個函式實現一樣,會有Input和Output,每個Component可以從另一個Component的Output去新增需要使用的一些輸出值,workflow會把它變成Component執行的容器的環境變數,注入到當前Component裡面。在Component容器程式裡面可以使用這些環境變數做一些邏輯,比如說克隆指定版本的程式碼,或者釋出上一個環節構建出的版本。Component的輸出直接放到標準輸出裡面,我們規定了每一行只要符合下面紅色地方標明的資料格式,我們就把它作為Component的輸出。選擇這樣不夠優美,但是非常實用的方式去實現,是考慮到如果使用外部的儲存比如redis對input/output 作為儲存,會對使用者編寫Component造成很大的影響:需要在Component裡面還要去呼叫外部儲存,儲存輸出需要儲存一個Key,在下一個階段使用時又要用key到儲存裡去取出來。雖然可能用一個第三方的儲存會帶來更豐富的資料結構方面的優勢,但是我們認為對客戶開發Component侵入性太大,所以我們直接用環境變數和標準輸出來做這個事情。

640?wx_fmt=jpeg

workflow引擎

workflow是在我們的TKE去執行的,選擇用DevOps做workflow引擎的執行叢集,是基於這些特性去考慮的。

640?wx_fmt=png

640?wx_fmt=jpeg

前面到使用Component作為外掛的時候,使用到 Prestart和Poststop這兩個hook,在設計的時候,我們調研過是否能複用kubernetes pod 的Poststop。發現是不行的,Poststop是在pod的生命沒結束之前由外面的controller去結束一個pod的時候執行(這才會有機會在容器之內去執行指定的hook程式)。但如果像workflow這種Task是一次性執行然後自行退出的,kubernetes沒有辦法在程式正常前呼叫呼叫Poststop裡面指定的程式 – kubernetes不知道什麼時候Task會退出 。所以我們最終實現了自己的方式: workflow把Component在kubernetes裡面執行變成pod前,把它的CMD替換成另一個程式CommandWrapper,這個程式採用靜態連結,保證可以在Linux上執行。CommandWrapper會在元件component任務被執行之前,會完成Cache和Artifact相應的邏輯,然後component會在子程式中執行,子程式退出後, CommandWrapper會繼續處理Cache和Artifact的上傳邏輯,最後,CommandWrapper程式會以子程式所退出狀態碼進行退出,如果是返回碼是0,workflow引擎就把它標記為是成功執行,然後會排程下一個job的執行。這就是我們去設計的Hook機制。

640?wx_fmt=jpeg

這裡簡單介紹如果workflow引擎如何獲取Task的Log。開發人員常常需要檢視每個執行的workflow job的Log。可能workflow裡面會有Bug,或者想看它當前進行到什麼地步。Log的獲取沒有使用Docker driver,因為這在kubernetes裡面目前還沒有實現,即時kubernetes支援了docker的log driver,通過單獨的通道去收集Log也會帶來很大的複雜性,所以workflow引擎直接呼叫了kubernetes的API去獲取它的Log,因為kubernetes API的Log只要在呼叫沒退出之前,會一直把pod執行的Log從叢集裡面讀出來,返回給呼叫方。Workflow引擎把整個Log存起來,放到COS裡面,或者最後把它通過websocket去寫到前端,讓使用者可以去實時檢視。當然,後者我們還沒有實現,開發人員太少,但是我們預留了,可以這樣去做。

640?wx_fmt=jpeg

今天沒有給大家分享怎麼去搭建自己DevOps流程的例子,因為我相信這是千差萬別的,在不同的公司,不同的組織裡面,流程都不相同,我們沒有辦法列舉出來。而是站在公共服務的提供商角度,考慮怎麼給使用者最大的便利去建立這樣一套工具。所以我今天的分享主要是關於DevOps的設計和一些實現細節,希望對大家會有一些幫助。

TencentHub這個產品目前已經在騰訊雲官網上開啟內測,邀請大家一起來體驗這個產品,也歡迎大家的反饋和吐槽。

https://cloud.tencent.com/product/thub

公眾號推薦:

640?wx_fmt=jpeg

640?wx_fmt=jpeg

相關文章