基於Kubernetes的私有容器雲建設實踐
本次分享為大家介紹易寶支付私有容器雲從0到1的建設之路。包括技術選型、理論基礎、基於Kubernetes的容器雲和CI/CD落地過程中的挑戰和踩過的坑。
建設背景及目標
在Docker技術流行開來之前,保證軟體交付的質量和速度對於大多數企業來說都是困難的。業務的複雜性帶來了應用的複雜性,面對成千上萬的不同應用,運維部門需要時刻應對來自不同應用、不同環境的挑戰。特別是在自動化運維程度不高的企業,“人肉運維”成了解決問題的常用手段,人肉運維使軟體交付的週期變得漫長、人為事故風險升高。 2013年,Docker橫空出世,它的”Build once, Run anywhere”的特性讓軟體交付煥然一新。我們在認真調研了Docker技術後,決定構建自己的私有容器雲,背景和目標如下:
實現運維自動化是我們立項之初最主要的目標,而它又是實現後面目標的基礎。這個因素直接決定了我們的技術選型。
技術選型
我們是在2015年6月份開始調研技術,2015年8月份開始容器雲立項,首先要面對的問題,就是如何進行容器編排引擎的選型,可供選擇的有Swarm,Mesos,Kubernetes,甚至自主研發叢集編排,我們認真調研了每一種方案:
Swarm當時是0.4版本,功能還相對簡單,優勢是技術棧比較簡單,小團隊即可駕馭,但是考慮到它不是穩定版,雖然它發展很快,但是沒有解決我們現有的問題,所以Swarm不被優先考慮。
Mesos當時是0.23版本,它能夠勝任大規模場景的容器編排,偏重於資源抽象,與我們大多數是Java Web的應用的場景不符,另外,Mesos技術棧與我們現有技術棧差別太大,不得不放棄這個選擇。
自主研發容器編排引擎我們也考慮過,但是經過認真的探討,自研編排引擎對標三個開源的元件的功能,研發投入需要很多的成本,可能結果並不能達到預期,投入產出比低。另外,容器雲作為底層的基礎設施,選擇更要慎重,如果自研專案失敗,可能會離主流的容器技術越來越遠,機會成本太高,所以自研的路線也被否定。
Kubernetes是我們的最終選擇,它當時是1.0.2版本,已經是”Production Ready”,我們選擇Kubernetes的最主要的原因是它理念的先進,而且非常適合我們公司的主流應用,Java Web應用都是Long time running的任務,Kubernetes的”Replication controller”對它支援非常好。Kubernetes以應用為中心的理念和社群的活躍度更是堅定了我們的選擇,歷時三個月的技術選型終於落下帷幕,我們決定使用Kubernetes構建我們的私有容器雲平臺。
理論基礎和原則
在我們決定使用Kubernetes的作為容器編排引擎後,關於選型的爭論持續了很長的一段時間,當時國內Kubernetes的使用者還比較少,很難找到成功的案例。我們需要深入的研究Docker, Kubernetes相關的容器技術,確保我們的決策是正確的,這對我們構建容器雲至關重要。經過很多的調研和討論,我們發現容器雲的是有一套完成的理論基礎支撐的,這些理論又引申出我們構建容器雲的原則:
- 不可變基礎設施,是利用Docker映象的不可變性,以更加便捷的方式維護基礎設施:當基礎設施損壞或者變更時,以直接替換的方式達到目的,而不是通過修繕損壞的基礎設施,這麼做需要替換的成本足夠低,Docker顯然做到了這一點;對於已經執行的Docker容器,如果它出現異常,不再是傳統ssh上去除錯的方式,應該是殺掉這個容器,重新啟動一個新的容器;替換操作具有快速和可重複的特性,任何操作都可以隨時回滾,安全可靠;對於生產環境的運維,不可變基礎設施的理念尤為重要,很多事故都是在生產環境中直接修改造成的。
- 基礎設施即程式碼,管理基礎設施像管理程式碼一樣,每個基礎設施都是“可描述”的,例如Kubernetes中的Node概念,他們也應該作為“程式碼”的一部分以程式碼的方式進行管理。
- 可程式設計的基礎設施,基礎設施不僅僅是提供計算、儲存、網路資源,還要為上層應用提供可程式設計的介面,讓上層應用可以更加靈活的使用基礎設施,容器雲從立項之初就考慮到了這一點,容器雲平臺有一套完整的對外Restful API,可供上層應用,甚至外部應用呼叫。
保證構建容器雲的過程能夠正確的進行,還需要一些原則,”Build once,Run anywhere”,一個Docker映象要貫穿QA到生產環境的每個環節,不允許QA和生產的映象出現不一致的情況。”All in one”,對於Java Web應用,由於歷史原因,可能多個Web App執行在同一個Tomcat中,要求每個Docker映象中只執行一個Web App。
以應用為中心,是我們最重要的原則,也是建設容器雲的出發點,這個原則確保我們關注的重點是應用,而不是進行計算資源的抽象和資源的排程,我們的理想目標是,在“優雅地“管理應用的整個生命週期同時,順便做好資源抽象,提高資源的利用率。
分層治理,基礎設施的治理由容器雲完成,上層應用的治理由應用治理層負責,從SaaS,到PaaS,再到CaaS,分層治理,各層通過介面相互呼叫,層與層之間互不侵入。
以Kubernetes為中心構建容器雲
容器雲的目標決定了我們面對的是應用的管理,即應用對應的Docker容器的管理,這就要求我們要以Kubernetes為中心構建容器雲,而不是以Docker為中心。Docker只作為應用打包、傳遞、執行時的工具,所有的API都要面向Kubernetes進行設計。
容器雲要實現高可用的基礎設施,能夠支援多個數據中心。對於應用,要有多維度的高可用保證,要貫通部署流水線,通過CI/CD實現快速交付,另外,容器雲的建設肩負的額外目標是要為未來2~4年的技術發展做鋪墊,為應用的CloudNative改造和整個技術團隊的DevOps實踐奠定基礎。
容器雲第一步是實現應用的全生命週期管理,讓應用實現秒級的上線、回滾、升級、擴容/縮容、下線。由於歷史的原因,有些應用的配置和環境耦合在一起,有的應用是對於外部依賴是硬編碼(例如服務方的IP地址)等,這些應用在遷移至容器雲之前需要進行改造。
容器雲要實現多資料中心多活,以保證資料中心級的高可用性。對於彈性擴容,我們的計劃是先實現手動擴容,再實現自動擴容; 對於自動擴容,先實現基於CPU/Memory的自動擴容,再實現基於Custom Metrics的自動擴容。與大多數構建容器雲的方式不同,我們首先解決生產環境的運維自動化的問題,其次再解決容器的構建問題(即CI/CD)。我們的網路選型是flannel,萬兆網路,flannel雖說有效能損失,但遠能滿足我們的實際需要。儲存我們使用Ceph的RBD方式,使用一年多來,RBD的方案非常穩定。Ceph FS的方式我們也有嘗試,但是由於團隊精力有限和可能的風險,一直沒有正式使用。
高可用基礎設施
容器雲要實現高可用的基礎設施,多維度保證應用/服務的高可用性:
在應用層面,每個應用有至少3個副本,通過Kubernetes ReplicationController/ReplicaSets來保證。強制每個應用暴露健康檢查介面,通過設定liveness和readness保證應用異常後能夠被及時的發現,從而用新的例項代替。
Kubernetes的元件也要實現高可用,特別是ETCD叢集的高可用,定期備份ETCD的資料是個好習慣。
為了保證資料中心級別的高可用,我們在每個資料中心部署了一套Kubernetes叢集,每個資料中心能夠獨立存活,多個數據中心互相災備。
計算資源QoS與超賣
由於資源限制,技術人員往往過於關注單機的資源利用率。Docker(Cgroup、Namespace)提供的資源共享與隔離的機制,讓我們對資源利用率有了新的認識,特別是使用容器編排引擎後,我們對資源的理解應該在叢集維度進行考量,而不是在考慮單機的利用率。同樣,在整個資料中心,甚至多個數據中心進行資源利用率的綜合考量也是非常必要的。
在提高資源利用率、降低成本的同時,需要在服務的QoS與優化資源利用率之間有個平衡。我們的原則是在保證服務質量的同時,儘量提高資源的利用率。
根據Kubernetes的資源模型,在Pod level的QoS分為三個等級:Guarantee、Burstable、BestEffort,我們也是依照這三個級別對應我們應用的優先順序來制定資源超賣的標準。
我們對應用設定的QoS標準:
- Kubernetes自帶的元件使用Guarantee
- 重要的元件和應用,比如ZooKeeper、Redis,使用者服務等使用Guarantee
- 普通的應用(Burstable)按照重要性分級,按重要程度CPU分為2,5,10三個超賣標準,10倍超賣適合boss後臺類的應用,大多數適合訪問量不高。記憶體使用固定的1.5倍超賣標準。
有一點需要特別注意,在生產環境中,不要使用BestEffort的方式,它會引發不確定的行為。
容器雲管理平臺
隨著越來越多的應用遷移到容器雲中,需要建立一個視覺化的管理系統,我們使用Kubernetes原生API搭建一套Web管理系統,通過對Namespace/ResourceQuota/Deployment/Service/Endpoint等API的呼叫實現資源配額的劃分和應用生命週期的管理。
容器雲平臺在易用性方面最大的挑戰是Troubleshooting的環節,容器雲最終是要交付開發人員使用,他們對Kubernetes並不瞭解,這讓Troubleshooting的環節充滿挑戰,我們現在只是想通過websocket將kubectl exec的console展示給使用者,或者讓使用者在日誌中心(EFK)中檢視日誌,還沒有更好的方案,如果各位有更好的方案,請不吝賜教。
容器雲未來要實現整個資料中心的視覺化,讓運維對所有的資料中心的實時執行情況一目瞭然,當然,實現這一目標有相當的難度。
容器雲的監控採用Heapster的方案,正在向Prometheus方式轉變。
日誌收集是主流的EFK的組合方式。
容器雲管理系統的基本功能如下圖所示:
日誌收集方案如下圖所示:
我們為Java應用提供了一個公共日誌元件——Appenders,它會將Java的日誌流式輸出到Fluentd中轉,輸出到Fluentd中轉的原因是與現有的日誌中心並行執行。其他的部分跟主流的EFK模式沒有任何區別。使用DaemonSet執行Fluentd和Fluentd與應用以Sidecar的方式進行日誌採集也是比較好的選擇。
在容器時代,CloudNative應用是必然的選擇,構建雲原生應用的原則請參考12因子。
容器雲管理系統自身也是CloudNative應用,它同樣執行在Kubernetes中,與傳統的上線工具不同的是,它能夠進行自我生命週期管理。
Container based、Mircoservices Oriented是Cloud Native倡導,只有應用向Cloud Native轉化,才能更好的發揮容器雲的效力。
CI/CD建設
按照我們預先的Roadmap,先解放生產環境的運維工作,再解決應用的構建、整合的問題。現在,容器雲的管理系統基本上替代了日常維護的手工操作,頻繁的手工觸發構建成了容器雲推進的瓶頸,所以,構建CI/CD平臺變得非常緊迫。
經過前期調研,我們決定使用Gitlab + Jenkins + Docker Registry的技術棧構建CI/CD平臺。為了統一技術標準和儘量減少構建過程中的不確定性,我們採用自動生成Dockerfile的方式,而不是讓開發自己編寫Dockerfile。我們採用穩定主幹的方式,MR自動觸發構建過程,經過單元測試,打包,編譯和Docker構建,容器雲的介面會實時顯示構建的過程,在構建結束後,使用者會收到構建的結果的郵件。最終,CI產出的Docker映象會被推送至QA環境的Registry上。
對我們來說,CI/CD最重要和最難的環節是自動化測試,尤其是自動化整合測試,我們正在努力解決。
CI的過程我們還做了程式碼的依賴庫檢查,程式碼版本追蹤和Docker映象自描述等,讓Docker映象從產生開始,在測試,生產測試,生產等每個環節都是可追溯的。這樣便於我們查詢問題和對CI的過程進行持續的改進。
對常用技術棧和配置進行標準化也是CI建設的一個重要目標。保證CI產出的映象的質量(類似次品率)是對CI系統考核的重要標準。
下圖是我們CI/CD平臺的工作流示意圖:
下圖展示了整個部署流水線,映象從構建到生產部署的全過程,以及過程、結果的反饋:
遇到的問題和挑戰
到目前為止,回顧整個容器雲的構建過程,來自技術上的挑戰並不多,但是也踩了一些坑。
遇到過RBD盤被鎖住,新產生的Pod無法掛載的情形,解決辦法是將RBD盤手工解鎖,新的Pod會自動掛載。
Kubernetes的一個Bug,Kubernetes的ReplicaSets名稱是根據Deployment的PodTemplate的摘要產生,使用的Adler演算法,Hash碰撞非常頻繁,會在升級過程中,Deployment不能建立最新的ReplicaSets而造成升級失敗。解決辦法是講adler演算法換成FNV演算法,來減少Hash碰撞的頻率,這顯然不是最終的解決方案,最終的方案還在持續討論中,有興趣的朋友可以參與:https://github.com/kubernetes/community/pull/384,https://github.com/kubernetes/kubernetes/issues/29735。
由於一直沒來得及遷移Harbor,我們一直直接使用Docker registry 2.1版本作為私有映象倉庫,使用Restful API時,_catalog預設返回字母序的前100個映象,客戶端需要處理分頁的問題。
應用向容器雲遷移是容器雲建設過程中花費最多精力的地方,由於需要適應容器雲背後的理念轉變和對現有應用改造進行改造,遷移過程中受到了很多挑戰,最大的挑戰是Dubbo應用的遷移問題,由於Flannel的Overlay網路使容器化的Dubbo應用不能與Overlay網路之外的應用連通,最後我們修改了網路策略,使得Dubbo的應用可以無縫的遷移到容器雲中。
下一階段容器雲工作的重點,是推動應用向Cloud Native和微服務化方向改造。
容器雲面臨的最大挑戰來自於理念轉變,容器技術改變了軟體交付的生態,容器時代需要技術人員以新的理念構建應用,如何讓技術人員順利的完成理念的轉變是每個容器雲的建設者們需要認真考慮的問題。
Q&A
Q:請教一下處理CI時,比如叢集自動化部署方面的粒度是怎樣的?比如修復一個bug改了一個class檔案,然後本地測試完之後需要到線上部署進AB測試,那麼就直接通過CI自動部署到叢集伺服器嗎?
A:我們的做法是隻要有修改就觸發重新構建,這隻適合我們公司的情況,您可以根據自己的情況做出粒度選擇。
Q:自動生成Dockerfile的目的是什麼?這樣做有什麼優勢?
A:這個問題有兩個方面,第一個是標準化規範化的問題,我們的應用大多是Java Web,相似度很高,所以可以自動生成,沒有什麼特殊需要開發自己寫的;另外,我們在CI平臺裡,留出了編輯Docker的口子,也可以針對特殊的情況自己編寫,但是這種是非常少數的情況。CI跟每個企業的業務情況緊密相關,還是具體情況具體分析吧。
Q:我起了一些Pod,對外有Service,然後我想讓Pod實現單任務,但問題是,Service對Pod選擇機制是隨機的,也就是說有可能會出現多個任務請求到一個Pod上,達不到我的要求,怎麼解決?
A:這個問題我個人的理解,您要解決的問題跟一個Service對應多個Pod的場景不太吻合,我建議您考慮其他的方式實現,比如多個sevice-pod的組合等等,或者考慮其他的方式。
Q:「Kubernetes master 高可用」如何設計?多個數據中間是stand-by關係?
A:API Server是無狀態的,可以部署多個,前端負載均衡,Scheduler/ControllerManager有狀態可以做成主備。Kubernetes還算穩定(當然我們的量小)。
Q:貴司使用的Kubernetes版本是?RBD鎖死的問題只能通過手動解鎖來解決嗎?有其他方案嗎?
A:我們上線比較早,生產系統還是1.2版本,我們正在升級1.6版本。RBD我只嘗試了手動解鎖的方法,別的方法沒有嘗試。
Q:想問下關於你們Kubernetes分散式儲存的選擇,以及在使用當中遇到了那些問題?
A:我們應用不掛盤,所以使用Ceph的場景不多。使用RBD沒遇到什麼問題,有些場景我們需要共享儲存(Filesystem),因為我們人手有限,沒精力嘗試Ceph FS或者其他方式,這算個問題吧。
Q:在EFK的架構中有Kafka的存在,目的何在?是保證日誌不丟失,還是提高吞吐量?
A:主要是做Buffering緩衝,我們這個裡還有個別日誌需要中間處理的過程,從Kafka取出加工,再放入Kafka,最後到Elasticsearch。
Q:能否詳細介紹下CI系統考核的重要標準:對常用技術棧和配置進行標準化。具體對哪些指標做了標準化?技術方面如何實現的?
A:對於Java應用,我們只提供JDK 7和JDK 8,規定日誌目錄的位置,提供標準的Log4j,配置與程式碼分離,war包與環境不管等等強制的要求。
Q:Docker Registry的映象複製是如何實現的?
A:根據模板生成了,比如對於Java Web,在規定好日誌輸出目錄等情況系,可變的只是工程名稱等很少部分,名稱CI系統知道,所以就可以自動生成了。
Q:kube-proxy 那邊效能怎麼樣? 還有一個問題就是一些特定的容器的固定IP是怎麼做的?
A:我們量比較小,沒有效能瓶頸,1.2(具體記不清了)以後kube-proxy是純iptables實現,沒那麼差吧,業內也有用HAProxy等代替的,個人覺得沒必要。特定的容器固定IP我們沒有實現,我們沒有這種場景。你可以折中一下,給個NodePort,固定IP我個人覺得儘量少用為好。
Q:映象的自描述能否展開講講呢?
A:就是每個映象裡都有描述它構建過程的Dockerfile。
Q:對於你們現在使用的這套容器雲平臺,服務之間的依賴是怎麼實現的?怎麼區分的環境?另外應用健康檢查和追蹤用的是什麼方案?
A:服務之間的依賴指什麼?如果是應用,他們還是通過Dubbo走,如果是非Java得應用,就通過Service呼叫。我們在不同的環境部署了Kubernetes叢集,每個叢集也部署了管理系統,這樣就知道每個系統對應哪個環境了。健康檢查就是通過Kubernetes的健康檢查機制實現的,livenessprobe。
Q:多資料中心災備能具體講一下嗎,是在多個dc有多套一樣的叢集,全部是冷備狀態嗎?
A:我們生產有三個資料中心,每次釋出,我們都會向每個資料中心發請求,不是冷備,是多活。
Q:監控體系搭建得細節和監控內容都是哪些,比如CPU 記憶體,Pod事件等,包括告警體系?
A:這個問題很好,我們這方面做得非常不足,監控的標準我們剛剛拿出細節方案。我們現在的方案是把CPU這些指標輸出到日誌中心(包含監控報警部分),由日誌中心來完成。Pod事件等還沒有監控報警。
Q:日誌如何讓元件方方便檢視同時可以排查問題,比如啟動時的日誌?
A:應用日誌通過日誌中心(ELK)檢視;啟動日誌通過容器雲介面檢視,通過Kubernetes的API介面實現。
Q:很多元件有IP白名單的問題,而Kubernetes叢集IP經常變換 ,如何解決?
A:要麼改元件,要麼在網路層做限制(比如Calico或者其他的),儘量別在Kubernetes層解決。
Q:容器管理平臺是自研的嗎?使用何種語言開發的?是全部基於API介面嗎?
A:是自研的,前臺AngularJS,後臺Golang,全部基於Kubernetes的API,開發過程比較簡單,Kubernetes的API設計的非常完善,推薦嘗試。
文章來自微信公眾號:Docker