k8s 調度流程和資源管理

1、Kubernetes 調度過程

首先來看第一部分 - Kubernetes 的調度過程。以下圖所示,畫了一個很簡單的 Kubernetes 集羣架構,它包括了一個 kube-ApiServer,一組 webhooks 的 Controller,以及一個默認的調度器 kube-Scheduler,還有兩臺物理機節點 Node1 和 Node2,分別在上面部署了兩個 kubelet。node

k8s 調度流程和資源管理
咱們來看一下,假如要向這個 Kubernetes 集羣提交一個 pod,它的調度過程是什麼樣的一個流程?web

假設咱們已經寫好了一個 yaml 文件,就是下圖中的橙色圓圈 pod1,而後咱們往 kube-ApiServer 裏面提交這個 yaml 文件。算法

此時 ApiServer 會先把這個待建立的請求路由給咱們的 webhooks 的 Controlles 進行校驗。markdown

在經過校驗以後,ApiServer 會在集羣裏面生成一個 pod,但此時生成的 pod,它的 nodeName 是空的,而且它的 phase 是 Pending 狀態。在生成了這個 pod 以後,kube-Scheduler 以及 kubelet 都能 watch 到這個 pod 的生成事件,kube-Scheduler 發現這個 pod 的 nodeName 是空的以後,會認爲這個 pod 是處於未調度狀態架構

接下來,它會把這個 pod 拿到本身裏面進行調度,經過一系列的調度算法,包括一系列的過濾和打分的算法後,Schedule 會選出一臺最合適的節點,而且把這一臺節點的名稱綁定在這個 pod 的 spec 上,完成一次調度的過程。ide

此時咱們發現,pod 的 spec 上,nodeName 已經更新成了 Node1 這個 node,更新完 nodeName 以後,在 Node1 上的這臺 kubelet 會 watch 到這個 pod 是屬於本身節點上的一個 pod。性能

而後它會把這個 pod 拿到節點上進行操做,包括建立一些容器 storage 以及 network,最後等全部的資源都準備完成,kubelet 會把狀態更新爲 Running,這樣一個完整的調度過程就結束了。spa

經過剛剛一個調度過程的演示,咱們用一句話來歸納一下調度過程:它其實就是在作一件事情,就是把 pod 放到合適的 node 上。設計

這裏有個關鍵字「合適」,什麼是合適呢?這裏給出了幾點合適定義的特色:3d

一、首先要知足 pod 的資源要求;

二、其次要知足 pod 的一些特殊關係的要求;

三、再次要知足 node 的一些限制條件的要求;

四、最後還要作到整個集羣資源的合理利用。

2、Kubernetes 基礎調度力

下面爲你們介紹一下 Kubernetes 的基礎調度能力,Kubernetes 的基礎調度能力會用兩部分來展開介紹:

第一部分是資源調度——介紹一下 Kubernetes 基本的一些 Resources 的配置方式,還有 Qos 的概念,以及 Resource Quota 的概念和使用方式;
第二部分是關係調度——在關係調度上,介紹兩種關係場景:

  • pod 和 pod 之間的關係場景,包括怎麼去親和一個 pod,怎麼去互斥一個 pod?
  • pod 和 node 之間的關係場景,包括怎麼去親和一個 node,以及有一些 node 怎麼去限制 pod 調度上來。

    如何知足 Pod 資源要求

    pod 的資源配置方法

k8s 調度流程和資源管理
上圖是 pod spec 的一個 demo,咱們的資源實際上是填在 pod spec 裏面,Container 裏面有一個 resources 裏面的 key 裏面。

resources 其實包含兩個部分:第一部分是 request;第二部分是 limits。

這兩部分裏面的內容是如出一轍的,可是它表明的含義有所不一樣:request 表明的是對這個 pod 基本保底的一些資源要求;limit 表明的是對這個 pod 可用能力上限的一種限制。具體的 request、limit 的理念,其實都是一個 resources 的一個 map 結構,它裏面能夠填不一樣的資源的 key。

咱們能夠大概分紅四大類的基礎資源:

  • 第一類是 CPU 資源;
  • 第二類是 memory;
  • 第三類是 ephemeral-storage,一種臨時存儲;
  • 第四類是通用的擴展資源,好比說像 GPU。
    在 CPU 上的話,好比說上面的例子,申請的是兩個 CPU,也能夠寫成 2000m 這種十進制的轉換方式,來表達有些時候可能對 CPU 多是一個小數的需求,好比說像 0.2 個,就是說 200m。在 memory 和 storage 之上,它是一個二進制的表達方式。如上圖右側所示,申請的是 1GB 的 memory,也能夠轉化成一個 1024mi 的表達方式,這樣能夠更清楚地表達咱們對 memory 的需求。

在擴展資源上,Kubernetes 有一個要求,即擴展資源必須是整數的,因此咱們無法申請到 0.5 的 GPU 這樣的資源,只能申請 1 個 GPU 或者 2 個 GPU,這裏爲你們介紹一下基礎資源的申請方式。

接下來,我會詳細的給你們介紹一下 request 和 limit 到底有什麼區別,以及如何經過 request/limit 來引出 Qos 的概念。

Pod QoS 類型

K8s 在 pod resources 裏面提供了兩種填寫方式:第一種是 request,第二種是 limit。它實際上是爲用戶提供了對 Pod 一種彈性能力的定義。好比說咱們能夠對 request 填 2 個 CPU,對 limit 填 4 個 CPU,這樣其實表明了我但願是有 2 個 CPU 的保底能力,但實際上是在閒置的時候,可使用 4 個 GPU。

說到這個彈性能力,咱們不得不提到一個概念:Qos 的概念。什麼是 Qos呢?Qos 全稱是 Quality of Service,它實際上是 Kubernetes 用來表達一個 pod 在資源能力上的服務質量的標準,Kubernetes 提供了三類的 Qos Class:

  • 第一類是 Guaranteed,它是一類高的 Qos Class,通常用 Guaranteed 來爲一些須要資源保障能力的 pod 進行配置;
  • 第二類是 Burstable,它實際上是中等的一個 Qos label,通常會爲一些但願有彈性能力的 pod 來配置 Burstable;
  • 第三類是 BestEffort,經過名字咱們也知道,它是一種盡力而爲式的服務質量。

K8s 其實有一個不太好的地方,就是用戶無法指定本身的 pod 是屬於哪一類 Qos,而是經過 request 和 limit 的組合來自動地映射上 Qos Class。

經過上圖的例子,你們能夠看到:假如我提交的是上面的一個 spec,在 spec 提交成功以後,Kubernetes 會自動給補上一個 status,裏面是 qosClass: Guaranteed,用戶本身提交的時候,是無法定義本身的 Qos 等級。因此將這種方式稱之爲隱性的 Qos class 用法。

Pod QoS 配置

接下來介紹一下,咱們怎麼經過 request 和 limit 的組合來肯定咱們想要的 Qos level。

Guaranteed Pod

k8s 調度流程和資源管理
首先咱們如何建立出來一個 Guaranteed Pod?Kubernetes 裏面有一個要求:若是你要建立出一個 Guaranteed Pod,那麼你的基礎資源(就是包括 CPU 和 memory),必須它的 request==limit,其餘的資源能夠不相等。只有在這種條件下,它建立出來的 pod 纔是一種 Guaranteed Pod,不然它會屬於 Burstable,或者是 BestEffort Pod。

Burstable Pod

而後看一下,咱們怎麼建立出來一個 Burstable Pod,Burstable Pod 的範圍比較寬泛,它只要知足 CPU/Memory 的 request 和 limit 不相等,它就是一種 Burstable Pod。

k8s 調度流程和資源管理

不一樣的 QoS 表現

接下來,爲你們介紹一下:不一樣的 Qos 在調度和底層表現有什麼樣的不一樣?不一樣的 Qos,它其實在調度和底層表現上都有一些不同。好比說調度表現,調度器只會使用 request 進行調度,也就是無論你配了多大的 limit,它都不會進行調度使用,它只會使用 request 進行調度。

在底層上,不一樣的 Qos 表現更不相同。好比說 CPU,它實際上是按 request 來劃分權重的,不一樣的 Qos,它的 request 是徹底不同的,好比說像 Burstable 和 BestEffort,它可能 request 能夠填很小的數字或者不填,這樣的話,它的權重實際上是很是低的。像 BestEffort,它的權重多是隻有 2,而 Burstable 或 Guaranteed,它的權重能夠多到幾千。

另外,當咱們開啓了 kubelet 的一個特性,叫 cpu-manager-policy=static 的時候,咱們 Guaranteed Qos,若是它的 request 是一個整數的話,好比說配了 2,它會對 Guaranteed Pod 進行綁核。也就是具體像下面這個例子,它分配 CPU0 和 CPU1 給 Guaranteed Pod。

非整數的 Guaranteed/Burstable/BestEffort,它們的 CPU 會放在一塊,組成一個 CPU share pool,好比說像上面這個例子,這臺節點假如說有 8 個核,已經分配了 2 個核給整數的 Guaranteed 綁核,那麼剩下的 6 個核 CPU2~CPU7,它會被非整數的 Guaranteed/Burstable/BestEffort 共享,而後它們會根據不一樣的權重劃分時間片來使用 6 個核的 CPU。

另外在 memory 上也會按照不一樣的 Qos 進行劃分:OOMScore。好比說 Guaranteed,它會配置默認的 -998 的 OOMScore;Burstable 的話,它會根據內存設計的大小和節點的關係來分配 2-999 的 OOMScore。BestEffort 會固定分配 1000 的 OOMScore,OOMScore 得分越高的話,在物理機出現 OOM 的時候會優先被 kill 掉。

另外在節點上的 eviction 動做上,不一樣的 Qos 也是不同的,好比說發生 eviction 的時候,會優先考慮驅逐 BestEffort 的 pod。因此不一樣的 Qos 其實在底層的表現是大相徑庭的。這也反過來要求咱們在生產過程當中,根據不一樣業務的要求和屬性來配置資源的 Limits 和 Request,作到合理的規劃 Qos Class。

k8s 調度流程和資源管理
Kubernetes 給咱們提供了一個能力叫:ResourceQuota 方法。它能夠作到限制 namespace 資源用量。

具體的作法如上圖右側的 yaml 所示,能夠看到它的 spec 包括了一個 hard 和 scopeSelector。hard 內容其實和 Resourcelist 很像,這裏能夠填一些基礎的資源。可是它比 ResourceList 更豐富一點,它還能夠填寫一些 Pod,這樣能夠限制 Pod 數量能力。而後 scopeSelector 爲這個 Resource 方法定義更豐富的索引能力。

好比上面的例子中,索引出非 BestEffort 的 pod,限制的 cpu 是 1000 個,memory 是 200G,Pod 是 10 個,而後 Scope 除了提供 NotBestEffort,它還提供了更豐富的索引範圍,包括 Terminating/Not Terminating,BestEffort/NotBestEffort,PriorityClass。

當咱們建立了這樣的 ResourceQuota 做用於集羣,若是用戶真的用超了資源,表現的行爲是:它在提交 Pod spec 時,會收到一個 forbidden 的 403 錯誤,提示 exceeded quota。這樣用戶就沒法再提交 cpu 或者是 memory,或者是 Pod 數量的資源。

假如再提交一個沒有包含在這個 ResourceQuota 方案裏面的資源,仍是能成功的。這就是 Kubernetes 裏 ResourceQuota 的基本用法。 咱們能夠用 ResourceQuota 方法來作到限制每個 namespace 的資源用量,從而保證其餘用戶的資源使用。

小結:如何知足 Pod 資源要求?

上面介紹完了基礎資源的使用方式,也就是咱們作到了如何知足 Pod 資源要求。下面作一個小結:

  • Pod 要配置合理的資源要求
  • CPU/Memory/EphemeralStorage/GPU

經過 Request 和 Limit 來爲不一樣業務特色的 Pod 選擇不一樣的 QoS

  • Guaranteed:敏感型,須要業務保障
  • Burstable:次敏感型,須要彈性業務
  • BestEffort:可容忍性業務

3、Kubernetes 高級調度能力

介紹完了基礎調度能力以後,下面來了解一下高級調度能力。
優先級調度
優先級調度和搶佔,主要概念有:

  • Priority
  • Preemption

首先來看一下調度過程提到的四個特色,咱們如何作到集羣的合理利用?當集羣資源足夠的話,只須要經過基礎調度能力就能組合出合理的使用方式。可是假如資源不夠,咱們怎麼作到集羣的合理利用呢?一般的策略有兩類:

  • 先到先得策略 (FIFO) -簡單、相對公平,上手快
  • 優先級策略 (Priority) - 符合平常公司業務特色

在實際生產中,若是使用先到先得策略,是一種不公平的策略,由於公司業務裏面確定是有高優先級的業務和低優先級的業務,因此優先級策略會比先到先得策略更可以符合平常公司業務特色。

k8s 調度流程和資源管理
接着介紹一下優先級策略下的優先級調度是什麼樣的一個概念。好比說有一個 Node 已經被一個 Pod 佔用了,這個 Node 只有 2 個 CPU。另外一個高優先級 Pod 來的時候,低優先級的 Pod 應該把這兩個 CPU 讓給高優先級的 Pod 去使用。低優先級的 Pod 須要回到等待隊列,或者是業務從新提交。這樣的流程就是優先級搶佔調度的一個流程。

在 Kubernetes 裏,PodPriority 和 Preemption,就是優先級和搶佔的特色,在 v1.14 版本中變成了 stable。而且 PodPriority 和 Preemption 默認都是開啓的。

優先級調度配置

怎麼使用?
如何使用優先級調度呢?須要建立一個 priorityClass,而後再爲每一個 Pod 配置上不一樣的 priorityClassName,這樣就完成了優先級以及優先級調度的配置。

k8s 調度流程和資源管理

首先來看一下如何建立一個 priorityClass。上圖右側定義了兩個 demo:

  • 一個是建立名爲 high 的 priorityClass,它是高優先級,得分爲 10000;
  • 而後還建立了一個 low 的 priorityClass,它的得分是 100。
    而且在第三部分給 Pod 配置上了 high,Pod2 上配置了 low priorityClassName,藍色部分顯示了 pod 的 spec 的配置位置,就是在 spec 裏面填寫一個 priorityClassName: high。這樣 Pod 和 priorityClass 作完配置,就爲集羣開啓了一個 priorityClass 調度。

    內置優先級配置

    固然 Kubernetes 裏面還內置了默認的優先級。如 DefaultpriorityWhenNoDefaultClassExistis,若是集羣中沒有配置 DefaultpriorityWhenNoDefaultClassExistis,那全部的 Pod 關於此項數值都會被設置成 0。

另外一個內置優先級是用戶可配置最大優先級限制:HighestUserDefinablePriority = 10000000000(10 億)

系統級別優先級:SystemCriticalPriority = 20000000000(20 億)

內置系統級別優先級:

  • system-cluster-critical
  • system-node-critical

這就是優先級調度的基本配置以及內置的優先級配置。

優先級調度過程

當作完上面的配置後,整個優先級調度是怎樣一個流程呢?下面將會介紹一下簡單的過程。

首先介紹一下只觸發優先級調度可是沒有觸發搶佔調度的流程。

假若有一個 Pod1 和 Pod2,Pod1 配置了高優先級,Pod2 配置了低優先級。同時提交 Pod1 和 Pod2 到調度隊列裏。

k8s 調度流程和資源管理
調度器處理隊列的時候會挑選一個高優先級的 Pod1 進行調度,通過調度過程把 Pod1 綁定到 Node1 上。

緊接着再調度 Pod1,由於 Node1 上已經存在了兩個 Pod,資源不足,因此會遇到調度失敗。

在調度失敗時 Pod1 會進入搶佔流程,這時會進行整個集羣的節點篩選,最後挑出要搶佔的 Pod 是 Pod2,此時調度器會把 Pod2 從 Node1 上移除數據。

k8s 調度流程和資源管理
再把 Pod1 調度到 Node1 上。這樣就完成了一次搶佔調度的流程。

優先級搶佔策略

接下來介紹一下具體的搶佔策略和搶佔的流程是什麼樣的。
k8s 調度流程和資源管理
上圖右側是整個優先級搶佔的調度流程,也就是 kube-scheduler 的工做流程。首先一個 Pod 進入搶佔的時候,會判斷 Pod 是否擁有搶佔的資格,有可能上次已經搶佔過一次。若是符合搶佔資格,它會先對全部的節點進行一次過濾,過濾出符合此次搶佔要求的節點,若是不符合就過濾掉這批節點。

接着從過濾剩下的節點中,挑選出合適的節點進行搶佔。此次搶佔的過程會模擬一次調度,也就是把上面優先級低的 Pod 先移除出去,再把待搶佔的 Pod 嘗試可否放置到此節點上。而後經過這個過程選出一批節點,進入下一個過程叫 ProcessPreemptionWithExtenders。這是一個擴展的鉤子,用戶能夠在這裏加一些本身搶佔節點的策略,若是沒有擴展的鉤子,這裏面是不作任何動做的。

接下來的流程叫作 PickOneNodeForPreemption,就是從上面 selectNodeForPreemption list 裏面挑選出最合適的一個節點,這是有必定的策略的。上圖左側簡單介紹了一下策略:

  • 優先選擇打破 PDB 最少的節點;
  • 其次選擇待搶佔 Pods 中最大優先級最小的節點;
  • 再次選擇待搶佔 Pods 優先級加和最小的節點;
  • 接下來選擇待搶佔 Pods 數目最小的節點;
  • 最後選擇擁有最晚啓動 Pod 的節點;
    經過這五步串行策略過濾以後,會選出一個最合適的節點。而後對這個節點上待搶佔的 Pod 進行 delete,這樣就完成了一次待搶佔的過程。

小結

簡單介紹了一下調度的高級策略,在集羣資源緊張的時候也能合理調度資源。咱們回顧一下作了哪些事情:

  • 建立自定義的一些優先級類別 (PriorityClass);
  • 給不一樣類型 Pods 配置不一樣的優先級 (PriorityClassName);
  • 經過組合不一樣類型 Pods 運行和優先級搶佔讓集羣資源和調度彈性起來。
相關文章
相關標籤/搜索