iT邦幫忙

2025 iThome 鐵人賽

DAY 8
1
DevOps

30 天挑戰 CKAD 認證!菜鳥的 Kubernetes 學習日記系列 第 8

【Day08】叢集資源怎麼分?ResourceQuota 和 LimitRange 配額管理實戰

  • 分享至 

  • xImage
  •  

gh

前情提要

昨天我們透過 Namespace 可以避免資源名稱衝突,讓開發、測試、生產環境各自獨立運作。我們也實際操作了如何建立 Namespace,並學會用聲明式方式在 YAML 檔中直接指定 Namespace。

不過在文章最後我提到了一個更深層的問題:Namespace 雖然提供了邏輯隔離,但如何控制每個 Namespace 實際能使用多少資源? 今天我們要看看 ResourceQuotaLimitRange,看看如何為不同的 Namespace 設定資源配額和限制。

ResourceQuota 和 LimitRange 是什麼?

在了解這兩個工具之前,要先知道 Kubernetes 的一個重要特性:預設情況下,如果沒有特別指定,Pod 是不會限制 CPU 和 Memory 資源的,可以完全占滿 Node 的所有資源。

它們的關係可以想像成宿舍的管理方式:ResourceQuota 就像是限制整層樓總共能用多少水電,而 LimitRange 則是限制每個房間的使用上下限

ResourceQuota

ResourceQuota 就是總量的概念,他管的是整個 Namespace 能用多少資源總量,比如說這個 Namespace 總共只能跑 4 個 Pod,或者所有容器加起來最多只能要求 500m 的 CPU。

LimitRange

LimitRange 則是針對「單一容器」做限制,確保沒有人建立超大或超小的容器,也會自動幫沒有設定資源的 Pod 分配預設值。

‼️ LimitRange 限制的對象是 Container,而不是 Pod 或其他資源!

資源的計算單位

這邊的設計是基於 Linux 的 cgroup 機制,把 CPU 時間分成很多小片段來分配,但是CPU 限制跟記憶體的行為完全不同。

  • CPU 他是屬於可壓縮資源,意思就是說當 CPU 不夠用的時候,CPU 只會被限制住執行速度,程式不會被殺掉,只是跑得比較慢而已。
  • 如果是 Memory 的話,因為他是不可壓縮資源,當應用程式吃太多記憶體會直接被系統強制終止(OOM (Out of Memory) Kill)。

CPU

CPU 在 Kubernetes 裡用 m 當單位,代表千分之一核心。所以 1000m = 1 個完整的 CPU 核心,500m 就是半個核心。如果你想要兩個完整核心,可以寫 22.0

Memory

記憶體的部分就比較直觀了,用的是我們熟悉的容量單位:Mi(Mebibyte)、Gi(Gibibyte)等等。100Mi 就是 100 Mebibyte 的記憶體,1Gi 就是 1 Gibibyte。要注意的是這裡用的是二進制單位(1024 為基底),跟我們平常講的 MB、GB(1000 為基底)稍微不一樣。

Request vs Limit

在設定資源時會看到 requestslimits 兩種設定。Request 就像是跟 Kubernetes 說「我至少需要這些資源才能正常運作」,Limit 則是「我最多只能用到這些資源,超過就不行了」。

CPU 的 Request vs Limit

CPU Request 是 Kubernetes 調度器在決定要把 Pod 放到哪個 Node 時的參考依據。假設你的 Pod 要求 200m CPU,調度器就會找一個至少還有 200m 可用 CPU 的 Node 來放你的 Pod。

CPU Limit 則是硬性限制,當你的程式真的需要更多 CPU 時,系統會「節流」你的程式,讓它跑得比較慢,但不會把它殺掉。就像限速一樣,你的車不會爆炸,只是被迫開慢一點。

Memory 的 Request vs Limit

Memory Request 一樣是調度時的參考,確保 Node 有足夠記憶體給你的 Pod。

Memory Limit 就比較殘酷了,如果你的程式真的吃超過這個限制,系統會直接把它 OOM Kill(Out of Memory Kill),Pod 就掛了。這是因為記憶體不像 CPU 可以「慢慢分配」,記憶體就是有就有、沒有就沒有。

實戰 🔥

我以下的實作都在 ithome 這個 namespace 實作,因此我們先建立 namespace。

kubectl create namespace ithome

ResourceQuota 配置

先來看看 ResourceQuota 的 YAML 結構,這邊我們直接在 yaml 檔裡面指定 namespace:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-resources
  namespace: ithome
spec:
  hard:
    pods: "4"
    requests.cpu: "500m"
    requests.memory: 100Mi
    limits.cpu: "700m"
    limits.memory: 500Mi

這樣就把 ResourceQuota 建立起來了。

gh


接著我們測試看看當 Pod 超過配額限制時會發生什麼:

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: nginx
  name: nginx
  namespace: ithome
spec:
  containers:
  - image: nginx
    name: nginx
    resources:
      limits:
        cpu: "600m"
        memory: 800Mi
      requests:
        cpu: "100m"
        memory: 100Mi
  dnsPolicy: ClusterFirst
  restartPolicy: Never
status: {}

可以看到這個 Pod 把 limits 的 memory 設到了 800Mi,但是 Quota 只有 500Mi,因此被擋下來不給建立:

gh


接下來我寫一個腳本來測試建立多個 Pod:

#!/bin/bash
nginx_pod=$1
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: ${nginx_pod}
  name: ${nginx_pod}
  namespace: ithome
spec:
  containers:
  - image: nginx
    name: ${nginx_pod}
    imagePullPolicy: IfNotPresent
    resources:
      limits:
        cpu: "100m"
        memory: 20Mi
      requests:
        cpu: "100m"
        memory: 20Mi
  dnsPolicy: ClusterFirst
  restartPolicy: Never
status: {}
EOF

可以看到我們一直建立 Pod,最後達到 Quota 的 4 個 Pod 限制之後就不能再建立了:

gh

LimitRange 配置

接下來看看 LimitRange 的 YAML 結構,一樣直接指定 ithome namespace:

apiVersion: v1
kind: LimitRange
metadata:
  name: limit-workspace
  namespace: ithome
spec:
  limits:
  - max:
      cpu: "800m"
      memory: "1Gi"
    min:
      cpu: "100m"
      memory: "100Mi"
    default:
      cpu: "700m"
      memory: "900Mi"
    defaultRequest:
      cpu: "110m"
      memory: "110Mi"
    type: Container

這邊可以看到有一個欄位是 default,對應到實際生成的結果是 Default Limit。這算是 Kubernetes 的小 bug,明明另一個有寫 Request,但讓人搞不懂為何另一個就只寫 default。🤣🤣
並且可以看到 LimitRange 他是針對 Container 進行資源的限制!!

gh


接下來建立一個符合限制的 Pod:

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: nginx
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
    resources:
      limits:
        cpu: "600m"
        memory: 800Mi
      requests:
       cpu: "500m"
       memory: 250Mi
  dnsPolicy: ClusterFirst
  restartPolicy: Never
status: {}

可以看到因為這個 Pod 的 CPU 在我們限制的 100~800m,然後 Memory 也在我們限制的 100Mi~1Gi,因此這個 Pod 成功被建立:

gh


接下來我們測試超過限制的情況,建立一個資源需求過大的 Pod:

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: nginx1
  name: nginx1
spec:
  containers:
  - image: nginx
    name: nginx1
    resources:
      limits:
        cpu: "2"
        memory: 1300Mi
      requests:
       cpu: "1"
       memory: 1300Mi
  dnsPolicy: ClusterFirst
  restartPolicy: Never
status: {}

下圖可以看到因為不管是 CPU 還是 Memory 都超過我們所設定的 Limit,因此沒辦法被建立:

gh


最後我們測試不設定任何 CPU 和 Memory 限制的情況:

gh

可以看到 Pod 自動帶入我們在 LimitRange 中設定的預設值。

總結

今天我們看了 Kubernetes 的兩個重要資源管理工具:

ResourceQuota 讓我們可以控制整個 Namespace 的資源總量,避免某個團隊或專案把整個叢集的資源吃光。就像公司的預算管制一樣,每個部門都有自己的額度。

LimitRange 則是針對個別容器做限制,確保不會有人建立過大或過小的容器,同時也會自動幫沒有設定資源限制的 Pod 分配合理的預設值。

這兩個工具搭配使用,就能建立一套完整的資源管理機制。在實際的生產環境中,合理的資源配額設定不但能避免資源濫用,也能讓團隊更有效率地使用叢集資源。這些概念在一開始可能看起來有點抽象,但只要動手試試,其實發現它們很好理解。

到目前為止我們已經看了很多重要的資源物件了,不過光是這樣還不夠,應用程式通常需要一些設定檔和敏感資料才能正常運作。比如資料庫連線字串、API Keys、應用程式的設定參數等等。明天我們要來看看 ConfigMapSecret,看看 Kubernetes 如何幫我們儲存這些參數!

下一篇文章:配置檔案不寫死!ConfigMap 與 Secret 管理應用程式配置


上一篇
【Day07】Kubernetes 的管家:Namespace 的分區魔法
系列文
30 天挑戰 CKAD 認證!菜鳥的 Kubernetes 學習日記8
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言