《基于Kubernetes的私有容器云建設實踐》要點:
本文介紹了基于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相關的容器技術,確保我們的決策是正確的,這對我們構建容器云至關重要.經過很多的調研和討論,我們發現容器云的是有一套完成的理論基礎支撐的,這些理論又引申出我們構建容器云的原則:
保證構建容器云的過程能夠正確的進行,還需要一些原則,”Build once,Run anywhere”,一個Docker鏡像要貫穿QA到生產環境的每個環節,不允許QA和生產的鏡像出現不一致的情況.”All in one”,對于Java Web應用,由于歷史原因,可能多個Web App運行在同一個Tomcat中,要求每個Docker鏡像中只運行一個Web App.
以應用為中心,是我們最重要的原則,也是建設容器云的出發點,這個原則確保我們關注的重點是應用,而不是進行計算資源的抽象和資源的調度,我們的理想目標是,在“優雅地“管理應用的整個生命周期同時,順便做好資源抽象,提高資源的利用率.
分層治理,基礎設施的治理由容器云完成,上層應用的治理由應用治理層負責,從SaaS,到PaaS,再到CaaS,分層治理,各層通過接口相互調用,層與層之間互不侵入.
容器云的目標決定了我們面對的是應用的管理,即應用對應的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集群,每個數據中心能夠獨立存活,多個數據中心互相災備.
由于資源限制,技術人員往往過于關注單機的資源利用率.Docker(Cgroup、Namespace)提供的資源共享與隔離的機制,讓我們對資源利用率有了新的認識,特別是使用容器編排引擎后,我們對資源的理解應該在集群維度進行考量,而不是在考慮單機的利用率.同樣,在整個數據中心,甚至多個數據中心進行資源利用率的綜合考量也是非常必要的.
在提高資源利用率、降低成本的同時,需要在服務的QoS與優化資源利用率之間有個平衡.我們的原則是在保證服務質量的同時,盡量提高資源的利用率.
根據Kubernetes的資源模型,在Pod level的QoS分為三個等級:Guarantee、Burstable、BestEffort,我們也是依照這三個級別對應我們應用的優先級來制定資源超賣的標準.
我們對應用設置的QoS標準:
有一點需要特別注意,在生產環境中,不要使用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轉化,才能更好的發揮容器云的效力.
按照我們預先的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
轉載請注明本頁網址:
http://www.snjht.com/jiaocheng/4095.html