當多個應用運行在同一節點時,CPU資源爭搶和頻繁的環境切換可能導致效能敏感型應用產生抖動。通過啟用CPU拓撲感知調度,可將應用進程獨佔綁定到指定CPU核心(即CPU綁核),減少因核心切換和跨NUMA訪存帶來的效能不確定性。
工作原理
Kubernetes預設依賴核心的CFS調度器(Completely Fair Scheduler)來實現CPU的負載平衡。該調度器通過公平分配CPU時間片將負載“打散”到各個核心,但可能因忽略了CPU的物理拓撲導致效能敏感型應用的效能抖動。
Kubernetes的CPU Manager(static策略)能夠為Pod綁定並獨佔CPU核心,但仍存在以下局限:
調度器無感知:原生kube-scheduler僅在節點維度做決策,無法感知整個叢集的CPU拓撲,不能為Pod在全域範圍內尋找最優的物理核心布局。
拓撲不敏感:
static策略在節點內分配核心時,不感知NUMA架構,可能導致跨NUMA節點的記憶體訪問,引入額外延遲。靈活性不足:該策略嚴格要求Pod的QoS為
Guaranteed,不適用於Burstable和BestEffort類型的Pod。
為此,ACK基於新版Scheduling Framework提供了增強CPU拓撲感知調度能力,由ACK kube-scheduler和ack-koordinator協同完成。
節點拓撲上報:ack-koordinator即時感知本地的CPU物理拓撲(如Socket、NUMA、緩衝),並將其上報至調度中心。
全域拓撲感知調度:kube-scheduler基於全域拓撲資訊,在叢集範圍內為Pod篩選出當前最優節點,並規劃核心分配方案(如在選擇最優節點時預設尋找已綁定應用數量最少的核)。該分配方案會作為調度結果寫入Pod的Annotation中。
本地綁核執行:Pod調度至目標節點後,ack-koordinator會根據Pod的 Annotation 並修改其對應Cgroup的
cpuset.cpus檔案,以完成物理核心的綁定。
適用情境
效能敏感型應用:對CPU環境切換延遲極度敏感,如高頻交易、即時資料處理。
NUMA敏感型應用:部署在神龍裸金屬(Intel、AMD)等多核、多Socket伺服器上,且業務效能受記憶體訪問延遲影響巨大,期望避免跨NUMA訪存。
確定性算力需求:需要穩定、可預期的計算能力,如科學計算、巨量資料分析任務。
遺留應用適配:應用尚未完成對雲原生情境的適配,例如在設定線程數量時未考慮容器規格(而是整機物理核心數量),導致效能下降。
不建議在以下情境中啟用此功能:
CPU超賣環境:綁核的資源獨佔特性與超賣的資源共用模型存在不相容,可能造成資源浪費並幹擾超賣調度邏輯。
通用型或I/O密集型應用:大多數Web服務、中介軟體等應用對CPU核心切換不敏感,無需啟用此功能。
準備工作
已建立ACK託管叢集Pro版,且已配置節點池的CPU Policy(
cpuManagerPolicy)為none。請參見自訂節點池kubelet配置配置cpuManagerPolicy取值。已安裝ack-koordinator,且組件版本為0.2.0及以上。
步驟一:部署樣本應用
本文以一個Nginx應用為例,介紹如何啟用CPU拓撲感知調度,實現CPU綁核。
建立
nginx-app.yaml。部署應用。
kubectl apply -f nginx-app.yaml登入Pod所在節點,擷取Pod的UID和Container ID。
擷取Pod名稱。
執行kubectl get pods -n <your-namespace>查看Pod名稱,如nginx-deployment-6f5899*****。擷取Pod UID。
# 將 <your-pod-name> 替換為Pod實際名稱 kubectl get pod <your-pod-name> -n default -o jsonpath='{.metadata.uid}{"\n"}'預期輸出:
uid: a78a02b5-c87f-4e74-9ddd-254c163*****擷取Container ID。
# 將 <your-pod-name> 替換為Pod實際名稱 kubectl describe pod <your-pod-name> -n default在輸出的
Containers欄位下定位Container ID,並刪去首碼(如containerd://)。預期輸出:
Containers: nginx: Container ID: containerd://b8b88a70096aabb0aea197dd2aba78d15bcbe9145198ef46a0474b31*****
確認節點的cgroup版本。
stat -fc %T /sys/fs/cgroup/輸出為
cgroup_root:系統使用 cgroup v1。輸出為
cgroup2fs:系統使用 cgroup v2。
根據cgroup版本執行對應的驗證命令,查看CPU綁核情況。
在cgroup路徑中,Pod UID中的
-需替換為_。如原始Pod UID為a78a02b5-c87f-4e74-9ddd-254c163*****,則路徑中使用的格式為a78a02b5_c87f_4e74_9ddd_254c163*****。cgroup v1:
# 將 <POD_UID> 和 <CONTAINER_ID> 替換為實際值 cat /sys/fs/cgroup/cpuset/kubepods.slice/kubepods-pod<POD_UID>.slice/cri-containerd-<CONTAINER_ID>.scope/cpuset.cpuscgroup v2:
# 將 <POD_UID> 和 <CONTAINER_ID> 替換為實際值 cat /sys/fs/cgroup/kubepods.slice/kubepods-pod<POD_UID>.slice/cri-containerd-<CONTAINER_ID>.scope/cpuset.cpus.effective
預期輸出為一個範圍,代表容器可以使用節點上的所有核心,未進行綁定。
0-31
步驟二:啟用CPU拓撲感知調度功能
可通過為Pod添加Annotation來啟用CPU拓撲感知調度。
普通綁核策略:通用策略,遵循1:1的綁核原則,為Pod綁定
resources.limits.cpu所指定的數量的核心。優先選擇同一NUMA節點內的CPU核,以保證良好的記憶體訪問效能。自動綁核策略:針對特定硬體最佳化的策略,優先通過綁定一個完整的物理核心簇(如AMD CPU的CCX/CCD)來最大化CPU局部性並提升並發度。推薦在特定的大規格(如32核及以上)AMD機型上使用。
啟用CPU拓撲感知調度時,請勿在Pod上直接指定nodeName,kube-scheduler不參與此類Pod的調度過程。可使用nodeSelector等欄位配置親和性策略來指定節點調度。
普通綁核策略
配置方法:
在YAML中添加Annotation
cpuset-scheduler: "true"Pod中:在
metadata.annotations欄位中添加工作負載(例如Deployment)中:在
spec.template.metadata.annotations欄位中添加
在
Containers欄位中配置resources.limits.cpu的取值(需為整數),限定CPU綁核範圍。
配置樣本:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment namespace: default labels: app: nginx spec: replicas: 1 selector: matchLabels: app: nginx template: metadata: annotations: # 設定為true,啟用CPU拓撲感知調度 cpuset-scheduler: "true" labels: app: nginx spec: containers: - name: nginx image: alibaba-cloud-linux-3-registry.cn-hangzhou.cr.aliyuncs.com/alinux3/nginx_optimized:20240221-1.20.1-2.3.0 ports: - containerPort: 80 command: - "sleep" - "infinity" resources: requests: cpu: 4 memory: 8Gi limits: # 設定cpu值,需為整數 cpu: 4 memory: 8Gi結果驗證:
可通過以下兩種方式查看。
查看節點Cgroup檔案
等待Pod正常運行後,再次登入其所在節點,查看CPU綁核情況。
Pod UID中的
-需替換為_。cgroup v1:
# 將 <POD_UID> 和 <CONTAINER_ID> 替換為實際值 cat /sys/fs/cgroup/cpuset/kubepods.slice/kubepods-pod<POD_UID>.slice/cri-containerd-<CONTAINER_ID>.scope/cpuset.cpuscgroup v2:
# 將 <POD_UID> 和 <CONTAINER_ID> 替換為實際值 cat /sys/fs/cgroup/kubepods.slice/kubepods-pod<POD_UID>.slice/cri-containerd-<CONTAINER_ID>.scope/cpuset.cpus.effective
預期輸出為一組核心ID,與
limits.cpu: 4的設定相符,表明綁核成功。0-3檢查Pod Annotation
Pod調度至目標節點後,ack-koordinator會根據Pod的 Annotation 並修改其對應Cgroup的
cpuset.cpus檔案,以完成物理核心的綁定。查看Pod
cpuset資訊。# 將 <your-pod-name> 替換實際Pod名稱 kubectl get pod <your-pod-name> -n default -o yaml | grep "cpuset:"預期輸出:
cpuset: '{"nginx":{"0":{"elems":{"0":{},"1":{},"2":{},"3":{}}}}}'輸出結果說明:
"nginx":{...}:針對名為nginx的容器的配置。"0":{...}:最外層的鍵"0"代表NUMA節點ID。本樣本表示所有綁定的核都位於同一NUMA Node 0上,避免了跨NUMA訪存的效能損耗。"elems":{"0":{},"1":{},"2":{},"3":{}}:鍵代表被綁定的物理CPU核心ID。此樣本中,容器被成功綁定到了0、1、2、3這四個核心上,與limits.cpu: 4相符。
自動綁核策略
配置方法:
添加兩個Annotation:
cpuset-scheduler: "true"和cpu-policy: "static-burst"Pod中:在
metadata.annotations欄位中添加工作負載(例如Deployment)中:在
spec.template.metadata.annotations欄位中添加
在
Containers欄位中配置resources.limits.cpu的取值(需為整數),限定CPU綁核範圍。
配置樣本:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment namespace: default labels: app: nginx spec: replicas: 1 selector: matchLabels: app: nginx template: metadata: annotations: # 設定為true,啟用CPU拓撲感知調度 cpuset-scheduler: "true" # 設定為static-burst,啟用自動綁核與NUMA親和策略 cpu-policy: "static-burst" labels: app: nginx spec: containers: - name: nginx image: alibaba-cloud-linux-3-registry.cn-hangzhou.cr.aliyuncs.com/alinux3/nginx_optimized:20240221-1.20.1-2.3.0 ports: - containerPort: 80 command: - "sleep" - "infinity" resources: requests: cpu: 4 memory: 8Gi limits: # 設定cpu值,需為整數 cpu: 4 memory: 8Gi結果驗證:
自動綁核策略會即時分析節點的CPU拓撲和資源使用方式,綁核心數量可能會大於Pod的顯式請求。綁核情況可通過以下兩種方式查看。
查看節點Cgroup檔案
等待Pod正常運行後,再次登入其所在節點,查看CPU綁核情況。
Pod UID中的
-需替換為_。cgroup v1:
# 將 <POD_UID> 和 <CONTAINER_ID> 替換為實際值 cat /sys/fs/cgroup/cpuset/kubepods.slice/kubepods-pod<POD_UID>.slice/cri-containerd-<CONTAINER_ID>.scope/cpuset.cpuscgroup v2:
# 將 <POD_UID> 和 <CONTAINER_ID> 替換為實際值 cat /sys/fs/cgroup/kubepods.slice/kubepods-pod<POD_UID>.slice/cri-containerd-<CONTAINER_ID>.scope/cpuset.cpus.effective
預期輸出為一組確切的核心ID,表明綁核成功。
0-7檢查Pod Annotation
Pod調度至目標節點後,ack-koordinator會根據Pod的 Annotation 並修改其對應Cgroup的
cpuset.cpus檔案,以完成物理核心的綁定。查看Pod
cpuset資訊。# 將 <your-pod-name> 替換實際Pod名稱 kubectl get pod <your-pod-name> -n default -o yaml | grep "cpuset:"預期輸出:
cpuset: '{"nginx":{"0":{"elems":{"0":{},"1":{},"2":{},"3":{},"4":{},"5":{},"6":{},"7":{}}}}}'輸出結果說明:
"nginx":{...}:針對名為nginx的容器的配置。"0":{...}:最外層的鍵"0"代表NUMA節點ID。本樣本表示所有綁定的核都位於同一NUMA Node 0上,避免了跨NUMA訪存的效能損耗。"elems":{"0":{},"1":{},"2":{},"3":{},"4":{},"5":{},"6":{},"7":{}}:鍵代表被綁定的物理CPU核心ID。此樣本中,容器已綁定至0~7核心上。
相關操作
停用CPU拓撲感知調度(CPU核心解除綁定)
編輯應用YAML,從
spec.template.metadata.annotations中移除Annotationcpuset-scheduler: "true"和cpu-policy: "static-burst"(如有)。在業務低峰期應用修改後的YAML,等待Pod重啟後變更生效。
解除綁定後,Pod 進程不再被綁定到特定物理核心,可能會在節點所有可用 CPU 核心之間切換。潛在影響包括:
因跨核心的環境切換,CPU 使用率可能微幅上升。
對於計算密集型應用,由於無法獨佔核心,可能會重新出現因 CPU 資源爭搶導致的效能抖動。
當多個高負載 Pod 的進程被調度至同一核心時,可能導致該核心負載瞬時過高,進而觸發容器的 CPU 限流(Throttling)。
生產環境使用建議
可觀測性:在啟用綁核前後,接入阿里雲Prometheus監控,密切關注應用的關鍵效能指標(如RT、QPS)以及節點的CPU使用率、CPU Throttling等指標,關注綁核帶來的效能變化。
分批變更:對於多副本應用,建議採用金絲雀發布或分批更新的方式,逐步啟用或停用綁核策略,以控制變更風險。
費用說明
ack-koordinator組件本身的安裝和使用免費,但在以下情境中可能產生額外費用。
ack-koordinator是非託管組件,安裝後將佔用Worker節點資源。您可以在安裝組件時配置各模組的資源申請量。
ack-koordinator預設會將資源畫像、精細化調度等功能的監控指標以Prometheus的格式對外透出。若您配置組件時開啟了ACK-Koordinator開啟Prometheus監控指標選項並使用了阿里雲Prometheus服務,這些指標將被視為自訂指標併產生相應費用。具體費用取決於您的叢集規模和應用數量等因素。建議您在啟用此功能前,仔細閱讀阿里雲PrometheusPrometheus 執行個體計費,瞭解自訂指標的免費額度和收費策略。您可以通過用量查詢,監控和管理您的資源使用方式。