新東方的Kubernetes實踐:從服務化ES到Kafka和Redis
- 2019 年 12 月 4 日
- 筆記
口述/作者:
姜江,新東方資訊管理部平台架構負責人
陳博暄,新東方資訊管理部高級運維工程師
編輯:
Rancher Labs

新東方資訊管理部平台架構負責人 姜江

新東方資訊管理部高級運維工程師 陳博暄
2017年,新東方開始了利用容器化手段將中間件業務服務化的探索,基於Rancher 1.6使用ES;2019年,新東方再次開始了擴大了中間件的業務服務化,基於Kubernetes使用Kafka、ES和Redis。利用容器化手段將中間件服務化,有效提升了運維團隊的工作效率,極大地縮短了軟體開發流程。本文將分享新東方在中間件服務化上的嘗試。

從幼兒園、小學、中學、大學和出國留學,新東方几乎涉及了每一個教育領域。我們的教育產品線非常長,也非常複雜。那麼,這麼長的教育線,我們是用怎樣的IT能力進行支援的呢?——新東方雲。
我們目前有16個雲數據中心,包括自建、租用IDC,我們還通過雲聯網直接連接了阿里雲和騰訊雲,最終形成了一套橫跨多個雲提供商的混合雲架構。新東方的雲體系很特別,你可以在裡面看到一些相對傳統的部分,比如SQL Server、windows和櫃面服務程式。但也有比較新潮的東西,比如TiDB、容器、微服務等等。除此之外,還能找到影片雙師、互動直播等偏互聯網應用的部分。一個企業的IT架構和一個企業的業務階段是密切相關的。新東方和萬千從傳統業務走向互聯網+的企業一樣,正處於數字化轉型的關鍵階段。

接下來我們來談一談新東方的容器化之路。新東方做容器也很多年了。2016年,新東方嘗試了基於Docker Swarm的一些商業方案方案,不是很理想。2017年正是容器編排架構風雲變換的一年,我們選擇了Rancher的Cattle引擎開始了容器化的自主建設,同時也在觀望業界動向。到了2018年,新東方的容器建設再次進化,最終全面轉向了K8S。

那麼在新東方是怎麼看待K8S的呢?我們認為K8S是介於PaaS和IaaS層之間的中間層,對下層的IaaS層和上層的PaaS層都制定了介面和規範。但是並不關心功能實現。而我們要做一套完整的容器雲,只有K8S是遠遠不夠的,還需要引入其他開源組件進行補充。

從上圖可以發現,我們利用各種開源組補充K8S生態,組合成目前的新東方的容器雲平台。
我們的運行時組件基於Docker,Host主機作業系統選擇的是Ubuntu,K8S網路組件用的是Canal,同時採用Mellanox網卡加速技術。Rancher 2.0作為我們的k8s管理平台,提供多租戶管理、可視化、許可權對接AD域等重要功能,幫助我們避免了大量的後台集成整合工作,為我們節約了人力的同時提供了一套穩定的圖形化管理平台。

下面介紹一下我們的K8S實踐, 從上圖可以看到,我們完全基於原生社區版本的K8S。通過kubeadm工具和nginx stream負載均衡部署成一個三節點HA架構。
集群關鍵組件運行在host網路模式。這樣可以減少網路上的資源消耗,獲得更好地性能,比如Ingress組件,通過Flannel構建overlay容器網路,運行上層應用。

使用容器肯定需要涉及到鏡像管理。新東方是早期Harbor的用戶,我們從1.2版本就開始用Harbor,後端存儲對接ceph對象存儲。目前,我們在嘗試鏡像分發的功能,使用的是阿里雲開源的Dragonfly。它可以將南北向的下載流量轉換為東西向,使鏡像在node之間複製成為可能。當集群規模非常大的時候,減緩拉取鏡像對Harbor伺服器造成的壓力負載。

我們的K8S集群是完全跑在物理機上的,當集群規模大了之後,物理機增多,我們必須要引用物理機的管理軟體減少我們的運維成本。
在這裡,我們使用的是Ubuntu的Maas,它是個裸機管理平台,可以將沒有作業系統的物理機,按照模板裝成指定的標準物理機。我們再結合Ansible playbook初始化物理節點,將它變成我們想要的物理機節點,加入到集群裡邊。
從上圖中可以看到,通過載入TiDB的role把標準物理機變成TiDB的節點,通過K8S的role把標準物理機變成K8S節點,我們會在每一種role里將Osquery和Filebeat推到物理機上,它們可以收集物理機的機器資訊,推送到CMDB進行資產管理。

我們的CI/CD是基於業務來區分的,有一部分我們直接對接新東方自己的Jenkins,另一部分業務直接對接Rancher Pipeline功能來實現CI/CD。

集群監控方面,我們現在用的是開源社區Prometheus的Operator。一開始我們用的是原生的Prometheus,但是原生的Prometheus在配置告警發現和告警規則上特別麻煩。
引用了Operator以後,Operator幫我們簡化了配置的過程,比較好用,推薦大家使用。
值得一提的是,Rancher 2.2版本之後的集群監控都是基於Prometheus Operator,大家感興趣的話,可以回去下一個新版本的Rancher體驗一下。

我們的日誌是針對兩個級別來設置的。業務日誌通過sidecar的方式運行filebeat,把數據收集到kafka集群裡面,再由logstash消費到ES,可以減輕ES的壓力負載。
另一方面是集群層面,集群層面的日誌通過Rancher 2.2提供日誌收集功能,用fluentd收集到ES集群當中。

我們一共有五套集群,一個是跑線上業務的,生產和測試兩套;一個是Platform1集群,跑中間件類應用,比如ES、Redis、Kafka,也是分生產和測試兩套;還有一個是測試集群,它是測試功能的,K8S集群升級迭代、測試新的組件、測試新的功能都是在這個集群上完成的。
可能細心的朋友發現我們的集群是1.14.1版本的,版本非常新,為什麼?因為Kubernetes 1.14有一個非常重要的功能,叫local PV,目前已經GA,我們非常看中這個功能,因此將集群一路升級到1.14。

目前,業務應用主要是兩個方面:
- 掌上泡泡APP以及新東方APP後端服務都跑在容器雲架構上。
- 中間件的服務化,比如Kafka、Redis、ES集群級別的中間件服務都跑在我們的容器雲架構上。
為什麼要將中間件服務化?
那麼,我們為什麼要將中間件服務化呢?

在我們看來,中間件比如ES、隊列、Redis快取等都有幾個共同的特點,就像圖中的怪獸一樣,體型很大。
我舉個例子做個比較: 比如我啟動一個業務的虛機,4C8G比較常見,起10個就是40C 80G。作為對比, 40C80G是否能啟動一個elasticsearch的節點呢? 40C 80G啟動一個es節點是很緊張的。實際生產中, 一個高吞吐的ES節點一般需要100G以上的記憶體。從這個例子我們就可以體會到中間件類負載的單體資源消費非常的大。
另外,中間件在項目中應用非常廣,隨便一個應用,肯定會使用Redis、MQ等組件。隨便一個組件單獨部署就要佔用佔多台虛機。各個項目又都希望自己能有個小灶,希望自己能獨佔一個環境。而小灶對資源的耗費就更多,加上中間件不可避免的各種版本、各種配置不一樣,我們需要僱傭非常多的人來維護中間件,這就是一個很大的問題。
當然,如果整個公司一共也就有十來個項目的話,完全使用虛機也可以。但是新東方現在有三四百個項目,中間件消耗了相當大的資源。如果全部用虛機資源的話,成本還是很高的。

那我們怎麼解決這個問題呢?我們祭出三箭:容器化、自動化、服務化。

容器化最好理解,剛才提到了雜七雜八的各種配置,通通用容器來統一,你必須按我的標準來。直接將容器部署到物理機,以獲得更好的性能和彈性。

容器化的下一步是自動化,自動化更精確地說其實是程式碼化。就是將基礎設施程式碼化,以程式碼上線迭代的方式管理基礎設施。我們使用Helm和Ansible來實現程式碼化和自動化。

前面兩步都做好之後,我們就可以進入到第三步。如果我們拿自己的管理規範和最佳實踐去約束大家,可能作用並不大。最簡單的辦法就是服務輸出,讓大家來用我們的服務。
逐漸地將小灶合併成大鍋飯,削峰填谷,也避免了資源的浪費。每個公司多少多有一些超級VIP的項目. 這種業務就變成了大鍋飯裡面單獨的小灶。同樣是大鍋飯的機制,但我單獨為你提供資源隔離、許可權隔離。
在服務化之前,我們對運維人員更多的理解是項目的勞務輸出。每天都很忙,但也看不到做太多成果。服務化之後,我們將勞務輸出轉變為對服務平台的建設,賦能一線人員,讓二線人員做更有意義的事。
我們的實踐:ELK/ES
接下來,我們來逐一講解新東方各個中間件編排的方式。

Elastic公司有個產品叫做ECE,是業內第一個容器化管理ES的平台,ECE基於K8S的1.7(也可能是1.8)實現。通過容器的方式、用物理機為用戶提供各個版本的ES實例。但是它也存一些局限性:只能管ES,其他的中間件管不了。
這對我們啟發很大,我們就想能不能用Rancher+Docker的方式,模仿製作一個我們自己服務平台。於是誕生了我們的第一版平台,來使用Rancher 1.6管理ELK。

上圖就是我們的ELK集群,它是橫跨Rancher 1.6和k8s和混合體,目前處於向k8s遷移的中間狀態。

ELK的編排方式我們有兩個版本:UAT環境的編排和生產編排。在UAT環境我們採用rook(ceph)的方案, ES節點用Statefulset方式啟動。這種方案的好處是萬一哪個節點掛了,存儲計算完全分離,你想漂移到哪裡就可以漂移到哪裡。

我們的生產環境會比較不一樣,我們將每個ES節點都做成一個deployment,我們不讓它漂移,用Taint和label將deployment限定在某一主機上。POD的存儲不再使用RBD,直接寫入本地磁碟hostpath, 網路使用host網路獲取最好的性能。
如果掛了怎麼辦呢?掛了就等待就地復活,機器掛了就地重啟,換盤或者換硬體。如果還不能復活怎麼辦呢?我們有個機器的管理,機器幹掉,直接從池裡面重新拉一台新機器出來,上線,利用ES的複製功能把數據複製過去。

大家可能會很奇怪,你們怎麼搞兩套方案,而且生產編排還那麼丑?
我們認為簡約架構才是最美架構,中間環節的組件越少,故障點也越少,也就越可靠。本地磁碟的性能要優於RBD,本地網路要優於K8s網路棧。最重要的是:我們編排的這些中間件應用,實際上全部都是分散式的(或者內置HA架構),他們都有內置的副本機制,我們完全不用考慮在K8S這一層做保護機制。
我們也實驗對比過這兩種方案,如果節點掛掉,本地重啟的時間要遠低於漂移的時間,RBD有時候還會出現漂移不過去的問題。物理節完全掛掉的幾率還是很小的。所以我們最終在線上環境選擇了稍微保守一點的方案。
我們的實踐:Redis

我們現在的Redis主要是哨兵的方案,同樣採用deployment限定到特定節點的方式編排。我們的Redis不做任何持久化,純作為cache使用。這就會帶來一個問題:假如master掛了,那麼K8S就會馬上重啟,這個重啟時間是一定小於哨兵發現它掛了的時間。它起來之後還是master,是個空空的master,隨後剩下的slave中的數據也會全部丟掉,這是不可接受的。
我們先前也做了很多調研,參考攜程的做法, 在容器啟動的時候用Supervisord來啟動Redis,即使POD中的Redis掛掉也不會馬上重啟POD,從而給哨兵充分的時間進行主從切換,然後再通過人工干預的方式恢復集群。
在Redis的優化上,我們為每個Redis實例綁定CPU。我們知道 Redis進程會受到CPU上下文切換或者網卡軟中斷的影響。因此我們在Redis實例所在node上做了一些限制,並打上了taint。我們把作業系統所需要的進程全部要綁到前N個CPU上,空出來後面的CPU用來跑Redis。在啟動Redis 的時候會將進程和CPU一一對應,獲得更佳的性能。
我們的實踐:Kafka

眾所周知,Kafka是一種高吞吐量的分散式發布訂閱消息,對比其他中間件,具有吞吐量高、數據可持久化、分散式架構等特點。

那麼,新東方是怎樣使用Kafka的?對Kafka集群有什麼特殊的要求呢?
我們按照業務應用場景來分組,會劃分為三類:第一類是使用Kafka作為交易類系統的消息隊列;第二類是使用Kafka作為業務日誌的中間件;第三類是Kafka作為交易類系統的消息隊列。
如果想滿足這三類應用場景,我們的Kafka就必須滿足安全要求。比如不能明文傳輸交易數據,所以一定要進行安全加密。
下面,我們來講解一下Kafka原生的安全加密,我們是怎麼做的?又是如何選擇的?

除了金融行業以外,其他行業使用Kafka一般不會使用它們的安全協議。在不使用安全協議情況下,Kafka集群的性能非常好,但是它明顯不符合新東方對Kafka集群的要求,所以我們開啟了數據加密。
我們使用Kafka原生支援,通過SSL對Kafka進行信道加密,使明文傳輸變成密文傳輸,通過SASL進行用戶驗證,通過ACL控制它的用戶許可權。

我們簡單看一下兩種SASL用戶認證的區別。SASL_PLAIN是將用戶名密碼以明文的方式寫在jaas文件裡面,然後將jaas文件以啟動參數的形式載入到Kafka進程裡面,這樣Kafka的client端訪問伺服器的時候會帶著jaas文件去認證,就啟動了用戶認證。
SASL_GASSAPI是基於Kerberos KDC網路安全協議,熟悉AD域的朋友肯定了解kerberos,AD域也用到了Kerberos網路安全協議,客戶端直接請求KDC伺服器和KDC伺服器交互,實現用戶認證。
兩種方法各有利弊,最終新東方選擇的是第一個SASL_PLAIN的方式,原因很簡單,這樣我們可以避免單獨維護KDC服務,減少運維部署成本。但是這種方法有一個問題,因為Kafka用戶名和密碼都是通過這個進程載入進去的,你想改文件比如添加用戶、修改用戶密碼,那你就必須重啟Kafka集群。
重啟Kafka集群勢必對業務造成影響,這是不能接受的。因此我們採用變通的方法, 按照許可權分組,總共在jaas文件裡面預先設置了150個用戶,管理員為項目分配不同的用戶.這樣就避免了增加項目重啟集群的尷尬。

如上圖, 我們在Kafka集群上我們開放了兩個埠,一個是帶用戶認證並且帶SSL加密的埠,另一個是沒有SSL加密,只啟用了用戶認證的SASL_PLAIN埠。連接Kafka的客戶端根據自己的需求選擇埠進行訪問。

說完架構,我們來說說kafka的編排。我們kafka和zk集群都通過host網路部署,數據卷通過hostpath方式落到本地物理機,以獲取更好地性能。
Kafka和zk都是單個deployment部署,固定在節點上,即使出現問題我們也讓他在原機器上重新啟動,不讓容器隨意遷移。
監控方面採用exporter+Prometheus方案,運行在overlay的容器網路。
我們的實踐:服務化平台

我們在做這個服務化平台時想法很簡單:不要重複發明輪子,盡量利用已有技術棧,組合helm、ansible、k8s。
以kafka為例, ansible 會根據環境生成helm chart , 像ssl證書,預埋用戶配置等是由ansible按照用戶輸入進行生成,生成的結果插入到helm chart中,隨後helm根據chart創建對應實例。
以下是我們平台1.0 Demo的一些截圖。

這是集群管理,部署到不同的集群上會有不同集群的入口維護它的狀態。



上面展示的是申請服務的步驟。整個步驟非常簡單,選中集群和想要的版本就可以了。

在這個管理介面,你可以看到你的IP、訪問入口、你的實例使用的埠(埠是平台自動分配的)。如果是SSL連接,你還可以得到你的證書,可以在頁面上直接下載。我們後期還會將集群的日誌都接入到平台中。

我們的後台還是挺複雜的。後台使用ansible 的AWX平台。這裡可以看到創建一個集群其實需要很多的輸入項,只不過這些輸入項我們在前台介面中就直接幫用戶生成出來了。


這是部署出來的完整的Kafka集群,有Zookeeper,有Kafka,有監控用的exporter等。我們為每個集群都配置了一個kafka Manager,這是一套圖形化的管理控制台,你可以直接在manager中管理kafka。

監控報警是必不可少的,我們基於Prometheus Operator做了一些預置的報警規則。比如topic是否有延遲。當集群生成後,Operator會自動發現你的端點,也就是我們剛看的exporter,operator發現端點後就開始自動加入報警,完全不需要人工接入。

我們為每個項目生成了可視化面板,需要監控的時候可以直接登錄Grafana查看。

上圖是簡單的壓測結果。512K的Message,SSL+ACL的配置五分區三副本,約為100萬消息每秒, 配置是五個16C 140G記憶體的容器,SSD硬碟。我們發現隨著Message體積變大,性能也會隨之下降。

服務化平台路線展望
剛才講了我們今年的一些工作,那麼我們明年想做什麼呢?

2020財年開始,新東方計劃將Redis、ES這些服務也全部服務化,並將這些暴露出來的API最終整合到雲門戶里,給集團內的用戶或者是第三方系統調用。

另外一個不得不提的就是Operator,上周Elastic又發布了一個新項目叫ECK,ECK就是ES的官方Operator。

通過Operator,你只需要簡單地輸入CRD,Operator就會自動生成你需要的集群。
我們認為基於Helm的方式,雖然能極大地簡化Yaml的工作,但它還不是終點,我們相信這個終點是Operator。

編者按:
本文是根據新東方姜江和陳博暄在Rancher於2019年6月20日在北京舉辦的第三屆企業容器創新大會(Enterprise Container Innovation Conference,簡稱ECIC)上的演講整理而成。本屆ECIC規模宏大,全天共設置了17場主題演講,吸引了近千名容器技術愛好者參加,超過10000名觀眾在線觀看。您可在Rancher公眾號中閱讀更多大會演講實錄的文章。

推薦閱讀

Rancher Labs中國團隊招聘架構師啦!坐標北上深,年薪30-50萬上不封頂!我們等的那個人,會是你嗎?(點擊下方圖片,查看詳情!)


About Rancher Labs
Rancher Labs由CloudStack之父梁勝創建。旗艦產品Rancher是一個開源的企業級Kubernetes管理平台,實現了Kubernetes集群在混合雲+本地數據中心的集中部署與管理。Rancher一向因操作體驗的直觀、極簡備受用戶青睞,被Forrester評為2018年全球容器管理平台領導廠商,被Gartner評為2018年全球最酷的雲基礎設施供應商。
目前Rancher在全球擁有超過一億的下載量,並擁有包括中國人壽、華為、中國平安、興業銀行、民生銀行、平安證券、海航科技、廈門航空、上汽集團、海爾、米其林、豐田、本田、中船重工、中聯重科、迪斯尼、IBM、Cisco、Nvidia、輝瑞製藥、西門子、CCTV、中國聯通等全球著名企業在內的共25000家企業客戶。