之前的調度框架是基於 Mesos 自研的。採用的語言是 Python。運行了大概兩年多的時間了,也一直比較穩定。但隨着業務的增長,現有的框架的問題逐漸暴露。
-
調度速度遇到瓶頸,影響大業務的部署速度。
-
不能很好的支持有狀態服務。
解決上述問題的方案有兩個,一個是對現有系統進行改進重構,另一個是遷移到 Kubernetes。我們最終選擇遷移到 Kubernetes,主要基於以下考慮。
-
Kubernetes 的架構設計簡單明瞭,容器管理的抽像做的很好,重易進行復用和二次開發,沒有必要造重複的輪子。比較典型的像Pod、Mesos 也已經引進了類似概念。
-
Kubernetes 已經逐漸成爲業界主流。社區很活躍,新的特性不斷地被添加進來,這導致 Kubernetes 變的越來越重,但基本的架構和核心功能是一直比較穩定的。
-
相對於 Mesos 來講,基於 Kubernetes 的開發成本是要低一些的,尤其是在熟悉之後。便於 Kubernetes 的推廣使用。除了主要的業務運行平臺 bay,我們的負載均衡平臺、Kafka 平臺以及定時任務平臺全部都是基本 Kubernetes 的。
資源層
這一層主要是集羣資源,主要包括內存、CPU、存儲、網絡等。主要運行的組件有 Docker daemon、kubelet、cAdvisor、CNI 網絡插件等,主要爲上層提供資源。
控制層(Kubernetes master)
控制層主要包括 Kubernetes 的 master 組件,Scheduler、Controller、API Server。提供對 Kubernetes 集羣資源的控制。
接入層(Watch Service、API)
-
容器平臺 bay 。
一是用來向部署系統提供容器/容器組的創建、更新、刪除、擴容等接口,是對 Kubernetes 原生 API 的一層封裝,主要是爲了與部署系統對接。二是用來註冊服務發現、業務監控報警等所需要的配置。
-
負載均衡平臺(Load Balance)。
我們的負載均衡平臺採用的主要是 Haproxy。也是跑在容器裏的。由於負載均衡系統的特殊性,並沒有跑在容器平臺 bay 上面,而是單獨開發了一個平臺對 HAProxy 進行管理。可以方便的創建和管理 HAProxy 集羣,並在業務流量發生變化的時候進行自動或者手動擴容。
-
Kafka 平臺(Kafka)。
主要提供在 Kubernetes 上創建管理 Kafka 集羣的服務。我們在 Kubernetes 之上開發了一套基於本地磁盤的調度框架,可以使 pod 根據集羣中的本地磁盤信息進行調度。沒有使用共享存儲系統主要是爲了性能考慮。最新的 Kubernetes 好像也加入了類似本地磁盤的調度特性,不過在我們開發的時候是還沒有支持的。
-
定時任務平臺。
這個平臺是在 Kubernetes 支持 Cron job 之前開發的。也是最早接入 Kubernetes 的服務。
管理層(Castle Black;Monitor;Auto Scale)
主要是根據接入層提供的一些配置或者信息來完成特定的功能。
-
Castle Black,這個服務是一個比較關鍵的服務。這個服務通過 Kubernetes 的 watch API,及時的同步業務容器的信息,根據容器信息進行服務註冊和反註冊。我們主要是註冊 Consul 和 DNS。Kafka 平臺和負載均衡平臺也依賴於這個服務進行服務註冊。同時對外提供查詢接口,可以查詢業務的實時容器信息。
-
Monitor,這個主要是業務容器的監控,主要包含該業務總容器數、不正常容器數以及註冊信息是否一致等信息,CPU 和內存等資源的監控我們採用 cAdvisor 和我們內部的監控系統來實現。
-
Auto Scale,我們沒有使用 Kubernetes 本身的自動擴容機制,而是單獨進行了開發,主要是爲了支持更加靈活的擴容策略。
配置層(etcd)
應用層的組件所需要的配置信息全部由接入層的服務寫入到 etcd 中。應用層組件通過 watch etcd 來及時獲取配置的更新。
cAdvisor
我們的監控指標的收集主要是採用 CAvisor。沒有采用 Heapster 的主要原因有以下幾點:
-
針對 CAvisor 我們進行了二次開發,與內部指標系統結合的也比較好,應用的時間也較長。
-
Heapster 採用 pull 模型,雖然是並行 pull,但在集羣規模較大的情況下,有成爲性能瓶頸的可能,而且目前無法進行橫向擴展。
-
Heapster 中默認提供的很多聚合指標我們是不需要的。也沒有維護兩個監控系統的必要。
內部指標與報警系統
指標和報警都是用的我們內部比較成熟的系統。
Logspout Kafka ES/HDFS,日誌收集我們使用的也是 ELK,但跟通常的 ELK 有所不同。我們這裏的 L 用的是 Logspout,一個主要用於收集容器日誌的開源軟件。我們對其進行了二次開發,使之可以支持動態 topic 收集。我們通過環境變量的形式把 topic 注入到容器中。logspout 會自動發現這個容器並提取出 topic,將該容器的日誌發送到 Kafka 對應的 topic 上。因此我們每個業務日誌都有自己的 topic,而不是打到一個大的 topic 裏面。日誌打到 Kafka 裏之後,會有相應的 consumer 消費日誌,落地 ES 和 HDFS。ES 主要用來作日誌查詢,HDFS 主要用來做日誌備份。
CNI Bridge host-local,網絡部分我們做的比較簡單。首先我們的每個主機都給分配了一個 C 段的 IP 池,這個地址段裏的每個 IP 都是可以跨主機路由的。IP 地址從 X.X.X.2 到 X.X.X.255,容器可以使用的地址是 X.X.X.3 到 X.X.X.224,這個 IP 數量是足夠的。然後在主機上創建一個該地址段的 Linux Bridge。容器的 IP 就從 X.X.X.3 到 X.X.X.224 這個地址空間內分配,容器的 veth pair 的一段掛在 Linux Bridge 上,通過 Linux Brigde 進行跨主機通信。性能方面基本沒有損耗。
具體的實現我們採用了 Bridge 和 host-local 這兩個 CNI 插件,Bridge 主要用來掛載/卸載容器的 veth pair 到 Linux Bridge 上,host-local 主要利用本地的配置來給容器分配 IP。
上面主要介紹了知乎在容器和 Kubernetes 應用的一些現狀,在這個過程中我們也踩了不少坑,在這裏與大家分享一下。
etcdv3 版本問題
Kubernetes 的較新版本默認使用的存儲後端是 etcd3。etcd 選用的版本不對,是會有坑的。etcd 3.10 之前的版本,V3 的 delete api 默認是不會返回被刪除的 value 的。導致 Kubernetes API server 會收不到 delete event。被佔用的資源會得不到釋放。最終導致資源耗盡。scheduler 無法再調度任何任務。詳細信息可以看這個 issue(https://github.com/coreos/etcd/issues/4620)。
Pod Eviction
CNI 插件 Docker daemon 重啓 IP 泄露
在使用 CNI 網絡插件的時候,如果 Docker daemon 發生了重啓,會重新分配新的 IP,但舊的 IP 不會被釋放,會導致 IP 地址的泄漏。由於時間和精力問題,我們採取了比較 tricky 的方式,在 Docker dameon 啓動之前,我們會默認把本機的 IP 全部釋放掉。這個是通過 Supervisor 的啓動腳本來實現的。希望後續 Kubernetes 社區可以從根本上解決這個問題。
Docker bug
Docker 使用過程中也遇到了一些 bug。比如 docker ps 會卡住, 使用 portmapping 會遇到端口泄漏的問題等。我們內部自己維護了一個分支,修復了類似的問題。Docker daemon 是基礎,它的穩定性一定要有保證,整個系統的穩定性纔有保證。
Rate Limit
Kubernetes 的 Controller manager、Scheduler、以及 API Server 都是有默認的 rate limit 的,在集羣規模較大的時候,默認的 rate limit 肯定是不夠用的,需要自己進行調整。
轉自:https://mp.weixin.qq.com/s/ZYAu_dBXLkWkP2ARd6uZ2w