優化生產環境中的 Kubernetes 資源分配

原文連接:優化生產環境中的 Kubernetes 資源分配html

我和 Kubernetes 的初次接觸就涉及到將應用容器化並部署到生產環境集羣中,當時個人工做重點是把 buffer 吞吐量最高(低風險)的某個端點從單個應用程序中分離出來,由於這個特殊的端點會給咱們帶來很大的困擾,偶爾還會影響到其餘更高優先級的流量。git

在使用 curl 進行一些手動測試以後,咱們決定將這個剝離出來的端點部署在 Kubernetes 上。當有 1% 的流量打進來時,服務運行正常,一切看起來都是那麼地美好;當流量增長到 10% 時,也沒有什麼大問題;最後我將流量增長到 50%,麻煩來了,這時候服務忽然陷入了 crash 循環狀態。當時個人第一反應是將該服務的副本數擴到 20 個,擴完以後有一點成效,但沒過多久 Pod 仍然陷入 crash 循環狀態。經過 kubectl describe 查看審計日誌,我瞭解到 Kubelet 由於 OOMKilled 殺掉了 Pod,即內存不足。深刻挖掘後,我找到了問題根源,當時我從另外一個 deployment 文件中複製粘貼 YAML 內容時設置了一些嚴格的內存限制,從而致使了上述一系列問題。這段經歷讓我開始思考如何纔能有效地設置資源的 requestslimitsgithub

1. 請求(requests)和限制(limits)

Kubernetes 容許在 CPU,內存和本地存儲(v1.12 中的 beta 特性)等資源上設置可配置的請求和限制。像 CPU 這樣的資源是可壓縮的,這意味着對 CPU 資源的限制是經過 CPU 管理策略來控制的。而內存等其餘資源都是不可壓縮的,它們都由 Kubelet 控制,若是超過限制就會被殺死。使用不一樣的 requests 和 limits 配置,能夠爲每一個工做負載實現不一樣的服務質量(QoS)。正則表達式

Limits

limits 表示容許工做負載消耗資源的上限,若是資源的使用量越過配置的限制閾值將會觸發 Kubelet 殺死 Pod。若是沒有設置 limits,那麼工做負載能夠佔用給定節點上的全部資源;若是有不少工做負載都沒有設置 limits,那麼資源將會被盡最大努力分配。apache

Requests

調度器使用 requests 來爲工做負載分配資源,工做負載可使用全部 requests 資源,而無需 Kubernetes 的干預。若是沒有設置 limits 而且資源的使用量超過了 requests 的閾值,那麼該容器的資源使用量很快會被限制到低於 requests 的閾值。若是隻設置了 limits,Kubernetes 會自動把對應資源的 requests 設置成和 limits 同樣。微信

QoS(服務質量)

在 Kubernetes 中經過資源和限制能夠實現三種基本的 QoS,QoS 的最佳配置主要仍是取決於工做負載的需求。app

Guaranteed QoS

經過只設置 limits 而不設置 requests 就能夠實現 Guaranteed QoS,這意味着容器可使用調度器爲其分配的全部資源。對於綁定 CPU 和具備相對可預測性的工做負載(例如,用來處理請求的 Web 服務)來講,這是一個很好的 QoS 等級。curl

Burstable QoS

經過配置 CPU 或內存的 limits 和 requests,而且 requests < limits,就能夠實現 Burstable QoS。這意味着容器的資源使用量能夠達到 requests 閾值,同時若是該容器運行的節點上資源充足,那麼容器能夠繼續使用資源,只要不超過 limits 閾值就行。這對短期內須要消耗大量資源或者初始化過程很密集的工做負載很是有用,例如:用來構建 Docker 容器的 Worker 和運行未優化的 JVM 進程的容器均可以使用該 QoS 等級。工具

Best effort QoS

經過既不設置 limits 也不設置 requests,能夠實現 Best effort QoS。這意味着容器可使用宿主機上任何可用的資源。從調度器的角度來看,這是最低優先級的任務,而且會在 Burstable QoS PodGuaranteed QoS Pod 以前被先殺掉。這對於可中斷和低優先級的工做負載很是有用,例如:迭代運行的冪等優化過程。post

2. 設置 requests 和 limits

設置 limits 和 requests 的關鍵是找到單個 Pod 的斷點。經過使用幾種不一樣的負載測試技術,能夠在應用程序部署到生產環境以前對應用程序的故障模式有一個全面的瞭解。當資源使用量達到限制閾值時,幾乎每一個應用程序都有本身的一組故障模式。

在準備測試以前,請確保將 Pod 的副本數設置爲 1,而且將 limits 設置爲一組保守的數字,例如:

# limits might look something like
replicas: 1
...
cpu: 100m # ~1/10th of a core
memory: 50Mi # 50 Mebibytes
複製代碼

**注意:**在測試過程當中設置 limits 很是重要,它可讓咱們看到預期的效果(在內存較高時限制 CPU 並殺死 Pod)。在測試的迭代過程當中,最好每次只更改一種資源限制(CPU 或內存),不要同時更改。

負載增長測試

負載增長測試會隨着時間的推移增長負載,直到負載下的服務忽然失敗或測試完成。

若是負載增長測試忽然失敗,則代表資源限制過於嚴格,這是一個很好的跡象。當觀察到圖像有明顯抖動時,將資源限制增長一倍並重復,直到測試成功完成。

當資源限制接近最優時,性能應該隨着時間的推移而可預測地下降(至少對於 Web 服務而言應該是這樣)。

若是在增長負載的過程當中性能並無太大的變化,則說明爲工做負載分配了太多的資源。

負載不變測試

在運行負載增長測試並調整資源限制以後,下一步就開始進行負載不變測試。負載不變測試會在一段很長的時間內(至少 10 分鐘,時間再長一點更好)對應用施加相同的負載,至於加多少負載,最好選擇在圖像出現斷點以前的壓力值(例如:客戶端數量)。

此測試的目的是識別內存泄漏和隱藏的排隊機制,由於這些機制在負載增長測試中很難被捕獲到。到了這個階段,即便還要對資源限制進行調整,調整的幅度也應該很小。理想狀況下,該階段測試期間性能應該會保持穩定。

記錄失敗日誌

在測試過程當中,記錄服務失敗時作了哪些操做是相當重要的。能夠將發現的故障模式添加到相關的書籍和文檔中,這對分類生產環境中出現的問題頗有用。下面是咱們在測試過程當中發現的一些故障模式:

  • 內存緩慢增長
  • CPU 使用率達到 100%
  • 響應時間太長
  • 請求被丟棄
  • 不一樣請求的響應時間差別很大

你最好將這些發現都收集起來,以備不時之需,由於有一天它們可能會爲你或團隊節省一成天的時間。

3. 一些有用的工具

雖然你可使用 Apache Bench 等工具來增長負載,也可使用 cAdvisor 來可視化資源使用率,但這裏我要介紹一些更適合負載測試的工具。

Loader.io

Loader.io 是一個在線負載測試工具,它容許你配置負載增長測試和負載不變測試,在測試過程當中可視化應用程序的性能和負載,並能快速啓動和中止測試。它也會保存測試結果的歷史記錄,所以在資源限制發生變化時很容易對結果進行比較。

Kubescope cli

Kubescope cli 是一個能夠運行在本地或 Kubernetes 中的工具,可直接從 Docker Daemon 中收集容器指標並可視化。和 cAdvisor 等其餘集羣指標收集服務同樣, kubescope cli 收集指標的週期是 1 秒(而不是 10-15 秒)。若是週期是 10-15 秒,你可能會在測試期間錯過一些引起性能瓶頸的問題。若是你使用 cAdvisor 進行測試,每次都要使用新的 Pod 做爲測試對象,由於 Kubernetes 在超過資源限制時就會將 Pod 殺死,而後從新啓動一個全新的 Pod。而 kubescope cli 就沒有這方面的憂慮,它直接從 Docker Daemon 中收集容器指標(你能夠自定義收集指標的時間間隔),並使用正則表達式來選擇和過濾你想要顯示的容器。

4. 總結

我發如今搞清楚服務何時會出現故障以及爲何會出現故障以前,不該該將其部署到生產環境中。我但願您能從個人錯誤中吸收教訓,並經過一些技術手段來設置應用的資源 limitsrequests。這將會爲你的系統增長彈性能力和可預測性,使你的客戶更滿意,並有望幫助你得到更多的睡眠

更多精彩內容請關注微信公衆號
相關文章
相關標籤/搜索