1. 程式人生 > >Kubernetes 在知乎上的應用_Kubernetes中文社群

Kubernetes 在知乎上的應用_Kubernetes中文社群

知乎在 2014 年開始使用容器技術,至今為止幾乎所有的業務都執行在容器平臺上。知乎最初使用 Mesos 來管理容器叢集,現在正處於向 Kubernetes 遷移的過程中。本次分享主要介紹知乎應用 Kubernetes 管理容器叢集的一些經驗。

從 Mesos 到 Kubernetes

之前的排程框架是基於 Mesos 自研的。採用的語言是 Python。運行了大概兩年多的時間了,也一直比較穩定。但隨著業務的增長,現有的框架的問題逐漸暴露。

  1. 排程速度遇到瓶頸,影響大業務的部署速度。
  2. 不能很好的支援有狀態服務。

解決上述問題的方案有兩個,一個是對現有系統進行改進重構,另一個是遷移到 Kubernetes。我們最終選擇遷移到 Kubernetes,主要基於以下考慮。

  1. Kubernetes 的架構設計簡單明瞭,容器管理的抽像做的很好,重易進行復用和二次開發,沒有必要造重複的輪子。比較典型的像Pod、Mesos 也已經引進了類似概念。
  2. Kubernetes 已經逐漸成為業界主流。社群很活躍,新的特性不斷地被新增進來,這導致 Kubernetes 變的越來越重,但基本的架構和核心功能是一直比較穩定的。
  3. 相對於 Mesos 來講,基於 Kubernetes 的開發成本是要低一些的,尤其是在熟悉之後。便於 k8s 的推廣使用。除了主要的業務執行平臺 bay,我們的負載均衡平臺、Kafka 平臺以及定時任務平臺全部都是基本 Kubernetes 的。

整體架構

資源層

這一層主要是叢集資源,主要包括記憶體、CPU、儲存、網路等。主要執行的元件有 Docker daemon、kubelet、cAdvisor、CNI 網路外掛等,主要為上層提供資源。

控制層(Kubernetes master)

控制層主要包括 Kubernetes 的 master 元件,Scheduler、Controller、API Server。提供對 Kubernetes 叢集資源的控制。

接入層(Watch Service、API)

這一層包含的東西比較多,主要包含各個平臺用於接入 Kubernetes 叢集所開發的元件。主要包含以下元件:

  1. 容器平臺 bay 。
    一是用來向部署系統提供容器/容器組的建立、更新、刪除、擴容等介面,是對 Kubernetes 原生 API 的一層封裝,主要是為了與部署系統對接。二是用來註冊服務發現、業務監控報警等所需要的配置。
  2. 負載均衡平臺(Load Balance)。
    我們的負載均衡平臺採用的主要是 Haproxy。也是跑在容器裡的。由於負載均衡系統的特殊性,並沒有跑在容器平臺 bay 上面,而是單獨開發了一個平臺對 HAProxy 進行管理。可以方便的建立和管理 HAProxy 叢集,並在業務流量發生變化的時候進行自動或者手動擴容。
  3. Kafka 平臺(Kafka)。 主要提供在 Kubernetes 上建立管理 Kafka 叢集的服務。我們在 Kubernetes 之上開發了一套基於本地磁碟的排程框架,可以使 pod 根據叢集中的本地磁碟資訊進行排程。沒有使用共享儲存系統主要是為了效能考慮。最新的 Kubernetes 好像也加入了類似本地磁碟的排程特性,不過在我們開發的時候是還沒有支援的。
  4. 定時任務平臺。
    這個平臺是在 Kubernetes 支援 Cron job 之前開發的。也是最早接入 Kubernetes 的服務。

管理層(Castle Black;Monitor;Auto Scale)

主要是根據接入層提供的一些配置或者資訊來完成特定的功能。

  1. Castle Black,這個服務是一個比較關鍵的服務。這個服務通過 Kubernetes 的 watch API,及時的同步業務容器的資訊,根據容器資訊進行服務註冊和反註冊。我們主要是註冊 Consul 和 DNS。Kafka 平臺和負載均衡平臺也依賴於這個服務進行服務註冊。同時對外提供查詢介面,可以查詢業務的實時容器資訊。
  2. Monitor,這個主要是業務容器的監控,主要包含該業務總容器數、不正常容器數以及註冊資訊是否一致等資訊,CPU 和記憶體等資源的監控我們採用 cAdvisor 和我們內部的監控系統來實現。
  3. Auto Scale,我們沒有使用 Kubernetes 本身的自動擴容機制,而是單獨進行了開發,主要是為了支援更加靈活的擴容策略。

配置層(etcd)

應用層的元件所需要的配置資訊全部由接入層的服務寫入到 etcd 中。應用層元件通過 watch etcd 來及時獲取配置的更新。

下面這張圖說明了在我們的容器平臺上,上面描述的一些元件是如何結合在一起,使業務可以對外提供服務的。通過 bay 平臺向 Kubernetes APIServer傳送請求,建立 deployment,pod 建立成功並且健康檢查通過後,Castle Black watch 到 pod 資訊,將 IP,port 等資訊註冊到 Consul 上,HAProxy watch 對應的 Consul key,將 pod 加入其後端列表,並對外提供服務。

監控與報警

cAdvisor

我們的監控指標的收集主要是採用 CAvisor。沒有采用 Heapster 的主要原因有以下幾點:

  1. 針對 CAvisor 我們進行了二次開發,與內部指標系統結合的也比較好,應用的時間也較長。
  2. Heapster 採用 pull 模型,雖然是並行 pull,但在叢集規模較大的情況下,有成為效能瓶頸的可能,而且目前無法進行橫向擴充套件。
  3. 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。

上述流程如下圖所示:

IP 池的分配由我們的雲服務商提供,我們不需要管具體的 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

這個是 Kubernetes 的一個特性,如果由於網路或者機器原因,node 離線了,變為 unready 狀態。Kubernetes 的 node controller 會將該 node 上的 pod 刪除,稱作 pod eviction。這個特性應該說是合理的,但在大概是 1.5 版本之前,當叢集中所有的 node 都變為 unready 狀態的時候,所有 node 上的 pod 都會被刪除。這個其實是不合理的,因為出現這種情況大概率是 API Server 的機器網路出了問題,所以這個時候不應該把所有 node 上的 pod 全部刪除。最新的版本將這個特性進行了改進,叢集中 ready 的 node 達到一定數量的情況下,才對 not ready 的 node 進行 pod eviction。這個就比較合理了。另外提醒大家一定要做好 API Server 的高可用。

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 肯定是不夠用的,需要自己進行調整。

作者簡介:王路,容器開發工程師,主要負責知乎容器平臺的開發和運維工作。