原文 Scaling Kubernetes to 2,500 Nodes

緣起

最近比較忙碌,都只能夜深人靜才能好好的來閱讀一些文章來充實自己.(公司牛人多到像牧場,只好不斷努力 lol )

OpenAI 最近有一篇技術文章,相當的值得一讀.就是他們分享他們如何管理超過 2500 個節點. 當然我們都知道,

Kubernetes 自從 1.6 之後就號稱可以乘載 5000 個節點以上,但是從 數十台到 2500 台的路上,難道不會遇到一些問題嗎? 這篇文章分享了他們遇到的問題,試著要解決與懷疑的地方,最後找到真正的問題.這篇文章適合所有 DevOp 好好的熟讀.

遇到的問題與解決方式

第一次遇到問題: 1 ~ 500 個節點之後

  • 問題徵兆:
    • kubectl 有時候會發生 timeout. (p.s. `kubectl -v=6 可以顯示所有API 細節指令)
  • 嘗試解決方式:
    • 一開始以為是 kube-apiserver 服務器過分忙碌,試著增加 proxy 來做 replica 來幫忙 load balance
    • 但是超過 10 個備份 master 的時候,他們發現問題一定不是因為 kube-apiserver 無法承受.因為 GKE 透過一台 32-core VM 就可以乘載 500台
  • 原因:
    • 扣除掉這些原因,開始要懷疑 master 上剩下的幾個服務. (etcd, kube-proxy)
    • 於是開始試試看來調整 etcd
    • 透過使用 datadog 來調查 etcd 的吞吐量,發現有異常延遲 (latency spiking ~100 ms)
    • 透過 Fio 工具來做效能評估,發現只用到了 10% 的IOPS(Input/Output Per Second) , 由於寫入延遲 (write latency 2ms),造成整個效能被拖累.
    • 試著把 SSD 從網路硬碟變成每台機器有個 local temp drive (依舊是 SSD)
    • 結果從 ~100ms —> 200us

第二次遇到問題: ~1000 個節點的時候

  • 問題徵兆:
    • 發現 kube-apiserver 每秒需要從 etcd 上面讀取 500Mb
  • 嘗試解決方式:
    • 透過 Prometheus (一種廣泛用於 Kubernetes 上面作為 Container 資料搜集與監控工具) 來查看每個 container 之間的網路流量
  • 原因:
    • 發現 Fluentd (一個作為資料與 log 轉發的工具) 與 Datadog ,太頻繁來抓取每個節點上面的資料.
    • 調低這兩個服務的抓取頻率,網路效率馬上就從 500 Mb/s 到幾乎沒有…
    • etcd 小技巧: 透過 --etcd-servers-overrides 可以將 Kubernetes Event 的資料寫入作為切割,分不同機器處理.範例為 :
    --etcd-servers-overrides=/events#https://0.example.com:2381;https://1.example.com:2381;https://2.example.com:2381
    

第三次問題: 當經營到 1000~2000節點

  • 問題徵兆:
    • 無法再寫入資料,回報 cascading failure
    • kubernetes-ec2-autoscaler 在全部的 etcd 都爆掉之後才回傳問題,並且關閉所有的 etcd
  • 嘗試解決方式:
    • 猜測 etcd 硬碟爆滿,但是檢查 SSD 硬碟空間依舊有許多空間.
    • 檢查是否有預設設定空間限制,發現有 2GB 大小限制
  • 解決方式:
    • etcd 啟動參數加上 --quota-backend-bytes
    • 修改 kubernetes-ec2-autoscaler logic 如果超過 50% 都出問題,預警性的關閉集群.

## 各種服務的優化

除了遇到的問題解決外, OpenAI 也提供他們一些設定方式. 可以幫助你更順利的在大量集群上的運作.

Kube masters 的 HA (High Availability)

一般來說,我們架設都是以一個 kube-master (主要的 Kubernetes 服務提供者,上面有 kube-apiserver, kube-schedulerkube-control-manager ) 加上多個 slave . 但是要達到 HA 設定,要根據以下的方式:

  • kube-apiserver 要設置多個服務,並且透過參數 --apiserver-count 重啟並且設定.
  • kubernetes-ec2-autoscaler 可以幫你自動關閉 idle 的資源,但是這跟 Kubernetes scheduler 的原則相反. 不過透過這些設定,可以幫助你盡量把資源集中.
{
"kind" : "Policy",
"apiVersion" : "v1",
"predicates" : [
  {"name" : "GeneralPredicates"},
  {"name" : "MatchInterPodAffinity"},
  {"name" : "NoDiskConflict"},
  {"name" : "NoVolumeZoneConflict"},
  {"name" : "PodToleratesNodeTaints"}
  ],
"priorities" : [
  {"name" : "MostRequestedPriority", "weight" : 1},
  {"name" : "InterPodAffinityPriority", "weight" : 2}
  ]
}

上面是個調整你 Kubernetes scheduler 的範例,這個範例會透過把 InterPodAffinityPriority 的權重調高,達到我們要的調整方向.更多的部分,可以參考原始範例

不過要注意,目前 Kubernetes Scheduler Policy 並不支援動態切換,需要重起你的 kube-apiserver (issue: 41600)

參考文件: VF技術部落格: 深入kubernetes调度之原理分析 – 關於 Kubernetes Scheduler 相關介紹文件(中文)

調整 scheduler policy 造成的影響

OpenAI 有使用 KubeDNS: 一個 Kubernetes 內部使用的 DNS ,但是經過不久發現

  • 問題徵兆:

    • 經常有 DNS 忽然查詢不到的 (隨機發生)
    • 超過 ~200QPS domain lookup
  • 嘗試解決問題方式:

    • 試著察看為何有這種狀態,發現有些 node 上面有跑超過 10+ KubeDNS
  • 解決方式:

    • 由於 scheduler policy 造成許多 POD 會儘量的集中.
    • KubeDNS 相當的 lightweight,所以很容易被分配到同一個節點.造成 domain lookup 集中.
    • 需要修改 POD affinity (這邊有更多相關介紹),儘量讓 KubeDNS 不要都分配到同一個節點 (node)
    affinity:
     podAntiAffinity:
       requiredDuringSchedulingIgnoredDuringExecution:
       - weight: 100
         labelSelector:
           matchExpressions:
           - key: k8s-app
             operator: In
             values:
             - kube-dns
         topologyKey: kubernetes.io/hostname
    

緩慢的 Docker image pulls (針對新建節點)

  • 問題徵兆:
    • 每次一個新的節點建立起來,都得花約 30mins 在 docker image pull
  • 嘗試解決方式:
    • 有一個相當龐大的 container image Dota, 裡面約莫 17GB.他影響了整個節點的 image pulling
    • 開始查看 kubelet 是否有其他選項可以讓你 image pull
  • 解決方式:
    • kubelet 加上選項 --serialize-image-pulls=false 來啟動 image pulling 來讓其他服務可以比較早 pull 參考:kubelet 啟動選項
    • 並且把 docker image 存放位置放到 SSD ,讓 image pull 更快

補充: 特地下去把 source trace 了一下

// serializeImagePulls when enabled, tells the Kubelet to pull images one
// at a time. We recommend *not* changing the default value on nodes that
// run docker daemon with version  < 1.9 or an Aufs storage backend.
// Issue #10959 has more details.
SerializeImagePulls *bool `json:"serializeImagePulls"`

增加 docker image pull 速度

此外,還可以透過以下方式來增加 pull 速度

  • kubelet 有個參數 --image-pull-progress-deadline 要提高到 30mins
  • docker daemon 有個參數 max-concurrent-download 要調到 10 才能多線程下載

網路效能的提升

Flannel 效能限制

OpenAI 節點間的網路流量,可以達到 10-15Gbit/s .但是由於 Flannel 所以導致流量會降到 ~2Gbit/s

解決方式就是將 Flannel 拿掉,使用實體網路.

  • hostNetwork: true
  • dnsPolicy: ClusterFirstWithHostNet

這裡有一些警告需要詳細閱讀

參考文章 :


Buy Me A Coffee

Evan

Attitude is everything