BizCloud:基於Kubernetes的私有雲實踐
隨著搜狗業務的快速增長,需要更有效地控制成本,提升研發效率,我們基於Docker和Kubernetes構建了一站式私有雲管理平台——BizCloud,此平台涵蓋服務管理、彈性伸縮、灰度發布、自動運維、持續集成等功能。本文將簡要介紹BizCloud的設計思路、架構及服務發現、授權、灰度發布等核心功能的實現。
BizCloud簡介
我們基礎環境非常複雜,目前有多個版本操作系統共存,應用也常常存在著多個版本同時測試或部署,在多版本並行測試過程中,經常出現環境借用的情況,因操作系統和基礎軟體不一致的問題,會出現線下測試沒問題,但上線後出問題;其次,線上的實體機在業務低峰時使用率較低,存在較大的提升空間;服務上下線也涉及到一系列機器的申請回收流程,需要手工執行,系統的彈性伸縮能力不足。這種種問題都是我們設計私有雲的原因,也就是要做到保持環境一致、提升資源利用率,並提升彈性計算能力。
為了解決上述問題,我們使用目前非常流行的容器技術Docker和容器編排工具Kubernetes,研發了商業雲平台BizCloud。但Docker和Kubernenets只提供了一個基礎功能:容器運行和容器編排,如何能快速地學會使用,並為大家所接受才是關鍵。
在自研BizCloud過程中,一個重點就是要對接、打通現存的系統和流程,盡量保持用戶操作習慣,服務在容器化的過程中,儘可能不需要調整,這樣系統才易於推廣落地;另一方面,我們要支持服務一鍵自動部署(QA特別需要這樣的功能),服務出現故障後,如系統宕機或服務掛掉後,服務能自動遷移,而且我們需要支持灰度發布,盡量實現運維的自動化。
商業平台系統的整體架構如下圖所示。共分為三層,IaaS層、PaaS層、SaaS層:在IaaS層,我們使用Docker+Kubernetes封裝了部門的基礎資源,提供容器化服務;PaaS層提供了很多基礎服務和基礎框架,並且也實現了一些自動化工具,包括剛才提到過的統一服務管理中心,統一配置中心,項目管理系統、SOA服務框架等,貫穿了應用開發、測試、運維整個生命周期的一體化平台,其中的紅色部分,包括服務管理、編譯中心、商業雲平台都是為BizCloud而新開發的模塊;第三層SaaS主要是一些商業平台業務系統。本文的分享也主要集中在PaaS層的研發實踐上。
關鍵功能解析
對於一個服務而言,不管是部署還是故障遷移,都有很重要的兩個功能:在服務啟動之前,要申請服務所需資源的許可權,如資料庫許可權,開通iptables等,也就是服務授權;而服務啟動之後,要暴露服務給使用方,現在服務部署到某個機器上了,需要將請求發送到這台機器上,這個過程是服務發現。以往這兩個工作主要是人工執行,在BizCloud中,這兩個過程需要自動化執行。
自動化執行這個過程需要解決3個問題:1) when,什麼時候執行;2)who,誰來執行;3)how,怎麼執行。為實現服務版本的平滑過渡,BizCloud也提供了灰度發布功能,以降低上線風險;灰度發布不是一個獨立的系統,但和系統的架構是緊密相連的。下面我們分別介紹服務發現,服務授權和灰度發布的實現要點。
服務發現
首先是when,即服務發現和服務授權的發起時間,我們知道在Kubernetes服務里,服務狀態的變化和Pod的變化是緊密相連的,因此,我們引入了一個模塊k8s-monitor,用來監控並判斷發起動作。
k8s-monitor是BizCloud的監控器,其主要功能是監控Kubernetes集群中的Pods的狀態事件:ADD、MODIFY、DELETE,監控到事件後,k8s-monitor計算是否需要進行服務發現或服務授權等相關處理,如果需要,則通知下游系統進行處理。除了常規的事件監控之外,k8s-monitor還會定期與服務管理模塊同步數據,清理服務管理模塊上可能存在的臟數據。
使用k8s-monitor這樣一個單獨的模塊,好處是顯而易見的:將相關許可權集中管理起來,避免雲平台入侵應用,如果沒有這樣一個模塊,每個應用都需要增加自己的的服務授權和服務發現的功能,對模塊入侵較大;其次,這個模塊非常容易擴展,其他服務也可以訂閱這個模塊的數據,實現自己的處理邏輯。
通常一個典型的服務都有兩層:一個用戶接入層,通常是用Nginx接入用戶流量,Nginx將流量分發到後面的Web伺服器上;第二層SOA層,這裡Web服務通過SOA調用後端服務,後端服務也可繼續調用其他服務,最終將用戶請求返回。在做服務發現時,需要完成這兩種類型的服務發現。
首先,對於接入層,如圖所示,如果Pod1因故障掛掉,Kubernetes重新調度了PodN之後,k8s-monitor監控到(至少)2個事件:Pod1 DEL,PodN ADD,k8s-monitor將事件通知到服務管理中心。Nginx會實時從服務管理中心獲取服務對應關係,動態載入Nginx配置,將已經掛掉的Pod1從Nginx中摘除,新增加的PodN暴露給外部。
而SOA服務的角色分為兩種,一種是consumer,一種是provider。consumer和provider之間的負載均衡、白名單控制是通過SOA的註冊中心來統一管理。像圖裡展示的,如果Pod1因故障掛掉,Kubernetes重新調度了PodN之後,k8s-monitor將監控到的事件Pod1 DEL,PodN ADD通知到SOA註冊中心,SOA註冊中心會將對應的變化更新到ZK上,ZK會觸發事件通知服務的consumer獲取最新的服務provider。
授權
由於商業平台的特殊性,對許可權控制非常嚴格,許可權控制的重要性在於:1)防止測試流量打到線上; 2)防止惡意訪問等。以前模式往往是人工檢驗進行授權,但在雲平台上,這種方式不再適用,Kubernetes也沒有直接提供授權的功能,而且授權是和系統架構緊密相關的。
為滿足BizCloud的需求,對服務授權進行了改造,當時改造面臨了一些挑戰:首先服務依賴關係從何獲取;其次,容器可能隨時啟動、銷毀,服務IP會隨容器變動;再次,我們需要同時支持DB授權、IP白名單授權、SOA等不同粒度的授權。
服務授權的發起仍然依賴於k8s-monitor,與服務發現類似,k8s-monitor將監控到的事件Pod1 DEL,PodN ADD,包括一些其他服務基本信息、IP等通知到授權模塊,授權模塊開始執行授權工作。
剛才說明了授權的時間,但具體給誰來授權呢?如下圖所示,每一個服務都有自己的服務配置信息,這裡展示一個服務,該服務依賴了很多資源,包Redis、資料庫(1個主庫、2個從庫)等資源。將這些配置文件上傳到配置中心後,配置中心會將這些配置解析,然後根據這些依賴關係計算出依賴圖,如右圖所示。新啟動的服務可以從配置中心獲取自己的資源依賴關係,要申請的資源。
對於不同類型的授權,我們有不同的處理方式,每種授權對應的粒度也不同:1)DB授權,授權信息包括ip+port+user,通過資料庫執行機來執行,將授權信息寫入資料庫許可權表;2)SOA授權,這裡授權信息包括服務實例+ip+port,該類授權通過SOA註冊中心執行,最終生成服務訪問白名單;3)iptable授權,授權信息包括ip+port,這類授權通過salt-stack執行,最終會寫入系統的iptables文件。
灰度發布
我們的灰度發布的周期一般比較長,為了保證灰度的一致性,我們會將上下游依賴的服務分組,一個正常組,一個灰度組。在具體執行時,我們會多建一個灰度的Deployment,這樣每個服務有2組Deployment,一個正常Deployment、一個灰度Deployment,然後根據灰度比例動態調節正常Deployment和灰度Deployment的實例數,從而實現灰度發布。在流量接入方面,我們已經實現了基於用戶ID的灰度,在BizCloud上,我們的灰度分流仍然基於用戶ID。
灰度發布時的服務發現同樣包括接入層和SOA層兩部分:對於接入層,我們使用了OpenResty,並引入了ngx_dynamic_upstream模塊,這樣可以通過HTTP API方式動態調整服務的Upstream;在SOA層的灰度發布中,我們對consumer-provider劃分為了可以動態更新的兩個組:正常組和灰度組,k8s-monitor將變更發送到SOA註冊中心後,SOA註冊中心還是通過ZK通知Consumer取最新的provider分組,從而實現灰度分流。
配套工具
為了更方便地使用BizCloud,我們提供了多個配套工具。
WebShell
首先是WebShell。通過WebShell,我們可以從Web瀏覽器以類似SSH的方式登錄並操作Docker容器,方便開發運維等查看、調試系統。
Webshell主要有3個組件:1)Web瀏覽器負責界面呈現;2)Docker Controller是Docker容器應用的控制中心,作為橋樑,負責消息的轉發;3)Docker Daemon提供HTTP API介面給外部系統調用以訪問容器內部。
Web瀏覽器運行JS腳本,通過Web Socket與Docker Controller建立通信鏈路。Docker Controller通過Docker HTTP API與Docker Daemon建立通信鏈路。這裡使用到Docker HTTP API的介面,利用返回的數據流承載Docker Controller和Docker Daemon之間的交互數據。鏈路建立後,用戶就可以在Web瀏覽器輸入字元與Docker容器交互。
模板生成
我們還提供了模塊自動生成的功能,這樣開發只需要關注自己的服務即可,不需要重複編寫發布等一系列Kubernetes部署文件。對於一個服務,服務部署模板是提供了一類模板的集合,包括Deployment、Namespace、ConfigMaps,這些模板都是可參數化的。在具體部署服務時,也就是實例化一個服務,我們先查找服務實例的部署環境具體的部署參數,然後將這些參數注入到模板中,生成具體的模板文件,然後發布。
相關係統
雲平台其他模塊在這裡做了一個簡單的展示,在左上角展示了一個應用正在運行的容器,並可以點擊控制台登錄到容器內部;右上角是統一配置中心,可以查看、操作引用對應的配置;左下角是統一服務管理平台,上面羅列了現在線上存在的一些服務信息;右下角是統一部署中心,在上面可以查看服務部署的情況,包括服務授權信息、服務發現信息等等。
小結
在本次分享中,我們對搜狗商業平台部的私有雲BizCloud的來龍去脈做了一個簡要介紹,然後介紹了在實現商業雲平台中的關鍵機制,包括授權、服務發現、灰度發布的實現,也介紹了一些相關配套工具如WebShell等。使用BizCloud後, 對於dev而言流程基本不變,QA在搭建測試環境時,能一鍵部署相關服務,方便了很多,但對ops,在BizCloud上配置好應用的資源需求後,即可部署系統,一鍵實現服務擴縮容(包括橫向擴展,增加或減少服務實例數,也可以橫向擴展,增加服務的CPU和內存數),自動進行服務發現和授權,避免了大量重複性手工操作。
目前我們正在進行一些有狀態服務的容器化工作,如Redis、MySQL的容器化。因為我們對數據的準確性和穩定性要求非常高,所以將資料庫的容器化是非常謹慎的,我們希望隨著容器技術不斷發展,在未來也能順利實現基礎資源的容器化。更智能的調度:我們希望能實現一個資源負載可預測的調度演算法,能結合應用的CPU、內存、磁碟、網路IO、DISKIO、DBIO等歷史數據指標進行綜合計算,給出多維度下的基於時間片、優先順序的準確實時的負載預測,來執行更智能的調度。
Q&A
Q:有狀態服務和無狀態服務的主要區別是哪些?
A:有狀態服務,是指伺服器端具備上下文關係,如Redis服務,當服務掛掉之後,Redis的內存數據丟失,我們不能簡單地在另一個機器上拉起服務來恢復服務,必須同時恢複數據(狀態),而無狀態服務沒有狀態(數據)依賴。
Q:通過模板的方式,會不會影響靈活性啊?因為大多數配置都是基本固定的。
A:這個會犧牲一定的靈活性,但是會提升發布的安全性,並且達到對上層應用無感知。目前我們也會針對不同的應用類型定製不同的模板,如無狀態服務、有狀態服務的模板是不一樣的。通過模板可以滿足支持絕大多數服務需求,對於特定服務,我們也預留了特殊配置介面。後續我們也會嘗試引入Helm Chart單元化模板。
Q:event存在丟失的現象,請問如何處理?我覺得過度依靠event會造成很緊的偶合。
A:我們watch event 的時候使用了ResourceVersion,不會出現丟事件的情況,如果watch提示ResourceVersion過早,我們會先List Pod,和服務管理模塊做一次同步,清理臟數據。
出現過收到不完整event的情況,因為最初給Kubernetes加上Nginx代理時,Nginx默認開啟了proxy_buffering,會收到不完整事件情況,關閉proxy_buffering解決這個問題。
Q:能否講下「Nginx會實時從服務管理中心獲取服務對應關係」的原理是什麼?
A:我們在OpenResty基礎上,通過Lua腳本從"服務管理中心"處查詢和訂閱服務對應關係,實時修改Nginx配置,進行負載均衡、服務發現。
Q:請問你們Docker是安裝在物理機還是?
A:Docker是安裝在物理機上,而且Docker安裝啟動是需要root許可權的。
Q:網路問題是如何處理的?沒有使用自帶的service嗎?
A:網路問題是指我們的網路模式么?我們的容器分配的是一個內網IP,對外部服務是可見的,如果發生了節點網路問題,在服務化這層本身會自動摘除這個節點,在調度層面一定超時後也會自動重新調度到另外一個節點上。我們沒有採用Kubernetes的service,原因是因為在早期Kubernetes的調研中,service存在iptables條數過多導致性能下降問題,也擔心service不穩定造成服務訪問問題。
Q:請問下你們服務動態擴容是全自動化的么?
A:目前服務動態擴容是一鍵化的,不過我們也預留了一些API,來實現全自動化,大致的方案是:雲平台會對接一個統一監控中心,通過統一監控中心的實時監控數據(系統數據,流量數據),分析服務的訪問壓力,來實時擴縮容。
Q:問下你們程序包分發如何實現的,程序包放鏡像內還是鏡像外?
A:我們開發了自己的CI模塊,可以一鍵從SVN中生程序包,然後成鏡像,編譯生成鏡像的時候會從統一服務管理模塊中獲取必要的服務信息。程序包放在鏡像內。
Q:請問你們Docker是基於CentOS製作的鏡像嗎?有什麼優化地方?
A:是基於CentOS 7.2製作的鏡像,我們在鏡像中做了一些嚴格的許可權控制,限制了應用的一些行為。
Q:WebShell的話,是每個節點都有Agent的吧,還是通過Kubernetes原生提供的功能呢?
A:不需要每個節點都有Agent,我們只要實現Docker Controller 即可,Docker Controller 會去Docker Daemon上獲取容器的具體信息。
Q:你們資料庫集群是用的哪種方案呢,能介紹下嗎?
A:無狀態服務的話,就是使用hostPath本地掛載,日誌數據我們會有實時採集的服務。有狀態服務如資料庫,我們目前還沒有在生產環境進行大規模的資料庫容器化。我們的基本思路是定製化CRD + Ceph分散式存儲。也希望後面隨著資料庫容器化工作不斷推進,能夠和大家有更深入的交流。
※國慶提前大放價!79 元秒殺 CNCF KEUC 直通票,僅有50席
※Intel Clear Containers 3.0發布
※Kubernetes中的Pod的到底是什麼?
※Kubernetes是什麼?
※如何開發部署Kubernetes Native應用
TAG:Docker |