歡迎光臨
每天分享高質量文章

Kubernetes節點擴容規模至2500的歷程

我們使用Kubernetes做深度學習的研究已經超過兩年。儘管我們最大的工作量直接管理裸雲VM,但是Kubernetes提供了快速的迭代周期、具有合理的擴展性以及不含樣板代碼(a lack of boilerplate),所以在Kubernetes上進行的實驗大多都達到了我們預期。我們現在運行著幾個Kubernetes集群(一些在雲上,一些在物理機機上),其中最大的已經超過2500節點。這些集群運行在Azure提供的D15v2和NC24 組合虛擬機上。
達到現在這個節點規模,我們也經歷過很多問題。比如許多系統組件引起的問題:包括etcd、Kubernetes的master節點、Docker鏡像獲取問題、Network問題、KubeDNS,甚至是我們機器上的ARP快取。我認為分享這些我們遇到的具體問題,以及我們是怎麼解決這些問題是很有意義的。

etcd

在我們集群到500個節點之後,我們的researchers開始從kubectl命令列工具得到定時超時的警告。 我們嘗試添加更多Kubernetes master(運行kube-apiserver的VM)。 這似乎暫時解決了這個問題,但是一連經歷10次後,我們意識到這隻是在處理癥狀,而沒發現真正的原因(相比之下,GKE使用32位單核虛擬機支撐500個節點)。
這讓我們非常懷疑是提供Kube master中央狀態儲存區的etcd集群出了問題。 從Datadog看來,儘管每台機器都使用能夠達到5,000 IOPS的P30 SSD,但是在運行我們的etcd副本的DS15v2機器上,我們看到寫入有幾百毫秒的延遲。

這些延遲峰值阻塞了整個集群!
在fio[1]的基準測試中,我們發現etcd只能使用大約10%的IOPS,因為寫延遲是2ms,etcd是連續的I/O,因此引起一連串的延遲。
然後,我們將每個節點的etcd目錄移動到本地臨時磁盤,這是一個直接連接到實體的SSD,而不是網絡連接。切換到本地磁盤帶後寫延遲達到200us,etcd恢復正常!
直到我們達到大約1,000個節點以前,我們的集群運行良好。在1,000這一點上,我們再次看到了etcd的提交高延遲。這一次,我們註意到kube-apiservers從etcd上讀取了超過500MB/s。我們設置了Prometheus來監視apiservers,還設置了–audit-log-path和–audit-log-maxbackup標誌,以便在apiserver上啟用更多的日誌記錄。這就出現了一些緩慢的查詢和對事件串列API的過度呼叫。
根本原因:Fluentd和Datadog監控行程的預設設置是從集群中的每個節點查詢apiservers(例如,現在已解決的問題[2])。 我們只是簡單地改變了這些呼叫的過程,使apiservers的負載變得穩定。

etcd出口從500MB / s 下降到幾乎為0(上圖中的負值表示出口)
另一個有用的調整是將Kubernetes事件儲存在一個單獨的etcd集群中,以便事件創建中的峰值不會影響主要etcd實體的性能。 要做到這一點,我們只需將–etcd-servers-overrides標誌設置為如下所示:
-etcd-servers-overrides=/events#https://0.example.com:2381;https://1.example.com:2381;https://2.example.com:2381
另一個超過1,000後節點故障是超過了etcd的硬碟儲存限制(預設2GB),導致硬碟拒絕寫入。 這引發了一個級聯失敗:所有的Kube節點都健康檢查失敗,我們的autoscaler決定它需要終止所有的任務。 我們用–quota-backend-bytes標誌增加了max etc的大小,現在autoscaler有了一個智慧的檢查,如果它終止超過50%的集群,不會採取行動。

Kube masters

我們在同一臺機器上對kube-apiserver、kube- controllermanager和kube-scheduler行程進行定位。對於高可用性,我們總是至少有2個master,並將apiserver-count標誌設置為我們正在運行的apiservers的數量(否則,Prometheus監視會在實體間混淆)。
我們主要使用Kubernetes作為批量調度系統,並依靠我們的自動調節器動態擴容和縮容我們的集群——這使我們可以顯著降低空閑節點的成本,同時在快速迭代時仍然保證低延遲。 預設的kube-scheduler策略是在負載均勻分佈在節點之間。但是我們希望相反,這樣可以終止未使用的節點,也可以快速調度大的Pods。 所以我們切換到以下策略:
{
"kind" : "Policy",
"apiVersion" : "v1",
"predicates" : [
 {"name" : "GeneralPredicates"},
 {"name" : "MatchInterPodAffinity"},
 {"name" : "NoDiskConflict"},
 {"name" : "NoVolumeZoneConflict"},
 {"name" : "PodToleratesNodeTaints"}
 ],
"priorities" : [
 {"name" : "MostRequestedPriority", "weight" : 1},
 {"name" : "InterPodAffinityPriority", "weight" : 2}
 ]
}
我們的服務發現功能廣泛使用KubeDNS,但在推出新的調度策略後不久就開始出現可靠性問題。 我們發現,失敗只發生在KubeDNS的某些Pods上。 在新的調度策略下一些機器最終運行了10多個KubeDNS副本,創建了熱點,而且我們已經超過了每個Azure虛擬機允許的查詢〜200QPS限制。
我們通過為KubeDNS Pod添加一個anti-affinity規則[3]來解決這個問題:
affinity:
podAntiAffinity:
  requiredDuringSchedulingIgnoredDuringExecution:
  - weight: 100
    labelSelector:
      matchExpressions:
      - key: k8s-app
        operator: In
        values:
        - kube-dns
    topologyKey: kubernetes.io/hostname

Docker image pulls

我們的Dota專案是在Kubernetes上起步的,隨著規模的擴大,我們註意到新增的Kubernetes節點經常有很長一段時間處在Pending。 游戲鏡像大概是17GB,通常需要30分鐘才能拉上一個新的集群節點,所以我們理解了Dota容器為什麼會暫停一段時間——但是其他容器也是如此。 進一步挖掘,我們發現kubelet有一個–serialize-image-pullsflag預設為true,這意味著Dota鏡像阻塞了所有其他鏡像。 更改為false需要將Docker切換到overlay2而不是AUFS。 為了進一步提高獲取鏡像的速度,我們也將Docker root 移到了實體連接的SSD上,就像我們為etcd機器所做的那樣。
即使在優化獲取鏡像速度之後,我們也看到Pod無法啟動一個詭異的錯誤信息:rpc error: code = 2 desc = net/http: request canceled。 kubelet和Docker日誌訊息顯示“由於缺乏進度,鏡像的獲取已經取消”。 我們追蹤了問題的根源是需要花費太多時間來獲取/加壓提取的大鏡像,或者當我們很多積壓的鏡像要獲取的時候。 為瞭解決這個問題,我們將kubelet的-image-pull-progress-deadline標誌設置為30分鐘,並將Docker守護行程的最大併發下載選項設置為10。(第二個選項沒有加速獲取大鏡像,但允許鏡像佇列並行獲取。)
我們最後一個Docker問題是由於Google Container Registry造成的。 預設情況下,kubelet從gcr.io獲取特殊的鏡像(由–pod-infra-container-image標誌控制)gcr.io經常用於創建一個新的容器。 如果因為任何原因(例如超過配額)而導致失敗,該節點將無法啟動任何容器。 由於我們的節點通過NAT到達gcr.io而不是擁有自己的公有IP,所以我們很可能會達到這個每IP配額的限制。 為瞭解決這個問題,我們通過使用docker image save -o /opt/preloaded_docker_images.tar和docker image load -i /opt/preloaded_docker_images.tar,簡單地在我們的Kubernetes worker的機器鏡像中預先加載了Docker鏡像。 為了提高性能,我們對於像Dota鏡像這樣的常見OpenAI內部圖像的白名單也是這樣做的。

Networking

隨著我們的實驗規模越來越大,它們也變得越來越複雜,這些系統在很大程度上依賴於網絡的運作。當我們第一次開始運行分佈式實驗時,很明顯我們的網絡並沒有被很好地配置。在機器之間,我們得到了10-15Gbit/s的吞吐量,但是我們使用Flannel的Kube Pod在~2Gbit/s上是最大的。機器區域的公共基準測試[4]顯示了類似的數字,這意味著這個問題不太可能是糟糕的配置,而是我們的環境所固有的一些東西。(相比之下,Flannel並沒有在我們的物理機器上增加這個開銷。)
為瞭解決這個問題,用戶可以添加兩個不同的設置來禁用他們的Pod:hostNetwork: true和dnsPolicy: ClusterFirstWithHostNet。(在此之前,請閱讀Kubernetes文件中的警告[5]。)

ARP Cache

儘管我們進行了DNS調優,但我們仍然看到DNS解析的間歇性問題。有一天,一位工程師報告說,nc -v到他們的Redis服務器上花了30秒的時間才打印出連接。我們跟蹤這個問題到內核的ARP堆棧。對Redis pod主機的初步調查顯示,該網絡出現了嚴重的問題:任何端口上的通信都被掛了好幾秒鐘,而且沒有DNS名稱可以通過本地的dnsmasq守護行程來解決,而dig命令只是打印一個加密的失敗訊息:socket。socket.c:1915: internal_send: 127.0.0.1#53: Invalid argument.。dmesg日誌信息更豐富:neighbor table overflow!這意味著ARP快取已經耗盡了空間。ARP用於將網絡地址(比如IPv4地址)映射到物理地址,比如MAC地址。幸運的是,通過在/etc/sysctl.conf中設置一些選項,這很容易解決。

net.ipv4.neigh.default.gc_thresh1 = 80000
net.ipv4.neigh.default.gc_thresh2 = 90000
net.ipv4.neigh.default.gc_thresh3 = 100000

在HPC集群中調優這個設置是很常見的,並且在Kubernetes集群中尤其重要,因為每個Pod都有自己的IP地址,它占用了ARP快取中的空間。
我們的Kubernetes集群已經有3個月的歷史了,我們計劃在2018年擴展到更大的集群。我們最近升級到1.8.4版本,並且很高興看到它現在正式支持5000。
相關鏈接:
  1. https://github.com/axboe/fio

  2. https://github.com/DataDog/dd-agent/issues/3381

  3. https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity

  4. http://machinezone.github.io/research/networking-solutions-for-kubernetes/

  5. https://kubernetes.io/docs/concepts/configuration/overview/

原文鏈接:https://blog.openai.com/scaling-kubernetes-to-2500-nodes/

Kubernetes 實戰培訓

本次培訓內容包括:Docker容器的原理與基本操作;容器網絡與儲存解析;Kubernetes的架構與設計理念詳解;Kubernetes的資源物件使用說明;Kubernetes 中的開放接口CRI、CNI、CSI解析;Kubernetes監控、網絡、日誌管理;容器應用的開發流程詳解等,點擊識別下方二維碼加微信好友瞭解具體培訓內容

3月23日開始上課,點擊閱讀原文鏈接即可報名。
赞(0)

分享創造快樂