基於 Kubernetes 實踐彈性的 CI/CD 系統

_2019_02_15_5_31_03

你們好,我是來自阿里雲容器服務團隊的華相。首先簡單解釋一下何爲 Kubernetes 來幫助你們理解。Kuberentes 是一個生產可用的容器編排系統。Kuberentes 一方面在集羣中把全部 Node 資源作一個資源池,而後它調度的單元是 Pod,固然 Pod 裏面能夠有多個容器。 就像一我的左手抓着 ECS 資源或計算資源,右手抓容器,而後把它們兩個匹配起來,這樣它就能夠做爲一個容器的編排系統。

而 Cloudnative 這個概念如今會常常被你們提起,不少人迷惑 Cloudnative 又與 Kuberentes 有什麼關聯?咱們又該如何判斷一個應用是 Cloudnative 呢?我認爲有如下三個判斷標準:

第一,它可以給資源作池化;
第二,應用能夠快速接入池的網絡。在 Kuberentes 裏面有一層本身的獨立網絡,而後只須要知道我要去訪問哪一個服務名就能夠,就是各類服務發現的一些功能,它能夠經過 service mesh 去作一個快速地訪問;
第三是有故障轉移功能,若是一個池子裏面有一臺主機,或者某一個節點 down 掉了,而後整個應用就不可用了,這確定不算是 Cloudnative 的應用。


比較這三點就能夠發現 Kuberentes 作的很是好。首先咱們看一個資源池的概念,Kuberentes 一個大的集羣就是一個資源池,咱們不再用去關心說我這個應用要跑在哪臺主機上了,我只要把咱們部署的 yaml 文件往 Kuberentes 上發佈就能夠了,它會自動作這些調度,而且它能夠快速地接入咱們整個應用的網絡,而後故障轉移也是自動。接下來我就來分享如何基於 Kuberentes 實現一個彈性的 CI/CD 系統。node

CI/CD 的現狀

首先了解一下 CI/CD 的現狀。CI/CD 這個概念實際上已經提出不少年了,可是隨着技術地演進和新工具地不斷推出,它在整個流程和實現方式上逐漸豐富。而咱們一般最先接觸 CI/CD 就是代碼提交,隨之觸發一個事件,而後在某個 CI/CD 系統上作自動構建。git

下圖能夠反映目前 CI/CD 的現狀:
1程序員


另外還有 Gitlab CI,它主要特色是與 Gitlab 代碼管理工具結合地比較好。Jenkins 2.0 開始引入 pipeline as code 特性,pipeline as code 能夠幫咱們自動生成一個 jenkins file。



在 Jenkins 1.0 時,若是咱們想要配置一條流水線,須要先登陸 Jenkins 建一個項目,而後在裏面寫一些 shell。這樣雖然能達到一樣的效果,可是它有一個最大的弊端,就是可複製性和遷移性很差。並且它與 Devops 有自然地割裂,好比通常是由運維人員來管理 Jenkins 系統,開發人員編寫代碼,可是這個代碼怎麼去構建,發佈到哪裏,開發人員徹底不知道。這就形成了開發和運維地割裂。但 pipeline as code 方式出現了以後,jenkins file 與代碼源碼能夠放在一樣的倉庫裏面。

首先它有一個很是大的好處是發佈的流程也能夠歸入版本管理,這樣對一個錯誤就可追溯。這是一個很是大地改動,可是實際上咱們在與客戶溝通中發現,雖然不少人在 Jenkins 上都已經升到 2.0 系列了,可是它們的用法仍是徹底在 1.0 系列,不少用戶都沒有把 jenkins file 這種方式用起來。另一個就是對容器地支持,大概 2016 年左右,那時對容器的支持是很是弱的,在容器裏面運行 Jenkins,同時構建的產物也是 Docker 會很是麻煩。 

可是, Drone 對容器的支持力度就很是好。首先它徹底用 Docker 方式來運行,就是說你的構建環境也在一個容器裏,你須要構建一個 Docker build 鏡像,而後在推送出去的時候,它也在容器裏面運行,而後它須要一個 privilege 權限。它這種方式有幾個特別好的地方,首先它不會對宿主機產生任何殘留,好比說你這個容器一銷燬,構建中產生的一些中間文件徹底都跟着銷燬了,可是你若是用 Jenkins 的話,隨着用的時間愈來愈久會沉澱出不少臨時文件,它佔的空間會愈來愈大。你須要按期作一些清理,並且清理過程當中你又不能直接一鍵清空,因此這是很麻煩的過程。


而後插件的管理 Jenkins 還有一個特別讓人頭疼的地方,就是它的插件升級。首先你在 Jenkins 登陸進去,而後就作插件升級。若是說我想臨時在一個新的環境裏面,起一個 Jenkins 先測試一下或作一些調試可能每新建一個環境,都須要把這些插件升級一次。並且剛纔咱們說的在 Jenkins 裏面作的全部配置,也都須要從新配置一遍,這是一個很是繁瑣的一個過程。

可是 Drone 這個工具它有一個特別好的地方,就是全部的插件都是 Docker 容器,好比說你在 pipeline 裏用這個插件,你只要聲明用這個插件就能夠了,你不用去本身管理把插件下載到哪裏,而後怎麼安裝,它這個一切都是全自動,只要說你網絡能把插件容器鏡像訪問到就能夠了,這很是便利。

而後關於生態的構建,Jenkins 的最大的優點就是它的插件很是多,就是你想用的各類東西都有,並且它基礎的底座很是好,你的插件能夠實現的能力很是的強。好比說 pipeline 就是這種方式,它從 1.0 到 2.0 雖然有了,可是它徹底是經過插件來實現的。可是如今 Jenkins 的發展又開始有點第二春的感受。他開始對 Kuberentes 的支持力度明顯的加大了不少,首先從 JenkinsX 開始,它融合了一些 Kuberentes 生態相關的一些工具,好比 Harbor、Helm 它能夠很是便利地在 Kuberentes 集羣上來作一些構建,而且把一些作服務的固化的一些編排文件放到 Helm 裏面。

另外,如今它有一個新的子項目叫 config as code,也就是說他把全部 Jenkin 裏面作了一些配置,均可以輸出成一個 code 的形式,就是對整個 Jenkins 的遷移,或者說複製都是一個很便利的改進。

講了那麼多,實際上最後咱們選擇的東西仍是 Jenkins,由於最重要的是生態的構建,他們已經很好了。今天咱們要講的在作一個彈性在 CI/CD 這個 Jenkins 上已經有這個插件了,可是在 Drone 的社區裏面,有人提這個事,可是如今尚未看到。github

CI/CD 工具的選擇

接下來,咱們看一下 CI/CD 這些工具的選擇和他們的發展。首先最老牌的確定是 Jenkins。實際上在容器技術興起以前,CI/CD 簡直約等於 Jenkins。可是在出現容器技術以後,不少新生 CI/CD 的工具也應運而生,好比說圖中 Drone 工具,它是一個徹底基於容器來實現的 CI/CD 工具。它與容器地結合地很是好,而且它的構建過程是徹底在容器中實現的。golang

2


第三個是 Gitlab CI,它主要特色是與 Gitlab 代碼管理工具結合地比較好。Jenkins 2.0 時開始引入 pipeline as code 特性,什麼叫 pipeline as code?pipeline as code 能夠幫咱們自動生成一個 jenkins file。在 Jenkins 1.0 時,若是咱們想要配置一條流水線,須要先登陸 Jenkins,而後建一個項目,而後在裏面寫一些 shell。這樣雖然能達到一樣的效果,可是它有一個最大的弊端,就是可複製性和遷移性很差。並且它與 Devops 有自然地割裂,好比通常是運維人員來管理 Jenkins 這個系統。開發人員編寫代碼,可是這個代碼怎麼去構建,發佈到哪裏,他是徹底不知道的,是運維人員在Jenkins 裏面配的。這個就形成了開發和運維地割裂。但 pipeline as code 這種方式出現了以後,咱們能夠把 jenkins file 跟代碼源碼放在一樣的倉庫裏面。



首先它有一個很是大的好處就是咱們發佈的流程也能夠歸入版本管理,這樣對一個錯誤就可追溯。這是一個很是大地改動,可是實際上咱們在與客戶溝通中發現,雖然不少人在 Jenkins 上都已經升到 2.0 系列了,可是它們的用法仍是徹底在 1.0 系列,不少用戶都沒有把 jenkins file 這種方式用起來。另一個就是對容器地支持,大概 2016 年左右,那時對容器的支持是很是弱的,在容器裏面運行 Jenkins,同時構建的產物也是 Doker 會很是麻煩。 


可是, Drone 對容器的支持力度就很是好。首先它徹底用 Doker 方式來運行,就是說你的構建環境也在一個容器裏,你須要構建一個 Doker build 鏡像,而後在推送出去的時候,它也在容器裏面運行,而後它須要一個 privilege 權限。它這種方式有幾個特別好的地方,首先它不會對宿主機產生任何殘留,好比說你這個容器一銷燬,構建中產生的一些中間文件徹底都跟着銷燬了,可是你若是用 Jenkins 的話,隨着用的時間愈來愈久會沉澱出不少臨時文件,它佔的空間會愈來愈大。你須要按期作一些清理,並且清理過程當中你又不能直接一鍵清空,因此這是很麻煩的過程。而後插件的管理 Jenkins 還有一個特別讓人頭疼的地方,就是它的插件升級。首先你在 Jenkins 登陸進去,而後就作插件升級。若是說我想臨時在一個新的環境裏面,起一個 Jenkins 先測試一下或作一些調試可能每新建一個環境,都須要把這些插件升級一次。並且剛纔咱們說的在 Jenkins 裏面作的全部配置,也都須要從新配置一遍,這是一個很是繁瑣的一個過程。


可是 Drone 這個工具它有一個特別好的地方,就是全部的插件都是 Doker 容器,好比說你在 pipeline 裏用這個插件,你只要聲明用這個插件就能夠了,你不用去本身管理把插件下載到哪裏,而後怎麼安裝,它這個一切都是全自動,只要說你網絡能把插件容器鏡像訪問到就能夠了,這很是便利。


而後關於生態的構建,Jenkins 的最大的優點就是它的插件很是多,就是你想用的各類東西都有,並且它基礎的底座很是好,你的插件能夠實現的能力很是的強。好比說 pipeline 就是這種方式,它從 1.0 到 2.0 雖然有了,可是它徹底是經過插件來實現的。可是如今 Jenkins 的發展又開始有點第二春的感受。他開始對 Kuberentes 的支持力度明顯的加大了不少,首先從 JenkinsX 開始,它融合了一些 Kuberentes 生態相關的一些工具,好比 Harbor、Helm 它能夠很是便利地在 Kuberentes 集羣上來作一些構建,而且把一些作服務的固化的一些編排文件放到 Helm 裏面。

另外,如今它有一個新的子項目叫 config as code,也就是說他把全部 Jenkin 裏面作了一些配置,均可以輸出成一個 code 的形式,就是對整個 Jenkins 的遷移,或者說複製都是一個很便利的改進。


講了那麼多,實際上最後咱們選擇的東西仍是 Jenkins,由於最重要的是生態的構建,他們已經很好了。今天咱們要講的在作一個彈性在 CI/CD 這個 Jenkins 上已經有這個插件了,可是在 Drone 的社區裏面,有人提這個事,可是如今尚未看到。
 web

CI/CD的系統業務場景

而後咱們看一下 CI/CD 它的系統的業務場景,它有一個比較典型的場景與特色,首先它面向開發人員,這是比較少見的,由於開發人員通常都比較挑剔一點。因此你要是這個系統作的不夠穩健了,或者說響應時間比較長一點的話,會被常常吐槽。docker

3


而後就是有時效性要求,由於咱們代碼寫完以後,往上提交,咱們都不但願在這個代碼的構建中一直排隊,咱們但願立刻就開始進行構建,而且資源足夠豐富。另一個就是它的資源佔用的波峯波谷是很是明顯的。就由於開發人員不可能時時刻刻都在提交代碼,有的人可能一天提交幾回,有的人會提交不少次。


由於我以前看過有一個分享,有一我的畫了一條反映自家公司構建任務的曲線。他們公司大概是天天下午3、四點的時候代碼提交量最高,其餘時間都比較平緩。這說明他們公司3、四點的時候,程序員提交代碼開始划水了。而後隨着 CI/CD 資源地需求愈來愈高,構建集羣是一個必需要作的一件事情。就是提升負載能力,縮短任務的排隊時間。固然真正的集羣有一個讓人以爲很不太好的地方,就是它的 Master 實際上只有一個,固然這個也能夠經過插件來作改進。




容器能夠給咱們 CI/CD 系統來注入新的能力,就是環境隔離的能力。咱們能夠經過 Kubernetes 來爲 CI/CD 系統注入更多的能力,而後矛盾點就出現了。開發人員老是但願 CI/CD 系統可以快速地響應代碼提交的一個事件,可是每一個公司資源都不多是無限的。由於就像上面提到的,若是天天下午3、四點的時候是一個代碼提交的高峯,這個時候可能須要 30 或 40 臺機器才能知足構建的任務。可是我不可能天天就開着 30 或 40 臺機器在這裏,就爲了天天下午3、四點,可能會構建1、兩個小時。

Kubernetes 能夠爲 jenkins 注入新的能力,讓 CI/CD 系統實現彈性的能力。咱們指望的目標是什麼呢?有構建任務的時候,能夠自動爲咱們資源增長新的機器也好,或增長新的運計算能力也好,反正就是當我須要的時候,能夠幫我自動地作一個資源擴張,可是同時也在我不須要的時候,能夠自動把這些資源釋放掉。shell

4


咱們指望的目標就是這樣,Kuberentes 就能夠爲 Jenkins 來作這樣的能力。Kuberentes 做爲一個容器編排的系統,它所能提供的能力,它能夠快速地彈出一些新的實例,而且把它們自動調度到空閒的機器上,作一個資源池,在資源池裏面作一個調度,而且他執行完任務以後,它能夠作一個回收。並且若是把這 Jenkins Master 也部署在 Kuberentes 之上,它能夠對 Master 作一個故障轉移,就是說若是咱們系統能夠容忍的話,Master 就算掛了,我能夠快速把它調到另一臺機器上,這個響應時間不會很長。後端

Kuberentes-plugin

這裏面也是用一個插件來實現,這個插件名字比較直接叫 Kuberentes-plugin,它這個插件所能提供的能力就是說,他直接管理一個 Kuberentes 集羣,它在 Jenkins 裏面安裝以後,它能夠監聽 Jenkins 的構建任務。有構建任務,在等待資源的時候,它就能夠向 Kuberenetes 去申請一個新的資源,申請一個新的 Pod 去作自動地構建完以後,它就會自動的清理。

5微信


先簡單介紹一下它的能力,由於這個插件安裝完以後,它對 pipeline 的語法也有一個改造,一會咱們來看一下實例。可是就算到了這一步,仍是不行的。首先,Kuberentes 的集羣規劃仍是一個問題。比說我有個集羣有 30 個節點,真正的 master 部署在這上面,而後裝了那些插件,作了一個管理以後,咱們能夠發現來了一個新的任務,它就起一個新的 Pod,把這個構建任務給執行制定完。


執行完以後,這 Pod 自動銷燬不佔集羣的資源。 平時咱們能夠在這集羣上作一些別的任務,但這個終究仍是有一點很差,就是咱們這個集羣到底規劃多大,而且這個集羣咱們平時不作構建任務的時候,能夠在上面作一些別的任務。可是若是正在作任務,忽然來了一些構建任務,它可能會出現資源的衝突問題。
 

Kubernetes Autoscaler

總的來講,仍是有一些不完美的地方,那麼咱們能夠利用 Kuberentes 一些比較沒那麼常見的特性,來解決咱們剛纔說的這個問題。這兩個東西一個是叫 Autoscaler,一個叫 Virtual node。咱們先看一下 Autoscaler,Autoscaler 是一個 Kubernetes 官方的一個組件。在 Kuberentes 的 group 下面支持三種能力:
 

  • Cluster Autoscaler,能夠對集羣節點作自動伸縮;
  • Vertical Pod Autoscaler,對集羣的 Pod 豎直方向的資源伸縮。由於 Kuberentes 自己裏面就帶着 HPA 能夠作水平方向的 Pod 伸縮、節點數量的伸縮;這個特性還不是生產可用的特性;
  • Addone Resizer,是 Kuberentes 上那些 addone 好比說 Ingress Controler、 DNS 能夠根據 Node 的數量來對資源的分配作調整。

Cluster autoscaler

我要講的是 Cluster autoscaler,是對集羣 node 節點數量作一個擴縮容。 首先咱們看一下,這個是在阿里雲咱們容器服務上所實現的 Autoscaler 的一個方式。咱們看一下這個圖,這個是 HPA 和 Autoscler 作結合使用的一個場景。
6


HPA 監聽監控的事件時發現資源使用率上升到必定程度了以後,HPA 會自動通知 workload,來彈出一個新的 Pod,彈出一個新的 Pod,可能這時候集羣資源就已經不夠了,因此這個 Pod 可能就會 pending 在這裏。它就會觸發 Autoscaler 的事件,Autoscaler 就會根據咱們以前配置好的 ESS 這個模板來去彈出來一臺新的 Node,而後自動地把 Node 加入到咱們集羣裏面。 它是利用了 ESS 定製的模板功能,而且它能夠支持多種的 Node 實例的類型,能夠支持普通實例,支持 gpu,還有搶佔式實例。
 

Virtual node

而後第二種是 Virtual node,Virtual node 實現方式是基於微軟開源的 Virtual Kubelet 這個項目。它作了一個虛擬的 Kubelet,而後向 Kubernetes 集羣裏面去註冊上面。但若是不太好理解的話,能夠想象一下 MySQL proxy ,而後他把本身假裝成一個MySQL server,而後後端可能管理着很是多的 MySQL server,而且它能夠幫你自動的作一些 SQL 查詢的路由或者拼接。
7


Virtual kubelet 作的也是相似的工做,就是說它自己向着 Kubernetes 註冊說我是一個節點,但實際上它後端管理的多是整個公有云上的很是多的資源,他可能對接公有云的一些 ECI 或者說對接的這些 VPC,這是一個它的大致的示意圖。

8


在阿里雲上他們對接的是阿里雲的 ECI 作一個彈性的容器實例,它響應時間就很是快,由於它不須要把 Node 去加入到集羣裏面,它是大概可以到一分鐘一百個 Pod 左右這種性能。並且咱們能夠在 Pod 上聲明這種資源的使用狀況,這是一個很是快的響應速度時間。

而後剛纔說咱們利用這兩種方式,就能夠對咱們 CI/CD 彈性的系統作出新的改造,咱們不用很是早規劃好咱們集羣的規模,咱們可讓集羣規模在須要的時候自動的作一些伸縮的動做。可是你作了這些動做以後,咱們作了這些把真正的放在容器裏面的這些動做以後,引入了一些新的門檻:docker-outside-of-docker 和 docker in docker。

9


咱們在 Docker 中運行 Jenkins 時,一般有兩種方式,一個是把宿主機的 docker.sock 掛載到容器裏面,讓 Jenkins 經過這個文件來和本機的 docker daemon 作一個通訊,而後它在這裏面作 docker build 構建鏡像,或者把這些鏡像 push 到遠程的倉庫裏面,因此它中間產生的全部鏡像都會堆積在本機上,這是一個問題。

在一些 serverless 的那些場景上使用,它就會有一些限制,由於 serverless 自己就不容許把 socket 文件給掛在進去。另一個就是 docker in docker 這種方式,它的優勢就在於在容器裏面它啓動一個新的 Docker daemon,它全部的中間產物、構建產物隨着容器都一塊兒銷燬,可是它有問題,它就是須要 privilege 的權限。


不少時候咱們是儘可能不要用它。另一個就是說你作 docker build 的時候能在宿主機上作的時候,它若是有已經有鏡像了,它會直接就使用這個鏡像,可是你用 docker in docker 這種方式來使用的,它每次都會從新拉進項,拉鏡像也是須要必定時間,這個取決於咱們各個使用場景來判斷。

新的構建工具——Kaniko

這時又引入了一個谷歌開源的新工具——Kaniko。它作的東西是 docker in docker 的方式。它有一個很是大的好處就是不依賴 Docker,並且因此它不須要 privilege 權限就能夠在容器裏面用用戶態的模式,來徹底構建 docker image。用戶態執行 Dockerfile 的命令,它把這個鏡像徹底構建出來。

10


這算是一個比較指望的彈性的 CI/CD 系統。而後這個時候就是說從真正的節點到底層的計算資源所有是彈性擴縮的,並且知足交付的需求,能夠很是精細化地管理咱們的資源。

Demo 演示

而後咱們能夠看一下 Demo 演示:
https://github.com/hymian/webdemo 這裏是我準備好的一個例子,重點在這個 Jenkinsfile 文件,裏面定義了agent 的 pod template,包含兩個容器,一個用來作 golang 的 build,一個用來作 image 的 build。

而後咱們如今構建它。開始構建了,剛開始的,由於是如今咱們在這環境裏面只有一個,只有一個 master,因此他就是沒有不會有構建節點。你們能夠看到,它如今新啓動了一個 Pod,這個 Pod 是做爲節點加進來的,可是由於我在這個 Pod 模板裏面定義了一個 label,因此它沒有這個節點,因此它 Pod 狀態是 pending 的。因此咱們在構建日誌裏面顯示的這個是 agent 節點是離線的。 

可是咱們在這個集羣裏面定義了一個彈性伸縮的一個東西,當沒有節點的時候,它會自動作一個新節點分配加入,能夠看到有一個節點正在加入,這個我就能夠稍等一下。就是說這段時間可能會有個一分鐘兩分鐘的時間。




這個是異常,是由於這個節點正在向集羣加入,因此它顯示是異常,這是咱們從命令行看一下,好,已是四個節點了,加了一個節點,這時候咱們看 Pod,這時候在 agent 正在建立,這時候你們可能有一個小的細節,你們能夠看一下,就是 0/3 是顯示 Pod,它有三個容器,可是我剛纔在這個裏面定義的,它其實是 Pod 裏面只有兩個容器,這就是咱們剛纔 PPT 上寫的一個地方。 

JNLP 那個容器,是 plugin 自動注入的一個容器,它經過這個容器實時的向 master 彙報構建的一箇中間的狀態,我把它的日誌給發送出去。這個是 agent 的節點在初始化的一個過程一個事情這時候 slave節點已經在運行了。我這邊已經輸出完了,構建完成。個人分享內容就這些,謝謝你們。

 

原文連接 更多技術乾貨 請關注阿里云云棲社區微信號 :yunqiinsight

相關文章
相關標籤/搜索