Kubernetes系列之Kubernetes資源管理php
Kubernetes 從建立之初的核心模塊之一就是資源調度。想要在生產環境使用好 Kubernetes,必須對它的資源模型以及資源管理很是瞭解。這篇文章算是對散佈在網絡上的 Kubernetes 資源管理內容的一個總結。乾貨文章,強列推薦一讀。node
在 Kubernetes 中,有兩個基礎可是很是重要的概念:Node 和 Pod。Node 翻譯成節點,是對集羣資源的抽象;Pod 是對容器的封裝,是應用運行的實體。Node 提供資源,而 Pod 使用資源,這裏的資源分爲計算(CPU、Memory、GPU)、存儲(Disk、SSD)、網絡(Network Bandwidth、IP、Ports)。這些資源提供了應用運行的基礎,正確理解這些資源以及集羣調度如何使用這些資源,對於大規模的 Kubernetes 集羣來講相當重要,不只能保證應用的穩定性,也能夠提升資源的利用率。git
在這篇文章,咱們主要介紹 CPU 和內存這兩個重要的資源,它們雖然都屬於計算資源,但也有所差距。CPU 可分配的是使用時間,也就是操做系統管理的時間片,每一個進程在必定的時間片裏運行本身的任務(另一種方式是綁核,也就是把 CPU 徹底分配給某個 Pod 使用,但這種方式不夠靈活會形成嚴重的資源浪費,Kubernetes 中並無提供);而對於內存,系統提供的是內存大小。github
CPU 的使用時間是可壓縮的,換句話說它自己無狀態,申請資源很快,也能快速正常回收;而內存大小是不可壓縮的,由於它是有狀態的(內存裏面保存的數據),申請資源很慢(須要計算和分配內存塊的空間),而且回收可能失敗(被佔用的內存通常不可回收)。算法
把資源分紅可壓縮和不可壓縮,是由於在資源不足的時候,它們的表現很不同。對於不可壓縮資源,若是資源不足,也就沒法繼續申請資源(內存用完就是用完了),而且會致使 Pod 的運行產生沒法預測的錯誤(應用申請內存失敗會致使一系列問題);而對於可壓縮資源,好比 CPU 時間片,即便 Pod 使用的 CPU 資源不少,CPU 使用也能夠按照權重分配給全部 Pod 使用,雖然每一個人使用的時間片減小,但不會影響程序的邏輯。docker
在 Kubernetes 集羣管理中,有一個很是核心的功能:就是爲 Pod 選擇一個主機運行。調度必須知足必定的條件,其中最基本的是主機上要有足夠的資源給 Pod 使用。數據庫
資源除了和調度相關以外,還和不少事情緊密相api
####Kubernetes 資源的表示
用戶在 Pod 中能夠配置要使用的資源總量,Kubernetes 根據配置的資源數進行調度和運行。目前主要能夠配置的資源是 CPU 和 Memory,對應的配置字段是 spec.containers[].resource.limits/request.cpu/memory。服務器
須要注意的是,用戶是對每一個容器配置 Request 值,全部容器的資源請求之和就是 Pod 的資源請求總量,而咱們通常會說 Pod 的 資源請求 和 Limits。網絡
Limits 和 Requests 的區別咱們下面會提到,這裏先說說比較容易理解的 CPU 和 Memory。
CPU 通常用核數來標識,一核 CPU 相對於物理服務器的一個超線程核,也就是操做系統 /proc/cpuinfo 中列出來的核數。由於對資源進行了池化和虛擬化,所以 Kubernetes 容許配置非整數個的核數,好比 0.5 是合法的,它標識應用可使用半個 CPU 核的計算量。CPU 的請求有兩種方式,一種是剛提到的 0.5,1 這種直接用數字標識 CPU 核心數;另一種表示是 500m,它等價於 0.5,也就是說 1 Core = 1000m。
內存比較容易理解,是經過字節大小指定的。若是直接一個數字,後面沒有任何單位,表示這麼多字節的內存;數字後面還能夠跟着單位, 支持的單位有 E、P、T、G、M、K,前者分別是後者的 1000 倍大小的關係,此外還支持 Ei、Pi、Ti、Gi、Mi、Ki,其對應的倍數關係是 2^10 = 1024。好比要使用 100M 內存的話,直接寫成 100Mi便可。
理想狀況下,咱們但願節點上全部的資源均可以分配給 Pod 使用,但實際上節點上除了運行 Pods 以外,還會運行其餘的不少進程:系統相關的進程(好比 SSHD、Udev等),以及 Kubernetes 集羣的組件(Kubelet、Docker等)。咱們在分配資源的時候,須要給這些進程預留一些資源,剩下的才能給 Pod 使用。預留的資源能夠經過下面的參數控制:
--kube-reserved=[cpu=100m][,][memory=100Mi][,][ephemeral-storage=1Gi]
:控制預留給 Kubernetes 集羣組件的 CPU、Memory 和存儲資源。--system-reserved=[cpu=100mi][,][memory=100Mi][,][ephemeral-storage=1Gi]
:預留給系統的 CPU、Memory 和存儲資源。這兩塊預留以後的資源纔是 Pod 真正能使用的,不過考慮到 Eviction 機制(下面的章節會提到),Kubelet 會保證節點上的資源使用率不會真正到 100%,所以 Pod 的實際可以使用資源會稍微再少一點。主機上的資源邏輯分配圖以下所示:
NOTE:須要注意的是,Allocatable 不是指當前機器上能夠分配的資源,而是指能分配給 Pod 使用的資源總量,一旦 Kubelet 啓動這個值是不會變化的。
Allocatable 的值能夠在 Node 對象的 status 字段中讀取,好比下面這樣:
status: allocatable: cpu: "2" ephemeral-storage: "35730597829" hugepages-2Mi: "0" memory: 3779348Ki Pods: "110" capacity: cpu: "2" ephemeral-storage: 38770180Ki hugepages-2Mi: "0" memory: 3881748Ki Pods: "110"
在這部分,咱們來介紹 Kubernetes 中提供的讓咱們管理 Pod 資源的原生對象。
前面說過用戶在建立 Pod 的時候,能夠指定每一個容器的 Requests 和 Limits 兩個字段,下面是一個實例:
resources: requests: #這塊設定的值是必需要分配這麼多,一開始就劃分出來了 memory: "64Mi" cpu: "250m" limits: #這塊設置的值能夠不一開始就分配,等須要的時候在分配 memory: "128Mi" cpu: "500m"
Requests 是容器請求要使用的資源,Kubernetes 會保證 Pod 能使用到這麼多的資源。請求的資源是調度的依據,只有當節點上的可用資源大於 Pod 請求的各類資源時,調度器纔會把 Pod 調度到該節點上(若是 CPU 資源足夠,內存資源不足,調度器也不會選擇該節點)。
須要注意的是,調度器只關心節點上可分配的資源,以及節點上全部 Pods 請求的資源,而不關心節點資源的實際使用狀況,換句話說,若是節點上的 Pods 申請的資源已經把節點上的資源用滿,即便它們的使用率很是低,好比說 CPU 和內存使用率都低於 10%,調度器也不會繼續調度 Pod 上去。
Limits 是 Pod 能使用的資源上限,是實際配置到內核 cgroups 裏面的配置數據。對於內存來講,會直接轉換成 docker run 命令行的 --memory 大小,最終會配置到 cgroups 對應任務的 /sys/fs/cgroup/memory/……/memory.limit_in_bytes 文件中。
NOTE:若是 Limit 沒有配置,則代表沒有資源的上限,只要節點上有對應的資源,Pod 就可使用。
使用 Requests 和 Limits 概念,咱們能分配更多的 Pod,提高總體的資源使用率。可是這個體系有個很是重要的問題須要考慮,那就是怎麼去準確地評估 Pod 的資源 Requests?若是評估地太低,會致使應用不穩定;若是太高,則會致使使用率下降。這個問題須要開發者和系統管理員共同討論和定義。
####Limit Range(默認資源配置)
爲每一個 Pod 都手動配置這些參數是挺麻煩的事情,Kubernetes 提供了 LimitRange 資源,可讓咱們配置某個 Namespace 默認的 Request 和 Limit 值,好比下面的實例:
apiVersion: "v1" kind: "LimitRange" metadata: name: huoban-php spec: limits: - type: "Container" max: cpu: "2" memory: "1Gi" min: cpu: "100m" memory: "4Mi" default: cpu: "500m" memory: "200Mi" defaultRequest: cpu: "200m" memory: "100Mi"
若是對應 Namespace 建立的 Pod 沒有寫資源的 Requests 和 Limits 字段,那麼它會自動擁有下面的配置信息:
內存請求是 100Mi,上限是 200Mi
CPU 請求是 200m,上限是 500m
固然,若是 Pod 本身配置了對應的參數,Kubernetes 會使用 Pod 中的配置。使用 LimitRange 可以讓 Namespace 中的 Pod 資源規範化,便於統一的資源管理。
####資源配額(Resource Quota)
前面講到的資源管理和調度能夠認爲 Kubernetes 把這個集羣的資源整合起來,組成一個資源池,每一個應用(Pod)會自動從整個池中分配資源來使用。默認狀況下只要集羣還有可用的資源,應用就能使用,並無限制。Kubernetes 自己考慮到了多用戶和多租戶的場景,提出了 Namespace 的概念來對集羣作一個簡單的隔離。
基於 Namespace,Kubernetes 還可以對資源進行隔離和限制,這就是 Resource Quota 的概念,翻譯成資源配額,它限制了某個 Namespace 可使用的資源總額度。這裏的資源包括 CPU、Memory 的總量,也包括 Kubernetes 自身對象(好比 Pod、Services 等)的數量。經過 Resource Quota,Kubernetes 能夠防止某個 Namespace 下的用戶不加限制地使用超過時望的資源,好比說不對資源進行評估就大量申請 16核 CPU 32G 內存的 Pod。
下面是一個資源配額的實例,它限制了 Namespace 只能使用 20 核 CPU 和 1G 內存,而且能建立 10 個 Pod、20 個 RC、5 個 Service,可能適用於某個測試場景。
apiVersion: v1 kind: ResourceQuota metadata: name: quota spec: hard: cpu: "20" memory: 1Gi Pods: "10" replicationcontrollers: "20" resourcequotas: "1" services: "5"
Resource Quota 可以配置的選項還不少,好比 GPU、存儲、Configmaps、PersistentVolumeClaims 等等,更多信息能夠參考官方文檔。
Resource Quota 要解決的問題和使用都相對獨立和簡單,可是它也有一個限制:那就是它不能根據集羣資源動態伸縮。一旦配置以後,Resource Quota 就不會改變,即便集羣增長了節點,總體資源增多也沒有用。Kubernetes 如今沒有解決這個問題,可是用戶能夠經過編寫一個 Controller 的方式來本身實現。
Requests 和 Limits 的配置除了代表資源狀況和限制資源使用以外,還有一個隱藏的做用:它決定了 Pod 的 QoS 等級。
上一節咱們提到了一個細節:若是 Pod 沒有配置 Limits ,那麼它可使用節點上任意多的可用資源。這類 Pod 能靈活使用資源,但這也致使它不穩定且危險,對於這類 Pod 咱們必定要在它佔用過多資源致使節點資源緊張時處理掉。優先處理這類 Pod,而不是處理資源使用處於本身請求範圍內的 Pod 是很是合理的想法,而這就是 Pod QoS 的含義:根據 Pod 的資源請求把 Pod 分紅不一樣的重要性等級。
Kubernetes 把 Pod 分紅了三個 QoS 等級:
Pod 的 Requests 和 Limits 是如何對應到這三個 QoS 等級上的,能夠用下面一張表格歸納:
看到這裏,你也許看出來一個問題了:若是不配置 Requests 和 Limits,Pod 的 QoS 居然是最低的。沒錯,因此推薦你們理解 QoS 的概念,而且按照需求必定要給 Pod 配置 Requests 和 Limits 參數,不只可讓調度更準確,也能讓系統更加穩定。
NOTE:按照如今的方法根據 Pod 請求的資源進行配置不夠靈活和直觀,更理想的狀況是用戶能夠直接配置 Pod 的 QoS,而不用關心具體的資源申請和上限值。但 Kubernetes 目前尚未這方面的打算。
Pod 的 QoS 還決定了容器的 OOM(Out-Of-Memory)值,它們對應的關係以下:
能夠看到,QoS 越高的 Pod OOM 值越低,也就越不容易被系統殺死。對於 Bustable Pod,它的值是根據 Request 和節點內存總量共同決定的:
oomScoreAdjust := 1000 - (1000*memoryRequest)/memoryCapacity
其中 memoryRequest
是 Pod 申請的資源,memoryCapacity
是節點的內存總量。能夠看到,申請的內存越多,OOM 值越低,也就越不容易被殺死。
QoS 的做用會在後面介紹 Eviction 的時候詳細講解。
###Pod 優先級(Priority)
除了 QoS,Kubernetes 還容許咱們自定義 Pod 的優先級,好比:
apiVersion: scheduling.k8s.io/v1alpha1 kind: PriorityClass metadata: name: high-priority value: 1000000 globalDefault: false description: "This priority class should be used for XYZ service Pods only."
優先級的使用也比較簡單,只須要在 Pod.spec.PriorityClassName
指定要使用的優先級名字,便可以設置當前 Pod 的優先級爲對應的值。
Pod 的優先級在調度的時候會使用到。首先,待調度的 Pod 都在同一個隊列中,啓用了 Pod priority 以後,調度器會根據優先級的大小,把優先級高的 Pod 放在前面,提早調度。
另外,若是在調度的時候,發現某個 Pod 由於資源不足沒法找到合適的節點,調度器會嘗試 Preempt 的邏輯。簡單來講,調度器會試圖找到這樣一個節點:找到它上面優先級低於當前要調度 Pod 的全部 Pod,若是殺死它們,能騰足夠的資源,調度器會執行刪除操做,把 Pod 調度到節點上。更多內容能夠參考:Pod Priority and Preemption - Kubernetes
###驅逐(Eviction)
至此,咱們講述的都是理想狀況下 Kubernetes 的工做情況,咱們假設資源徹底夠用,並且應用也都是在使用規定範圍內的資源。
但現實不會如此簡單,在管理集羣的時候咱們經常會遇到資源不足的狀況,在這種狀況下咱們要保證整個集羣可用,而且儘量減小應用的損失。保證集羣可用比較容易理解,首先要保證系統層面的核心進程正常,其次要保證 Kubernetes 自己組件進程不出問題;可是如何量化應用的損失呢?首先能想到的是若是要殺死 Pod,要儘可能減小總數。另一個就和 Pod 的優先級相關了,那就是儘可能殺死不那麼重要的應用,讓重要的應用不受影響。
Pod 的驅逐是在 Kubelet 中實現的,由於 Kubelet 能動態地感知到節點上資源使用率實時的變化狀況。其核心的邏輯是:Kubelet 實時監控節點上各類資源的使用狀況,一旦發現某個不可壓縮資源出現要耗盡的狀況,就會主動終止節點上的 Pod,讓節點可以正常運行。被終止的 Pod 全部容器會中止,狀態會被設置爲 Failed。
那麼哪些資源不足會致使 Kubelet 執行驅逐程序呢?目前主要有三種狀況:實際內存不足、節點文件系統的可用空間(文件系統剩餘大小和 Inode 數量)不足、以及鏡像文件系統的可用空間(包括文件系統剩餘大小和 Inode 數量)不足。
下面這圖是具體的觸發條件:
有了數據的來源,另一個問題是觸發的時機,也就是到什麼程度須要觸發驅逐程序?Kubernetes 運行用戶本身配置,而且支持兩種模式:按照百分比和按照絕對數量。好比對於一個 32G 內存的節點當可用內存少於 10% 時啓動驅逐程序,能夠配置 memory.available<10%
或者 memory.available<3.2Gi
。
NOTE:默認狀況下,Kubelet 的驅逐規則是
memory.available<100Mi
,對於生產環境這個配置是不可接受的,因此必定要根據實際狀況進行修改。
###軟驅逐(Soft Eviction)和硬驅逐(Hard Eviction)
由於驅逐 Pod 是具備毀壞性的行爲,所以必需要謹慎。有時候內存使用率增高只是暫時性的,有可能 20s 內就能恢復,這時候啓動驅逐程序意義不大,並且可能會致使應用的不穩定,咱們要考慮到這種狀況應該如何處理;另外須要注意的是,若是內存使用率太高,好比高於 95%(或者 90%,取決於主機內存大小和應用對穩定性的要求),那麼咱們不該該再多作評估和考慮,而是趕忙啓動驅逐程序,由於這種狀況再花費時間去判斷可能會致使內存繼續增加,系統徹底崩潰。
爲了解決這個問題,Kubernetes 引入了 Soft Eviction 和 Hard Eviction 的概念。
軟驅逐能夠在資源緊缺狀況並無哪些嚴重的時候觸發,好比內存使用率爲 85%,軟驅逐還須要配置一個時間指定軟驅逐條件持續多久才觸發,也就是說 Kubelet 在發現資源使用率達到設定的閾值以後,並不會當即觸發驅逐程序,而是繼續觀察一段時間,若是資源使用率高於閾值的狀況持續必定時間,纔開始驅逐。而且驅逐 Pod 的時候,會遵循 Grace Period ,等待 Pod 處理完清理邏輯。和軟驅逐相關的啓動參數是:
--eviction-soft:軟驅逐觸發條件,好比 memory.available<1Gi。
--eviction-sfot-grace-period:觸發條件持續多久纔開始驅逐,好比 memory.available=2m30s。
--eviction-max-Pod-grace-period:Kill Pod 時等待 Grace Period 的時間讓 Pod 作一些清理工做,若是到時間尚未結束就作 Kill。
前面兩個參數必須同時配置,軟驅逐才能正常工做;後一個參數會和 Pod 自己配置的 Grace Period 比較,選擇較小的一個生效。
硬驅逐更加直接乾脆,Kubelet 發現節點達到配置的硬驅逐閾值後,當即開始驅逐程序,而且不會遵循 Grace Period,也就是說當即強制殺死 Pod。對應的配置參數只有一個 --evictio-hard,能夠選擇上面表格中的任意條件搭配。
設置這兩種驅逐程序是爲了平衡節點穩定性和對 Pod 的影響,軟驅逐照顧到了 Pod 的優雅退出,減小驅逐對 Pod 的影響;而硬驅逐則照顧到節點的穩定性,防止資源的快速消耗致使節點不可用。
軟驅逐和硬驅逐能夠單獨配置,不過仍是推薦二者都進行配置,一塊兒使用。
###驅逐哪些 Pods?
上面咱們已經總體介紹了 Kubelet 驅逐 Pod 的邏輯和過程,那這裏就牽涉到一個具體的問題:要驅逐哪些 Pod?驅逐的重要原則是儘可能減小對應用程序的影響。
若是是存儲資源不足,Kubelet 會根據狀況清理狀態爲 Dead 的 Pod 和它的全部容器,以及清理全部沒有使用的鏡像。若是上述清理並無讓節點回歸正常,Kubelet 就開始清理 Pod。
一個節點上會運行多個 Pod,驅逐全部的 Pods 顯然是沒必要要的,所以要作出一個抉擇:在節點上運行的全部 Pod 中選擇一部分來驅逐。雖然這些 Pod 乍看起來沒有區別,可是它們的地位是不同的,正如喬治·奧威爾在《動物莊園》的那句話:
全部動物生而平等,但有些動物比其餘動物更平等。
Pod 也是不平等的,有些 Pod 要比其餘 Pod 更重要。只管來講,系統組件的 Pod 要比普通的 Pod 更重要,另外運行數據庫的 Pod 天然要比運行一個無狀態應用的 Pod 更重要。Kubernetes 又是怎麼決定 Pod 的優先級的呢?這個問題的答案就藏在咱們以前已經介紹過的內容裏:Pod Requests 和 Limits、優先級(Priority),以及 Pod 實際的資源使用。
簡單來講,Kubelet 會根據如下內容對 Pod 進行排序:Pod 是否使用了超過請求的緊張資源、Pod 的優先級、而後是使用的緊缺資源和請求的緊張資源之間的比例。具體來講,Kubelet 會按照以下的順序驅逐 Pod:
####防止波動
這裏的波動有兩種狀況,咱們先說說第一種。驅逐條件出發後,若是 Kubelet 驅逐一部分 Pod,讓資源使用率低於閾值就中止,那麼極可能過一段時間資源使用率又會達到閾值,從而再次出發驅逐,如此循環往復……爲了處理這種問題,咱們可使用 --eviction-minimum-reclaim解決,這個參數配置每次驅逐至少清理出來多少資源纔會中止。
另一個波動狀況是這樣的:Pod 被驅逐以後並不會今後消失不見,常見的狀況是 Kubernetes 會自動生成一個新的 Pod 來取代,並通過調度選擇一個節點繼續運行。若是不作額外處理,有理由相信 Pod 選擇原來節點的可能性比較大(由於調度邏輯沒變,而它上次調度選擇的就是該節點),之因此說可能而不是絕對會再次選擇該節點,是由於集羣 Pod 的運行和分佈和上次調度時極有可能發生了變化。
不管如何,若是被驅逐的 Pod 再次調度到原來的節點,極可能會再次觸發驅逐程序,而後 Pod 再次被調度到當前節點,循環往復…… 這種事情固然是咱們不肯意看到的,雖然看似複雜,但這個問題解決起來很是簡單:驅逐發生後,Kubelet 更新節點狀態,調度器感知到這一狀況,暫時不往該節點調度 Pod 便可。--eviction-pressure-transition-period 參數能夠指定 Kubelet 多久才上報節點的狀態,由於默認的上報狀態週期比較短,頻繁更改節點狀態會致使驅逐波動。
作一個總結,下面是一個使用了上面多種參數的驅逐配置實例(你應該能看懂它們是什麼意思了):
–eviction-soft=memory.available<80%,nodefs.available<2Gi \ –eviction-soft-grace-period=memory.available=1m30s,nodefs.available=1m30s \ –eviction-max-Pod-grace-period=120 \ –eviction-hard=memory.available<500Mi,nodefs.available<1Gi \ –eviction-pressure-transition-period=30s \ --eviction-minimum-reclaim="memory.available=0Mi,nodefs.available=500Mi,imagefs.available=2Gi"
Kubernetes 的調度器在爲 Pod 選擇運行節點的時候,只會考慮到調度那個時間點集羣的狀態,通過一系列的算法選擇一個當時最合適的節點。可是集羣的狀態是不斷變化的,用戶建立的 Pod 也是動態的,隨着時間變化,原來調度到某個節點上的 Pod 如今看來可能有更好的節點能夠選擇。好比考慮到下面這些狀況:
想要解決上述的這些問題,都須要把 Pod 從新進行調度(把 Pod 從當前節點移動到另一個節點)。可是默認狀況下,一旦 Pod 被調度到節點上,除非給殺死不然不會移動到另一個節點的。
爲此 Kubernetes 社區孵化了一個稱爲 Descheduler
的項目,專門用來作重調度。重調度的邏輯很簡單:找到上面幾種狀況中已經不是最優的 Pod,把它們驅逐掉(Eviction)。
目前,Descheduler 不會決定驅逐的 Pod 應該調度到哪臺機器,而是假定默認的調度器會作出正確的調度抉擇。也就是說,之因此 Pod 目前不合適,不是由於調度器的算法有問題,而是由於集羣的狀況發生了變化。若是讓調度器從新選擇,調度器如今會把 Pod 放到合適的節點上。這種作法讓 Descheduler 邏輯比較簡單,並且避免了調度邏輯出如今兩個組件中。
Descheduler 執行的邏輯是能夠配置的,目前有幾種場景:
RemoveDuplicates
:RS、Deployment 中的 Pod 不能同時出如今一臺機器上。LowNodeUtilization
:找到資源使用率比較低的 Node,而後驅逐其餘資源使用率比較高節點上的 Pod,指望調度器可以從新調度讓資源更均衡。RemovePodsViolatingInterPodAntiAffinity
:找到已經違反 Pod Anti Affinity 規則的 Pods 進行驅逐,多是由於反親和是後面加上去的。RemovePodsViolatingNodeAffinity
:找到違反 Node Affinity 規則的 Pods 進行驅逐,多是由於 Node 後面修改了 Label。固然,爲了保證應用的穩定性,Descheduler 並不會隨意地驅逐 Pod,仍是會尊重 Pod 運行的規則,包括 Pod 的優先級(不會驅逐 Critical Pod,而且按照優先級順序進行驅逐)和 PDB(若是違反了 PDB,則不會進行驅逐),而且不會驅逐沒有 Deployment、RS、Jobs 的 Pod 不會驅逐,Daemonset Pod 不會驅逐,有 Local storage 的 Pod 也不會驅逐。
Descheduler 不是一個常駐的任務,每次執行完以後會退出,所以推薦使用 CronJob 來運行。
總的來講,Descheduler 是對原生調度器的補充,用來解決原生調度器的調度決策隨着時間會變得失效,或者不夠優化的缺陷。
動態調整的思路:應用的實際流量會不斷變化,所以使用率也是不斷變化的,爲了應對應用流量的變化,咱們應用可以自動調整應用的資源。好比在線商品應用在促銷的時候訪問量會增長,咱們應該自動增長 Pod 運算能力來應對;當促銷結束後,有須要自動下降 Pod 的運算能力防止浪費。
運算能力的增減有兩種方式:改變單個 Pod 的資源,以及增減 Pod 的數量。這兩種方式對應了 Kubernetes 的 HPA 和 VPA。
橫向 Pod 自動擴展的思路是這樣的:Kubernetes 會運行一個 Controller,週期性地監聽 Pod 的資源使用狀況,當高於設定的閾值時,會自動增長 Pod 的數量;當低於某個閾值時,會自動減小 Pod 的數量。天然,這裏的閾值以及 Pod 的上限和下限的數量都是須要用戶配置的。
上面這句話隱藏了一個重要的信息:HPA 只能和 RC、Deployment、RS 這些能夠動態修改 Replicas 的對象一塊兒使用,而沒法用於單個 Pod、Daemonset(由於它控制的 Pod 數量不能隨便修改)等對象。
目前官方的監控數據來源是 Metrics Server 項目,能夠配置的資源只有 CPU,可是用戶可使用自定義的監控數據(好比:Prometheus)。其餘資源(好比:Memory)的 HPA 支持也已經在路上了。
####Vertical Pod AutoScaling
和 HPA 的思路類似,只不過 VPA 調整的是單個 Pod 的 Request 值(包括 CPU 和 Memory)。VPA 包括三個組件:
Recommander:消費 Metrics Server 或者其餘監控組件的數據,而後計算 Pod 的資源推薦值。
Updater:找到被 VPA 接管的 Pod 中和計算出來的推薦值差距過大的,對其作 Update 操做(目前是 Evict,新建的 Pod 在下面 Admission Controller 中會使用推薦的資源值做爲 Request)。
Admission Controller:新建的 Pod 會通過該 Admission Controller,若是 Pod 是被 VPA 接管的,會使用 Recommander 計算出來的推薦值。
能夠看到,這三個組件的功能是互相補充的,共同實現了動態修改 Pod 請求資源的功能。相對於 HPA,目前 VPA 還處於 Alpha,而且尚未合併到官方的 Kubernetes Release 中,後續的接口和功能極可能會發生變化。
隨着業務的發展,應用會逐漸增多,每一個應用使用的資源也會增長,總會出現集羣資源不足的狀況。爲了動態地應對這一情況,咱們還須要 CLuster Auto Scaler,可以根據整個集羣的資源使用狀況來增減節點。
對於公有云來講,Cluster Auto Scaler 就是監控這個集羣由於資源不足而 Pending 的 Pod,根據用戶配置的閾值調用公有云的接口來申請建立機器或者銷燬機器。對於私有云,則須要對接內部的管理平臺。
目前 HPA 和 VPA 不兼容,只能選擇一個使用,不然二者會相互干擾。並且 VPA 的調整須要重啓 Pod,這是由於 Pod 資源的修改是比較大的變化,須要從新走一下 Apiserver、調度的流程,保證整個系統沒有問題。目前社區也有計劃在作原地升級,也就是說不經過殺死 Pod 再調度新 Pod 的方式,而是直接修改原有 Pod 來更新。
理論上 HPA 和 VPA 是能夠共同工做的,HPA 負責瓶頸資源,VPA 負責其餘資源。好比對於 CPU 密集型的應用,使用 HPA 監聽 CPU 使用率來調整 Pods 個數,而後用 VPA 監聽其餘資源(Memory、IO)來動態擴展這些資源的 Request 大小便可。固然這只是理想狀況,
從前面介紹的各類 Kubernetes 調度和資源管理方案能夠看出來,提升應用的資源使用率、保證應用的正常運行、維護調度和集羣的公平性是件很是複雜的事情,Kubernetes 並無完美的方法,而是對各類可能的問題不斷提出一些針對性的方案。
集羣的資源使用並非靜態的,而是隨着時間不斷變化的,目前 Kubernetes 的調度決策都是基於調度時集羣的一個靜態資源切片進行的,動態地資源調整是經過 Kubelet 的驅逐程序進行的,HPA 和 VPA 等方案也不斷提出,相信後面會不斷完善這方面的功能,讓 Kubernetes 更加智能。
資源管理和調度、應用優先級、監控、鏡像中心等不少東西相關,是個很是複雜的領域。在具體的實施和操做的過程當中,經常要考慮到企業內部的具體狀況和需求,作出針對性的調整,而且須要開發者、系統管理員、SRE、監控團隊等不一樣小組一塊兒合做。可是這種付出從總體來看是值得的,提高資源的利用率能有效地節約企業的成本,也能讓應用更好地發揮出做用。
Kubernetes 官方文檔:
其餘文檔: