在CPU與GPU間通信頻繁的場景下,跨NUMA(Non-uniform memory access)節點訪問會導致延遲增加、帶寬受限等問題,從而影響系統整體性能。為了解決此類問題,ACK基于Scheduler Framework機制,實現NUMA拓撲感知調度,將Pod調度到最佳的NUMA節點上,從而減少跨NUMA節點的訪問以優化性能。
工作原理
NUMA節點是構成非一致性內存訪問(NUMA)系統的基本單位,一個NUMA集合是指單個Node節點上多個NUMA節點的組合,用于實現計算資源的有效分配,并降低處理器間內存訪問的競爭。在8 GPU卡機器,即配備了8塊GPU卡的高性能機器中,通常包含多個NUMA節點。當CPU沒有綁核或CPU沒有與GPU分配在相同NUMA上時,可能會由于CPU爭搶或CPU與GPU跨NUMA通信導致應用執行性能下降。為了使應用的執行性能最大化,您可以選擇將CPU與GPU綁定在相同NUMA下。
原生的基于kubelet CPU Policy以及NUMA Topology Policy的方案能夠在單機上實現將CPU與GPU綁定在相同NUMA上的效果,但是在集群中使用時會存在一些難點:
放置結果調度器不感知:調度器無法感知到節點上已分配的具體CPU以及GPU,繼而無法判斷剩余的CPU與GPU資源是否能夠滿足Pod的QoS要求,導致調度后大量出現AdmissionError狀態的Pod,嚴重時可能導致集群崩潰。
放置效果上層不可控:由于kubelet拓撲分配策略在原生Kubernetes中只能在節點的進程啟動參數里看到,無法通過節點標簽等集群層面手段獲取。因此,在提交任務時,您無法指定工作負載運行在支持聯合分配的節點上,導致應用的實際運行效果具有較大的波動性。
拓撲策略使用復雜:NUMA拓撲策略通過節點進行聲明,因此單一節點上只能帶有一種拓撲策略。提交任務并使用拓撲策略前,集群資源管理員需要手動劃分集群,在節點上進行特殊標記,且任務Pod上也需要聲明帶有特殊標記的節點親和。此外,不同策略的Pod即使相互之間沒有干擾也無法放置于相同節點上,導致集群利用率下降。
為了解決上述問題,ACK基于Scheduler Framework實現了拓撲感知調度能力,同時采用自主研發的gputopo-device-plugin以及ack-koordinator組件的ack-koordlet完成節點CPU、GPU拓撲結構上報,并且支持在工作負載上聲明NUMA拓撲分配策略的能力,解決了原生kubelet方案中存在的不足。整體方案架構如下所示。
前提條件
集群要求:
創建ACK集群Pro版,且集群為1.24及以上版本。具體步驟,請參見創建Kubernetes托管版集群。如需升級集群,請參見升級集群。
節點要求:
僅支持GPU計算型超級計算集群實例規格族sccgn7ex及靈駿節點,請參見實例規格族描述。靈駿節點請參見管理靈駿集群和靈駿節點。
GPU拓撲感知調度的節點需要手動添加標簽
ack.node.gpu.schedule=topology
。相關信息請參見GPU節點調度屬性標簽說明。
組件要求:
kube-scheduler組件版本需高于6.4.4版本。相關介紹,請參見kube-scheduler。如果您需要升級kube-scheduler組件,請在容器服務控制臺單擊目標集群,然后選擇 升級組件。
已安裝ack-koordinator(原ack-slo-manager)。具體操作,請參見ack-koordinator(ack-slo-manager)。
ACK靈駿集群:直接完成ack-koordinator組件的安裝。
ACK集群Pro版:配置ack-koordinator參數時,需設置Feature Gate agentFeatures的取值為NodeTopologyReport=true。
已安裝GPU拓撲感知調度組件。具體步驟,請參見安裝GPU拓撲感知調度組件。
重要如果您先安裝了GPU拓撲感知調度組件,然后安裝了ack-koordinator組件,您需在安裝ack-koordinator后重啟GPU拓撲感知調度組件。
使用限制
計費說明
使用該功能需要安裝AI套件,可能會產生額外費用。請參見云原生AI套件計費說明。
ack-koordinator組件本身的安裝和使用是免費的,不過需要注意的是,在以下場景中可能產生額外的費用:
ack-koordinator是非托管組件,安裝后將占用Worker節點資源。您可以在安裝組件時配置各模塊的資源申請量。
ack-koordinator默認會將資源畫像、精細化調度等功能的監控指標以Prometheus的格式對外透出。若您配置組件時開啟了ACK-Koordinator開啟Prometheus監控指標選項并使用了阿里云Prometheus服務,這些指標將被視為自定義指標并產生相應費用。具體費用取決于您的集群規模和應用數量等因素。建議您在啟用此功能前,仔細閱讀阿里云Prometheus計費說明,了解自定義指標的免費額度和收費策略。您可以通過賬單和用量查詢,監控和管理您的資源使用情況。
使用NUMA拓撲感知調度
您可以通過在Pod上增加如下Annotation來聲明NUMA拓撲感知調度要求:
apiVersion: v1
kind: Pod
metadata:
annotations:
cpuset-scheduler: required # 啟用綁定CPU
scheduling.alibabacloud.com/numa-topology-spec: | # 表明此Pod的NUMA拓撲需求
{
"numaTopologyPolicy": "SingleNUMANode",
"singleNUMANodeExclusive": "Preferred",
}
spec:
containers:
- name: example
resources:
limits:
aliyun.com/gpu: '4'
cpu: '24'
requests:
aliyun.com/gpu: '4'
cpu: '24'
以下為NUMA開啟拓撲感知調度相關參數:
參數 | 說明 |
| Pod需要將CPU與設備進行聯合分配。 目前取值僅支持 |
| Pod調度時需要采用哪種NUMA放置策略。
|
| Pod調度時是否需要避開某些NUMA節點。 說明 NUMA節點類型:
|
開啟前后性能對比
本文使用模型加載過程來測試開啟NUMA拓撲感知調度前后帶來的性能提升。我們將使用text-generation-inference工具在兩塊GPU卡上拉起模型,并使用NSight工具統計綁核前后GPU加載的速度變化。
本實驗采用靈駿節點,TGI1.4版本,請參見TGI下載地址,NSight工具請參見NSight工具下載。
采用不同的工具進行測試,測試結果會存在差異。本示例中的性能對比數據僅為采用NSight工具獲得的測試結果。實際數據以您的操作環境為準。
不開啟拓撲感知調度
相同場景下不開啟拓撲感知調度的應用YAML如下:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: tgi
name: tgi-deployment-basic
namespace: test
spec:
replicas: 1
selector:
matchLabels:
app: tgi
template:
metadata:
labels:
app: tgi
spec:
containers:
- command:
- sleep
- 3600d
image: >-
ghcr.io/huggingface/text-generation-inference:1.4
imagePullPolicy: IfNotPresent
name: tgi
ports:
- containerPort: 80
protocol: TCP
resources:
limits:
cpu: '24'
nvidia.com/gpu: '4'
requests:
cpu: '24'
nvidia.com/gpu: '4'
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /llm
name: volume-1710932083254
restartPolicy: Always
schedulerName: default-scheduler
volumes:
- name: volume-1710932083254
persistentVolumeClaim:
claimName: model
查看模型加載時間,可以看出模型完成加載需要15.9s。
開啟拓撲感知調度
相同場景下開啟拓撲感知調度的應用YAML如下:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: tgi-numa
name: tgi-numa-deployment-basic
namespace: yueming-test
spec:
replicas: 1
selector:
matchLabels:
app: tgi-numa
template:
metadata:
annotations:
cpuset-scheduler: required
scheduling.alibabacloud.com/resource-spec: |
{
"numaTopologyPolicy": "SingleNUMANode"
}
labels:
app: tgi-numa
spec:
containers:
- command:
- sleep
- 3600d
image: >-
ghcr.io/huggingface/text-generation-inference:1.4
imagePullPolicy: IfNotPresent
name: numa
resources:
limits:
aliyun.com/gpu: '4'
cpu: '24'
requests:
aliyun.com/gpu: '4'
cpu: '24'
volumeMounts:
- mountPath: /llm
name: volume-1710932083254
restartPolicy: Always
schedulerName: default-scheduler
volumes:
- name: volume-1710932083254
persistentVolumeClaim:
claimName: model
查看模型加載時間,可以看出模型完成加載需要5.4s,較未開啟時提升了66%。