# Kubernetes CPU throttling ## 問題背景 在 Kubernetes 環境中,Cgroup 是如何限制 container 的 CPU 使用,以及如何觸發 CPU throttling。 ## K8s 如何套用 CPU 資源的限制? 當 Kubelet 啟動 Pod 中的 container 時,會將該 container 在 Kubernetes 中設定的 CPU 與記憶體的 requests(請求)與 limits(限制) 傳遞給底層的 container runtime 執行(如 Containerd、CRI-O 等)。 在 Linux 系統中,container runtime 通常會設定在 kernel 中的 cgroups ,以應用並強制執行定義的資源限制。 Kubelet(containerd 會去執行 cgroup) 會使用 Linux Kernel CFS(Completely Fair Scheduler,完全公平調度) 來限制 Workload 的 CPU time 的使用率,要點如下: * CPU 使用量的計量週期為 100ms。 * CPU limit 決定每計量週期(100ms)內 container 可以使用的 CPU 時間的上限 * 本週期若 container 的 CPU 時間用量達到上限,CPU throttling (限流) 開始,container 只能在下個週期繼續執行。 * 換算如下: - limit 限制使用 1 顆 CPU = 每 100ms 可用 100ms 的 CPU 時間 - limit 限制使用 0.2 顆 CPU = 每 100ms 可用 20ms 的 CPU 時間 - limit 限制使用 2.5 顆 CPU = 每 100ms 可用 250ms 的 CPU 時間 * 若 container 內的應用程式為多執行緒(multi-threaded),在多核心情況下會將所有執行緒的 CPU 使用時間累加計算,整體仍受 CFS quota 所限制。 > cgroups 提供了資源限制與隔離的框架,而 CFS 則在此框架下,實施具體的 CPU 時間分配與調度。 所以當 Workload 的 CPU 達到限制時,他是限制 CPU 的使用時間,會造成應用延時,讓應用執行所需要的時間變得更長了。 ## Quality of Service * 在 Kubernetes 中,Pod 的 Quality of Service (QoS) 分為三種等級:Guaranteed(保證)、Burstable(突發)和 BestEffort(盡力而為)。這些等級是根據 Pod 中 container 所設定的 CPU 和 Memory 資源來自動判斷的,會影響資源爭用時的優先級與調度行為。 - Guaranteed : 所有 container 都設定了 requests = limits(CPU & Memory),擁有最高優先權,在節點壓力大時最不容易被驅逐。 - Burstable : 至少有一個 container 設定了 requests,但 requests 、 limits 的值不完全相同,中等優先權,在節點壓力下,有可能被驅逐,但優先於 BestEffort。 - BestEffort : 沒有設定任何資源 requests 或 limits,最低優先權,最容易被驅逐。 ## 在 Cgroup v1 架構驗證 * 檢查 Cgroup,出現 `tmpfs` 代表是 v1 架構 ``` $ stat -fc %T /sys/fs/cgroup/ tmpfs ``` * 產生一個 Guaranteed 等級的 Pod ``` apiVersion: apps/v1 kind: Deployment metadata: name: test namespace: default spec: replicas: 1 selector: matchLabels: app: test template: metadata: labels: app: test spec: containers: - image: nginx imagePullPolicy: IfNotPresent name: container-0 resources: limits: cpu: 100m memory: 512Mi requests: cpu: 100m memory: 512Mi nodeName: rke2-w2 # 更改自己環境的節點名稱 ``` * 檢查 container uid ``` $ kubectl get pod NAME READY STATUS RESTARTS AGE test-57bdb449bf-h6fcz 1/1 Running 0 8m22s $ kubectl describe pod test-57bdb449bf-h6fcz | grep QoS QoS Class: Guaranteed $ kubectl get pod test-57bdb449bf-h6fcz -o jsonpath="{.metadata.uid}" 5bac8518-d4bd-4088-b72d-46c993a9b2cd ``` * 在產生 pod 的節點進入 `/sys/fs/cgroup/cpu/kubepods.slice/` 目錄,如果是創建 Guaranteed 等級 的 Pod,那麼就是會在這裡產生檔案。 ``` $ cd /sys/fs/cgroup/cpu/kubepods.slice/ # kubepods-pod5bac8518_d4bd_4088_b72d_46c993a9b2cd.slice 是我們剛剛創建的 pod # 後面的 id 是 pod 產生的 uid,可以透過 kubectl get po -oyaml 找到對應的 pod $ ls -l total 0 -rw-r--r-- 1 root root 0 May 23 08:34 cgroup.clone_children -rw-r--r-- 1 root root 0 May 23 08:34 cgroup.procs -rw-r--r-- 1 root root 0 May 23 09:07 cpu.cfs_burst_us -rw-r--r-- 1 root root 0 May 23 08:34 cpu.cfs_period_us -rw-r--r-- 1 root root 0 May 23 08:34 cpu.cfs_quota_us -rw-r--r-- 1 root root 0 May 23 09:07 cpu.idle -rw-r--r-- 1 root root 0 May 23 08:34 cpu.shares -r--r--r-- 1 root root 0 May 23 08:34 cpu.stat -r--r--r-- 1 root root 0 May 23 08:34 cpuacct.stat -rw-r--r-- 1 root root 0 May 23 08:34 cpuacct.usage -r--r--r-- 1 root root 0 May 23 08:34 cpuacct.usage_all -r--r--r-- 1 root root 0 May 23 08:34 cpuacct.usage_percpu -r--r--r-- 1 root root 0 May 23 09:07 cpuacct.usage_percpu_sys -r--r--r-- 1 root root 0 May 23 09:07 cpuacct.usage_percpu_user -r--r--r-- 1 root root 0 May 23 09:07 cpuacct.usage_sys -r--r--r-- 1 root root 0 May 23 09:07 cpuacct.usage_user drwxr-xr-x 11 root root 0 May 23 08:34 kubepods-besteffort.slice drwxr-xr-x 6 root root 0 May 23 08:34 kubepods-burstable.slice drwxr-xr-x 4 root root 0 May 23 08:49 kubepods-pod5bac8518_d4bd_4088_b72d_46c993a9b2cd.slice -rw-r--r-- 1 root root 0 May 23 09:07 notify_on_release -rw-r--r-- 1 root root 0 May 23 09:07 tasks ``` ``` $ cd kubepods-pod5bac8518_d4bd_4088_b72d_46c993a9b2cd.slice $ ls -l total 0 -rw-r--r-- 1 root root 0 May 23 08:49 cgroup.clone_children -rw-r--r-- 1 root root 0 May 23 08:49 cgroup.procs -rw-r--r-- 1 root root 0 May 23 09:07 cpu.cfs_burst_us -rw-r--r-- 1 root root 0 May 23 08:49 cpu.cfs_period_us -rw-r--r-- 1 root root 0 May 23 08:49 cpu.cfs_quota_us -rw-r--r-- 1 root root 0 May 23 09:07 cpu.idle -rw-r--r-- 1 root root 0 May 23 08:49 cpu.shares -r--r--r-- 1 root root 0 May 23 08:49 cpu.stat -r--r--r-- 1 root root 0 May 23 08:49 cpuacct.stat -rw-r--r-- 1 root root 0 May 23 08:49 cpuacct.usage -r--r--r-- 1 root root 0 May 23 08:49 cpuacct.usage_all -r--r--r-- 1 root root 0 May 23 08:49 cpuacct.usage_percpu -r--r--r-- 1 root root 0 May 23 09:07 cpuacct.usage_percpu_sys -r--r--r-- 1 root root 0 May 23 09:07 cpuacct.usage_percpu_user -r--r--r-- 1 root root 0 May 23 09:07 cpuacct.usage_sys -r--r--r-- 1 root root 0 May 23 09:07 cpuacct.usage_user drwxr-xr-x 2 root root 0 May 23 08:49 cri-containerd-d6fbe13e17995b57615f45fc0c8386b651ecb51aa17a60c754eacf5330963518.scope drwxr-xr-x 2 root root 0 May 23 08:49 cri-containerd-f3a4beafc13f8c2b832d47110a5da9b6c0da6336c6a44992c1bff6d059402e99.scope -rw-r--r-- 1 root root 0 May 23 09:07 notify_on_release -rw-r--r-- 1 root root 0 May 23 09:07 tasks ``` * 透過 crictl 檢查對應的 container `cgroupsPath` 是在哪裡。 ``` $ crictl ps -a | grep container-0 d6fbe13e17995 a830707172e80 18 minutes ago Running container-0 0 f3a4beafc13f8 test-57bdb449bf-h6fcz default $ crictl inspect d6fbe13e17995 | grep cgroupsPath "cgroupsPath": "kubepods-pod5bac8518_d4bd_4088_b72d_46c993a9b2cd.slice:cri-containerd:d6fbe13e17995b57615f45fc0c8386b651ecb51aa17a60c754eacf5330963518", $ cd cri-containerd-d6fbe13e17995b57615f45fc0c8386b651ecb51aa17a60c754eacf5330963518.scope ``` * `cpu.cfs_period_us` : 定義重新分配 CPU 資源的時間週期長度。預設是 100000(即 100 毫秒)。 * `cpu.cfs_quota_us` : 在一個週期內,這個 cgroup 最多可以使用的 CPU 時間。如果用完會被 throttled。 * `nr_throttled` : cgroup 中被限制的次數。 * `throttled_time` : 表示總共被節流(Throttled)的時間奈秒(ns)。 ``` $ ls -l total 0 -rw-r--r-- 1 root root 0 May 22 13:53 cgroup.clone_children -rw-r--r-- 1 root root 0 May 22 13:53 cgroup.procs -rw-r--r-- 1 root root 0 May 22 13:55 cpu.cfs_burst_us -rw-r--r-- 1 root root 0 May 22 13:53 cpu.cfs_period_us -rw-r--r-- 1 root root 0 May 22 13:53 cpu.cfs_quota_us -rw-r--r-- 1 root root 0 May 22 13:55 cpu.idle -rw-r--r-- 1 root root 0 May 22 13:53 cpu.shares -r--r--r-- 1 root root 0 May 22 13:53 cpu.stat -r--r--r-- 1 root root 0 May 22 13:53 cpuacct.stat -rw-r--r-- 1 root root 0 May 22 13:53 cpuacct.usage -r--r--r-- 1 root root 0 May 22 13:53 cpuacct.usage_all -r--r--r-- 1 root root 0 May 22 13:53 cpuacct.usage_percpu -r--r--r-- 1 root root 0 May 22 13:55 cpuacct.usage_percpu_sys -r--r--r-- 1 root root 0 May 22 13:55 cpuacct.usage_percpu_user -r--r--r-- 1 root root 0 May 22 13:55 cpuacct.usage_sys -r--r--r-- 1 root root 0 May 22 13:55 cpuacct.usage_user -rw-r--r-- 1 root root 0 May 22 13:55 notify_on_release -rw-r--r-- 1 root root 0 May 22 13:55 tasks # 預設的 cpu 週期都是 100 ms $ cat cpu.cfs_period_us 100000 # 10000 就是代表限制(limit) 100m cpu $ cat cpu.cfs_quota_us 10000 # 419816941 ns = 419.816941 ms $ cat cpu.stat nr_periods 7 nr_throttled 3 throttled_time 419816941 ``` * 進到 test pod 去做 cpu 壓力測試 ``` $ kubectl get pod NAME READY STATUS RESTARTS AGE test-57bdb449bf-8kxvj 1/1 Running 0 3m31s $ kubectl exec test-57bdb449bf-8kxvj -- timeout 240 yes >/dev/null & ``` * 可以看到 `nr_throttled` 被限制次數變多了。 ``` $ cat cpu.stat nr_periods 255 nr_throttled 221 throttled_time 42323283399 ``` * 使用 PromQL 表達式查詢,可以看到 pod 有被節流,只要大於 0 就是有發生 throttled, 1 表示幾乎每次都被 throttled。 ``` sum(increase(container_cpu_cfs_throttled_periods_total{container!=""}[5m])) by (container, pod, namespace) / sum(increase(container_cpu_cfs_periods_total[5m])) by (container, pod, namespace) ```  ## 在 Cgroup v2 架構驗證 * 檢查 Cgroup,出現 `cgroup2fs` 代表是 v2 架構 ``` $ stat -fc %T /sys/fs/cgroup/ cgroup2fs ``` * 產生一個 Guaranteed 等級的 Pod ``` apiVersion: apps/v1 kind: Deployment metadata: name: test namespace: default spec: replicas: 1 selector: matchLabels: app: test template: metadata: labels: app: test spec: containers: - image: nginx imagePullPolicy: IfNotPresent name: container-0 resources: limits: cpu: 100m memory: 512Mi requests: cpu: 100m memory: 512Mi nodeName: rke2-m1 # 更改自己環境的節點名稱 ``` * 檢查 container uid ``` $ kubectl get pod NAME READY STATUS RESTARTS AGE test-f84769dfd-ngpj6 1/1 Running 0 59s $ kubectl describe pod test-f84769dfd-ngpj6 | grep QoS QoS Class: Guaranteed $ kubectl get pod test-f84769dfd-ngpj6 -o jsonpath="{.metadata.uid}" 3bd1d53b-35e4-4ee4-a73d-24a3645354bd ``` * 在產生 pod 的節點進入 `/sys/fs/cgroup/kubepods.slice/` 目錄,如果是創建 Guaranteed 等級 的 Pod,那麼就是會在這裡產生檔案。 ``` $ cd /sys/fs/cgroup/kubepods.slice/ # kubepods-pod3bd1d53b_35e4_4ee4_a73d_24a3645354bd.slice 是我們剛剛創建的 pod $ ls -l /sys/fs/cgroup/kubepods.slice/ total 0 -r--r--r-- 1 root root 0 May 22 11:24 cgroup.controllers -r--r--r-- 1 root root 0 May 22 11:24 cgroup.events -rw-r--r-- 1 root root 0 May 22 11:27 cgroup.freeze --w------- 1 root root 0 May 22 14:30 cgroup.kill -rw-r--r-- 1 root root 0 May 22 14:30 cgroup.max.depth -rw-r--r-- 1 root root 0 May 22 14:30 cgroup.max.descendants -rw-r--r-- 1 root root 0 May 22 11:24 cgroup.procs -r--r--r-- 1 root root 0 May 22 14:30 cgroup.stat -rw-r--r-- 1 root root 0 May 22 11:25 cgroup.subtree_control -rw-r--r-- 1 root root 0 May 22 14:30 cgroup.threads -rw-r--r-- 1 root root 0 May 22 11:24 cgroup.type -rw-r--r-- 1 root root 0 May 22 11:24 cpu.idle -rw-r--r-- 1 root root 0 May 22 11:24 cpu.max -rw-r--r-- 1 root root 0 May 22 14:30 cpu.max.burst -r--r--r-- 1 root root 0 May 22 11:24 cpu.stat -r--r--r-- 1 root root 0 May 22 14:30 cpu.stat.local -rw-r--r-- 1 root root 0 May 22 11:24 cpu.weight -rw-r--r-- 1 root root 0 May 22 14:30 cpu.weight.nice -rw-r--r-- 1 root root 0 May 22 11:24 cpuset.cpus -r--r--r-- 1 root root 0 May 22 11:24 cpuset.cpus.effective -rw-r--r-- 1 root root 0 May 22 14:30 cpuset.cpus.partition -rw-r--r-- 1 root root 0 May 22 11:24 cpuset.mems -r--r--r-- 1 root root 0 May 22 14:30 cpuset.mems.effective -r--r--r-- 1 root root 0 May 22 14:30 hugetlb.1GB.current -r--r--r-- 1 root root 0 May 22 14:30 hugetlb.1GB.events -r--r--r-- 1 root root 0 May 22 14:30 hugetlb.1GB.events.local -rw-r--r-- 1 root root 0 May 22 11:24 hugetlb.1GB.max -r--r--r-- 1 root root 0 May 22 14:30 hugetlb.1GB.numa_stat -r--r--r-- 1 root root 0 May 22 14:30 hugetlb.1GB.rsvd.current -rw-r--r-- 1 root root 0 May 22 11:24 hugetlb.1GB.rsvd.max -r--r--r-- 1 root root 0 May 22 14:30 hugetlb.2MB.current -r--r--r-- 1 root root 0 May 22 14:30 hugetlb.2MB.events -r--r--r-- 1 root root 0 May 22 14:30 hugetlb.2MB.events.local -rw-r--r-- 1 root root 0 May 22 11:24 hugetlb.2MB.max -r--r--r-- 1 root root 0 May 22 14:30 hugetlb.2MB.numa_stat -r--r--r-- 1 root root 0 May 22 11:25 hugetlb.2MB.rsvd.current -rw-r--r-- 1 root root 0 May 22 11:24 hugetlb.2MB.rsvd.max -rw-r--r-- 1 root root 0 May 22 11:24 io.bfq.weight -rw-r--r-- 1 root root 0 May 22 14:30 io.latency -rw-r--r-- 1 root root 0 May 22 14:30 io.max -r--r--r-- 1 root root 0 May 22 11:24 io.stat -rw-r--r-- 1 root root 0 May 22 11:24 io.weight drwxr-xr-x 16 root root 0 May 22 13:58 kubepods-besteffort.slice drwxr-xr-x 11 root root 0 May 22 11:27 kubepods-burstable.slice drwxr-xr-x 4 root root 0 May 23 09:10 kubepods-pod3bd1d53b_35e4_4ee4_a73d_24a3645354bd.slice drwxr-xr-x 4 root root 0 May 23 08:34 kubepods-pod51251f5d_8fc7_424e_9756_90d49dcc2c35.slice ...... ``` ``` $ cd kubepods-pod3bd1d53b_35e4_4ee4_a73d_24a3645354bd.slice $ ls -l total 0 -r--r--r-- 1 root root 0 May 23 09:10 cgroup.controllers -r--r--r-- 1 root root 0 May 23 09:10 cgroup.events -rw-r--r-- 1 root root 0 May 23 09:11 cgroup.freeze --w------- 1 root root 0 May 23 09:11 cgroup.kill -rw-r--r-- 1 root root 0 May 23 09:11 cgroup.max.depth -rw-r--r-- 1 root root 0 May 23 09:11 cgroup.max.descendants -rw-r--r-- 1 root root 0 May 23 09:10 cgroup.procs -r--r--r-- 1 root root 0 May 23 09:11 cgroup.stat -rw-r--r-- 1 root root 0 May 23 09:10 cgroup.subtree_control -rw-r--r-- 1 root root 0 May 23 09:11 cgroup.threads -rw-r--r-- 1 root root 0 May 23 09:10 cgroup.type -rw-r--r-- 1 root root 0 May 23 09:10 cpu.idle -rw-r--r-- 1 root root 0 May 23 09:10 cpu.max -rw-r--r-- 1 root root 0 May 23 09:11 cpu.max.burst -r--r--r-- 1 root root 0 May 23 09:10 cpu.stat -r--r--r-- 1 root root 0 May 23 09:11 cpu.stat.local -rw-r--r-- 1 root root 0 May 23 09:10 cpu.weight -rw-r--r-- 1 root root 0 May 23 09:11 cpu.weight.nice -rw-r--r-- 1 root root 0 May 23 09:10 cpuset.cpus -r--r--r-- 1 root root 0 May 23 09:10 cpuset.cpus.effective -rw-r--r-- 1 root root 0 May 23 09:11 cpuset.cpus.partition -rw-r--r-- 1 root root 0 May 23 09:10 cpuset.mems -r--r--r-- 1 root root 0 May 23 09:11 cpuset.mems.effective drwxr-xr-x 2 root root 0 May 23 09:10 cri-containerd-32dc3b88a5f2175809059f08bb2fd9e155fe6ef5a3266030094a4873435afc8f.scope drwxr-xr-x 2 root root 0 May 23 09:10 cri-containerd-e266625d0bfec4cd12819866d2e929cf8b4540670342ca3da77a32e336c5f2cc.scope ...... ``` * 透過 crictl 檢查對應的 container `cgroupsPath` 是在哪裡。 ``` $ crictl ps -a | grep container-0 e266625d0bfec a830707172e80 About a minute ago Running container-0 0 32dc3b88a5f21 test-f84769dfd-ngpj6 default $ crictl inspect e266625d0bfec | grep cgroupsPath "cgroupsPath": "kubepods-pod3bd1d53b_35e4_4ee4_a73d_24a3645354bd.slice:cri-containerd:e266625d0bfec4cd12819866d2e929cf8b4540670342ca3da77a32e336c5f2cc", $ cd cri-containerd-e266625d0bfec4cd12819866d2e929cf8b4540670342ca3da77a32e336c5f2cc.scope ``` * 環境是 cgroup v2 的話,則根據 cpu.max 的設定的值來限制 CPU,他的格式就是 `cfs_quota_us cfs_period_us`。 * `usage_usec` : 該 cgroup 總共使用的 CPU 時間(單位:微秒),包括使用者與核心空間的時間。 * `user_usec` : 使用者空間(user space)所使用的 CPU 時間(微秒),例如應用程式本身的運算。 * `system_usec` : 核心空間(kernel space)所使用的 CPU 時間(微秒),例如系統呼叫、網路 I/O。 * `nr_periods` : cpu.max 配額控制的週期總數。每個週期表示一次限速監控的單位(預設是 100ms)。 * `nr_throttled` : 有多少個週期因為超出 cpu.max 而被限速(throttled)。越高代表資源常常不夠用。 * `throttled_usec` : 累積被限速的 CPU 時間(微秒)。 ``` # 第一個值 10000 就是代表限制(limit) 100m cpu # 第二個值 100000 就是預設的 cpu 週期,都是 100 ms $ cat cpu.max 10000 100000 $ cat cpu.stat usage_usec 42485 user_usec 15935 system_usec 26550 core_sched.force_idle_usec 0 nr_periods 7 nr_throttled 4 throttled_usec 627048 nr_bursts 0 burst_usec 0 ``` * 進到 test pod 去做 cpu 壓力測試。 ``` $ kubectl get po NAME READY STATUS RESTARTS AGE test-746865dbb-c8tsf 1/1 Running 0 22m $ kubectl exec test-746865dbb-c8tsf -- timeout 240 yes >/dev/null & ``` * 可以看到 `nr_throttled` 和 `throttled_usec` 都變高,代表目前這個 pod 被 throttled ``` $ cat cpu.stat usage_usec 1211910 user_usec 84596 system_usec 1127314 core_sched.force_idle_usec 0 nr_periods 125 nr_throttled 119 throttled_usec 16374085 nr_bursts 0 burst_usec 0 ``` * 使用 PromQL 表達式查詢,可以看到 pod 有被節流,只要大於 0 就是有發生 throttled, 1 表示幾乎每次都被 throttled。 ``` sum(increase(container_cpu_cfs_throttled_periods_total{container!=""}[5m])) by (container, pod, namespace) / sum(increase(container_cpu_cfs_periods_total[5m])) by (container, pod, namespace) ```  ## K8s 是否有方式可以避免 CPU throttling? ### 答案是有的,分為以下兩種 1. 提高 CPU Limit 的用量 * Kubernetes 中 container 資源限制(Resources)可以分為: - cpu request:保證 container 最低可以使用的 CPU。 - cpu limit: container 最多可以使用的 CPU。 如果 container 的 cpu limit 設定得太低,而應用實際需要更多資源,當超過限制時,Kubernetes 的 CFS(Completely Fair Scheduler)就會對其進行「Throttling」(節流),導致應用程式效能下降或不穩定。 2. 檢視應用程式的 Thread 用量是否不正確 * 應用程式若開啟過多執行緒(threads),尤其是在多執行緒(multi-threaded)或非同步架構中,可能會造成: - 多個執行緒同時爭用 CPU,導致短時間內 CPU 使用急升。 - 每個執行緒都被排程,但在受限的 CPU limit 下無法順利執行 → 出現 CFS throttling。 ## 優化 CPU 使用效率 另外一個影響 Container CPU 效能的因素就是 CPU 的使用效率。 在 Kubernetes 中,Container 所使用的 CPU 資源是共享的。雖然我們可以透過 CPU request 為 Container 保留資源,但實際上這些 request 所對應的 vCPU 並不是固定綁定在某幾個實體 core 上。因此,在同一個 Container 中執行的多個 thread,可能會在不同的時間區段內被調度到不同的 CPU core。 舉例來說,假設應用是使用 java 開發,那麼他一定會有多執行緒的設計(multi-threaded),假設使用 4 條 thread 同時處理工作。在執行期間,這些 thread 被排程在哪些 CPU 上,其實是變動的,如下圖所示:  ### 什麼是 NUMA(Non-Uniform Memory Access) 在透過 Topology Manager 優化 K8s 系統中的 container CPU 使用效率前,我們先認識什麼是 NUMA。 NUMA 它是一種讓多顆 CPU 能更有效率地存取記憶體的設計方式,常見於多核心、高階伺服器或工作站上。  在執行應用時,系統為了分散使用 CPU 資源,可能會將應用分別執行在不同的 NUMA node 上,在應用執行過程中,只要有 thread 跨 NUMA node 兩者要互相讀寫記憶體時,就會經過 Interconnect,而透過 Interconnect 都是會增加整個應用所執行的時間。 想像你在辦公室裡有兩個團隊(代表 2 顆 CPU),每個團隊旁邊都有一個書櫃(代表 RAM)。每個團隊成員通常都會從「自己旁邊的書櫃」拿資料(速度很快),但有時也會需要去「另一邊的書櫃」找資料(花比較久)。 * 這就是 NUMA 架構的核心概念: - 每個 CPU 有自己「比較快的記憶體」可以用(稱為 local memory)。 - 但它也能用其他 CPU 的記憶體,只是速度會比較慢(稱為 remote memory)。 * 在 NUMA 架構下,本地記憶體存取 local memory access 的速度會比非本地記憶體存取 non-local memory access 快上許多。 上圖中的 Node1 中的 Core 要存取 Node1 中的記憶體,就比透過 Interconnect 存取 Node2 的記憶體快上許多。為了達到資源的最有效利用,就必須要盡可能地把資源集中在同一台 NUMA Node 上。 如果看到 NUMA node(s): 2,就代表你有兩個 NUMA 節點 ``` $ lscpu | grep "NUMA node(s)" NUMA node(s): 2 ``` 查看每個 NUMA node 對應的 CPU 和記憶體資訊 ``` $ numactl --hardware available: 2 nodes (0-1) # 機器有 2 個 NUMA node,編號為 0 和 1 node 0 cpus: 0 1 2 3 4 5 6 7 # Node 0 管理 CPU 0~7 node 0 size: 5874 MB node 0 free: 4855 MB node 1 cpus: 8 9 10 11 12 13 14 15 # Node 1 管理 CPU 8~15 node 1 size: 5735 MB node 1 free: 4461 MB node distances: node 0 1 0: 10 20 1: 20 10 ``` ### RKE2 啟用 Topology Manager 綁定 cpu 功能可以[參考](https://hackmd.io/URf7odnWStepSLUxWhSScQ) ## 參考 https://hackmd.io/@QI-AN/S1Z2FYUyxx https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/6/html/resource_management_guide/sec-cpu#sect-cfs https://www.hwchiu.com/docs/2023/container-vm#how-to-avoid https://kubernetes.io/docs/tasks/administer-cluster/topology-manager/
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up