前言
為了防止容器調度到資源不足的節點上,可以為容器指定資源最少要求量。
為了防止容器無節制的使用 CPU、內存 等資源,可以為容器指定資源最大允許使用量。
更新歷史
- 20200714 - 初稿 - 左程立
- 原文地址 - https://blog.zuolinux.com/2020/07/14/container-resources-request-limit.html
資源請求和資源約束
可以為容器指定資源請求量和資源約束量。
資源一般指 CPU、內存。
資源請求量,指容器要求節點分配的最小容量,如果該節點可用容量小於容器要求的請求量,容器將被調度到其他合適節點。
資源約束量,指容器要求節點限制其使用的最大容量,如果容器內存使用量超過內容約束量,容器將被殺掉,如果指定了重啟策略,容器殺掉后將被重啟。
涉及的參數
約束量
spec.containers[].resources.limits.cpu
spec.containers[].resources.limits.memory
spec.containers[].resources.limits.hugepages-<size>
請求量
spec.containers[].resources.requests.cpu
spec.containers[].resources.requests.memory
spec.containers[].resources.requests.hugepages-<size>
示例
apiVersion: v1
kind: Pod
metadata:
name: frontend
spec:
containers:
- name: app
image: images.my-company.example/app:v4
env:
- name: MYSQL_ROOT_PASSWORD
value: "password"
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
- name: log-aggregator
image: images.my-company.example/log-aggregator:v6
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
資源單位
K8S 中的一個 cpu 等於雲平台上的 1 個 vCPU/核或裸機 Intel 處理器上的 1 個超線程。
spec.containers[].resources.requests.cpu 為 0.5 的容器肯定能夠獲得請求 1 CPU 的容器的一半 CPU 資源。表達式 0.1 等價於表達式 100m, 可以看作 “100 millicpu”。具有小數點(如 0.1)的請求由 API 轉換為 100m;最大精度是 1m。
優先考慮使用 100m 的形式。
內存可以使用E、P、T、G、M、K。也可以使用對應的 2 的冪數:Ei、Pi、Ti、Gi、Mi、Ki。
Pod 的資源請求和約束
Pod 的請求量等於內部所有容器的請求量之和。
Pod 的約束量等於內部所有容器的約束量之和。
帶資源請求的 Pod 如何調度
調度程序確保所調度的 Pod 的資源請求量小於節點的容量。
即,調度程序將查找到資源可用量充足的,可用量大於請求量的節點來放置 Pod。
帶資源約束的 Pod 如何運行
內存資源約束
如果容器超過其內存最大限制,則可能會被終止。如果容器可重新啟動,kubelet 將重新啟動容器。
如果一個容器內存使用量超過其內存請求值,那么當節點內存不足時,容器所處的 Pod 可能被逐出。
CPU 資源約束
每個容器可能被允許也可能不被允許使用超過其 CPU 約束的處理時間。 但是,容器不會由於 CPU 使用率過高而被殺死。
spec.containers[].resources.limits.cpu 先被轉換為 millicore 值,再乘以 100。其結果就是每 100 毫秒內容器可以使用的 CPU 時間總量。在此期間(100ms),容器所使用的 CPU 時間不會超過它被分配的時間。
默認的配額(quota)周期為 100 毫秒。 CPU配額的最小精度為 1 毫秒。
如何獲知集群資源使用情況,需要通過 metrics-server 來獲取。
metrics-server
查看是否已安裝 metrics-server,以下說明沒安裝
# kubectl top node
Error from server (NotFound): the server could not find the requested resource (get services http:heapster:)
# kubectl get pods,svc,deployments -n kube-system | grep metrics-server
#
開始安裝 metrics-server
git clone https://github.com/kubernetes-incubator/metrics-server
cd metrics-server/
git checkout release-0.3
修改 metrics-server/deploy/1.8+/metrics-server-deployment.yaml
containers:
- name: metrics-server
#image: k8s.gcr.io/metrics-server/metrics-server:v0.3.6
image: registry.aliyuncs.com/google_containers/metrics-server-amd64:v0.3.6
imagePullPolicy: IfNotPresent
args:
- --cert-dir=/tmp
- --secure-port=4443
- --metric-resolution=30s
- --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP
- --kubelet-insecure-tls
參數說明
--metric-resolution=30s:從 kubelet 采集數據的周期;
--kubelet-preferred-address-types:優先使用 InternalIP 來訪問 kubelet,這樣可以避免節點名稱沒有 DNS 解析記錄時,通過節點名稱調用節點 kubelet API 失敗的情況(未配置時默認的情況);
--kubelet-insecure-tls:kubelet 的10250端口使用的是https協議,連接需要驗證tls證書。--kubelet-insecure-tls不驗證客戶端證書
安裝
kubectl apply -f deploy/1.8+/
再次查看,需要多等一會兒,確保采集到數據。
[root@master01 ~]# kubectl get --raw "/apis/metrics.k8s.io/v1beta1/nodes"
{"kind":"NodeMetricsList","apiVersion":"metrics.k8s.io/v1beta1","metadata":{"selfLink":"/apis/metrics.k8s.io/v1beta1/nodes"},"items":[]}
[root@master01 1.8+]# kubectl top nodes
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
master01 163m 4% 2093Mi 56%
master02 147m 3% 1638Mi 44%
master03 151m 3% 1609Mi 43%
work01 141m 3% 1084Mi 29%
work02 150m 3% 1097Mi 29%
work03 138m 3% 1471Mi 39%
[root@master01 1.8+]# kubectl top pod
NAME CPU(cores) MEMORY(bytes)
mysql-0 15m 202Mi
mysql-1 14m 191Mi
mysql-2 13m 182Mi
nfs-client-provisioner-7db698bbc9-8ph55 2m 8Mi
已經可以看到資源使用情況。
實踐內存限額
創建一個測試用命名空間
# kubectl create namespace mem-example
namespace/mem-example created
創建一個最大限額 100M,但最少需要 250M 的容器
# cat memory-request-limit-2.yaml
apiVersion: v1
kind: Pod
metadata:
name: memory-demo-2
namespace: mem-example
spec:
containers:
- name: memory-demo-2-ctr
image: polinux/stress
resources:
requests:
memory: "50Mi"
limits:
memory: "100Mi"
command: ["stress"]
args: ["--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1"]
# kubectl apply -f memory-request-limit-2.yaml
pod/memory-demo-2 created
查看發現創建失敗
# kubectl get pod -n mem-example
NAME READY STATUS RESTARTS AGE
memory-demo-2 0/1 CrashLoopBackOff 3 88s
查看原因
# kubectl get pod -n mem-example -o go-template='{{range.status.containerStatuses}}{{"Container Name: "}}{{.name}}{{"\r\nLastState: "}}{{.lastState}}{{end}}' memory-demo-2
Container Name: memory-demo-2-ctr
LastState: map[terminated:map[containerID:docker://aaf41e exitCode:1 reason:OOMKilled ]]
其中有 reason:OOMKilled,說明超過了內存最大限額,導致了 OOM 然后被殺掉。
結束語
通過 resources.requests 和 resources.limits,可以對資源進行最少容量要求和最大容量限制,確保容器可以運行在資源充足的節點上,同時也不會由於自身占用了過多資源而影響節點上其他容器。
聯系我
微信公眾號:zuolinux_com