前言
本篇是Kubernetes第十六篇,大家一定要把環境搭建起來,看是解決不了問題的,必須實戰。
-
Kubernetes介紹 -
Kubernetes環境搭建 -
Kubernetes-kubectl介紹 -
Kubernetes-Pod介紹(-) -
Kubernetes-Pod介紹(二)-生命周期 -
Kubernetes-Pod介紹(三)-Pod調度 -
Kubernetes-Pod介紹(四)-Deployment -
Kubernetes-Service介紹(一)-基本概念 -
Kubernetes-Service介紹(二)-服務發現 -
Kubernetes-Service介紹(三)-Ingress(含最新版安裝踩坑實踐) -
Kubernetes-網絡 -
Kubernetes-存儲(一) -
Kubernetes-存儲(二) -
Kubernetes-API Server -
Kubernetes-Kuboard
Kubernetes資源模型
在 Kubernetes 中,有兩個基礎但是非常重要的概念:Node 和 Pod。Node 翻譯成節點,是對集群資源的抽象;Pod 是對容器的封裝,是應用運行的實體。Node 提供資源,而 Pod 使用資源,這里的資源分為計算(CPU、Memory、GPU)、存儲(Disk、SSD)、網絡(Network Bandwidth、IP、Ports)。這些資源提供了應用運行的基礎,正確理解這些資源以及集群調度如何使用這些資源,對於大規模的 Kubernetes 集群來說至關重要,不僅能保證應用的穩定性,也可以提高資源的利用率。

所有的資源類型,又可以被划分為兩大類:可壓縮和不可壓縮的。在 Kubernetes 中,像CPU這樣的資源被稱作可壓縮資源。它的典型特點是,當可壓縮資源不足時,Pod 只會飢餓,但不會退出。而像內存這樣的資源,則被稱作不可壓縮資源。當不可壓縮資源不足時,Pod 就會因為 OOM被內核殺掉。
由於 Pod 可以由多個 Container 組成,所以 CPU 和內存資源的限額,是要配置在每個Container的定義上的。這樣,Pod 整體的資源配置,就由這些 Container的配置值累加得到。
舉個例子:
apiVersion: v1
kind: Pod
metadata:
name: frontend
spec:
containers:
- name: db
image: mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: "password"
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
- name: wp
image: wordpress
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
我們可以看到在Pod上資源類型配置主要有四個參數:
spec.container[].resources.requests.cpu
spec.container[].resources.limits.cpu
spec.container[].resources.requests.memory
spec.container[].resources.limits.memory
其中,limits對應資源量的上限,即最多允許使用這個上限的資源量。由於CPU資源是可壓縮的,進程無論如何也不可能突破上限,因此設置起來比較容易。對於Memory這種不可壓縮資源來說,它的Limit設置就是一個問題了,如果設置得小了,當進程在業務繁忙期試圖請求超過Limit限制的Memory時,此進程就會被Kubernetes殺掉。針對這種情況Kubernetes從資源計算、服務質量管理、資源配額等方面,設計了一套完善的機制來管理集群資源。
資源計算
容器的計算資源配額
CPU資源的計量方式
一個核心相當於1000個微核心,及1=1000m,0.5=500m;
內存資源的計量方式
默認單位為字節,也可以使用使用E、P、G、M和K后綴單位,或Ei、Pi、Gi、Mi和Ki后綴單位;
基於Requests和Limits的Pod調度機制
-
調度器在調度時,首先要確保調度后該節點上所有Pod的CPU和內存的Requests總和,不超過該節點能提供給Pod使用的CPU和Memory的最大容量值; -
即使某節點上的實際資源使用量非常低,但是已運行Pod配置的Requests值的總和非常高,再加上需要調度的Pod的Requests值,會超過該節點提供給Pod的資源容量上限,這時Kubernetes仍然不會將Pod調度到該節點上。如果Kubernetes將Pod調度到該節點上,之后該節點上運行的Pod又面臨服務峰值等情況,就可能導致Pod資源短缺;
Requests和Limits的背后機制

kubelet在啟動Pod的某個容器時,會將容器的Requests和Limits值轉化為相應的容器啟動參數傳遞給容器執行器 如果是docker 容器:
kubelet在啟動Pod的某個容器時,會將容器的Requests和Limits值轉化為相應的容器啟動參數傳遞給容器執行器 如果是docker 容器:
-
spec.container[].resources.requests.cpu: 轉化為docker 的--cpu-share; -
spec.container[].resources.limits.cpu: 轉為docker的--cpu-quota; -
spec.container[].resources.requests.memory: 提供給Kubernetes調度器作為調度和管理的依據,不會作為任何參數傳遞給Docker; -
spec.container[].resources.limits.memory: 會轉為--memory;
資源配置范圍管理(LimitRange)
在默認情況下,Kubernetes不會對Pod加上CPU和內存限制,這意味着Kubernetes系統中任何Pod都可以使用其所在節點的所有可用的CPU和內存。通過配置容器的計算資源Requests和Limits,我們可以限制Pod的資源使用,但對於Kubernetes集群管理員而言,配置每一個Pod的Requests和Limits是煩瑣的,而且很受限制。針對這樣的情況Kubernetes提出了LimitRange,對Kubernetes集群內Pod和容器的Requests和Limits進行一份全局的配置,用來限制資源的使用。
使用介紹
-
創建一個Namespace;
kubectl create namespace limit-example
-
Namespace設置LimitRange;
apiVersion: v1
kind: LimitRange
metadata:
name: mylimits
spec:
limits:
#Pod級別
- type: Pod
#Pod中所有容器的Requests值的總和最大值
max:
cpu: "4"
memory: 2Gi
#Pod中所有容器的Requests值的總和最小值
min:
cpu: 200m
memory: 6Mi
#Pod中所有容器的Limits值總和與Requests值總和的比例上限
maxLimitRequestRatio:
cpu: "3"
memory: "2"
#容器級別
- type: Container
#Pod中所有未指定Request/Limits值的容器的默認Request/Limits值
default:
cpu: 300m
memory: 200Mi
defaultRequest:
cpu: 200m
memory: 100Mi
#單個container的最大值
max:
cpu: "2"
#單個container的最小值
min:
cpu: 100m
memory: 3Mi
#Max Limit/Requests Ratio的比值
maxLimitRequestRatio:
cpu: "5"
memory: "4"
-
limit-example命名空間創建LimitRange的yaml文件;
kubectl apply -f limits.yaml --namespace=limit-example
-
查看limit-example中的LimitRange;
kubectl describe limits mylimits --namespace=limit-example

-
創建一個LimitRange對maxLimitRequestRatio比例操過限制的Pod,我們會發現Pod會創建失敗;
apiVersion: v1
kind: Pod
metadata:
name: limit-test-nginx
labels:
name: limit-test-nginx
spec:
containers:
- name: limit-test-nginx
image: nginx
resources:
limits:
cpu: "1"
memory: 512Mi
requests:
cpu: "0.8"
memory: 250Mi
kubectl apply -f max-limit-request-ratio.yaml --namespace=limit-example

特點
-
命名空間中LimitRange只會在Pod創建或者更新時執行檢查。如果手動修改LimitRange為一個新的值,那么這個新的值不會去檢查或限制之前已經在該命名空間中創建好的Pod; -
如果在創建Pod時配置的資源值(CPU或者內存)超過了LimitRange的限制,那么該創建過程會報錯,在錯誤信息中會說明詳細的錯誤原因;
資源服務質量管理(Resource Qos)
Kubernetes是根據Pod的Requests和Limits配置來實現針對Pod的不同級別的資源服務質量控制(QoS)。Requests是Kubernetes調度時能為容器提供的完全可保障的資源量,而Limits是系統允許容器運行時可能使用的資源量的上限。
Kubernetes中Pod的Requests和Limits資源配置有如下特點。
-
如果Pod配置的Requests值等於Limits值,那么該Pod可以獲得的資源是完全可靠的; -
如果Pod的Requests值小於Limits值,那么該Pod獲得的資源可分成兩部分;
完全可靠的資源,資源量的大小等於Requests值;
不可靠的資源,資源量最大等於Limits與 Requests的差額,這份不可靠的資源能夠申請到多少,取決於當時主機上容器可用資源的余量;
通過這種機制,Kubernetes可以實現節點資源的超售(Over Subscription),超售機制能有效提高資源的利用率,同時不會影響容器申請的完全可靠資源的可靠性。
調度策略的影響
-
Kubernetes的kubelet通過計算Pod中所有容器的Requests的總和來決定對Pod的調度; -
不管是CPU還是內存,Kubernetes調度器和kubelet都會確保節點上所有Pod的Requests的總和不會超過在該節點上可分配給容器使用的資源容量上限;
服務質量等級(QoS Classes)
在一個超用(Over Committed,容器Limits總和大於系統容量上限)系統中,由於容器負載的波動可能導致操作系統的資源不足,最終可能導致部分容器被殺掉。針對這種情況Kubernetes為了防止殺掉比較重要的容器,提出了服務質量等級,采用一種優先級的策略。

Kubernetes將容器划分成3個QoS等級:Guaranteed(完全可靠的)、Burstable(彈性波動、較可靠的)和BestEffort(盡力而為、不太可靠的),這三種優先級依次遞減。
使用介紹
Guaranteed
Pod中的所有容器對所有資源類型都定義了Limits和Requests,並且所有容器的Limits值都和Requests值全部相等,那么該Pod的QoS級別就是Guaranteed。注意:在這種情況下,容器可以不定義Requests,因為Requests值在未定義時默認等於Limits。
apiVersion: v1
kind: Pod
metadata:
name: myapp
labels:
name: myapp
spec:
containers:
- name: myapp
image: nginx
resources:
requests:
memory: "128Mi"
cpu: "500m"
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
Burstable
Pod中至少有一個容器設置了Requests,注意:在容器未定義Limits時,Limits值默認等於節點資源容量的上限。
apiVersion: v1
kind: Pod
metadata:
name: myapp
labels:
name: myapp
spec:
containers:
- name: myapp
image: nginx
resources:
requests:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
BestEffort
Pod中所有容器都未定義資源配置(Requests和Limits都未定義),那么該Pod的QoS級別就是BestEffort。
apiVersion: v1
kind: Pod
metadata:
name: myapp
labels:
name: myapp
spec:
containers:
- name: myapp
image: nginx
ports:
- containerPort: 80
特點
CPU是可以壓縮資源,所以在CPU不夠的時候會壓縮限流。內存是不可壓縮資源,所以QoS主要用於內存限制。
-
BestEffort Pod的優先級最低,在這類Pod中運行的進程會在系統內存緊缺時被第一優先殺掉。當然,從另外一個角度來看,BestEffort Pod由於沒有設置資源Limits,所以在資源充足時,它們可以充分使用所有的閑置資源;
-
Burstable Pod的優先級居中,這類Pod初始時會分配較少的可靠資源,但可以按需申請更多的資源。當然,如果整個系統內存緊缺,又沒有BestEffort容器可以被殺掉以釋放資源,那么這類Pod中的進程可能會被殺掉;
-
Guaranteed Pod的優先級最高,而且一般情況下這類Pod只要不超過其資源Limits的限制就不會被殺掉。當然,如果整個系統內存緊缺,又沒有其他更低優先級的容器可以被殺掉以釋放資源,那么這類Pod中的進程也可能會被殺掉;
OOM計分系統

oom_killer首先終止QoS等級最低,且超過請求資源最多的容器。這意味着與Burstable或BestEffort QoS類別的容器相比,具有更好QoS類別Guaranteed的容器被殺死的可能性更低。
但是,並非所有情況都如此。由於oom_killer還考慮了內存使用量與請求的關系,因此,由於內存使用量過多,具有更好QoS類的容器可能具有更高的oom_score,因此可能首先被殺死。
注意
-
對於操作系統來說,內存是支持Swap的,但是對於當前的QoS策略都是假定主機不啟用內存Swap。如果主機啟用了Swap,那么上面的QoS策略可能會失效,是因為Kubernetes和Docker尚不支持內存Swap空間的隔離機制,所以這一功能暫時還未實現; -
無法支持自定義QoS策略,當前的QoS策略都是基於Pod的資源配置來定義的,而資源配置本身又承擔着對Pod資源管理和限制的功能。兩種不同維度的功能使用同一個參數來配置,可能會導致某些復雜需求無法滿足,比如當前Kubernetes無法支持彈性的、高優先級的Pod。自定義QoS優先級能提供更大的靈活性,完美地實現各類需求,但同時會引入更高的復雜性,而且過於靈活的設置會給予用戶過高的權限,對系統管理也提出了更大的挑戰。
資源配額管理(Resource Quotas)
資源配額管理主要是解決不同命名空間總體資源限制,如果一個Kubernetes集群被多個用戶或者多個團隊共享,就需要考慮資源公平使用的問題,因為某個用戶可能會使用超過基於公平原則分配給其的資源量。通過ResourceQuota對象,我們可以定義資源配額,這個資源配額可以為每個命名空間都提供一個總體的資源使用的限制:它可以限制命名空間中某種類型的對象的總數目上限,也可以設置命名空間中Pod可以使用的計算資源的總上限。
使用介紹
-
創建一個命名空間,這里我們使用之前創建的limit-example的命名空間; -
創建ResourceQuota資源,名為compute-resources.yaml;
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-resources
spec:
hard:
pods: "4"
#資源配額設置Requests和Limits
requests.cpu: "1"
requests.memory: 1Gi
limits.cpu: "2"
limits.memory: 2Gi
#創建資源
kubectl apply -f compute-resources.yaml --namespace=limit-example
-
查看ResourceQuota的詳細信息;
kubectl describe quota compute-resources --namespace=limit-example

參數介紹

Master中開啟資源配額選型
資源配額可以通過在kube-apiserver的--admission-control參數值中添加ResourceQuota參數進行開啟。如果在某個命名空間的定義中存在ResourceQuota,那么對於該命名空間而言,資源配額就是開啟的。一個命名空間可以有多個ResourceQuota配置項。
計算資源配額(Compute Resource Quota)
資源配額可以限制一個命名空間中所有Pod的計算資源的總和;

存儲資源配額(Volume Count Quota)
給定的命名空間中限制所使用的存儲資源的總量;

對象數量配額(Object Count Quota)
指定類型的對象數量可以被限制;

配額的作用域(Quota Scopes)

每項資源配額都可以單獨配置一組作用域,配置了作用域的資源配額只會對符合其作用域的資源使用情況進行計量和限制,作用域范圍內超過了資源配額的請求都會報驗證錯誤。其中,BestEffort作用域可以限定資源配額來追蹤Pod資源的使用,Terminating、NotTerminating和NotBestEffort這三種作用域可以限定資源配額來追蹤以下7種類型:
-
cpu -
limits.cpu -
limits.memory -
memory -
pods -
requests.cpu -
requests.memory
設置Requests和Limits
資源配額也可以設置Requests和Limits。如果在資源配額中指定了requests.cpu或requests.memory,那么它會強制要求每個容器都配置自己的CPU Requests或CPU Limits(可使用LimitRange提供的默認值)。同理,如果在資源配額中指定了limits.cpu或limits.memory,那么它也會強制要求每個容器都配置自己的內存Requests或內存Limits(可使用LimitRange提供的默認值)。
注意
-
如果集群中總的可用資源小於各命名空間中資源配額的總和,那么可能會導致資源競爭。資源競爭時,Kubernetes系統會遵循先到先得的原則; -
不管是資源競爭還是配額的修改,都不會影響已經創建的資源使用對象;
總結
Kubernetes中資源管理的基礎是容器和Pod的資源配置(Requests和Limits)。容器的資源配置指定了容器請求的資源和容器能使用的資源上限,Pod的資源配置則是Pod中所有容器的資源配置總和上限。
通過資源配額機制,我們可以對命名空間中所有Pod使用資源的總量進行限制,也可以對這個命名空間中指定類型的對象的數量進行限制,使用作用域可以讓資源配額只對符合特定范圍的對象加以限制。對於資源配額機制我覺得可以理解為對特定場景的命名空間進行特殊管理。
通過使用資源配置范圍(LimitRange)可以有效地限制Pod和容器的資源配置的最大、最小范圍,也可以限制Pod和容器的Limits與Requests的最大比例上限,此外LimitRange還可以為Pod中的容器提供默認的資源配置。對於資源配額范圍我覺得可以理解一份保障清單,可以防止忘配置,同時可以防止亂配置。
Kubernetes基於Pod的資源配置實現了資源服務質量(QoS)。不同QoS級別的Pod在系統中擁有不同的優先級:高優先級的Pod有更高的可靠性,可以用於運行可靠性要求較高的服務;低優先級的Pod可以實現集群資源的超售,可以有效地提高集群資源利用率。
結束
歡迎大家點點關注,點點贊!