Kubernetes 節點彈性擴充套件實踐元件 Amazon Karpenter:部署 GPU 推理應用

亞馬遜雲開發者發表於2022-05-28

原來

Amazon Karpenter 是亞馬遜雲科技 2021 re:Invent 大會上正式釋出的針對 Kubernetes 叢集一直以來不斷擴充套件的元件中。在 Kubernetes 叢集中的元件可以針對 Unscheduleable Pod 的需求,自動建立節點的新節點並加入。由於在Kubernetes叢集中會使用Cluster AutoScaler (CA) 元件來進行節點的彈性縮放,通過對節點組(節點組,即在亞馬遜雲科技上的實現為EC2 Auto Scaling Group)的大小進行動態調整來確定相對而言,亞馬遜更傾向於選擇徹底拋棄了節點的實現,EC2 Fleet API 直接進行節點管理,因此可以靈活地選擇合適的 Amazon EC2 型號、可用區域和選項組。 (如需求或SPOT)等,在叢集中的卡彭特。

Amazon Karpenter 目前已經可以生產了,已經有使用者開始利用 Amazon Karpenter 這個亞馬遜雲科技的 EKS 叢集在一個 GPU 上進行節點管理。在部落格中我們會以 GPU 推理的場景為示例,詳細說明 Amazon Karpenter 的工作原理、配置過程以及測試效果。

架構描述

在這個部落格裡我們會使用 EKS 構建一個 Kubernetes 叢集:

image.png

可以看到在Kubernetes組和中,我們會先建立一個節點部署管理元件,部署CoreDNS,Amazon Load Balancer Controller Amazon Karpenter等管理元件。一般來說管理元件所需要的資源比較固定,我們可以提前設定預想好相關元件所需要的資源,並考慮跨可用區、高可用等因素後,該確定節點組的型別和數量。

我們會自動管理這些直播服務所需要的這些主要例項。Amazon Karer 的任務為 Unsh Amazon Pod 建立的節點,將在這些 Pod EC 使用 Amazon EC 排程節點的時候使用 Pod 節點Amazon Karpent 將根據需要的資源請求(如 Pod 的資源請求)、親和性設定(如 Pod Affinity 等)計算出符合要求的節點型別和、數量在這個示例工作組中,提前進行組節點型別計劃由我們為 GPU 例項建立單獨的組和配置C 組資源自動縮放器來進行,Karpenter 來自動完成。

我們會部署一個推理服務(resnet 伺服器),最後由多個 Pod 組成,每個 Pod 都可以獨立進行工作,通過服務暴露到 EKS 外部。通過一個 Python 客戶端,我們可以提交一張圖片到結合自動生成伺服器部署的時間,可以配置前端的變化,HPA Po的自動對大小的變化進行了縮放真正實現節點的彈性縮微。

考慮,考慮到我們會以負載均衡器提前暴露服務,因此在示例中會部署亞馬遜負載均衡器控制器,來根據服務建立網路負載均衡器。

部署與測試

我們會按照上述架構進行部署,整體指揮的部署過程如下:

image.png

1、建立EKS GPU推理協會

使用 eksctl 工具來進行指揮 EKS 的建立。

首先將類別名稱、區域、變數等資訊配置到環境時,可用於各種ID名稱、區域、變數等資訊配置時使用:

export CLUSTER_NAME="karpenter-cluster"
export AWS_REGION="us-west-2"
export AWS_ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)"

左滑檢視更多

準備組的配置檔案以供eksctl使用工具:

cat << EOF > cluster.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
 name: ${CLUSTER_NAME}
 region: ${AWS_REGION}
 version: "1.21"
 tags:
   karpenter.sh/discovery: ${CLUSTER_NAME}

iam:
 withOIDC: true

managedNodeGroups:
 - name: ng-1
   privateNetworking: true
   instanceType: m5.large
   desiredCapacity: 3
EOF

左滑檢視更多

通過以上配置檔案建立EKS叢集:

eksctl 建立叢集 -f cluster.yaml

eksct 能夠按設定裝置組和裝置組,在 5 個作為部分配置並建立了控制器組與使用組,Amazon 負載均衡器的部署節點,以便 GPU 任務組。

Endpoint 環境變數以供配置使用Amazon Karpenter

export CLUSTER_ENDPOINT="$(aws eks describe-cluster --name ${CLUSTER_NAME} --query "cluster.endpoint" --output text)"

左滑檢視更多

節點狀態是否都已經準備好檢視狀態:

image.png

image.png

2、安裝 Amazon Karpenter

部落格寫作時,Karpenter 版本為最新版本 6.3,在此部署 Amazon Karpenter 版本的安裝過程進行整理,讀者可以根據需要在 Amazon Karpenter 官網操作的安裝過程指南:

首先需要建立配置檔案,配置相應的許可權,以便 Amazon Karpenter 啟動的例項可以有可能的許可權進行網路配置和進行映象拉取等動作:

TEMPOUT=$(mktemp)
curl -fsSL https://karpenter.sh/v0.6.3/getting-started/cloudformation.yaml > $TEMPOUT \
&& aws cloudformation deploy \
 --stack-name "Karpenter-${CLUSTER_NAME}" \
 --template-file "${TEMPOUT}" \
 --capabilities CAPABILITY_NAMED_IAM \
 --parameter-overrides "ClusterName=${CLUSTER_NAME}"

左滑檢視更多

有權配置訪問許可權,以便 Amazon Karpenter 建立的例項可以有許可權連線到 EKS 叢集:

eksctl create iamidentitymapping \
 --username system:node:{{EC2PrivateDNSName}} \
 --cluster "${CLUSTER_NAME}" \
 --arn "arn:aws:iam::${AWS_ACCOUNT_ID}:role/KarpenterNodeRole-${CLUSTER_NAME}" \
 --group system:bootstrappers \
 --group system:nodes

左滑檢視更多

為 Karpenter 控制器建立 IAM 角色和服務帳戶:

eksctl utils associate-iam-oidc-provider --cluster ${CLUSTER_NAME} –approve
eksctl create iamserviceaccount \
 --cluster "${CLUSTER_NAME}" --name karpenter --namespace karpenter \
 --role-name "${CLUSTER_NAME}-karpenter" \
 --attach-policy-arn "arn:aws:iam::${AWS_ACCOUNT_ID}:policy/KarpenterControllerPolicy-${CLUSTER_NAME}" \
 --role-only \
 --approve

左滑檢視更多

配置環境變數以供安裝Amazon Karpenter 使用:

export KARPENTER_IAM_ROLE_ARN="arn:aws:iam::${AWS_ACCOUNT_ID}:role/${CLUSTER_NAME}-karpenter"

左滑檢視更多

最後通過Helm來安裝karpenter:

helm repo add karpenter https://charts.karpenter.sh/
helm repo update
helm upgrade --install --namespace karpenter --create-namespace \
 karpenter karpenter/karpenter \
 --version v0.6.3 \
 --set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"=${KARPENTER_IAM_ROLE_ARN} \
 --set clusterName=${CLUSTER_NAME} \
 --set clusterEndpoint=${CLUSTER_ENDPOINT} \
 --set aws.defaultInstanceProfile=KarpenterNodeInstanceProfile-${CLUSTER_NAME} \
 --set logLevel=debug \
 --wait

左滑檢視更多

檢查 Karenter Controller 是否已經正常執行:

image.png

3、配置 Karpenter Provisioner

當 Kubernetes 需要在中出現 Unscheduleable Pod 的時候,Amazon Karpenter 通過visioner來確定所建立的 EC2 例項和大小。Amazon Karpenter 安裝的時間會定義一個命名 Provisioner 的自定義資源。因為 Karpenter Provisioner 處理多個 Pod Amazon Karpent根據Pod和置備布點等不同屬性排程器組,使用Amazon Karpent額外管理多個節點。

建立Provisioner的配置檔案:

cat << EOF > provisioner.yaml
apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
 name: gpu
spec:
 requirements:
   - key: karpenter.sh/capacity-type
     operator: In
     values: ["on-demand"] 
   - key: node.kubernetes.io/instance-type
     operator: In
     values: ["g4dn.xlarge", "g4dn.2xlarge"]
 taints:
   - key: nvidia.com/gpu
     effect: "NoSchedule"
 limits:
   resources:
     gpu: 100
 provider:
   subnetSelector:
     karpenter.sh/discovery: karpenter-cluster  
   securityGroupSelector:
     kubernetes.io/cluster/karpenter-cluster: owned  
 ttlSecondsAfterEmpty: 30
EOF

左滑檢視更多

在這個配置檔案裡,我們在規範中指定了我們建立的購買選項和例項型別。我們在spec.requirements中將 karpenter.sh/capacity-type 中指定為on-demand。 同時,由於這是一個機器學習的推理應用,我們也將這裡的例項型別定義為G4,我們選擇 g4dn.xlargeg4dn .2x大大小。另外我們也設定了 taints nvidia.com/gpu可以看到,Pod 2 可能無法部署這些 Pod 的推斷,也需要在這個 Provisioner 提供一個例項的例項。會建立出 GPU 例項。

另外,我們也可以通過這個環境部署的資源配置器,在這個環境中設定我們的 gpu 設定。同時可以建立 100 子網選擇器和安全組選擇器的標籤來找到 Povisioner 如何去設定新的例項。的 VPC 子網和安全組。

在Emp tttSecondsAfterEmpty 來告訴Provisioner 來最後清理一下Amazon2 的備用閒置例項30,通過這個當建立的Amazon EC2 例項去掉配置30 秒後EC 執行時沒有任何成本,Provisioner 會要求EC 執行時沒有任何成本資源並直接進行回收,也就是終止 EC2 例項。

關於Provisioners的詳細配置可以參考官網上的文件。

image.png

我們就使用這個配置檔案建立授權 Provisioner:

kubectl apply -f provisioner.yaml

4、安裝 Amazon Loadbalancer Controller

在這個演示中我們會使用 Amazon Loadbalancer Controller 來自動為服務建立 NLB,可以參考亞馬遜雲科技官網檢視詳細的安裝步驟,這裡簡要記錄下 2.4.0 版本的安裝過程:

curl -o iam_policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.4.0/docs/install/iam_policy.json

 aws iam create-policy \
 --policy-name AWSLoadBalancerControllerIAMPolicy \
 --policy-document file://iam_policy.json

 eksctl create iamserviceaccount \
 --cluster=${CLUSTER_NAME} \
 --namespace=kube-system \
 --name=aws-load-balancer-controller \
 --attach-policy-arn=arn:aws:iam::${AWS_ACCOUNT_ID}:policy/AWSLoadBalancerControllerIAMPolicy \
 --override-existing-serviceaccounts \
 --approve

 helm repo add eks https://aws.github.io/eks-charts
 helm repo update
 helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
 -n kube-system \
 --set clusterName=${CLUSTER_NAME} \
 --set serviceAccount.create=false \
 --set serviceAccount.name=aws-load-balancer-controller

左滑檢視更多

5、部署機器學習推理應用

該示例將部署一個學習推理應用我們作為示例。這個示例來自一個 EKS 上進行 vGPU 部署的示例。該示例會部署一個基於 ResNet 的部落格的圖片推理服務並通過負載均衡釋出一個服務地址,使用一個Python編寫的客戶端將上傳給這個推理服務獲得推理結果。這裡並進行了圖片配置,而不是討論vGPU的GPU,直接讓這個推理服務使用G4例項的GPU。

這裡是修改後的推理服務的部署/服務的部署檔案:

cat << EOF > resnet.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
 name: resnet
spec:
 replicas: 1
 selector:
   matchLabels:
     app: resnet-server
 template:
   metadata:
     labels:
       app: resnet-server
   spec:
     # hostIPC is required for MPS communication
     hostIPC: true
     containers:
     - name: resnet-container
       image: seedjeffwan/tensorflow-serving-gpu:resnet
       args:
       - --per_process_gpu_memory_fraction=0.2
       env:
       - name: MODEL_NAME
         value: resnet
       ports:
       - containerPort: 8501
       # Use gpu resource here
       resources:
         requests:
           nvidia.com/gpu: 1
         limits:
           nvidia.com/gpu: 1
       volumeMounts:
       - name: nvidia-mps
         mountPath: /tmp/nvidia-mps
     volumes:
     - name: nvidia-mps
       hostPath:
         path: /tmp/nvidia-mps
     tolerations:
     - key: nvidia.com/gpu
       effect: "NoSchedule"
---
apiVersion: v1
kind: Service
metadata:
 name: resnet-service
 annotations:
   service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
   service.beta.kubernetes.io/aws-load-balancer-type: external
   service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
spec:
 type: LoadBalancer
 selector:
   app: resnet-server
 ports:
 - port: 8501
   targetPort: 8501
EOF

左滑檢視更多

可以看到在部署中設定了推理服務所的 GPU 資源,以及對汙點 nvidia.com/gpu的Gpu 的我們需要根據這些資訊來進行例項 4 的建立。另外在服務中也定義了相應的的註解,以便使用 Amazon Load Balancer Controller 來自動建立一個公網可訪問的 Network Load Balancer 。

部署完成後,可以檢查相應資源是否都執行正常:

image.png

檢查自動生成的NLB,該地址可以供客戶端訪問:

image.png

通過kubectl exec -it <podname> -- nvidia-smi檢視GPU容器狀態

image.png

我們部署一個程式碼客戶端,如下是相應的 Python 輸出,儲存為: resnet_client.py

from __future__ import print_function

import base64
import requests
import sys

assert (len(sys.argv) == 2), "Usage: resnet_client.py SERVER_URL"
# The server URL specifies the endpoint of your server running the ResNet
# model with the name "resnet" and using the predict interface.
SERVER_URL = f'http://{sys.argv[1]}:8501/v1/models/resnet:predict'
# The image URL is the location of the image we should send to the server
IMAGE_URL = 'https://tensorflow.org/images/blogs/serving/cat.jpg'

def main():
 # Download the image
 dl_request = requests.get(IMAGE_URL, stream=True)
 dl_request.raise_for_status()

 # Compose a JSON Predict request (send JPEG image in base64).
 jpeg_bytes = base64.b64encode(dl_request.content).decode('utf-8')
 predict_request = '{"instances" : [{"b64": "%s"}]}' % jpeg_bytes

 # Send few requests to warm-up the model.
 for _ in range(3):
   response = requests.post(SERVER_URL, data=predict_request)
   response.raise_for_status()

 # Send few actual requests and report average latency.
 total_time = 0
 num_requests = 10
 for _ in range(num_requests):
   response = requests.post(SERVER_URL, data=predict_request)
   response.raise_for_status()
   total_time += response.elapsed.total_seconds()
   prediction = response.json()['predictions'][0]

 print('Prediction class: {}, avg latency: {} ms'.format(
     prediction['classes'], (total_time*1000)/num_requests))

if __name__ == '__main__':
 main()

左滑檢視更多

這個Python客戶端圖片輸出結果是否通過執行引數獲取到服務端的自動執行,下載示例並上傳至進行推理,最後推理端。

py $(kubectl get svc resnet-service -o=jsonpath='{.status.loadBalancer.ingress[0].hostname}')

可以看到推理結果以及相應的演講:

image.png

6、Karpenter 節點擴張收縮容測試

Karpenter Controller會在後臺排程不可排程Pods的資訊,並安排Pod需求和不同的Tag持續監控/Taints等資訊,生成符合的例項型別和數量。 ,使用者無需提前進行組節點的例項型別匹配,由 Karpenter 來根據需要的資源自動節點型別和數量,更加靈活。因此規劃也需要減少額外的叢集 AutoScaler 元件來進行節點擴充套件容。

我們增加 Pod 以便觸發節點擴容:

kubectl 規模部署 resnet --replicas 6

通過 Karpenter Controller 的查詢日誌,我們可以檢視擴容時的具體執行邏輯:

kubectl 日誌 -f -n karpenter -l app.kubernetes.io/name=karpenter -c 控制器

image.png

每個 Pod 的需求狀態,因此,5 個 Pod 並沒有在 G4 例項上建立 GPU 而正在等待亞馬遜 。這時候Karpenter 彙總了這 5 個 Pod 的資源需求,並據此判斷使用的需求或狀態(或多種)型別的例項適合。結合我們前面的Provisioner的配置,Amazon Karpenter會自動額外建立5g4.xlarge的按需例項來執行這5個Pod。針對按需例項,Provisioner會選擇自動最低價格的可以滿足需求的例項。

AutoScaler的建立方式會立即將 Pod 的出現,然後在節點開始繫結拉取等模式。 準備就緒後 Kubernetes Scheduler 狀態將節點排程並開始建立活動,Amazon Karpenter 會更加高效。

刪除所有的 Pod,觀察Amazon Karpenter 如何處理節點縮小容的場景:

kubectl 規模部署 resnet --replicas 0

image.png

從前面的配置可以看到,在我們Karpenter的配置後,亞馬遜 會自動消除前面的異常。

小結

在這個部落格裡裡,我們學習了一個機器管理的應用部署器,這樣EKS中如何介紹了Karpenter來進行使用亞馬遜 的不同型別的彈性放大縮小。 Amazon Karpenter是完全開源的元件,目前支援在亞馬遜雲上對 EKS 和使用者自建的 Kubernetes 叢集進行一個節點擴容管理,通過其開放的雲提供商外掛功能,可以對非亞馬遜雲科技雲的支援。

在關注的部落格上進一步探討利用 Amazon Karpenter 實體進行管理會如何對我們進行現場討論

本文作者

image.png

邱萌

亞馬遜雲科技解決方案架構師

負責基於亞馬遜雲科技方案架構的諮詢和設計,推廣亞馬遜雲科技平臺技術和解決方案。在加入亞馬遜雲科技之前,曾在企業上雲、網際網路娛樂、媒體等行業挖掘業務,對公有云和架構業務有很深的理解。

image.png

林俊

亞馬遜雲科技解決方案架構師

主要負責企業客戶的解決方案諮詢與架構設計優化。

相關文章