Ray Lee | 李宗叡
Learn or Die
Published in
31 min readDec 21, 2020

--

Photo by Alexander Schimmeck on Unsplash

# 概述

哈囉! 本篇來介紹 Kubernetes 中的 Compute Resource。

接下來要介紹的可不是一般的 Resource, 像是元件那些, 而是可計算資源。

簡單來說, 我們在定義 Pod 的時候, 可以定義啟動這個 Pod 需要多少資源, 以及這個 Pod 最多可以使用多少資源, 這樣夠淺顯易懂吧! 下面就是詳細介紹了。

當你指定一個 Pod, 你可以選擇性的指定每個容器需要多少 CPU 以及 memory (RAM), 當你有指定 資源要求 (resource request) 給容器, scheduler 可以更加的分配 Pods 到 nodes 上。

而當你有指定容器的 資源限制 (resource limits), 在 node 上的資源競爭將以指定的方式來處理。 更多 requests 以及 limits 的不同之處可以參考 Resource QoS

# 資源類型

CPU 以及 memory 為 資源類型 (resource type), 資源類型有其基本單位。 CPU 以 cores 為單位, 而 memory 以 bytes 為單位。

如果你使用的是 Kubernetes v1.14 或更新的版本, 你可以指定 巨頁 (huge page) 資源。

Huge pages 是 Linux 上特定的功能, node 的 kernel 可以分配比預設 page 更大的 memory。 比如說, 在預設 page 大小為 4KiB 的系統, 你可以指定一個限制, hugepages-2Mi: 80Mi。 如果容器試圖分配超過 40 2MiB huge pages (總共 80MiB), 則分配將失敗。

Ray 第一次聽到 Huge Page 的概念時也是 一臉矇逼, 不過本著學習者的心態 Google 了幾下之後, 就不再那麼矇了。

簡單來說, memory 有過被管理的單位叫做 頁 (page), 在很多系統中預設是 4Ki, 所以 1Mi 的 memory 就有 256 pages, 沒問題吧? 而有個硬體叫做 TLB (Translation Lookaside Buffer), 它是個大小固定的緩存硬體, 而虛擬記憶體對應實體記憶體的 mapping 會緩存 TLB 上。

簡單來說, 如果是使用 4Ki 為 page 為單位的話, 那就要有很多 page, 而越多 page 記憶體的位置就需要越大的緩存去管理, 偏偏 TLB 這東西又是固定大小, 所以能怎麼做? 那自然就是加大 page 的大小, 讓總 page 的數量下降, 這樣緩存就不需要那麼大的空間來記住那麼多的記憶體位置了。 這便是 huge page 的簡單概念

注意: 跟 memory 以及 cpu 資源不同, 你無法過量使用 hugepages-* 資源

CPU 以及 memory 都被歸類在 compute resources, 或 resources。 Compute resources 為可被要求, 分配, 以及消耗的可量測數量。 他們與 [API resources] 不同。 API resources, 像是 Pod 以及 Services 為可通過 Kubernetes API server 讀取以及修改的物件。

# Pod 以及 Container 的資源要求以及資源限制

每個 Container 或 Pod 可被指定下列一個或多個條件:

  • 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>

儘管 requests 以及 limits 只可被指定再單獨的 Container 上, 但要計算出 Pod 的 request 以及 limits 也很方便。 Pod 對某項特定資源的 request/limit 就等於該 Pod 裡的所有容器的 request/limit 總和。

# CPU 解釋

CPU 資源的 limits 以及 requests 都以 cpu 為單位衡量。 1 cpu, 在 Kubernetes 中, 相當於以下單位:

  • 1 AWS vCPU
  • 1 GCP Core
  • 1 Azure vCore
  • 1 IBM vCPU
  • 1 Hyperthread, Hyperthreading 的 Intel bare-metal 處理器

小數的資源請求是容許的。 spec.container[].resources.requests.cpu 如果設為 0.5, 那相當於要求 1 CPU 的一半。 0.1 相等於 100m, 100m 也可讀為 "100 millicpu"。 有些人說 "100 millicores", 都可理解為同樣的東西。 有小數點的請求, 像是 0.1, 會被 API 轉換到 100m, 而精度最小為 1m, 所以設定 100m 會是比較理想的

CPU 都是絕對數字, 不會是相對數字; 舉例來說, 0.1 在 1-core, 2-core, 或 48-core 的機器中代表的量都是一樣的。

# Memory 解釋

Memory 中的 limits 以及 requests 資源管理以 bytes 為單位。 記憶體可被表示為簡單的 integer, 或固定位數的 integer, 可使用以下後綴: E, P, T, G, M, K 。 你也可以使用: Ei, Pi, Ti, Gi, Mi, Ki, 舉例來說, 下面都代表大約相同的值:

128974848, 129e6, 129M, 123Mi

這邊有個範例, 下面的 Pod 含有兩個容器。 每個容器都有 resource requests, 0.25 cpu 以及 64MiB 記憶體。 每個容器都有 resource limits 0.5 cpu 以及 128 MiB 記憶體。 可以說, 這個 Pod 有 resource requests 0.5 cpu 以及 128 MiB 記憶體, resource limits 1 cpu 以及 256 MiB 記憶體

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"

# 有 resource requests 的 Pod 是被如何調度的?

當你建立 Pod, Kubernetes scheduler 會挑選一個 node 來運行該 Pod。 每個 node 針對每種資源類型都有最大容量: 也就是說, 有多少 CPU 以及 memory 可以分配給 Pods。 Scheduler 會確保被調度的容器的各類型資源總和會小於該 Node 的資源總量。 注意到, 就算 node 上的資源使用量非常低, 如果容量確認沒過的話, Scheduler 還是不會分配 Pod 到這個 node 上。 這確保在 node 上資源短缺, 比如說, 在每日的資源用量高峰。

# 有 resource limits 的 Pod 是如何被調度?

當 Kubelet 啟動 Pod 中的一個 Container, 它會將 CPU 以及 memory limits 帶入到該 container runtime。 當使用 Docker:

  • spec.containers[].resources.requests.cpu 會被轉換成 core 值, 它可能會是有小數點的, 以及會被乘以 1024。 在 docker run 指令中使用 --cpu-shares, 可以設定比這更高的數字或 2
  • spec.containers[].resources.limits.cpu 會被轉換成 millicore 值以及乘以 100。 結果值為容器在每 100ms (也就是十分之一秒)的 CPU time 中可使用的總量, 容器無法使用超過被分到的 CPU 配額, 這個部分如果看不是很懂, 有興趣的話最下面會有補充說明。 注意: 預設的 quota period 為 100ms, 最小為 1ms
  • spec.containers[].resources.limits.memory 會被轉換成 integer, 如同 docker run 指令的 [--memory] flag 的效果

如果容器超出了其 memory limits 限制, 它會被終止。 如果它設置為可重啟, 跟其他的 runtime failure 一樣, kubelet 將會重啟它。 如果容器超出了其 memory request 限制, 當 node memory 耗光了之後, 就會把該 Pod 砍了, Ray 看到這一段的時候也有點矇逼, 下面會再補充解釋! 容器可能會或者可能不會被允許超出其 CPU limit 限制, 然而, 超出了也不會被砍了。 要得知容器是否無法被分配, 或者因為資源限制被殺掉了, 可以參考 Troubleshooting 區塊

# 監控資源使用量

資源使用率會以 Pod 狀態的部分回報

# Troubleshooting

# 容器顯示 pending 以及事件訊息 failedScheduling

如果 Scheduler 在 node 上無法找到足夠的資源來置放 Pod, 這個 Pod 將會持續處於未分配的狀態, 直到 Scheduler 找到足夠的資源。 每次 Scheduler 找不到足夠資源而分配失敗的話, 都會產生一個事件, 就像這樣:

  • 取得事件:
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

在上面的例子中, 因為 node 的 CPU 資源不足, 所以 Scheduler 無法分配名為 “frontend” 的 Pod。 類似的錯誤訊息也會出現在 memory 不足時 (PodExceedsFreeMemory)。 另外, 如果 Pod 顯示這類型的錯誤訊息, 並卡住, 你可以試試下面的操作:

  • 增加更多的 nodes 到叢集
  • 終止不必要的 Pods, 騰出空間給 pending Pods
  • 確認 Pod 沒有比所有的 nodes 大, 比方說, 如果所有的 Nodes 都有 cpu: 1 的容量, 然後 Pod 的 requests 限制為 cpu: 1.1, 那這個 pod 將無法被分配

可以使用 kubectl describe nodes 指令來確認可用容量以及已經分配出去的容量, 比如說:

  • 執行指令
kubectl describe nodes e2e-test-node-pool-4lw4
  • 輸出:
Name:            e2e-test-node-pool-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%)

從上面的輸出可以看到, Allocatable 就是可分配的資源, 而 Allocated resources 就是已經分配出去的資源, 所以如果 Pod 被定義 requests 限制超過 1120m CPUs, 或 6.23Gi memory, 那這個 Pod 將無法被分配在這個 node, 因為資源不足啊!

Pods 區塊中可以看到每個 Pod 各使用了這個 node 多少的資源。

Pod 可用的資源總量小於該 node 的 capacity, 因為系統 daemons 使用了一部分的可用資源。 allocatable 欄位 NodeStatus 提供了可分配給 Pods 的資源。 更多資訊, 可參考 Node Allocatable Resources

resource quota 功能可用來設置資源可被消耗的最大限制。 如果使用在 namespaces 上, 那可以有效防止一個團隊佔據了所有的資源。

# 容器被終止

容器可能會因為資源耗盡而被終止。 若要確認容器是否有因為達到資源限制而被殺掉, 使用 kubectl describe pod PodName

  • 執行指令
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

上面的 example 中, Restart Count: 5 顯示容器 simmemleak 容器已被終止並重啟五次

你可以呼叫 kubectl get pod 以及 option -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

FEATURE STATE: kubernetes v1.17 kubernetes 版本 1.8 介紹了一個新的資源, 用來管理本地臨時儲存空間的 ephemeral-storage。 在每一個 node 中, kubelet 的 root 資料夾 (預設 /var/lib/kubelet) 以及 log 資料夾 (預設 /var/log) 都被儲存在 root 分區。 這個分區同時也經由 emptyDir volumes, container logs, image layers 以及 container writable layers 被 Pods 共享

# 本地臨時儲存區的 Requests 以及 Limits 資源限制設定

每個 Pod 中的容器都可以指定一個或多個以下的限制:

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

ephemeral-storage 的 limits 以及 requests 資源管理以 bytes 為單位。 儲存區可被表示為簡單的 integer, 或固定位數的 integer, 可使用以下後綴: E, P, T, G, M, K 。 你也可以使用: Ei, Pi, Ti, Gi, Mi, Ki, 舉例來說, 下面都代表大約相同的值:

128974848, 129e6, 129M, 123Mi

舉例來說, 以下的 Pod 有兩個容器。 每個容器都有 2 GiB 的 本地臨時儲存區 request 限制, 而每個容器都有 4GiB 的本地臨時儲存區 limit 限制。 因此, 這個 Pod 有 4GiB 的本地臨時儲存 request 限制以及 8GiB 的 limit 限制

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 request 限制的 Pod 是如何被分配的?

當建立一個 Pod 時, Kubernetes scheduler 會挑選 node 來運行該 Pod。 每個 node 都有 local ephemeral storage 可提供給 Pods 的最大值。 更多資訊可參考 Node Allocatable

Scheduler 會確保被分配的容器的 request 限制小於 node 的容量

# 有 ephemeral-storage limit 限制的 Pod 是如何被分配的?

從容器等級隔離層面來說, 如果容器的可寫層 (writable layer) 以及 logs 使用量超過了儲存限制, 那們 Pod 會被殺掉。 從 Pod 等級隔離層面來說, 如果該 Pod 內的容器 ephemeral storage 使用量超過了限制, 以及該 Pods 的 emptyDir volumes 超過了限制, 該 Pod 也會被殺掉。

官方文件就先到這裡, 以下為針對 Memory 以及 CPU 的進一步分析

# 頗析 (Memory)

首先, 先針對 memory 的部分來頗析 limits 跟 requests 的詳細運作

# 建立一個沒有資源限制的 pod

  • 輸入
kubectl run limit-test --image=busybox --command -- /bin/sh -c "while true; do     sleep 2; done"
  • 輸出
deployment.apps "limit-test" created

# 使用 Kubectl 來驗證看看 Kubernetes 是否建立沒有資源限制的 pod

  • 輸入
kubectl get pods limit-test-7cff9996fc-zpjps -o=jsonpath='{.spec.containers[0]    .resources}'
  • 輸出
map[]

# 現在我們 ssh 到該 node, 然後使用以下指令看看 docker 是如何運行這個容器

  • 輸入以下指令取得容器名稱
docker ps | grep busy | cut -d' ' -f1
  • 得到容器名稱
5c3af3101afb
  • 輸入指令取得容器限制的 memory value
docker inspect 5c3af3101afb -f "{{.HostConfig.Memory}}"
  • 得到輸出
0
  • .HostConfig.Memory 代表的意思是? 對應到 docker run--memory 參數

# 讓我們繼續往下追, 追到最終的 cgroup

  • 取得容器 pid
ps ax | grep /bin/sh
  • 輸出
9513 ?        Ss     0:00 /bin/sh -c while true; do sleep 2; done
  • 取得該 pid cgroup
sudo cat /proc/9513/cgroup
  • 輸出
...
6:memory:/kubepods/burstable/podfbc202d3-da21-11e8-ab5e-42010a80014b/0a1b22ec1361a97c3511db37a4bae932d41b22264e5b97611748f8b662312574
- (1)**kubepods**: 程序將會繼承所有這個 group 中的屬性
- (2)**burstable**: 可參考[burstable QOS class](https://kubernetes.io/docs/tasks/configure-pod-container/quality-service-pod/#qos-classes)
  • 取得細節, 將上面的路徑 append 到 /sys/fs/cgroups/memory
ls -l /sys/fs/cgroup/memory/kubepods/burstable/podfbc202d3-da21-11e8-ab5e-42010a80014b/0a1b22ec1361a97c3511db37a4bae932d41b22264e5b97611748f8b662312574
  • 輸出
...
-rw-r--r-- 1 root root 0 Oct 27 19:53 memory.limit_in_bytes
-rw-r--r-- 1 root root 0 Oct 27 19:53 memory.soft_limit_in_bytes

# 先來看 memory.limit_in_bytes, 這是設定 memory limit 的 cgroup, 他等同於 docker 裡頭的 --memory, 以及 Kubernetes 限制的 memory limit

  • 輸入以下指令取得數值
sudo cat /sys/fs/cgroup/memory/kubepods/burstable/podfbc202d3-da21-11e8-ab5e-42010a80014b/0a1b22ec1361a97c3511db37a4bae932d41b22264e5b97611748f8b662312574/memory.limit_in_bytes
  • 輸出
9223372036854771712

這個數據代表沒有限制 memory, 可參考 Stackoverflow 我們一開始在 Kubernetes 沒有設定 memory 限制, 導致 docker 建立一個 HostConfig.Memory 設定為 0 的容器, 最終導致這個容器的程序被放到 memory.limit_in_bytes 屬性設為 "no limit" 的 memory cgroup

# 現在讓我們來建立另外一個 pod, 限制 memory limit 為 100Mi

  • 執行以下指令
kubectl run limit-test --image=busybox --limits "memory=100Mi" --command -- /bin/sh -c "while true; do sleep 2; done"
  • 輸出
deployment.apps "limit-test" created

# 驗證這個 pod 有被設置特定的 limit

  • 執行以下指令
kubectl get pods limit-test-5f5c7dc87d-8qtdx -o=jsonpath='{.spec.containers[0].resources}'
  • 輸出
map[limits:map[memory:100Mi] requests:map[memory:100Mi]]

這邊可以看到, 如果我們有設置 limits resource, 但是沒設置 requests resource 的時候, Kubernetes 預設將 requests 設為 limits

# 取得容器 id

  • 執行以下指令
docker ps | grep busy | cut -d' ' -f1
  • 輸出
8fec6c7b6119

# 取得容器 pid

  • 輸入以下指令
ps ax | grep /bin/sh
  • 輸出
29532 ?      Ss     0:00 /bin/sh -c while true; do sleep 2; done

# 取得該容器的 memory cgroup

  • 輸入以下指令
sudo cat /proc/29532/cgroup
  • 輸出
...
6:memory:/kubepods/burstable/pod88f89108-daf7-11e8-b1e1-42010a800070/8fec6c7b61190e74cd9f88286181dd5fa3bbf9cf33c947574eb61462bc254d11

# 印出 memory cgroup 中的 memory.limit_in_bytes 數值

  • 輸入以下指令
sudo cat /sys/fs/cgroup/memory/kubepods/burstable/pod88f89108-daf7-11e8-b1e1-42010a800070/8fec6c7b61190e74cd9f88286181dd5fa3bbf9cf33c947574eb61462bc254d11/memory.limit_in_bytes
  • 輸出
104857600

# 最後, 讓我們來看看前面跳過的, 位於 memory cgroup 當中的 momery.soft_limit_in_bytes

  • 輸入以下指令
sudo cat /sys/fs/cgroup/memory/kubepods/burstable/pod88f89108-daf7-11e8-b1e1-42010a800070/8fec6c7b61190e74cd9f88286181dd5fa3bbf9cf33c947574eb61462bc254d11/memory.soft_limit_in_bytes
  • 輸出
9223372036854771712

從上面的值可以得知, 就算我們有設定 requests 的限制, kubernetes 也不會命令 docker 去做這件事, 儘管 docker run 的參數 --memory-reservation 是可以做到這件事的

# 頗析 (CPU)

CPU 的頗析模式大致上與 memory 相同, 但還是有不一樣的地方, 讓我們繼續看下去。

# 建立一個有 request CPU 限制的 pod

  • 輸入以下指令建立 pod
kubectl run limit-test --image=busybox --requests "cpu=50m" --command -- /bin/sh -c "while true; do sleep 2; done"
  • 輸出
deployment.apps "limit-test" created

# 使用 kubectl 來檢視 pod 的資源限制資訊

  • 輸入以下指令
kubectl get pods limit-test-5b4c495556-p2xkr -o=jsonpath='{.spec.containers[0].resources}'
  • 輸出
map[requests:map[cpu:50m]]

從輸出可看到 kubernetes 有確實的設定 request 資源限制

# 取得容器 id

  • 輸入以下指令
docker ps | grep busy | cut -d' ' -f1
  • 輸出
f2321226620e

# 查看 Docker 是否確實設定資源限制

  • 輸入以下指令
docker inspect f2321226620e --format '{{.HostConfig.CpuShares}}'
  • 輸出
51

為什麼不是 50? 那是因為 Docker 以及 cgroup 都將 core 分成 1024 份, 而 Kubernetes 則是分成 1000 份

# 取得容器 pid

  • 輸入以下指令
ps ax | grep /bin/sh
  • 輸出
60554 ?      Ss     0:00 /bin/sh -c while true; do sleep 2; done

# 取得容器的 CPU cgroup

  • 輸入以下指令
sudo cat /proc/60554/cgroup
  • 輸出
...
4:cpu,cpuacct:/kubepods/burstable/pode12b33b1-db07-11e8-b1e1-42010a800070/3be263e7a8372b12d2f8f8f9b4251f110b79c2a3bb9e6857b2f1473e640e8e75

# 取得 cgroup 屬性

  • 輸入以下指令
ls -l /sys/fs/cgroup/cpu,cpuacct/kubepods/burstable/pode12b33b1-db07-11e8-b1e1-42010a800070/3be263e7a8372b12d2f8f8f9b4251f110b79c2a3bb9e6857b2f1473e640e8e75
  • 輸出
total 0
drwxr-xr-x 2 root root 0 Oct 28 23:19 .
drwxr-xr-x 4 root root 0 Oct 28 23:19 ..
...
-rw-r--r-- 1 root root 0 Oct 28 23:19 cpu.shares

以上輸出可知, Docker 的 HostConfig.CpuShares 屬性映射到 cgroup 的 cpu.shares 屬性

# 取得 cpu.shares 的值

  • 輸入以下指令
/sys/fs/cgroup/cpu,cpuacct/kubepods/burstable/podb5c03ddf-db10-11e8-b1e1-42010a800070/64b5f1b636dafe6635ddd321c5b36854a8add51931c7117025a694281fb11444/cpu.shares
  • 輸出
51

咦, 以上的行為跟 memory 不太一樣對吧? 限制 memory 的 resource limit 是不會作用到 cgroup 的 soft limits 的

# 接下來, 建立一個有著 limit resource 的 pod

  • 輸入以下指令
kubectl run limit-test --image=busybox --requests "cpu=50m" --limits "cpu=100m" --command -- /bin/sh -c "while true; do
sleep 2; done"
  • 輸出
deployment.apps "limit-test" created

# 使用 kubectl 來檢視第二個 pod 的資源限制資訊

  • 輸入以下指令
kubectl get pods limit-test-5b4fb64549-qpd4n -o=jsonpath='{.spec.containers[0].resources}'
  • 輸出
map[limits:map[cpu:100m] requests:map[cpu:50m]]

從輸出可看到 kubernetes 有確實的設定 request 資源限制

# 取得第二個容器 id

  • 輸入以下指令
docker ps | grep busy | cut -d' ' -f1
  • 輸出
f2321226620e

# 查看 Docker 是否確實設定資源限制

  • 輸入以下指令
docker inspect 472abbce32a5 --format '{{.HostConfig.CpuShares}} {{.HostConfig.CpuQuota}} {{.HostConfig.CpuPeriod}}'
  • 輸出
51 10000 100000

Dokcer 使用了兩個值來代表 memory resource limit, HostConfig.CpuPeriodHostConfig.CpuQuota, 而這兩個值又分別映射到 cgroup 的兩個屬性 cpu.cfs_period_uscpu.cfs_quota_us

# 取得 cpu.cfs_period_uscpu.cfs_quota_us 的值

  • 輸入以下指令
sudo cat /sys/fs/cgroup/cpu,cpuacct/kubepods/burstable/pod2f1b50b6-db13-11e8-b1e1-42010a800070/f0845c65c3073e0b7b0b95ce0c1eb27f69d12b1fe2382b50096c4b59e78cdf71/cpu.cfs_period_us
  • 輸出
100000
  • 輸入以下指令
sudo cat /sys/fs/cgroup/cpu,cpuacct/kubepods/burstable/pod2f1b50b6-db13-11e8-b1e1-42010a800070/f0845c65c3073e0b7b0b95ce0c1eb27f69d12b1fe2382b50096c4b59e78cdf71/cpu.cfs_quota_us
  • 輸出
10000

# CPU 頗析的總結

  • period 為百萬分之一秒
  • quota 為程序容許在 period 中可調動的 CPU
  • 100 m 就剛好是 1/10 core, 也是 10000/100000 百萬分之一秒
  • CPU 的資源限制中, request 是會作用到 cgroup 的, 該種類在 memory 是不會作用到 cgroup 的

# 參考資源

--

--

Ray Lee | 李宗叡
Learn or Die

It's Ray. I do both backend and frontend, but more focus on backend. I like coding, and would like to see the whole picture of a product.