kubernetes之計算機資源管理

周國通發表於2019-06-15

系列目錄

當你編排一個pod的時候,你也可以可選地指定每個容器需要多少CPU和多少記憶體(RAM).當容器請求特定的資源時,排程器可以更好地根據資源請求來確定把pod排程到哪個節點上.當容器請求限制特定資源時,特定節點會以指定方式對容器的資源進行限制.

對於資源請求和資源限制的區別,可以檢視QoS

資源型別

CPU和RAM都是資源型別,資源型別有一個基本單位.CPU資源通過單位核數來指定,記憶體通過單位位元組來指定.它們和api資源不同.api資源,例如pod和service是kubernetes api server可以讀取和修改的物件.

pod和容器的資源請求與資源限制

pod裡的每一個容器都可以指一個或多個以下內容:

  • spec.containers[].resources.limits.cpu

  • spec.containers[].resources.limits.memory

  • spec.containers[].resources.requests.cpu

  • spec.containers[].resources.requests.memory

儘管請求和限制只能通過單個的容器來指定,但是我們通常說pod的資源請求和資源限制,這樣更方便.Pod對於某一特定資源的請求(或限制)是pod裡單個容器請求資源的和.

CPU的意義

對cpu資源的請求和限制用單位cpu來衡量,一個cpu在kubernetes裡和以下任意一個是等價的:

  • 1 AWS vCPU

  • 1 GCP Core

  • 1 Azure vCore

  • 1 IBM vCPU

  • 在祼機因特爾超執行緒處理器上一個超執行緒

帶小數的請求也是允許的.一個指定spec.containers[].resources.requests.cpu值為0.5的容器會被確保分配1個cpu的一半資源.值0.1和100m是等價的,可以讀作100 millicpu,也有人讀作100 millicores,實際上意義是一樣的.請求資源的值為0.1將會被api轉換為100m,請求的精度比1m更小是不允許的,因此100m一個較優的選擇.CPU資源的請求總是絕對量,永遠不會是一個相對值,值是0.1時對於單核cpu,雙核cpu或者甚至48核cpu都是一樣的.

記憶體的意義

對記憶體資源的請求/限制用位元組來衡量.你可以使用一個純整數或者定點整數帶上E, P, T, G, M, K這些字尾,你也可以使用2的冪次方的:Ei, Pi, Ti, Gi, Mi, Ki來表示,比如以下表示的量相等

128974848, 129e6, 129M, 123Mi

以下定義的pod包含兩個容器,每一個都請求0.25cpu和64MiB (226 位元組)的記憶體.每一個都限制0.5cpu和128MIB記憶體.你可以說pod請求0.5cpu和128MiB記憶體,限制1cpu和256MiB記憶體

前面說過,pod的資源請求/限制是pod裡的容器資源請求/限制的和

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"

kubernetes如何排程有資源請求的pod

當你建立一個pod,kubernetes會選擇一個節點來執行.對於每種資源型別,不論是cpu還是記憶體,節點可以提供給pod的都是有上限的.對於每種資源,排程器都會確保被排程的pod的請求的總和小於節點的承載能力.請注意即便節點上的cpu或者記憶體的使用率非常低,但是如果容量檢測失敗,排程器仍然可能拒絕把某一pod放到指定節點上.這確保後來資源使用增多時不會出現資源短缺的情況.

有資源限制的pod如何執行

當kubelete啟動了pod的容器,它會把cpu和記憶體的限制傳入到容器的runtime裡

當使用的是docker時:

  • spec.containers[].resources.requests.cpu的值被轉換為核數值,它也可能是小數,乘1024.這個值和2兩者較大的一個會被作為--cpu-shares標識的值傳入docker run命令

  • spec.containers[].resources.limits.cpu被轉換為millicore值並且乘以100.結果值是容器每100毫秒可用的cpu時間的總和.

注意預設的時間段配額是100毫秒,最小配額是1毫秒.

  • spec.containers[].resources.limits.memory被轉換為整數,然後作為--momory標識的值傳入docker run命令.

如果一個容器超過了它的資源限制,則它可能會被終止.如果它是可重啟的(根據pod編排時重啟策略),則kubelete會重啟它,就像對待其它執行時錯誤一樣.

一個容器可能會被允許或者不被允許超過超過它的cpu限制.但是超過cpu資源限制時它不會被殺掉.

問題處理

pod掛起,event訊息是:failedScheduling

如果排程器找不到任何可以放置pod的節點,pod一直是未排程狀態找到一個合適的節點.排程器每當找不到合適節點來放置pod,就會產生一個事件,資訊如下

kubectl describe pod frontend | grep -A 3 Events

Events:
  FirstSeen LastSeen   Count  From          Subobject   PathReason      Message
  36s   5s     6      {scheduler }              FailedScheduling  Failed for reason PodExceedsFreeCPU and possibly others

上面的示例中,由於節點的cpu資源不足,名稱為frontend的pod排程失敗.類似的錯誤提示也可能是由於記憶體資源不足(PodExceedsFreeMemory).一般地,如果類似這樣的訊息出現,可以進行以下嘗試:

  • 叢集中新增更多節點

  • 終止一些非必須程式來為掛起的pod騰出資源

  • 檢測確保pod小於node,比如節點的容量是cpu:1,如果pod請求的是cpu:1.1將永遠不會被排程.

你可以通過kubectl describe nodes命令來檢視節點的容量以及可分配的量:

kubectl describe nodes e2e-test-minion-group-4lw4
Name:            e2e-test-minion-group-4lw4
[ ... lines removed for clarity ...]
Capacity:
 cpu:                               2
 memory:                            7679792Ki
 pods:                              110
Allocatable:
 cpu:                               1800m
 memory:                            7474992Ki
 pods:                              110
[ ... lines removed for clarity ...]
Non-terminated Pods:        (5 in total)
  Namespace    Name                                  CPU Requests  CPU Limits  Memory Requests  Memory Limits
  ---------    ----                                  ------------  ----------  ---------------  -------------
  kube-system  fluentd-gcp-v1.38-28bv1               100m (5%)     0 (0%)      200Mi (2%)       200Mi (2%)
  kube-system  kube-dns-3297075139-61lj3             260m (13%)    0 (0%)      100Mi (1%)       170Mi (2%)
  kube-system  kube-proxy-e2e-test-...               100m (5%)     0 (0%)      0 (0%)           0 (0%)
  kube-system  monitoring-influxdb-grafana-v4-z1m12  200m (10%)    200m (10%)  600Mi (8%)       600Mi (8%)
  kube-system  node-problem-detector-v0.1-fj7m3      20m (1%)      200m (10%)  20Mi (0%)        100Mi (1%)
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  CPU Requests    CPU Limits    Memory Requests    Memory Limits
  ------------    ----------    ---------------    -------------
  680m (34%)      400m (20%)    920Mi (12%)        1070Mi (14%)

通過上面的輸出,你可以看到如果有一個pod請求或者 6.23Gi 記憶體,則它不適合排程到此節點上.

通過檢視pods部分,你可以看到你可以看到哪些pod在本節點上佔用了資源

可供pod使用的資源數量小於節點的實際容量,這是因為系統守護程式按比例使用可用資源.allocatable欄位裡的數量是pod實際可用的資源數量,詳情請檢視可用資源

容器終止

pod可能由於資源不足被終止,你可以使用kubectl describe pod來檢測pod是否由於資源達到上限被殺死

kubectl describe pod simmemleak-hra99
Name:                           simmemleak-hra99
Namespace:                      default
Image(s):                       saadali/simmemleak
Node:                           kubernetes-node-tf0f/10.240.216.66
Labels:                         name=simmemleak
Status:                         Running
Reason:
Message:
IP:                             10.244.2.75
Replication Controllers:        simmemleak (1/1 replicas created)
Containers:
  simmemleak:
    Image:  saadali/simmemleak
    Limits:
      cpu:                      100m
      memory:                   50Mi
    State:                      Running
      Started:                  Tue, 07 Jul 2015 12:54:41 -0700
    Last Termination State:     Terminated
      Exit Code:                1
      Started:                  Fri, 07 Jul 2015 12:54:30 -0700
      Finished:                 Fri, 07 Jul 2015 12:54:33 -0700
    Ready:                      False
    Restart Count:              5
Conditions:
  Type      Status
  Ready     False
Events:
  FirstSeen                         LastSeen                         Count  From                              SubobjectPath                       Reason      Message
  Tue, 07 Jul 2015 12:53:51 -0700   Tue, 07 Jul 2015 12:53:51 -0700  1      {scheduler }                                                          scheduled   Successfully assigned simmemleak-hra99 to kubernetes-node-tf0f
  Tue, 07 Jul 2015 12:53:51 -0700   Tue, 07 Jul 2015 12:53:51 -0700  1      {kubelet kubernetes-node-tf0f}    implicitly required container POD   pulled      Pod container image "k8s.gcr.io/pause:0.8.0" already present on machine
  Tue, 07 Jul 2015 12:53:51 -0700   Tue, 07 Jul 2015 12:53:51 -0700  1      {kubelet kubernetes-node-tf0f}    implicitly required container POD   created     Created with docker id 6a41280f516d
  Tue, 07 Jul 2015 12:53:51 -0700   Tue, 07 Jul 2015 12:53:51 -0700  1      {kubelet kubernetes-node-tf0f}    implicitly required container POD   started     Started with docker id 6a41280f516d
  Tue, 07 Jul 2015 12:53:51 -0700   Tue, 07 Jul 2015 12:53:51 -0700  1      {kubelet kubernetes-node-tf0f}    spec.containers{simmemleak}         created     Created with docker id 87348f12526a

上面的例子中,Restart Count:5表示simmemleak容器中止並且被重啟了五次.

你可以通過kubectl get pod加上-o go-template=...選項來獲取上次終止的容器的狀態

kubectl get pod -o go-template='{{range.status.containerStatuses}}{{"Container Name: "}}{{.name}}{{"\r\nLastState: "}}{{.lastState}}{{end}}'  simmemleak-hra99
Container Name: simmemleak
LastState: map[terminated:map[exitCode:137 reason:OOM Killed startedAt:2015-07-07T20:58:43Z finishedAt:2015-07-07T20:58:43Z containerID:docker://0e4095bba1feccdfe7ef9fb6ebffe972b4b14285d5acdec6f0d3ae8a22fad8b2]]

你可以看到容器由於reason:OOM Killed錯誤被中止,OOM表示out of memory(記憶體溢位)

Local ephemeral storage(本地暫存容量)

此功能在1.14版本中為beta狀態

Kubernetes在1.8的版本中引入了一種類似於CPU,記憶體的新的資源模式:ephemeral-storage,並且在1.10的版本在kubelet中預設開啟了這個特性。ephemeral-storage是為了管理和排程Kubernetes中執行的應用的短暫儲存。在每個Kubernetes的節點上,kubelet的根目錄(預設是/var/lib/kubelet)和日誌目錄(/var/log)儲存在節點的主分割槽上,這個分割槽同時也會被Pod的EmptyDir型別的volume、容器日誌、映象的層、容器的可寫層所佔用。ephemeral-storage便是對這塊主分割槽進行管理,通過應用定義的需求(requests)和約束(limits)來排程和管理節點上的應用對主分割槽的消耗。

在節點上的kubelet啟動的時候,kubelet會統計當前節點的主分割槽的可分配的磁碟資源,或者你可以覆蓋節點上kubelet的配置來自定義可分配的資源。在建立Pod時會根據儲存需求排程到滿足儲存的節點,在Pod使用超過限制的儲存時會對其做驅逐的處理來保證不會耗盡節點上的磁碟空間。

如果執行時指定了別的獨立的分割槽,比如修改了docker的映象層和容器可寫層的儲存位置(預設是/var/lib/docker)所在的分割槽,將不再將其計入ephemeral-storage的消耗。

對Local ephemeral storage的請求/限制

所有的container可以指定以下一個或多個選項

  • spec.containers[].resources.limits.ephemeral-storage

  • spec.containers[].resources.requests.ephemeral-storage

ephemeral-storage資源的請求/限制通過位元組來衡量.同上面記憶體的請求/限制方式一樣,這裡不再細述.

比如以下pod包含兩個容器,每一個都對Local ephemeral storage的請求為2GiB,限制為4GiB.此時,整個pod對資源的請求為4GiB,對資源的限制為8GiB

apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: db
    image: mysql
    env:
    - name: MYSQL_ROOT_PASSWORD
      value: "password"
    resources:
      requests:
        ephemeral-storage: "2Gi"
      limits:
        ephemeral-storage: "4Gi"
  - name: wp
    image: wordpress
    resources:
      requests:
        ephemeral-storage: "2Gi"
      limits:
        ephemeral-storage: "4Gi"

對有ephemeral-storage請求的pod如何排程

當你建立一個pod,kubernetes排程器選擇一個節點來執行它.每個節點都有可供pod使用的最大Local ephemeral storage最大量,詳細資訊請檢視Node Allocatable

排程器保證被排程的容器資源總和小於節點的容量

帶有Local ephemeral storage的pod如何執行

對於容器級別的隔離,如果容器的可寫層( writable layer)和日誌(log)超出了容量限制,容器所在的pod將會被驅離;對於pod級別的隔離,如果pod裡所有容器使用的總Local ephemeral storage和pod的emptydir儲存卷超過限制,pod將會被驅離.

示例

apiVersion: v1
kind: Pod
metadata:
  name: teststorage
  labels:
    app: teststorage
spec:
  containers:
  - name: busybox
    image:  busybox
    command: ["bash", "-c", "while true; do dd if=/dev/zero of=$(date '+%s').out count=1 bs=10MB; sleep 1; done"] # 持續寫入檔案到容器的rootfs中
    resources:
      limits:
        ephemeral-storage: 100Mi #定義儲存的限制為100M
      requests:
        ephemeral-storage: 100Mi

測試這個Pod就能發現在容器寫入超過儲存限制時就會被驅逐掉了:

 /tmp kubectl apply -f pod.yaml
pod "teststorage" created
  /tmp kubectl get pod -w
NAME          READY     STATUS              RESTARTS   AGE
teststorage   0/1       ContainerCreating   0          3s
teststorage   1/1       Running   0         7s
teststorage   0/1       Evicted   0         1m

檢視kubernetes的pod的事件也能看到它是由於超過了限制的ephemeral-storage被驅逐掉:

Events:
  Type     Reason                 Age   From               Message
  ----     ------                 ----  ----               -------
  Normal   Scheduled              2m    default-scheduler  Successfully assigned teststorage to minikube
  Normal   SuccessfulMountVolume  2m    kubelet, minikube  MountVolume.SetUp succeeded for volume "default-token-l7wp9"
  Normal   Pulling                2m    kubelet, minikube  pulling image "busybox"
  Normal   Pulled                 2m    kubelet, minikube  Successfully pulled image "busybox"
  Normal   Created                2m    kubelet, minikube  Created container
  Normal   Started                2m    kubelet, minikube  Started container
  Warning  Evicted                1m    kubelet, minikube  pod ephemeral local storage usage exceeds the total limit of containers {{104857600 0} {<nil>} 100Mi BinarySI}
  Normal   Killing                1m    kubelet, minikube  Killing container with id docker://busybox:Need to kill Pod

相關文章