Kubernetes Limit / Request 使用指南

Kubernetes Limit / Request 使用指南

在 Kubernetes 部署應用程式時,resources.requestsresources.limits 是很重要的設定。它們會影響 Pod 是否能被排程、容器最多能使用多少 CPU 與記憶體,以及命名空間內的資源是否會被合理控管。

這篇文章會透過三份測試配置檔,依序觀察以下幾件事:

  • 只在 Deployment 內設定 requestslimits
  • 使用 LimitRange 統一限制命名空間內的單一容器資源範圍
  • 使用 ResourceQuota 控制整個命名空間的資源總配額

什麼是 requests 與 limits?

在 Kubernetes 的容器設定中,可以透過 resources 宣告 CPU 與記憶體需求:

1
2
3
4
5
6
7
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"

requests 是容器啟動前向 Kubernetes 宣告的最低資源需求。Scheduler 會根據這個數值判斷目前有哪些 Node 還有足夠的可配置資源可以放置 Pod。如果 requests 超過 Node 剩餘可配置資源,Pod 就會停留在 Pending,並在事件中看到類似 Insufficient cpuInsufficient memory 的訊息。

limits 則是容器可使用資源的上限。CPU 超過 limits 時通常會被節流,記憶體超過 limits 時則可能被 OOMKilled。也就是說,requests 主要影響排程,limits 則影響容器執行時的資源邊界。

CPU 常用 m 表示 millicore,例如 250m 代表 0.25 顆 CPU,1000m 代表 1 顆 CPU。記憶體常用 MiGi 表示,例如 512Mi2Gi


測試一:只設定 Deployment resources

第一份配置檔 dockertest_resource_v1.yml 建立了 dockertest Namespace、Registry Secret、Deployment 與 ClusterIP Service。Deployment 中的容器設定了基本的 requests 與 limits:

1
2
3
4
5
6
7
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"

套用配置檔:

1
kubectl apply -f dockertest_resource_v1.yml

套用完成後,可以看到 Namespace、Secret、Deployment 與 Service 都成功建立,Pod 也正常進入 Running 狀態。

接著檢查 Deployment 的設定,可以看到容器內已經有 limitsrequests。在這個測試中,Pod 有明確宣告資源需求,因此 Kubernetes 可以依據 requests 進行排程。

如果將 Deployment 的 CPU 與記憶體需求手動調得太高,例如 requests 與 limits 都設定成超過單一 Node 可提供的資源,就會建立出新的 ReplicaSet,但新的 Pod 會停在 Pending

kubectl describe pod 的事件可以看到:

1
2
0/1 nodes are available: 1 Insufficient cpu, 1 Insufficient memory.
preemption: 0/1 nodes are available: 1 Preemption is not helpful for scheduling.

這代表 Scheduler 找不到符合 requests 條件的 Node,因此 Pod 無法被排程。這個狀況不是容器啟動失敗,而是 Kubernetes 在排程階段就判斷資源不足。


測試二:使用 LimitRange 控制單一容器資源範圍

第二份配置檔 dockertest_resource_v2.yml 在同一個 Namespace 中加入 LimitRange

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: v1
kind: LimitRange
metadata:
name: dockertest-limitrange
namespace: dockertest
spec:
limits:
- type: Container
max:
cpu: "5000m"
memory: "2Gi"
min:
cpu: "150m"
memory: "128Mi"
default:
cpu: "500m"
memory: "512Mi"
defaultRequest:
cpu: "250m"
memory: "256Mi"

LimitRange 是命名空間層級的限制,用來規範單一容器可以設定的資源範圍。它有幾個常見用途:

  • max:限制單一容器可設定的最大 limits
  • min:限制單一容器可設定的最小 requests 或 limits
  • default:容器沒有設定 limits 時,自動補上的預設 limits
  • defaultRequest:容器沒有設定 requests 時,自動補上的預設 requests

套用第二份配置檔:

1
kubectl apply -f dockertest_resource_v2.yml

套用後可以看到 limitrange/dockertest-limitrange 被建立,Deployment 與 Service 也正常建立。

建立完成後,可以使用 kubectl get limitrange 查看 Namespace 內有哪些 LimitRange

1
kubectl get limitrange -n dockertest

如果要確認 LimitRange 實際套用的 minmaxdefaultdefaultRequest,可以使用 kubectl describe limitrange

1
kubectl describe limitrange dockertest-limitrange -n dockertest

也可以輸出完整 YAML,確認 Kubernetes 儲存的資源內容:

1
kubectl get limitrange dockertest-limitrange -n dockertest -o yaml

在目前這份 v2 設定中,Deployment 的容器 requests 與 limits 都落在 LimitRange 允許的範圍內,所以 Pod 可以正常啟動。

如果後續把 Deployment 的 CPU limit 調高到超過 LimitRangemax.cpu,Kubernetes 會直接拒絕建立 Pod。這時候錯誤會出現在 ReplicaSet 的事件中,而不是讓 Pod 進入 Pending

從畫面中的事件可以看到類似訊息:

1
2
Error creating: pods "dockertest-deployment-..." is forbidden:
maximum cpu usage per Container is 5, but limit is 6

這表示限制是在 API Server admission 階段生效。也就是說,LimitRange 不是用來判斷 Node 有沒有資源,而是用來檢查使用者提交的容器資源設定是否符合命名空間規範。


測試三:使用 ResourceQuota 控制命名空間總配額

第三份配置檔 dockertest_resource_v3.yml 同時加入 ResourceQuotaLimitRangeLimitRange 控制單一容器的資源範圍,而 ResourceQuota 控制整個 Namespace 的 requests 與 limits 加總:

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: ResourceQuota
metadata:
name: dockertest-resourcequota
namespace: dockertest
spec:
hard:
requests.cpu: "2"
requests.memory: 4Gi
limits.cpu: "4"
limits.memory: 8Gi

這份設定代表 dockertest Namespace 內所有 Pod 的資源宣告加總不得超過:

項目 上限
requests.cpu 2
requests.memory 4Gi
limits.cpu 4
limits.memory 8Gi

同時,v3 的 Deployment 將副本數設定為 4,每個 Pod 的資源設定如下:

1
2
3
4
5
6
7
8
replicas: 4
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "1000m"

每個 Pod 的 CPU limit 是 1000m,也就是 1 顆 CPU。4 個副本加總後剛好是 4 顆 CPU,符合 limits.cpu: "4" 的 ResourceQuota 設定,因此可以正常建立。

1
kubectl apply -f dockertest_resource_v3.yml

建立完成後,可以使用 kubectl get resourcequotas 查看 Namespace 內有哪些 ResourceQuota

1
kubectl get resourcequotas -n dockertest

如果要確認目前配額的 UsedHard 數值,可以使用 kubectl describe resourcequota。這個指令很適合用來觀察目前 requests 與 limits 已經宣告了多少,以及是否接近 Namespace 的配額上限:

1
kubectl describe resourcequota dockertest-resourcequota -n dockertest

也可以用 YAML 形式查看完整設定:

1
kubectl get resourcequota dockertest-resourcequota -n dockertest -o yaml

套用後可以看到 4 個 Pod 都正常進入 Running,Deployment 也顯示 4/4

接著如果將 Deployment scale 到 5 個副本,第 5 個 Pod 會讓 Namespace 內的 limits.cpu 加總變成 5 顆 CPU,超過 ResourceQuota 設定的 4 顆 CPU,因此新的 Pod 會被拒絕建立。

1
2
kubectl scale deployment dockertest-deployment --replicas=5 -n dockertest
kubectl describe rs -n dockertest

在 ReplicaSet 事件中可以看到:

1
2
3
4
exceeded quota: dockertest-resourcequota,
requested: limits.cpu=1,
used: limits.cpu=4,
limited: limits.cpu=4

這代表目前 Namespace 已經用滿 limits.cpu 配額,因此 ReplicaSet 無法再建立新的 Pod。


LimitRange 與 ResourceQuota 的差異

LimitRangeResourceQuota 都是 Namespace 層級的資源治理工具,但它們負責的範圍不同。

項目 LimitRange ResourceQuota
控制對象 單一 Pod 或單一容器 整個 Namespace
常見用途 設定預設 requests/limits、限制單一容器最小與最大資源 限制 Namespace 內所有 Pod 的資源總量
超出限制時 Pod 建立請求被拒絕 超出總配額的新 Pod 建立請求被拒絕
是否看 Node 剩餘資源
是否影響 Scheduler 排程 間接影響,因為會補上或限制 requests 間接影響,因為限制可建立的總資源宣告

簡單來說,LimitRange 像是單一容器的規格表,避免有人建立過小或過大的容器;ResourceQuota 則像是整個 Namespace 的預算表,避免某個專案把整個叢集資源宣告用光。


常見觀察重點

在這次測試中,可以整理出幾個重要結論:

  • requests 會影響 Pod 是否能被 Scheduler 排到 Node 上
  • limits 會限制容器執行時最多能使用多少 CPU 與記憶體
  • requests 超過 Node 可配置資源時,Pod 會維持 Pending
  • 超過 LimitRange 的單一容器限制時,Pod 建立會直接被拒絕
  • 超過 ResourceQuota 的 Namespace 總配額時,新的 Pod 建立會被拒絕
  • LimitRangeResourceQuota 都不等於 Node 實際剩餘資源,它們檢查的是資源宣告是否符合規範

因此,在實務上通常會同時使用三層設定:

  1. 在 Deployment 中明確設定每個容器的 requestslimits
  2. 在 Namespace 中使用 LimitRange 建立預設值與單一容器上下限
  3. 在 Namespace 中使用 ResourceQuota 控制整體資源使用額度

這樣可以讓應用程式部署時有明確的排程依據,也能避免單一服務或單一命名空間過度宣告資源,影響同一個叢集中的其他工作負載。