知乎容器平臺(tái)演進(jìn)及與大數(shù)據(jù)融合實(shí)踐
原創(chuàng)【51CTO.com原創(chuàng)稿件】2018 年 5 月 18-19 日,由 51CTO 主辦的全球軟件與運(yùn)維技術(shù)峰會(huì)在北京召開。
在“開源與容器技術(shù)”分論壇上,來自知乎的計(jì)算平臺(tái)負(fù)責(zé)人張阜興發(fā)表了題為“知乎容器平臺(tái)演進(jìn)及與大數(shù)據(jù)融合實(shí)踐”的精彩演講。
本文將按照如下三個(gè)部分展開討論:
- 知乎容器平臺(tái)的演進(jìn)歷程
- 容器平臺(tái)維護(hù)踩過的坑
- 容器與大數(shù)據(jù)的融合實(shí)踐
知乎容器平臺(tái)的演進(jìn)歷程
知乎容器平臺(tái)的演進(jìn)歷程大致可以分成三個(gè)階段:
- 2015 年 9 月,我們的容器平臺(tái)正式在生產(chǎn)環(huán)境中上線應(yīng)用。
- 到了 2016 年 5 月,我們已經(jīng)將 90% 的業(yè)務(wù)遷移到了容器平臺(tái)之上。
- 如今,除了業(yè)務(wù)容器之外,包括 HBase、Kafka 等多個(gè)基礎(chǔ)組件都已遷至容器平臺(tái)。
總的節(jié)點(diǎn)數(shù)達(dá)到了 2 千多個(gè),而容器數(shù)則達(dá)到了 3 萬多個(gè)??梢哉f知乎基本已經(jīng) All in 容器平臺(tái)之上。
在容器平臺(tái)整體演進(jìn)的過程中,我們總結(jié)了五個(gè)要點(diǎn):
- 從 Mesos 到 Kubernetes 的技術(shù)選型變化。
- 從單集群到多集群混合云的架構(gòu)調(diào)整。
- 從滾動(dòng)部署到部署與發(fā)布相分離的使用優(yōu)化。
- 在容器使用上,從無狀態(tài)到有狀態(tài),引入持久化的存儲(chǔ)。
- 在容器網(wǎng)絡(luò)上,從 NAT 轉(zhuǎn)換成 Underlay IP 模式。
從 Mesos 到 Kubernetes
早在 2015 年,我們就已經(jīng)開始在生產(chǎn)環(huán)境中使用容器平臺(tái)了。由于那時(shí) Kubernetes 剛被發(fā)布,且不成熟,因此我們當(dāng)時(shí)選用的是 Mesos 技術(shù)方案。
Mesos 的優(yōu)勢(shì)如下:
- 非常穩(wěn)定。
- 在架構(gòu)設(shè)計(jì)中,由于大部分狀態(tài)是由 Mesos 的 Slave 向 Master 去匯報(bào),因此 Master 的負(fù)載較輕。
- 單集群所能容納的容器規(guī)模較大(官方稱:?jiǎn)蝹€(gè)集群可容納 5000 個(gè)節(jié)點(diǎn))。
Mesos 的劣勢(shì):由于是單獨(dú)開發(fā)一套 Framework,因此開發(fā)的成本較高。我們最開始采用的就是自研 Framework。
Kubernetes 的優(yōu)勢(shì)如下:
- 有強(qiáng)大的社區(qū)支持。
- 功能較為完善,包含有 Pods、Deployment 等概念。
- 由于功能完善,接入使用成本較低。
Kubernetes 的劣勢(shì):由于它將所有的狀態(tài)都放入 Etcd 中進(jìn)行存儲(chǔ),因此單集群的規(guī)模沒有 Mesos 那么大。官方稱:在將 Etcd 升級(jí)到 V3 版本之后,才能達(dá)到 5000 個(gè)節(jié)點(diǎn)。
在運(yùn)行之初,我們使用的是一些簡(jiǎn)單的無狀態(tài)容器。后來隨著 Proxy、Kafka 等基礎(chǔ)組件的引入,針對(duì)每一套組件都需要開發(fā)一套 Framework 的接入成本太高。
因此在后續(xù)的實(shí)踐過程中,我們直接采用了 Kubernetes,通過資源調(diào)度層,統(tǒng)一進(jìn)行資源的調(diào)度和管理。
從單集群到混合云
在生產(chǎn)環(huán)境中,我們有著如下實(shí)際需求:
- 對(duì)于 Mesos 或 Kubernetes 的任意參數(shù)變更,都需要在灰度集群上去先進(jìn)行驗(yàn)證。只有驗(yàn)證通過之后,才能大規(guī)模地部署到生產(chǎn)環(huán)境之中。
因此,我們需要有一個(gè)線上與線下相似的環(huán)境,它們的區(qū)別僅在于測(cè)試的集群規(guī)模略小于實(shí)際運(yùn)行的集群。
- 對(duì)于 Kubernetes 的單個(gè)集群來說,容量是有限的,所以需要通過實(shí)施多集群方案來對(duì)容量進(jìn)行水平擴(kuò)展。
- 可容忍單集群級(jí)別的故障。
- 需要采用混合云的架構(gòu)。由于公有云的集群池比較大,它既能夠大幅提升彈性資源池的容量,又能夠抵御突發(fā)的擴(kuò)容需求。
計(jì)費(fèi)模式較為靈活,可按需計(jì)費(fèi),并“廉價(jià)”地實(shí)現(xiàn)一些臨時(shí)性的活動(dòng)所帶來的計(jì)算資源的消耗。
在混合云架構(gòu)的實(shí)現(xiàn)過程中,我們?cè){(diào)研過 Kubernetes 的 Federation 方案,但是發(fā)現(xiàn)它存在如下兩點(diǎn)不足:
- 由于尚不成熟,目前官方并不推薦運(yùn)行在生產(chǎn)環(huán)境之中。
- 組件過多,在部署和管理上較為繁瑣。
因此我們采用了自行研發(fā)的管理方案,其特點(diǎn)是:
- 每一組業(yè)務(wù)容器都會(huì)同時(shí)在多個(gè)集群上創(chuàng)建 Deployment。
- 這些 Deployment 的配置,包括:容器版本、CPU / 內(nèi)存資源的配額,都是完全相同的,唯一不同的只是容器的數(shù)量,它們會(huì)根據(jù)不同的集群大小做出相應(yīng)的調(diào)整。
從滾動(dòng)部署到部署發(fā)布分離
我們優(yōu)化了部署與發(fā)布的流程,從原先的滾動(dòng)部署模式轉(zhuǎn)換成了部署與發(fā)布相分離的模式。
在此,我們來區(qū)分一下部署與發(fā)布這兩個(gè)概念:
- 部署,是分發(fā)代碼的配置,啟動(dòng)諸如 Web Service 進(jìn)程之類的服務(wù)實(shí)例。
- 發(fā)布,是指把一組服務(wù)實(shí)例注冊(cè)到負(fù)載均衡、或者其他流量分發(fā)系統(tǒng)上,使其能夠?qū)ν饨邮樟髁俊?/li>
如上圖下方的流程所示,上線的基本流程為:內(nèi)網(wǎng)流量測(cè)試→金絲雀流量測(cè)試→生產(chǎn)環(huán)境全量。
那么在每一個(gè)階段,我們都需要去觀察線上業(yè)務(wù)的指標(biāo),一旦出現(xiàn)異常,我們就需要及時(shí)地進(jìn)行回滾操作。
我們來深入分析一下滾動(dòng)部署方式的優(yōu)缺點(diǎn)。
優(yōu)點(diǎn)如下:
- 每次先升級(jí)一部分的容器實(shí)例,然后再迭代運(yùn)行。
- 能夠保證在升級(jí)過程中服務(wù)的不中斷,而瞬時(shí)產(chǎn)生的***資源消耗是受限制的。
缺點(diǎn)如下:
- 在滾動(dòng)部署的過程中,我們無法做到:先部署 10%→停下來→再部署 20%→再停下來→接著部署 50%,因此無法靈活地控制進(jìn)度,也不能對(duì)應(yīng)到前面所提到的各個(gè)發(fā)布階段。
- 如果每個(gè)發(fā)布的階段都采用獨(dú)立的滾動(dòng)部署方式,那么整體部署速度將會(huì)比較緩慢。
- 在滾動(dòng)部署過程中,舊的容器實(shí)例會(huì)被立刻銷毀。一旦此時(shí)線上指標(biāo)出現(xiàn)問題,而我們的觀察卻有所滯后,那么所涉及到的“銷毀新實(shí)例和啟動(dòng)舊實(shí)例”,回滾速度會(huì)比較緩慢。
針對(duì)上述問題,我們?cè)谠O(shè)計(jì)上采用了“部署與發(fā)布相分離”的模式。
例如:我們?cè)谏暇€時(shí),先在后臺(tái)啟動(dòng)一組新的業(yè)務(wù)容器實(shí)例,當(dāng)容器實(shí)例達(dá)到并滿足了內(nèi)網(wǎng)發(fā)布的數(shù)量要求時(shí),我們就把它注冊(cè)到內(nèi)網(wǎng)之中,然后將舊的實(shí)例從內(nèi)網(wǎng)流量分發(fā)的系統(tǒng)中“反注冊(cè)掉”。
如此,新的實(shí)例就能夠在內(nèi)網(wǎng)中被發(fā)布與驗(yàn)證。在后臺(tái)繼續(xù)啟動(dòng)新的容器實(shí)例的同時(shí),也避免了用戶能感知到容器啟動(dòng)實(shí)例的時(shí)間。
我們實(shí)現(xiàn)了內(nèi)部用戶在驗(yàn)證完畢的時(shí)刻,下一個(gè)階段的部署就在數(shù)秒內(nèi)直接升級(jí)完畢了。
另外,由于我們舊的容器實(shí)例并非立刻被銷毀掉,而是要等到在生產(chǎn)環(huán)境發(fā)布完成的一段安全時(shí)期之后,再按照類似于金絲雀的策略將舊的一組容器完全銷毀掉。
因此,在金絲雀發(fā)布的過程中,如果線上出現(xiàn)任何問題,我們就能夠立刻把舊的實(shí)例重新注冊(cè)回來,以實(shí)現(xiàn)秒級(jí)回滾。
從無狀態(tài)到有狀態(tài)
在容器的使用模式上,我們最初部署的是一些無狀態(tài)的業(yè)務(wù) Web 容器。但是隨著其他基礎(chǔ)組件被遷移到了該容器平臺(tái)之上,我們引入了持久化存儲(chǔ),以提供服務(wù)支持。
因此在生產(chǎn)環(huán)境中,我們用到了如下典型的持久化存儲(chǔ)方式:
- HostPath。主要是配合 DaemonSet 進(jìn)行使用。因?yàn)?HostPath 本身就能夠保證在每一個(gè) Node 上只啟動(dòng)一個(gè) Pod 實(shí)例,因此不出現(xiàn)多個(gè) Pod 同時(shí)使用同一個(gè) HostPath,進(jìn)而導(dǎo)致產(chǎn)生路徑?jīng)_突的問題。
例如,我們讓 Consul Agent 采用 DaemonSet 部署,那么由于 Consul Service 在注冊(cè)的時(shí)候需要持久化到本地存儲(chǔ)目錄之中,因此就很適合于用這種方式去實(shí)現(xiàn)。
- Local。***版的 Kubernetes 已經(jīng)能夠支持 Local Volume 了。優(yōu)勢(shì)在于:因?yàn)槭褂玫氖潜镜氐拇疟P,它相對(duì)于網(wǎng)絡(luò)存儲(chǔ)有著較高的 IOPS,且延時(shí)較低。
所以對(duì)于諸如 MySQL 和 Kafka 之類的有著高 IOPS、低延時(shí)的存儲(chǔ)類應(yīng)用來說,是非常合適的。
- NFS。我們通過將分布式文件系統(tǒng)的 Fuse 接口映射到容器之中,以保證業(yè)務(wù)能夠從分布式文件系統(tǒng)上去讀取各種數(shù)據(jù)文件。
從 NAT 到 Underlay IP
在容器網(wǎng)絡(luò)的模式上,我們最早采用的是 iptable 所實(shí)現(xiàn)的 NAT 模式。該模式實(shí)現(xiàn)起來較為方便,不需要對(duì)現(xiàn)有網(wǎng)絡(luò)予以調(diào)整。
但是它存在一定的性能損耗問題,這對(duì)于我們?cè)缙诘哪切I(yè)務(wù)容器來說尚可接受。
到了后期,需要將一些大流量的高速網(wǎng)絡(luò)應(yīng)用,如 Ngix、Haproxy 放到容器里時(shí),我們對(duì)這種性能開銷就無法容忍了。
因此我們結(jié)合自身機(jī)房的網(wǎng)絡(luò)實(shí)際,在 Kubernetes 方案上選用了 Underlay IP,這種簡(jiǎn)單互聯(lián)的網(wǎng)絡(luò)模式,以保證容器的 IP 與所在物理機(jī)的 IP 是完全對(duì)等的。
由于不存在 Overlay 的封包 / 解包處理,因此性能幾乎沒有損耗。通過實(shí)測(cè),我們覺得性能非常好,所以將各種大流量的分發(fā)應(yīng)用都放在了此容器里。
另外,由于 IP 模式具有良好的業(yè)務(wù)對(duì)應(yīng)關(guān)系,因此我們可以方便地去定位網(wǎng)絡(luò)連接的來源和故障。
例如:倘若在 MySQL 側(cè)發(fā)現(xiàn)有大量的連接產(chǎn)生,你將如何定位這些連接的來源呢?
按照原有的端口映射方式,你可能只能定位到是來自于某臺(tái)機(jī)器,但是往往一臺(tái)機(jī)器上會(huì)被部署了多個(gè)業(yè)務(wù)應(yīng)用,因此也就沒法精確地定位到是由哪個(gè)業(yè)務(wù)方所導(dǎo)致的。
如今,采用了 IP 方式之后,你就可以很方便地根據(jù) IP 地址來判定對(duì)應(yīng)的是哪個(gè)業(yè)務(wù)容器了。
同時(shí),在具體的實(shí)踐過程中,我們還給每一臺(tái)機(jī)器分配了一個(gè)固定的網(wǎng)段(如一個(gè) C 類網(wǎng)段),然后通過 Kubelet 的 CNI 插件,即 APAM,來負(fù)責(zé)每臺(tái)機(jī)器 IP 地址的創(chuàng)建、分配和釋放。
容器平臺(tái)維護(hù)踩過的坑
由于我們?cè)谏a(chǎn)環(huán)境中大規(guī)模地使用了容器平臺(tái),可見容器平臺(tái)已經(jīng)成為了我們基礎(chǔ)組件中的基礎(chǔ)組件。
那么它一旦出現(xiàn)問題,將會(huì)對(duì)我們的生產(chǎn)環(huán)境造成重大的故障。下面給大家分享一些我們?cè)?jīng)在生產(chǎn)環(huán)境中踩過的坑。
K8S Events
一次半夜三更,我們的 API Server 突然全部無法訪問了。通過調(diào)查,我們發(fā)現(xiàn)原因就在于 K8S Events。
眾所周知,K8S Events 就像 Log 一樣記錄著 K8S 集群里發(fā)生的任何變更事件。
如果你沒有進(jìn)行額外配置的話,它會(huì)根據(jù)默認(rèn)模式將這些變化全部記錄到線上的同一個(gè) Etcd 里。
同時(shí),K8S 為這些 Events 配置了相應(yīng)的過期策略 TTL,以保證在經(jīng)過一段時(shí)間后,該 Events 會(huì)被自動(dòng)回收掉,從而釋放 Etcd 的存儲(chǔ)空間。
該設(shè)計(jì)看似合理,但是在 Etcd 實(shí)現(xiàn) TTL 時(shí),卻采用的是遍歷的方式,因此實(shí)現(xiàn)效率比較低下。
隨著集群規(guī)模的逐漸變大,集群上會(huì)出現(xiàn)頻繁的發(fā)布、部署與變更,這就會(huì)導(dǎo)致 Etcd 的負(fù)載逐漸增大,直至最終造成 Etcd 無法再選舉出一個(gè) Leader,而整個(gè) K8S 集群就此崩潰。
針對(duì)上述事故,其實(shí) K8S 也意識(shí)到了,它為我們提供了一項(xiàng)可以將 Events 記錄到某個(gè)單獨(dú) Etcd 集群中的配置。
不過,針對(duì)該單獨(dú)的 Etcd 集群,我們是不需要進(jìn)行高可用存儲(chǔ)的,因此我們就直接使用了單節(jié)點(diǎn)的 Etcd,而并沒有采用 Raft 方式去組建具有更好性能的集群。
另一方面,我們意識(shí)到 K8S 所給定的諸如“每隔三個(gè)小時(shí)就回收掉 Events”的 TTL 機(jī)制過于精確。
因此我們自行實(shí)現(xiàn)了一種過期清理策略,即:固定在每天晚低峰的時(shí)候,再將整個(gè) Events 的 Etcd 集群清空。
K8S Eviction
除了上面提到的“由于 API Server不響應(yīng),所導(dǎo)致的整個(gè) K8S 集群失控”故障,我們也碰到過“所有生產(chǎn)環(huán)境中的 Pods 全部給直接刪掉”的坑。
在所有 API Server 都“掛掉”的極端情況下,如果你沒有及時(shí)去處理,并超過了一段時(shí)間(如五分鐘),那么在 K8S 1.5 之前的版本中,Controller Manager 會(huì)認(rèn)為這些集群的 Node 都已與之失聯(lián),并開始進(jìn)行 Eviction。
即:將這些不健康 Node 上的所有 Pods 全都 terminate 掉了。此時(shí),如果你恢復(fù)了 API Server,那么所有的 Kubelet 會(huì)根據(jù)命令,將運(yùn)行在自己上面的 Pods 全部刪掉。
當(dāng)然,在 K8S 1.5 之后的官方版本中,已經(jīng)增加了一項(xiàng)“-unhealthy-zone-threshold”的配置。
例如:一旦它發(fā)現(xiàn)有超過 30% 的 Node 處于失聯(lián)狀態(tài),就會(huì)認(rèn)為該大規(guī)模故障必有其他原因,因此禁用且不再執(zhí)行 Controller Manager 激進(jìn)的驅(qū)逐(Eviction)策略。
Docker 容器端口泄露
另外我們也曾經(jīng)在生產(chǎn)環(huán)境中發(fā)現(xiàn)“port is already allocated(端口已經(jīng)被使用)”的現(xiàn)象。
我們檢查后發(fā)現(xiàn):容器雖然已經(jīng)釋放了端口,但是它的 Proxy 還在占用該端口。
通過進(jìn)一步檢查 Docker Daemon 的代碼,我們得知:Docker Daemon 從分配使用某個(gè)端口,直到將該端口記錄到自己的內(nèi)部持久化存儲(chǔ)中,該過程并非“原子性”。
如果 Docker Daemon 在中間階段退出,那么它在重啟恢復(fù)的內(nèi)部存儲(chǔ)過程中,會(huì)忽略掉已經(jīng)分配的端口,從而導(dǎo)致了容器端口的泄露。
針對(duì)該問題,我們已向官方提交了帶有對(duì)應(yīng)解決方案的 Issue。
TCP Connection Reset
我們?cè)?Docker NAT 網(wǎng)絡(luò)模式下也遇到過 TCP Connection Reset 的問題。
如上圖所示,該系統(tǒng)默認(rèn)的配置對(duì)于網(wǎng)絡(luò)數(shù)據(jù)包可能出現(xiàn)的亂序情況過于敏感和嚴(yán)格。
在我們的系統(tǒng)訪問公網(wǎng)的過程中,如果網(wǎng)絡(luò)環(huán)境較差,且出現(xiàn)的亂序包超過了 TCP 窗口,那么系統(tǒng)就會(huì)根據(jù)該配置直接將這些連接進(jìn)行 Reset。因此,我們直接將其關(guān)閉掉,就可以解決此問題了。
下面介紹一些我們?cè)谌萜骷夹g(shù)與大數(shù)據(jù)應(yīng)用融合方面所做過的嘗試與實(shí)踐。
容器與大數(shù)據(jù)的融合實(shí)踐
基于 Kubernetes 的大數(shù)據(jù)融合
在大數(shù)據(jù)的場(chǎng)景下,我們使用兩條處理路徑,實(shí)現(xiàn)了容器平臺(tái)和大數(shù)據(jù)組件的融合。如上圖所示,左邊綠色的是實(shí)時(shí)處理,右邊灰色的是批處理。
由于出發(fā)點(diǎn)的不同,這些組件的設(shè)計(jì)思想有著較大的差異:
批處理,主要是運(yùn)行 ETL 任務(wù),包括數(shù)據(jù)倉庫的構(gòu)建、離線分析等,因此它追求的是數(shù)據(jù)吞吐率和資源利用率,而對(duì)于時(shí)延本身并不敏感。
例如:一個(gè) Map-Reduce 任務(wù)需要運(yùn)行 1~2 個(gè)小時(shí),這是非常正常的。并且它被設(shè)計(jì)為具有高容錯(cuò)性。
例如:某個(gè) Map-Reduce 任務(wù)“掛掉”了,你完全可以通過上層的 Ozzie 或 Azkaban 對(duì)整個(gè)任務(wù)(job)進(jìn)行重試。只要最終完成了重啟,這些對(duì)于上層業(yè)務(wù)都將是“無感”的。
實(shí)時(shí)處理,對(duì)于時(shí)延較為敏感,且對(duì)于組件的可用性也要求比較高。一旦其中的任何節(jié)點(diǎn)“掛掉”或重啟,都會(huì)導(dǎo)致數(shù)據(jù)“落地”(運(yùn)營指標(biāo))的延遲,以及數(shù)據(jù)展示的失敗。因此它的組件要求機(jī)器的負(fù)載不能太高。
當(dāng)然,我們?cè)趯?duì)大數(shù)據(jù)生產(chǎn)環(huán)境的維護(hù)過程中,也經(jīng)常遇到如下各種問題:
由于某個(gè)業(yè)務(wù)的變更,伴隨著 Kafka 寫入的流量出現(xiàn)猛增,也會(huì)拉高 Kafka 整個(gè)集群的負(fù)載。那么如果無法恢復(fù)的話,就會(huì)導(dǎo)致集群的癱瘓,進(jìn)而影響整個(gè)生產(chǎn)環(huán)境。
我們治理的思路是:按照業(yè)務(wù)方將集群予以劃分和隔離。
我們按照業(yè)務(wù)方將集群劃分出幾十套,那么面對(duì)這么多的集群所帶來的成本,又該如何統(tǒng)一進(jìn)行配置與部署管理呢?
我們通過使用 K8S 模板,方便地實(shí)現(xiàn)了一鍵搭建出多種相同的運(yùn)行環(huán)境。
由于每個(gè)業(yè)務(wù)方使用量的不同,會(huì)造成那些業(yè)務(wù)方使用量較小的應(yīng)用,也需要被分配多臺(tái)機(jī)器,且需要維護(hù)該集群的高可用性,從而帶來了大量的資源浪費(fèi)。
我們的解決方案是:運(yùn)用容器來實(shí)現(xiàn)細(xì)粒度的資源分配和配置。例如:對(duì)于這些較小的業(yè)務(wù),我們僅分配一個(gè)配有單 CPU、單磁盤和 8G 內(nèi)存的容器,而不是一整臺(tái)物理機(jī)。
基于 Kubernetes 的 Kafka 集群平臺(tái)
由于 Kafka 的性能瓶頸主要存在于磁盤存儲(chǔ)的 IOPS 上,我們通過如下的合理設(shè)計(jì),實(shí)現(xiàn)了資源的分配管理。
具體方案是:以單塊磁盤為資源單位,進(jìn)行細(xì)粒度分配,即:用單個(gè) Broker 去調(diào)度一塊物理磁盤。
如此劃分資源的好處在于:
- 它本身就能對(duì) IOPS 和磁盤容量予以隔離。
- 對(duì)于幾個(gè) T 的硬盤而言,資源的劃分粒度較細(xì),而不像物理機(jī)那樣動(dòng)輒幾十個(gè) T。因此資源的利用率會(huì)有所提升,而且更適合于小型應(yīng)用。
- 我們利用 Kafka 自身的 Replica 實(shí)現(xiàn)了數(shù)據(jù)的高可用性。不過容器與物理機(jī)在具體實(shí)現(xiàn)策略上有所區(qū)別:原來我們將 Broker 部署到一臺(tái)機(jī)器之上,如今將 Broker 部署在一個(gè)容器里。
此時(shí)容器就變成了原來的物理機(jī),而包含容器的物理機(jī)就相當(dāng)于原來物理機(jī)所在的物理機(jī)架。
同時(shí),我們也對(duì)控制 Replica 副本的分布策略進(jìn)行了調(diào)整。我們把 Broker 的機(jī)架式感知,改成按機(jī)器予以處理,這樣就避免了出現(xiàn)相同 topic 的副本被放在同一臺(tái)機(jī)器上的 Broker 的情況。
- 采用了容器方式之后,故障的處理也變得相對(duì)簡(jiǎn)單。
由于采取的是單塊硬盤的模式,因此一旦出現(xiàn)任何一塊硬盤的故障,運(yùn)維人員只需將故障盤更換下來,通過 Kafka 的 Replication 方案,從他處將數(shù)據(jù)拷貝過來便可,而不需要其他部門人員的介入。
在創(chuàng)建流程方面,由于當(dāng)時(shí) K8S 并不支持 LocalPV,因此我們采用了自定義的第三方資源接口,自己實(shí)現(xiàn)了類似于如今生產(chǎn)環(huán)境中 Local Volume 的機(jī)制。
其流程為:我們靜態(tài)地根據(jù)磁盤的資源創(chuàng)建 LocalPV,而在平臺(tái)創(chuàng)建 Kafka 集群時(shí),動(dòng)態(tài)地創(chuàng)建 LocalPVC。
此時(shí)調(diào)度器就可以根據(jù)其 LocalPVC 和現(xiàn)有的 LocalPV 資源去創(chuàng)建 RC,然后在對(duì)應(yīng)的節(jié)點(diǎn)上去啟動(dòng) Broker。
當(dāng)然,目前 K8S 已有了類似的實(shí)現(xiàn)方式,大家可以直接使用了。
容器與 HBase 融合
我們的另一個(gè)實(shí)踐案例是將 HBase 平臺(tái)放到了容器之中。具體需求如下:
- 根據(jù)業(yè)務(wù)方去對(duì) HBase 集群予以隔離。
- 由于 HBase 的讀寫都是發(fā)生在 Region Server 節(jié)點(diǎn)上,因此需要對(duì) Region Server 予以限制。
- 由于存在著大、小業(yè)務(wù)方的不同,因此我們需要對(duì)資源利用率予以優(yōu)化。
同時(shí),由于數(shù)據(jù)都是被存放到 HDFS 上之后,再加載到內(nèi)存之中,因此我們可以在內(nèi)存里通過 Cache 進(jìn)行高性能的讀寫操作。
可見,Region Server 的性能瓶頸取決于 CPU 與內(nèi)存的開銷。
因此我們將每個(gè) HBase Cluster 都放到了 Kubernetes 的 Namespace 里,然后對(duì) HBase Master 和 Region Server 都采用 Deployment 部署到容器中。
其中 Master 較為簡(jiǎn)單,只需通過 Replication 來實(shí)現(xiàn)高可用性;而 Region Server 則針對(duì)大、小業(yè)務(wù)方進(jìn)行了資源限制。
例如:我們給大的業(yè)務(wù)方分配了 8 核 + 64G 內(nèi)存,而給小的業(yè)務(wù)方只分配 2 核 + 16G 內(nèi)存。
另外由于 ZooKeeper 和 HDFS 的負(fù)載較小,如果直接放入容器的話,則會(huì)涉及到持久化存儲(chǔ)等復(fù)雜問題。
因此我們讓所有的 HBase 集群共享相同的 ZooKeeper 和 HDFS 集群,以減少手工維護(hù) ZooKeeper 和 HDFS 集群的開銷。
容器與 Spark Streaming 融合
不同于那些需要大量地讀寫 HDFS 磁盤的 Map-Reduce 任務(wù),Spark Streaming 是一種長駐型的任務(wù)。
它在調(diào)度上,不需要去優(yōu)化處理大數(shù)據(jù)在網(wǎng)絡(luò)傳輸中的開銷,也不需要對(duì) HDFS 數(shù)據(jù)做 Locality。
然而,由于它被用來做實(shí)時(shí)處理計(jì)算,因此對(duì)機(jī)器的負(fù)載較為敏感。如果機(jī)器的負(fù)載太高,則會(huì)影響到它處理的“落地”時(shí)延。
而大數(shù)據(jù)處理集群的本身特點(diǎn)就是追求高吞吐率,因此我們需要將 Spark Streaming 從大數(shù)據(jù)處理的集群中隔離出來,然后將其放到在線業(yè)務(wù)的容器之中。
在具體實(shí)踐上,由于當(dāng)時(shí)尚無 Spark 2.3,因此我們自己動(dòng)手將 YARN 的集群放到了容器之中。
即:首先在 Docker 里啟動(dòng) YARN 的 Node Manager,將其注冊(cè)到 Resource Manager 之上,以組成一個(gè)在容器里運(yùn)行的 YARN 集群。
然后我們?cè)偻ㄟ^ Spark Submit 提交一項(xiàng) Spark Streaming 任務(wù)到該集群處,讓 Spark Streaming 的 Executor 能夠運(yùn)行在該容器之中。
如今,Spark 2.3 之后的版本,都能支持對(duì)于 Kubernetes 調(diào)度器的原生使用,大家再也不必使用 YARN,而可以直接通過 Kubernetes 使得 Executor 運(yùn)行在容器里了。
大數(shù)據(jù)平臺(tái)的 DevOps 管理
在大數(shù)據(jù)平臺(tái)的管理方面,我們踐行了 DevOps 的思想。例如:我們自行研發(fā)了一個(gè) PaaS 平臺(tái),以方便業(yè)務(wù)方直接在該平臺(tái)上自助式地對(duì)資源進(jìn)行申請(qǐng)、創(chuàng)建、使用、擴(kuò)容、及管理。
根據(jù) DevOps 的思想,我們定位自己為工具開發(fā)的平臺(tái)方,而非日常操作的運(yùn)維方。
我們都通過該 PaaS 平臺(tái)的交付,讓業(yè)務(wù)方能夠自行創(chuàng)建、重啟、擴(kuò)容集群。
此舉的好處在于:
- 減少了溝通的成本,特別是在公司越來越大、業(yè)務(wù)方之間的溝通越來越復(fù)雜之時(shí)。
- 業(yè)務(wù)便捷且有保障,他們的任何擴(kuò)容需求,都不需要聯(lián)系我們,而直接可以在該平臺(tái)上獨(dú)立操作完成。
- 減輕了日常工作的負(fù)擔(dān),我們能夠更加專注于技術(shù)本身,專注于如何將該平臺(tái)的底層技術(shù)做得更好。
由于業(yè)務(wù)本身對(duì)于像 Kafka、HBase 之類系統(tǒng)的理解較為膚淺,因此我們需要將自己積累的對(duì)于集群的理解和經(jīng)驗(yàn),以一種專家的視角呈現(xiàn)給他們。
作為 DevOps 實(shí)踐中的一環(huán),我們?cè)谠摯髷?shù)據(jù)平臺(tái)上提供了豐富的監(jiān)控指標(biāo)。
圖中以 Kafka 為例,我們提供的監(jiān)控指標(biāo)包括 Topic Level、Broker Level,和 Host Level。
可見,我們旨在將 Kafka 集群變成一個(gè)“白盒”,一旦發(fā)生故障,業(yè)務(wù)方就能直接通過我們所定制的報(bào)警閾值,在指標(biāo)界面上清晰地看到各種異常,并能及時(shí)自行處理。
總結(jié)
從實(shí)踐經(jīng)驗(yàn)來看,我們的基本思路是:
- 按業(yè)務(wù)方進(jìn)行集群隔離。
- 利用 K8S 進(jìn)行多集群部署和管理。
- 利用 Docker 進(jìn)行資源隔離和監(jiān)控。
- 利用 Docker 實(shí)現(xiàn)更細(xì)粒度資源分配。
- 運(yùn)用 DevOps 實(shí)現(xiàn)運(yùn)維管理。
后續(xù),我們將嘗試更多的基礎(chǔ)設(shè)施組件,利用 K8S 去實(shí)現(xiàn)集群的隔離,實(shí)現(xiàn)更細(xì)粒度的資源分配和進(jìn)程級(jí)的資源監(jiān)控。
通過更好地在生產(chǎn)環(huán)境中實(shí)施管理和維護(hù),以及提升資源的利用率,我們將為業(yè)務(wù)提供更穩(wěn)定的 PaaS 平臺(tái)服務(wù),并最終實(shí)現(xiàn)數(shù)據(jù)中心的資源統(tǒng)一。
同時(shí),我們通過交給 K8S 來進(jìn)行調(diào)度管理,進(jìn)而實(shí)現(xiàn) DCOS(數(shù)據(jù)中心操作系統(tǒng))。
張阜興,知乎計(jì)算平臺(tái)負(fù)責(zé)人。2012 年從中科院計(jì)算所畢業(yè)后,分別在搜狐和雅虎從事分布式存儲(chǔ)系統(tǒng)研發(fā)和云平臺(tái)建設(shè)的工作,加入知乎后從無到有推動(dòng)了知乎內(nèi)部容器平臺(tái)的建設(shè),目前主要研究方向在資源調(diào)度管理以及大數(shù)據(jù)計(jì)算和存儲(chǔ)等。
【51CTO原創(chuàng)稿件,合作站點(diǎn)轉(zhuǎn)載請(qǐng)注明原文作者和出處為51CTO.com】