協助改善此頁面
本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。
若要提供此使用者指南,請選擇位於每個頁面右窗格中的在 GitHub 上編輯此頁面連結。
本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。
Amazon EKS 即時推論最佳實務叢集設定指南
簡介
本指南提供實作演練,以設定針對即時線上推論工作負載最佳化的 Amazon Elastic Kubernetes Service (EKS) 叢集,並整合 AWS 專家在整個過程中策劃的最佳實務。它使用 EKS Quickstart 架構,這是一組符合模型、加速器和擴展 AWS 最佳實務的精選驅動程式、執行個體類型和組態。此方法可協助您略過選取叢集設定的任務,讓您快速啟動和執行功能正常且預先設定的叢集。在此過程中,我們將部署範例工作負載來驗證您的設定、解釋關鍵架構概念 (例如從 GPU 密集型運算解耦 CPU 繫結任務)、解決常見問題 (例如,為什麼選擇 Bottlerocket AMI over AL2023?),並概述後續步驟以擴展叢集的功能。
本指南專為Machine Learning (ML) 和人工智慧 (AI) 工程師、平台管理員、運算子和初次使用 AWS 和 EKS 生態系統的資料/AI 專家而設計,假設熟悉 Kubernetes,但先前沒有 EKS 經驗。它旨在協助您了解啟動和執行即時線上推論工作負載所需的步驟和程序。本指南說明建立單一節點推論叢集的基本概念,包括佈建 GPU 資源、整合模型成品的儲存體、啟用安全 AWS 的服務存取,以及公開推論端點。在整個過程中,它強調了面向使用者的應用程式的低延遲、彈性設計,例如詐騙偵測、即時聊天機器人,以及客戶意見回饋系統中的情緒分析。
在本指南中,我們只專注於使用 G5 EC2 執行個體設定基礎、規範性的起點。如果您要尋找 AWS Inferentia 特定的叢集組態或end-to-end工作流程,請參閱 將 AWS Inferentia 執行個體與 Amazon EKS 搭配使用以進行Machine Learning或 中的研討會在 Amazon EKS 上開始使用 AI/ML 的資源。
開始之前
開始之前,請確定您已執行下列任務:
架構
即時線上推論是指使用訓練的機器學習模型,以最低延遲在傳入資料串流上產生預測或輸出的程序。例如,它會啟用即時詐騙偵測、影像分類或產生知識圖表以回應使用者輸入。即時線上推論系統的架構透過從 GPU 密集 AI 運算分離 CPU 繫結的 Web 流量處理,在使用者面向的應用程式中提供低延遲的機器學習預測。此程序通常位於更大的應用程式生態系統中,通常包括後端、前端、向量和模型服務,專注於特殊元件,以啟用獨立擴展、平行開發和應對故障的彈性。隔離專用 GPU 硬體上的推論任務,並利用 APIs 和 WebSockets等界面,確保高度並行、快速處理轉換器等模型,以及透過前端進行使用者互動。請注意,雖然向量資料庫和擷取增強生成 (RAG) 管道通常在即時推論系統中扮演重要角色,但本指南並未涵蓋這些元件。典型架構至少包括:
-
前端服務:作為面向使用者的界面,處理用戶端邏輯,轉譯動態內容,並促進即時互動,它與後端服務通訊以啟動推論請求和顯示結果,通常向後端服務發起請求,該服務使用 WebSockets進行串流更新或 APIs進行結構化資料交換。此服務通常不需要專用負載平衡器,因為它可以託管在內容交付網路 (CDNs) 上,例如靜態資產的 AWS CloudFront 或直接從 Web 伺服器提供,如果動態內容需要,可透過自動擴展群組處理擴展。
-
後端服務:做為應用程式的協調器,管理商業邏輯,例如使用者身分驗證、資料驗證和服務協調 (例如,透過 RESTful 端點APIs 或持久性連線WebSockets)。它會與推論服務通訊、在多核心 CPUs 和 RAM 上獨立擴展,在不依賴 GPUs 的情況下處理高 Web 流量,而且通常需要負載平衡器 (例如 AWS Application Load Balancer 或 Network Load Balancer) 才能在多個執行個體之間分配傳入請求,尤其是在高並行情況下。輸入控制器可以進一步管理外部存取和路由規則,以提高安全性和流量形狀。
-
推論服務:作為 AI 運算的核心,在具有足夠 VRAM (例如,8-12 GB 用於 DistilBERT 等模型) 的 GPUs 上執行,以使用自訂或開放原始碼模型執行向量內嵌、知識擷取和模型推論 (例如,透過 APIs 用於批次請求或 WebSockets用於即時串流)。此隔離可防止相依性衝突、允許模型更新而不停機,並針對多個並行請求啟用具有負載平衡的水平擴展。為了有效地公開模型服務,它通常位於負載平衡器後方,以在複寫的執行個體之間分配 GPU 繫結工作負載,而傳入資源或控制器 (例如 ALB 傳入控制器 AWS) 則處理外部路由、SSL 終止和路徑型轉送,以確保安全且有效率的存取,而不會讓個別 GPUs 不堪負荷。
解決方案概觀
即時線上推論系統需要高效能的彈性架構,可提供極低的延遲,同時處理無法預測的大量流量暴增。此解決方案概觀說明下列 AWS 元件如何在我們將建立的 Amazon EKS 叢集中一起運作,以確保我們的叢集能夠託管和管理機器學習模型,以盡可能減少最終使用者的即時資料預測。
-
Amazon G5 EC2 執行個體
— 對於 GPU 密集型推論任務,我們使用 g5.xlarge 和 g5.2xlarge G5 EC2 執行個體類型,其具有單一 (1) NVIDIA A10G GPU 和 24GB 記憶體 (例如 FP16 的 80 億個參數)。這些 GPUs 以 NVIDIA Ampere 架構為基礎,採用 NVIDIA A10G Tensor 核心 GPUs 和第二代 AMD EPYC 處理器,支援 4-8 個 vCPUs、高達 10 Gbps 的網路頻寬,以及 250-450 GB 的本機 NVMe SSD 儲存體,可確保複雜模型的快速資料移動和運算能力,因此非常適合低延遲、高輸送量的推論任務。選擇 EC2 執行個體類型是應用程式特定的,取決於您的模型 (例如影像、影片、文字模型),以及您的延遲和輸送量需求。例如,如果使用映像和/或影片模型,您可能想要使用 P5 EC2 執行個體 來獲得最佳的即時延遲。我們建議您從 G5 EC2 執行個體 開始,因為它為快速啟動和執行提供了良好的起點,然後透過效能基準測試評估它是否適合您的工作負載。對於更進階的案例,請考慮 G6 EC2 執行個體 。 -
Amazon EC2 M7g 執行個體
— 對於資料預先處理、API 請求處理、託管 Karpenter 控制器、附加元件和其他系統元件等 CPU 密集型任務,我們使用 m5.xlarge M7g EC2 執行個體類型。M7g 執行個體是以 ARM 為基礎的執行個體,具有 4 個 vCPUs、16 GB 記憶體、高達 12.5 Gbps 的網路頻寬,並採用 AWS Graviton3 處理器。選擇 EC2 執行個體類型是應用程式特定的,取決於工作負載的運算、記憶體和可擴展性需求。對於運算最佳化工作負載,您可以考慮使用 C7g EC2 執行個體 ,這些執行個體也使用 Graviton3 處理器,但針對特定使用案例的 M7g 執行個體,已針對更高的運算效能進行最佳化。或者,較新的 C8g EC2 執行個體 (如果有的話) 提供比 C7g 執行個體高 30% 的運算效能。我們建議您從 M7g EC2 執行個體開始,確保其成本效益和與各種工作負載的相容性 (例如,應用程式伺服器、微服務、遊戲伺服器、中型資料存放區),然後透過效能基準測試評估它是否適合您的工作負載。 -
Amazon S3 掛載點 CSI 驅動程式 — 對於在單一 GPU 執行個體上的工作負載,其中多個 Pod 共用 GPU (例如,在同一節點上排程多個 Pod 以利用其 GPU 資源),我們使用掛載點 S3 CSI 驅動程式來最佳化記憶體使用量,對於成本敏感、低複雜度設定中的大型模型推論等任務來說至關重要。它將 Amazon S3 儲存貯體公開為 Kubernetes 叢集可用的類似 POSIX 檔案系統,允許推論 Pod 直接將模型成品 (例如模型權重) 讀取到記憶體中,而無需先下載它們,並使用標準檔案操作輸入資料集。此外,S3 具有幾乎無限制的儲存容量,並加速資料密集型推論工作負載。選擇儲存 CSI 驅動程式是應用程式特定的,取決於工作負載的輸送量和延遲需求。雖然 FSx for OpenZFS CSI 驅動程式跨節點為隨機 I/O 或完全 POSIX 相容共用持久性磁碟區提供低於毫秒的延遲,但我們建議您從掛載點 S3 CSI 驅動程式開始,因為其可擴展性、大型資料集的成本較低,以及內建與 S3-managed物件儲存的整合,用於讀取密集型推論模式 (例如,串流模型輸入),然後透過效能基準測試評估它是否適合您的工作負載。
-
EKS Pod 身分代理程式 — 為了啟用對 AWS 服務的存取,我們使用 EKS Pod 身分代理程式,該代理程式使用單一服務主體,並促進 Amazon EKS 叢集內的 Pod 層級 IAM 角色關聯。EKS Pod Identity 透過使用單一服務主體 (https://) 來提供傳統 IAM Roles for Service Accounts (IRSA) 方法的簡化替代方案,而不是依賴每個叢集的個別 OIDC 供應商,這可讓您更輕鬆地指派許可。 pods.eks.amazonaws.com 此外,它可讓角色在多個叢集之間重複使用,並支援進階功能,例如 IAM 角色工作階段標籤和目標 IAM 角色。
-
EKS 節點監控代理程式 — 為確保推論服務的持續可用性和可靠性,我們使用 EKS 節點監控代理程式搭配自動修復,可自動偵測並取代運作狀態不佳的節點,將停機時間降至最低。它使用增強型運作狀態檢查 (例如 KernelReady、NetworkingReady) 持續監控節點是否有硬體、核心、聯網和儲存問題。對於 GPU 節點,它會偵測加速器特定的故障,透過封鎖運作狀態不佳的節點、等待 10 分鐘以解決暫時性 GPU 問題,以及在 30 分鐘後取代節點以持續失敗,來啟動正常修復。
-
Bottlerocket AMI — 為了為 EKS 叢集提供安全強化的基礎,我們使用 Bottlerocket AMI,這只包含執行容器所需的基本元件,並提供最短的開機時間以快速擴展。選擇節點 AMI 是應用程式特定的,取決於工作負載的自訂、安全性和可擴展性需求。雖然 AL2023 AMI 為主機層級安裝和自訂提供更大的彈性 (例如,在 PV/PVC 中指定專用快取目錄,而不需要任何其他節點組態),但我們建議您從 Bottlerocket AMI 開始,以降低其佔用空間,並針對容器化工作負載 (例如微服務、推論伺服器、可擴展APIs) 進行內建最佳化,然後透過效能基準測試來評估其是否適合您的工作負載。
-
AWS Load Balancer控制器 (LBC) – 為了公開即時推論端點,我們使用 AWS Load Balancer控制器,其會自動佈建和管理適用於 HTTP/HTTPS 流量的 Application Load Balancer (ALBs),以及適用於基於 Kubernetes Ingress and Service 資源的 TCP/UDP 流量的 Network Load Balancer (NLBs),以便將推論模型與外部用戶端整合。此外,它支援路徑型路由等功能,可將推論請求分散到多個 Pod 或節點,確保在流量激增期間可擴展性,並透過連線多工和運作狀態檢查等 AWS原生最佳化將延遲降至最低。
1. 建立您的 EKS 叢集
在此步驟中,我們使用採用 AWS CloudFormation 技術的 eksctl ClusterConfig
根據預設, eksctl
會為 CIDR 區塊為 的叢集建立專用 VPC192.168.0.0/16
。VPC 包含三個公有子網路和三個私有子網路,每個子網路分佈在三個不同的可用區域 (或us-east-1
區域中AZs),這是部署 Kubernetes 工作負載的理想方法。範本也會部署網際網路閘道,透過路由表中的預設路由和其中一個公有子網路中的單一 NAT 閘道提供公有子網路的網際網路存取,而私有子網路路由表中的預設路由會將傳出流量引導至 NAT 閘道以進行網際網路存取。若要進一步了解此設定,請參閱將節點部署至私有子網路。
檢查您的登入資料
檢查您的 AWS CLI 登入資料是否有效,並且可以使用 AWS 服務進行身分驗證:
aws sts get-caller-identity
如果成功,CLI 將傳回有關 AWS 您的身分 (UserId、帳戶和 Arn) 的詳細資訊。
檢查執行個體可用性
並非所有區域都提供 G5 執行個體類型。檢查離您最近的區域。例如:
aws ec2 describe-instance-types --instance-types g5.xlarge g5.2xlarge --region us-east-1
如果成功,G5 執行個體類型可在您指定的區域中使用。
Bottlerocket AMI 並非所有區域都提供。透過擷取離您最近區域的 Bottlerocket AMI ID 進行檢查。例如:
aws ssm get-parameter --name /aws/service/bottlerocket/aws-k8s-1.33/arm64/latest/image_id \ --region us-east-1 --query "Parameter.Value" --output text
如果成功, Bottlerocket AMI 可在您指定的區域中使用。
準備您的環境
首先,在新的終端機視窗中設定下列環境變數。注意:請務必以您的唯一值取代範例預留位置,包括叢集名稱、所需區域、Karpenter 發行版本
提示
部分變數 (例如 ${AWS_REGION}
和 ${K8S_VERSION}
) 會在區塊的早期定義,然後在稍後的命令中參考,以確保一致性並避免重複。請務必依序執行命令,以便正確匯出這些值,並可用於後續定義。
export TEMPOUT="$(mktemp)" export K8S_VERSION=1.33 export KARPENTER_VERSION="1.5.0" export AWS_REGION="us-east-1" export EKS_CLUSTER_NAME="eks-rt-inference-${AWS_REGION}" export S3_BUCKET_NAME="eks-rt-inference-models-${AWS_REGION}-$(date +%s)" export NVIDIA_BOTTLEROCKET_AMI="$(aws ssm get-parameter --name /aws/service/bottlerocket/aws-k8s-${K8S_VERSION}-nvidia/x86_64/latest/image_id --query Parameter.Value --output text)" export STANDARD_BOTTLEROCKET_AMI="$(aws ssm get-parameter --name /aws/service/bottlerocket/aws-k8s-${K8S_VERSION}/arm64/latest/image_id --query Parameter.Value --output text)" export AWS_ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)" export ALIAS_VERSION="$(aws ssm get-parameter --name "/aws/service/eks/optimized-ami/${K8S_VERSION}/amazon-linux-2023/x86_64/standard/recommended/image_id" --query Parameter.Value | xargs aws ec2 describe-images --query 'Images[0].Name' --image-ids | sed -r 's/^.*(v[[:digit:]]+).*$/\1/')"
建立必要的角色和政策
Karpenter 需要特定的 IAM 角色和政策 (例如 Karpenter 控制器 IAM 角色、執行個體描述檔和政策),才能將 EC2 執行個體管理為 Kubernetes 工作者節點。它使用這些角色來執行動作,例如啟動和終止 EC2 執行個體、標記資源,以及與其他 AWS 服務互動。使用 Karpenter 的 cloudformation.yaml
curl -fsSL https://raw.githubusercontent.com/aws/karpenter-provider-aws/v${KARPENTER_VERSION}/website/content/en/preview/getting-started/getting-started-with-karpenter/cloudformation.yaml > "${TEMPOUT}" \ && aws cloudformation deploy \ --stack-name "Karpenter-${EKS_CLUSTER_NAME}" \ --template-file "${TEMPOUT}" \ --capabilities CAPABILITY_NAMED_IAM \ --parameter-overrides "ClusterName=${EKS_CLUSTER_NAME}"
AWS LBC 需要佈建和管理 AWS 負載平衡器的許可,例如為輸入資源建立 ALBs或為 類型的服務建立 NLBsLoadBalancer
。我們將在叢集建立期間指定此許可政策。在叢集建立期間,我們會在 ClusterConfig 中使用 eksctl 建立服務帳戶。建立 LBC IAM 政策:
aws iam create-policy \ --policy-name AWSLoadBalancerControllerIAMPolicy \ --policy-document "$(curl -fsSL https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.13.0/docs/install/iam_policy.json)"
安裝掛載點 S3 CSI 驅動程式時,其 DaemonSet Pod 會設定為使用服務帳戶執行。掛載點 S3 CSI 驅動程式的掛載點需要許可,才能與您稍後在本指南中建立的 Amazon S3 儲存貯體互動。我們將在叢集建立期間指定此許可政策。在叢集建立期間,我們會在 ClusterConfig 中使用 eksctl 建立服務帳戶。建立 S3 IAM 政策:
aws iam create-policy \ --policy-name S3CSIDriverPolicy \ --policy-document "{\"Version\": \"2012-10-17\", \"Statement\": [{\"Effect\": \"Allow\", \"Action\": [\"s3:GetObject\", \"s3:PutObject\", \"s3:AbortMultipartUpload\", \"s3:DeleteObject\", \"s3:ListBucket\"], \"Resource\": [\"arn:aws:s3:::${S3_BUCKET_NAME}\", \"arn:aws:s3:::${S3_BUCKET_NAME}/*\"]}]}"
注意:如果具有此名稱的角色已存在,請為角色指定不同的名稱。我們在此步驟中建立的角色專屬於您的叢集和 S3 儲存貯體。
建立叢集
在此範本中,eksctl 會自動為 EKS Pod Identity、Node Monitoring Agent、CoreDNS、Kubeproxy、VPC CNI 外掛程式建立 Kubernetes 服務帳戶。截至目前為止,掛載點 S3 CSI 驅動程式不適用於 EKS Pod 身分,因此我們會建立服務帳戶 (IRSA) 的 IAM 角色和 OIDC 端點。此外,我們為 AWS Load Balancer控制器 (LBC) 建立服務帳戶。為了存取 Bottlerocket 節點,eksctl 會自動連接適用於 Bottlerocket 的 AmazonSSMManagedInstanceCore,以允許透過 SSM 的安全 shell 工作階段。
在設定環境變數的相同終端機中,執行下列命令區塊來建立叢集:
eksctl create cluster -f - <<EOF --- apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: ${EKS_CLUSTER_NAME} region: ${AWS_REGION} version: "${K8S_VERSION}" tags: karpenter.sh/discovery: ${EKS_CLUSTER_NAME} # Add more tags if needed for billing iam: # Creates an OIDC endpoint and IRSA service account for the Mountpoint S3 CSI Driver # Uses the S3 CSI Driver policy for permissions withOIDC: true podIdentityAssociations: # Creates the pod identity association and service account # Uses the Karpenter controller IAM policy for permissions - namespace: "kube-system" serviceAccountName: karpenter roleName: ${EKS_CLUSTER_NAME}-karpenter permissionPolicyARNs: - arn:aws:iam::${AWS_ACCOUNT_ID}:policy/KarpenterControllerPolicy-${EKS_CLUSTER_NAME} # Creates the pod identity association and service account # Uses the AWS LBC policy for permissions - namespace: kube-system serviceAccountName: aws-load-balancer-controller createServiceAccount: true roleName: AmazonEKSLoadBalancerControllerRole permissionPolicyARNs: - arn:aws:iam::${AWS_ACCOUNT_ID}:policy/AWSLoadBalancerControllerIAMPolicy iamIdentityMappings: - arn: "arn:aws:iam::${AWS_ACCOUNT_ID}:role/KarpenterNodeRole-${EKS_CLUSTER_NAME}" username: system:node:{{EC2PrivateDNSName}} groups: - system:bootstrappers - system:nodes managedNodeGroups: # Creates 2 CPU nodes for lightweight system tasks - name: ${EKS_CLUSTER_NAME}-m7-cpu instanceType: m7g.xlarge amiFamily: Bottlerocket desiredCapacity: 2 minSize: 1 maxSize: 10 labels: role: cpu-worker # Enable automatic Pod Identity associations for VPC CNI Driver, coreDNS, kube-proxy addonsConfig: autoApplyPodIdentityAssociations: true addons: # Installs the S3 CSI Driver addon and creates IAM role # Uses the S3 CSI Driver policy for IRSA permissions - name: aws-mountpoint-s3-csi-driver attachPolicyARNs: - "arn:aws:iam::${AWS_ACCOUNT_ID}:policy/S3CSIDriverPolicy" - name: eks-pod-identity-agent - name: eks-node-monitoring-agent - name: coredns - name: kube-proxy - name: vpc-cni EOF
這個過程需要幾分鐘的時間來完成。如果您想要監控狀態,請參閱 AWS CloudFormation
2. 驗證叢集節點和 Pod 運作狀態
讓我們執行幾個運作狀態檢查,以確保叢集準備就緒。當先前的命令完成時,請檢視執行個體類型,並使用下列命令確認您的 CPU 系統節點已達到 Ready
狀態:
kubectl get nodes -L node.kubernetes.io/instance-type
預期的輸出應如下所示:
NAME STATUS ROLES AGE VERSION INSTANCE-TYPE ip-192-168-35-103.ec2.internal Ready <none> 12m v1.33.0-eks-802817d m7g.xlarge ip-192-168-7-15.ec2.internal Ready <none> 12m v1.33.0-eks-802817d m7g.xlarge
使用下列命令,驗證所有 Pod Identity 關聯,以及它們如何將角色映射至叢集中命名空間中的服務帳戶:
eksctl get podidentityassociation --cluster ${EKS_CLUSTER_NAME} --region ${AWS_REGION}
輸出應會顯示 Karpenter ("karpenter") 和 AWS LBC ("aws-load-balancer-controller") 的 IAM 角色。
驗證 DaemonSets 是否可用:
kubectl get daemonsets -n kube-system
預期的輸出應如下所示:
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE aws-node 3 3 3 3 3 <none> 12m dcgm-server 0 0 0 0 0 kubernetes.io/os=linux 12m eks-node-monitoring-agent 3 3 3 3 3 kubernetes.io/os=linux 12m eks-pod-identity-agent 3 3 3 3 3 <none> 12m kube-proxy 3 3 3 3 3 <none> 12m s3-csi-node 2 2 2 2 2 kubernetes.io/os=linux 12m
確認叢集上安裝了所有附加元件:
eksctl get addons --cluster ${EKS_CLUSTER_NAME} --region ${AWS_REGION}
預期的輸出應如下所示:
NAME VERSION STATUS ISSUES IAMROLE UPDATE AVAILABLE CONFIGURATION VALUES POD IDENTITY ASSOCIATION ROLES aws-mountpoint-s3-csi-driver v1.15.0-eksbuild.1 ACTIVE 0 arn:aws:iam::143095308808:role/eksctl-eks-rt-inference-us-east-1-addon-aws-m-Role1-RAUjk4sJnc0L coredns v1.12.1-eksbuild.2 ACTIVE 0 eks-node-monitoring-agent v1.3.0-eksbuild.2 ACTIVE 0 eks-pod-identity-agent v1.3.7-eksbuild.2 ACTIVE 0 kube-proxy v1.33.0-eksbuild.2 ACTIVE 0 metrics-server v0.7.2-eksbuild.3 ACTIVE 0 vpc-cni v1.19.5-eksbuild.1 ACTIVE 0
3. 安裝 Karpenter
在 CPU 工作者節點上安裝 Karpenter 控制器 (cpu-worker
),以最佳化成本並節省 GPU 資源。我們將將其安裝在「kube-system」命名空間中,並指定我們在叢集建立期間定義的「karpenter」服務帳戶。此外,此命令會設定 CPU 節點的叢集名稱和 Spot 執行個體中斷佇列。Karpenter 將使用 IRSA 擔任此 IAM 角色。
# Logout of helm registry before pulling from public ECR helm registry logout public.ecr.aws # Install Karpenter helm upgrade --install karpenter oci://public.ecr.aws/karpenter/karpenter --version "${KARPENTER_VERSION}" --namespace "kube-system" --create-namespace \ --set "settings.clusterName=${EKS_CLUSTER_NAME}" \ --set "settings.interruptionQueue=${EKS_CLUSTER_NAME}" \ --set controller.resources.requests.cpu=1 \ --set controller.resources.requests.memory=1Gi \ --set controller.resources.limits.cpu=1 \ --set controller.resources.limits.memory=1Gi \ --set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"="arn:aws:iam::${AWS_ACCOUNT_ID}:role/${EKS_CLUSTER_NAME}-karpenter" \ --wait
預期的輸出應如下所示:
Release "karpenter" does not exist. Installing it now. Pulled: public.ecr.aws/karpenter/karpenter:1.5.0 Digest: sha256:9a155c7831fbff070669e58500f68d7ccdcf3f7c808dcb4c21d3885aa20c0a1c NAME: karpenter LAST DEPLOYED: Thu Jun 19 09:57:06 2025 NAMESPACE: kube-system STATUS: deployed REVISION: 1 TEST SUITE: None
確認 Karpenter 正在執行:
kubectl get pods -n kube-system -l app.kubernetes.io/name=karpenter
預期的輸出應如下所示:
NAME READY STATUS RESTARTS AGE karpenter-555895dc-865bc 1/1 Running 0 5m58s karpenter-555895dc-j7tk9 1/1 Running 0 5m58s
4. 設定 Karpenter NodePools
在此步驟中,我們會設定互斥 CPU 和 GPU Karpenter NodePoolslimits
欄位會限制每個 NodePool 在所有佈建節點中可以使用的資源 (例如 CPU、記憶體、GPUs) 總數上限,如果超過這些限制,則可防止其他節點佈建。雖然 NodePools 支援廣泛的執行個體類別 (例如 c
、g
),但指定特定執行個體類型
設定 GPU NodePool
在此 NodePool 中,我們會設定資源限制來管理具有 GPU 功能的節點佈建。這些限制旨在限制集區中所有節點的總資源,總共允許最多 10 個執行個體。每個執行個體可以是 g5.xlarge (4 個 vCPUs、16 GiB 記憶體、1 個 GPU) 或 g5.2xlarge (8 個 vCPUs、32 GiB 記憶體、1 個 GPU),只要總 vCPUs 不超過 80 個、總記憶體不超過 320GiB,且總 GPUs 不超過 10 個。例如,集區可以佈建 10 g5.2xlarge 執行個體 (80 個 vCPUs、320 GiB、10 GPUs) 或 10 g5.xlarge 執行個體 (40 個 vCPUs、160 GiB、10 GPUs),或混合 5 g5.xlarge 和 5 g5.2xlarge (60 個 vCPUs、240 GiB、10 GPUs),確保根據工作負載需求的彈性,同時遵守資源限制。
此外,我們指定 Bottlerocket AMI 的 Nvidia 變體 ID。最後,我們將中斷政策consolidateAfter: 30m
) 後移除空節點,並將節點生命週期上限設定為 30 天 (expireAfter: 720h
),以最佳化成本並維護 GPU 密集型任務的節點運作狀態。若要進一步了解,請參閱針對中斷敏感工作負載停用 Karpenter 合併,以及使用 ttlSecondsAfterFinished 自動清除 Kubernetes 任務。
cat <<EOF | envsubst | kubectl apply -f - apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: gpu-a10g-inference-g5 spec: template: metadata: labels: role: gpu-worker gpu-type: nvidia-a10g spec: requirements: - key: node.kubernetes.io/instance-type operator: In values: ["g5.xlarge", "g5.2xlarge"] - key: "karpenter.sh/capacity-type" operator: In values: ["on-demand"] taints: - key: nvidia.com/gpu value: "true" effect: NoSchedule nodeClassRef: name: gpu-a10g-inference-ec2 group: karpenter.k8s.aws kind: EC2NodeClass expireAfter: 720h limits: cpu: "80" memory: "320Gi" nvidia.com/gpu: "10" disruption: consolidationPolicy: WhenEmpty consolidateAfter: 30m --- apiVersion: karpenter.k8s.aws/v1 kind: EC2NodeClass metadata: name: gpu-a10g-inference-ec2 spec: amiFamily: Bottlerocket amiSelectorTerms: - id: ${NVIDIA_BOTTLEROCKET_AMI} role: "KarpenterNodeRole-${EKS_CLUSTER_NAME}" subnetSelectorTerms: - tags: karpenter.sh/discovery: "${EKS_CLUSTER_NAME}" securityGroupSelectorTerms: - tags: karpenter.sh/discovery: "${EKS_CLUSTER_NAME}" tags: nvidia.com/gpu: "true" EOF
預期的輸出應如下所示:
nodepool.karpenter.sh/gpu-a10g-inference-g5 created ec2nodeclass.karpenter.k8s.aws/gpu-a10g-inference-ec2 created
確認 NodePool 已建立且運作狀態良好:
kubectl get nodepool gpu-a10g-inference-g5 -o yaml
尋找status.conditions
類似 ValidationSucceeded: True
、 NodeClassReady: True
和 Ready: True
,以確認 NodePool 運作狀態良好。
設定 CPU NodePool
在此 NodePool 中,我們會設定支援約 50 個執行個體的限制,以符合中等 CPU 工作負載 (例如 100-200 個 Pod) 和一般 AWS vCPU 配額 (例如 128-1152)。這些限制的計算假設 NodePool 應擴展到 50 m7.xlarge 執行個體:CPU (每個執行個體 4 個 vCPUs × 50 個執行個體 = 200 個 vCPUs) 和記憶體 (每個執行個體 16 GiB × 50 個執行個體 = 800 GiB)。這些限制旨在限制集區中所有節點的總資源,允許最多 50 m7g.xlarge 執行個體 (每個執行個體都有 4 個 vCPUs和 16 GiB 記憶體),只要總 vCPUs 不超過 200 且總記憶體不超過 800GiB。
此外,我們會指定 Bottlerocket AMI 標準變體的 ID。最後,我們將中斷政策consolidateAfter: 60m
),並將節點生命週期上限設定為 30 天 (expireAfter: 720h
),以最佳化成本並維護 GPU 密集型任務的節點運作狀態。若要進一步了解,請參閱針對中斷敏感工作負載停用 Karpenter 合併,以及使用 ttlSecondsAfterFinished 自動清除 Kubernetes 任務。
cat <<EOF | envsubst | kubectl apply -f - apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: cpu-inference-m7gxlarge spec: template: metadata: labels: role: cpu-worker spec: requirements: - key: node.kubernetes.io/instance-type operator: In values: ["m7g.xlarge"] - key: karpenter.sh/capacity-type operator: In values: ["on-demand"] taints: - key: role value: cpu-intensive effect: NoSchedule nodeClassRef: name: cpu-inference-m7gxlarge-ec2 group: karpenter.k8s.aws kind: EC2NodeClass expireAfter: 720h limits: cpu: "200" memory: "800Gi" disruption: consolidationPolicy: WhenEmpty consolidateAfter: 60m --- apiVersion: karpenter.k8s.aws/v1 kind: EC2NodeClass metadata: name: cpu-inference-m7gxlarge-ec2 spec: amiFamily: Bottlerocket amiSelectorTerms: - id: ${STANDARD_BOTTLEROCKET_AMI} role: "KarpenterNodeRole-${EKS_CLUSTER_NAME}" subnetSelectorTerms: - tags: karpenter.sh/discovery: "${EKS_CLUSTER_NAME}" securityGroupSelectorTerms: - tags: karpenter.sh/discovery: "${EKS_CLUSTER_NAME}" EOF
預期的輸出應如下所示:
nodepool.karpenter.sh/cpu-inference-m7gxlarge created ec2nodeclass.karpenter.k8s.aws/cpu-inference-m7gxlarge-ec2 created
確認 NodePool 已建立且運作狀態良好:
kubectl get nodepool cpu-inference-m7gxlarge -o yaml
尋找status.conditions
類似 ValidationSucceeded: True
、 NodeClassReady: True
和 Ready: True
,以確認 NodePool 運作狀態良好。
5. 部署 GPU Pod 以公開 GPU
您需要 Nvidia 裝置外掛程式,才能讓 Kubernetes 向 Kubernetes 叢集公開 GPU 裝置。一般而言,您需要將外掛程式部署為 DaemonSet;不過, Bottlerocket AMI 會將外掛程式預先安裝為 AMI 的一部分。這表示使用 Bottlerocket AMIs 時,不需要部署 Nvidia 裝置外掛程式 DaemonSet。若要進一步了解,請參閱 Kubernetes 裝置外掛程式以公開 GPUs。
部署範例 Pod
Karpenter 可動態運作:當工作負載 (Pod) 請求 GPU 資源時,它會佈建 GPU 節點。若要驗證 Pod 能夠請求和使用 GPUs,請部署 Pod 以其限制請求nvidia.com/gpu
資源 (例如 nvidia.com/gpu: 1
)。若要進一步了解這些標籤,請參閱使用 Well-Known 標籤以 GPU 需求排程工作負載。
cat <<EOF | envsubst | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: gpu-nvidia-smi spec: restartPolicy: OnFailure tolerations: - key: "nvidia.com/gpu" operator: "Exists" effect: "NoSchedule" nodeSelector: role: gpu-worker # Matches GPU NodePool's label containers: - name: cuda-container image: nvidia/cuda:12.9.1-base-ubuntu20.04 command: ["nvidia-smi"] resources: limits: nvidia.com/gpu: 1 requests: nvidia.com/gpu: 1 EOF
預期的輸出應如下所示:
pod/gpu-ndivia-smi created
給它一分鐘,然後檢查 Pod 是否具有「待定」、「ContainerCreating」、「執行中」以及「已完成」狀態:
kubectl get pod gpu-nvidia-smi -w
驗證 Pod 的節點是否屬於 GPU NodePool:
kubectl get node $(kubectl get pod gpu-nvidia-smi -o jsonpath='{.spec.nodeName}') -o custom-columns="Name:.metadata.name,Nodepool:.metadata.labels.karpenter\.sh/nodepool"
預期的輸出應如下所示:
Name Nodepool ip-192-168-83-245.ec2.internal gpu-a10g-inference-g5
檢查 Pod 的日誌:
kubectl logs gpu-nvidia-smi
預期的輸出應如下所示:
Thu Jul 17 04:31:33 2025 +---------------------------------------------------------------------------------------+ | NVIDIA-SMI 570.148.08 Driver Version: 570.148.08 CUDA Version: 12.9 | |-----------------------------------------+----------------------+----------------------+ | GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. | | | | MIG M. | |=========================================+======================+======================| | 0 NVIDIA A10G On | 00000000:00:1E.0 Off | 0 | | 0% 30C P8 9W / 300W | 0MiB / 23028MiB | 0% Default | | | | N/A | +---------------------------------------------------------------------------------------+ +---------------------------------------------------------------------------------------+ | Processes: | | GPU GI CI PID Type Process name GPU Memory | | ID ID Usage | |=======================================================================================| | No running processes found | +---------------------------------------------------------------------------------------+
6. (選用) 準備和上傳模型成品以進行部署
在此步驟中,您將部署模型服務以進行即時影像分類,從將模型權重上傳至 Amazon S3 儲存貯體開始。為了示範,我們使用 NVIDIA GPUNet 的開放原始碼 GPUNet-0
設定您的環境
若要下載 GPUNet-0 模型權重 在此步驟中,您需要存取安裝在本機電腦上的 NVIDIA NGC 目錄和 Docker
-
註冊免費的 NGC 帳戶
,並從 NGC 儀表板產生 API 金鑰 (使用者圖示 > 設定 > 產生 API 金鑰 > 產生個人金鑰 > NGC 目錄)。 -
下載並安裝 NGC CLI
(Linux/macOS/Windows),並使用 設定 CLI ngc config set
。出現提示時輸入您的 API 金鑰;將 org 設定為nvidia
,然後按 Enter 接受其他人的預設值。如果成功,您應該會看到類似:Successfully saved NGC configuration to /Users/your-username/.ngc/config
。
驗證服務帳戶許可
開始之前,請檢查 Kubernetes 服務帳戶許可:
kubectl get serviceaccount s3-csi-driver-sa -n kube-system -o yaml
在叢集建立期間,我們將 S3CSIDriverPolicy 連接至 IAM 角色,並註釋服務帳戶 ("s3-csi-driver-sa")。掛載點 S3 CSI 驅動程式 Pod 會在與 S3 互動時繼承 IAM 角色的許可。預期的輸出應如下所示:
apiVersion: v1 kind: ServiceAccount metadata: annotations: eks.amazonaws.com/role-arn: arn:aws:iam::143095308808:role/eksctl-eks-rt-inference-us-east-1-addon-aws-m-Role1-fpXXjRYdKN8r creationTimestamp: "2025-07-17T03:55:29Z" labels: app.kubernetes.io/component: csi-driver app.kubernetes.io/instance: aws-mountpoint-s3-csi-driver app.kubernetes.io/managed-by: EKS app.kubernetes.io/name: aws-mountpoint-s3-csi-driver name: s3-csi-driver-sa namespace: kube-system resourceVersion: "2278" uid: 50b36272-6716-4c68-bdc3-c4054df1177c
新增容錯
S3 CSI 驅動程式會在所有節點上以 DaemonSet 身分執行。Pod 在這些節點上使用 CSI 驅動程式來掛載 S3 磁碟區。若要允許它在具有污點的 GPU 節點上排程,請將容錯新增至 DaemonSet:
kubectl patch daemonset s3-csi-node -n kube-system --type='json' -p='[{"op": "add", "path": "/spec/template/spec/tolerations/-", "value": {"key": "nvidia.com/gpu", "operator": "Exists", "effect": "NoSchedule"}}]'
預期的輸出應如下所示:
daemonset.apps/s3-csi-node patched
將模型權重上傳至 S3
在此步驟中,您將建立 Amazon S3 儲存貯體、從 NVIDIA GPU 雲端 (NGC) 下載 GPUNet-0 模型權重,並將其上傳至儲存貯體。我們的應用程式將在執行時間存取這些權重以進行推論。
建立 Amazon S3 儲存貯體:
aws s3 mb s3://${S3_BUCKET_NAME} --region ${AWS_REGION}
啟用儲存貯體的 S3 版本控制,以防止意外刪除和覆寫造成立即和永久的資料遺失:
aws s3api put-bucket-versioning --bucket ${S3_BUCKET_NAME} --versioning-configuration Status=Enabled
將生命週期規則套用至儲存貯體,以在物件版本變成非最新版本的 14 天後移除覆寫或刪除的物件版本、移除過期的刪除標記,並在 7 天後移除不完整的分段上傳。若要進一步了解,請參閱 S3 生命週期組態的範例。
aws s3api put-bucket-lifecycle-configuration --bucket $S3_BUCKET_NAME --lifecycle-configuration '{"Rules":[{"ID":"LifecycleRule","Status":"Enabled","Filter":{},"Expiration":{"ExpiredObjectDeleteMarker":true},"NoncurrentVersionExpiration":{"NoncurrentDays":14},"AbortIncompleteMultipartUpload":{"DaysAfterInitiation":7}}]}'
從 NGC 下載 GPUNet-0 模型權重。例如,在 macOS 上:
ngc registry model download-version nvidia/dle/gpunet_0_pyt_ckpt:21.12.0_amp --dest ~/downloads
注意
您可能需要為您的作業系統調整此下載命令。若要讓此命令在 Linux 系統上運作,您可能需要建立 目錄做為命令的一部分 (例如 mkdir ~/downloads
)。
預期的輸出應如下所示:
{ "download_end": "2025-07-18 08:22:39", "download_start": "2025-07-18 08:22:33", "download_time": "6s", "files_downloaded": 1, "local_path": "/Users/your-username/downloads/gpunet_0_pyt_ckpt_v21.12.0_amp", "size_downloaded": "181.85 MB", "status": "Completed", "transfer_id": "gpunet_0_pyt_ckpt[version=21.12.0_amp]" }
重新命名檢查點檔案以符合稍後步驟中應用程式程式碼中預期的命名 (不需要擷取,因為它是包含模型狀態字典的標準 PyTorch *.pth.tar 檢查點):
mv ~/downloads/gpunet_0_pyt_ckpt_v21.12.0_amp/0.65ms.pth.tar gpunet-0.pth
在 CLI AWS 中啟用AWS 通用執行期
aws configure set s3.preferred_transfer_client crt
將模型權重上傳至 S3 儲存貯體:
aws s3 cp gpunet-0.pth s3://${S3_BUCKET_NAME}/gpunet-0.pth
預期的輸出應如下所示:
upload: ./gpunet-0.pth to s3://eks-rt-inference-models-us-east-1-1752722786/gpunet-0.pth
建立模型服務
在此步驟中,您將使用 GPUNet-0 視覺模型為 GPU 加速影像分類設定 FastAPI Web 應用程式。應用程式會在執行時間從 Amazon S3 下載模型權重、從 NVIDIA 的儲存庫擷取模型架構以進行快取,並透過 HTTP 下載 ImageNet 類別標籤。應用程式包含映像預先處理轉換並公開兩個端點:用於狀態檢查的根 GET 和接受映像 URL 的 POST /predict
端點。
我們使用 FastAPI 搭配 PyTorch 為模型提供服務,在容器化設定中於執行時間從 Amazon S3 載入權重,以進行快速原型設計和 Kubernetes 部署。如需最佳化批次處理或高輸送量引擎等其他方法,請參閱服務 ML 模型。
建立應用程式
為您的應用程式檔案建立目錄,例如 model-testing
,然後將目錄變更為其中,並將下列程式碼新增至名為 的新檔案app.py
:
import os import torch import json import requests from fastapi import FastAPI, HTTPException from PIL import Image from io import BytesIO, StringIO import torchvision.transforms as transforms from torch.nn.functional import softmax import warnings from contextlib import redirect_stdout, redirect_stderr import argparse import boto3 app = FastAPI() # Suppress specific warnings from the model code (quantization is optional and unused here) warnings.simplefilter("ignore", UserWarning) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # Load model code from cache (if present) # Use backed cache directory torch.hub.set_dir('/cache/torch/hub') # Allowlist for secure deserialization (handles potential issues in older checkpoints) torch.serialization.add_safe_globals([argparse.Namespace]) # Load the model architecture only on container startup (changed to pretrained=False) # Precision (FP32 for full accuracy, could be 'fp16' for speed on Ampere+ GPUs) with redirect_stdout(StringIO()), redirect_stderr(StringIO()): gpunet = torch.hub.load('NVIDIA/DeepLearningExamples:torchhub', 'nvidia_gpunet', pretrained=False, model_type='GPUNet-0', model_math='fp32') # Download weights from S3 if not present, then load them model_path = os.getenv('MODEL_PATH', '/cache/torch/hub/checkpoints/gpunet-0.pth') os.makedirs(os.path.dirname(model_path), exist_ok=True) # Ensure checkpoints dir exists if not os.path.exists(model_path): s3 = boto3.client('s3') s3.download_file(os.getenv('S3_BUCKET_NAME'), 'gpunet-0.pth', model_path) checkpoint = torch.load(model_path, map_location=device, weights_only=True) gpunet.load_state_dict(checkpoint['state_dict']) # Move to GPU/CPU gpunet.to(device) gpunet.eval() # Preprocessing preprocess = transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), ]) # Load ImageNet labels labels_url = "https://s3.amazonaws.com/deep-learning-models/image-models/imagenet_class_index.json" response = requests.get(labels_url) json_data = json.loads(response.text) labels = [json_data[str(i)][1].replace('_', ' ') for i in range(1000)] # Required, FastAPI root @app.get("/") async def hello(): return {"status": "hello"} # Serve model requests @app.post("/predict") async def predict(image_url: str): try: response = requests.get(image_url) response.raise_for_status() img = Image.open(BytesIO(response.content)).convert("RGB") input_tensor = preprocess(img).unsqueeze(0).to(device) with torch.no_grad(): output = gpunet(input_tensor) probs = softmax(output, dim=1)[0] top5_idx = probs.topk(5).indices.cpu().numpy() top5_probs = probs.topk(5).values.cpu().numpy() results = [{ "label": labels[idx], "probability": float(prob) } for idx, prob in zip(top5_idx, top5_probs)] return {"predictions": results} except Exception as e: raise HTTPException(status_code=400, detail=str(e))
建立 Dockerfile
下列 Dockerfile 會使用 NVIDIA 深度學習範例的 Tensor 核心
我們透過使用僅限執行時間的 PyTorch 基礎、僅安裝具有快取清除的基本套件、預先快取模型程式碼,以及避免容器映像中的「製作」權重,來實現更快的提取和更新。若要進一步了解,請參閱減少容器映像大小。
在與 相同的目錄中app.py
,建立 Dockerfile
:
FROM pytorch/pytorch:2.4.0-cuda12.4-cudnn9-runtime # Install required system packages required for git cloning RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/* # Install application dependencies RUN pip install --no-cache-dir fastapi uvicorn requests pillow boto3 timm==0.5.4 # Pre-cache the GPUNet code from Torch Hub (without weights) # Clone the repository containing the GPUNet code RUN mkdir -p /cache/torch/hub && \ cd /cache/torch/hub && \ git clone --branch torchhub --depth 1 https://github.com/NVIDIA/DeepLearningExamples NVIDIA_DeepLearningExamples_torchhub COPY app.py /app/app.py WORKDIR /app CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "80"]
測試應用程式。
從與 app.py
和 相同的目錄Dockerfile
,為推論應用程式建置容器映像,以 AMD64 架構為目標:
docker build --platform linux/amd64 -t gpunet-inference-app .
為您的 AWS 登入資料設定環境變數,並選擇性地設定 AWS 工作階段字符。例如:
export AWS_REGION="us-east-1" export AWS_ACCESS_KEY_ID=ABCEXAMPLESCUJFEIELSMUHHAZ export AWS_SECRET_ACCESS_KEY=123EXAMPLEMZREoQXr8XkiicsOgWDQ5TpUsq0/Z
在本機執行容器,注入 AWS 登入資料做為 S3 存取的環境變數。例如:
docker run --platform linux/amd64 -p 8080:80 \ -e S3_BUCKET_NAME=${S3_BUCKET_NAME} \ -e AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} \ -e AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} \ -e AWS_DEFAULT_REGION=${AWS_REGION} \ gpunet-inference-app
預期的輸出應如下所示:
INFO: Started server process [1] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
在新的終端機視窗中,使用公有映像 URL 做為查詢參數傳送範例 POST 請求,以測試推論端點:
curl -X POST "http://localhost:8080/predict?image_url=http://images.cocodataset.org/test-stuff2017/000000024309.jpg"
預期的輸出應該是具有前 5 個預測的 JSON 回應,與此類似 (實際標籤和機率可能會根據影像和模型精確度而略有不同):
{"predictions":[{"label":"desk","probability":0.28885871171951294},{"label":"laptop","probability":0.24679335951805115},{"label":"notebook","probability":0.08539070934057236},{"label":"library","probability":0.030645888298749924},{"label":"monitor","probability":0.02989606373012066}]}
使用「Ctrl + C」退出應用程式。
將容器推送至 Amazon ECR
在此步驟中,我們會將 GPUNet-0 模型服務的容器映像上傳至 Amazon Elastic Container Registry (ECR),以便在 Amazon EKS 上進行部署。此程序涉及建立新的 ECR 儲存庫來存放映像、使用 ECR 驗證,然後標記容器映像並將其推送至我們的登錄檔。
首先,導覽回您在本指南開頭設定環境變數的目錄。例如:
cd ..
在 Amazon ECR 中建立儲存庫:
aws ecr create-repository --repository-name gpunet-inference-app --region ${AWS_REGION}
登入 Amazon ECR:
aws ecr get-login-password --region ${AWS_REGION} | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com
預期的輸出應如下所示:
Login Succeeded
標記映像:
docker tag gpunet-inference-app:latest ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/gpunet-inference-app:latest
將映像推送到您的 Amazon ECR 儲存庫:
docker push ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/gpunet-inference-app:latest
這個最後一個步驟需要幾分鐘的時間才能完成。
7. (選用) 公開模型服務
在此步驟中,您將使用 AWS Load Balancer控制器 (LBC) 在 Amazon EKS 外部公開即時推論模型服務。這包括設定 LBC、使用掛載點 Amazon S3 S3 的模型權重掛載為持久性磁碟區、部署 GPU 加速應用程式 Pod、建立服務和輸入以佈建 Application Load Balancer (ALB),以及測試端點。
首先,驗證 AWS LBC 的 Pod Identity 關聯,確認服務帳戶已正確連結至所需的 IAM 角色:
eksctl get podidentityassociation --cluster ${EKS_CLUSTER_NAME} --namespace kube-system --service-account-name aws-load-balancer-controller
預期的輸出應如下所示:
ASSOCIATION ARN NAMESPACE SERVICE ACCOUNT NAME IAM ROLE ARN OWNER ARN arn:aws:eks:us-east-1:143095308808:podidentityassociation/eks-rt-inference-us-east-1/a-buavluu2wp1jropya kube-system aws-load-balancer-controller arn:aws:iam::143095308808:role/AmazonEKSLoadBalancerControllerRole
標記您的叢集安全群組
AWS Load Balancer控制器僅支援具有標籤索引鍵的單一安全群組,karpenter.sh/discovery: "${EKS_CLUSTER_NAME}"
用於 Karpenter 的安全群組選擇。使用 eksctl 建立叢集時,預設叢集安全群組 (具有 "kubernetes.io/cluster/<cluster-name>: owned"
標籤) 不會自動加上karpenter.sh/discovery
標籤。此標籤對於 Karpenter 探索此安全群組並將其連接到其佈建的節點至關重要。連接此安全群組可確保與 AWS Load Balancer控制器 (LBC) 的相容性,使其能夠自動管理透過輸入公開之服務的傳入流量規則,例如這些步驟中的模型服務。
匯出叢集的 VPC ID:
CLUSTER_VPC_ID="$(aws eks describe-cluster --name ${EKS_CLUSTER_NAME} --query cluster.resourcesVpcConfig.vpcId --output text)"
匯出叢集的預設安全群組:
CLUSTER_SG_ID="$(aws ec2 describe-security-groups --filters Name=vpc-id,Values=$CLUSTER_VPC_ID Name=tag-key,Values=kubernetes.io/cluster/${EKS_CLUSTER_NAME} --query 'SecurityGroups[].[GroupId]' --output text)"
將karpenter.sh/discovery
標籤新增至預設叢集安全群組。這將允許我們的 CPU 和 GPU EC2NodeClass 選擇器使用它:
aws ec2 create-tags --resources ${CLUSTER_SG_ID} --tags Key=karpenter.sh/discovery,Value=${EKS_CLUSTER_NAME}
確認已新增標籤:
aws ec2 describe-security-groups --group-ids ${CLUSTER_SG_ID} --query "SecurityGroups[].Tags"
在結果中,您應該會看到具有 標籤和叢集名稱的以下內容。例如:
{ "Key": "karpenter.sh/discovery", "Value": "eks-rt-inference-us-east-1" }
設定 AWS Load Balancer控制器 (LBC)
AWS LBC 對於在 Amazon EKS 上管理 AI/ML 工作負載的輸入流量至關重要,可確保存取推論端點或資料處理管道。透過與 AWS Application Load Balancer (ALB) 和 Network Load Balancer (NLB) 整合,LBC 可將流量動態路由到容器化應用程式,例如執行大型語言模型、電腦視覺模型或即時推論服務的應用程式。由於我們已在叢集建立期間建立服務帳戶和 Pod Identity Association,因此我們serviceAccount.name
將 設定為符合叢集組態 () 中定義的項目aws-load-balancer-controller
。
新增 AWS擁有的 eks-charts Helm Chart 儲存庫:
helm repo add eks https://aws.github.io/eks-charts
使用最新的圖表重新整理本機 Helm 儲存庫:
helm repo update eks
使用 Helm 部署 AWS LBC,指定 EKS 叢集名稱並參考預先建立的服務帳戶:
helm install aws-load-balancer-controller eks/aws-load-balancer-controller \ -n kube-system \ --set clusterName=${EKS_CLUSTER_NAME} \ --set serviceAccount.create=false \ --set serviceAccount.name=aws-load-balancer-controller
預期的輸出應如下所示:
NAME: aws-load-balancer-controller LAST DEPLOYED: Wed Jul 9 15:03:31 2025 NAMESPACE: kube-system STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: AWS Load Balancer controller installed!
在持久性磁碟區中掛載模型
在此步驟中,您將使用 Amazon S3 CSI 驅動程式掛載點支援的PersistentVolume (PV),從 Amazon S3 儲存貯體掛載模型權重。這可讓 Kubernetes Pod 以本機檔案的形式存取 S3 物件,消除暫時性 Pod 儲存或初始化容器的資源密集下載,非常適合大型、多 GB 模型權重。
PV 掛載整個儲存貯體根目錄 ( 中未指定路徑volumeAttributes
),支援多個 Pod 並行唯讀存取,並在容器內公開模型權重 (/models/gpunet-0.pth
) 等檔案以進行推論。這可確保應用程式 (app.py
) 中的備用「下載」不會觸發,因為檔案是透過掛載存在。透過從容器映像解耦模型,這可啟用共用存取和獨立模型版本更新,而無需重建映像。
建立PersistentVolume(PV)
建立 PersistentVolume (PV) 資源來掛載包含模型權重的 S3 儲存貯體,為多個 Pod 啟用唯讀存取,而無需在執行時間下載檔案:
cat <<EOF | envsubst | kubectl apply -f - apiVersion: v1 kind: PersistentVolume metadata: name: s3-model-pv spec: capacity: storage: 5Gi # Ignored by the driver; can be any value accessModes: - ReadOnlyMany # Read only persistentVolumeReclaimPolicy: Retain storageClassName: "" # Required for static provisioning claimRef: namespace: default # Adjust if you prefer a different namespace name: s3-model-pvc mountOptions: - allow-other # Enables multi-user access (useful for non-root pods) - region ${AWS_REGION} # Optional, include if your bucket is in a different region than the cluster csi: driver: s3.csi.aws.com volumeHandle: gpunet-model-volume # Must be unique across all PVs volumeAttributes: bucketName: ${S3_BUCKET_NAME} EOF
建立 PersistentVolumeClaim (PVC)
建立 PersistentVolumeClaim (PVC) 以繫結至 PV,請求對掛載的 S3 模型資料的唯讀存取權:
cat <<EOF | envsubst | kubectl apply -f - apiVersion: v1 kind: PersistentVolumeClaim metadata: name: s3-model-pvc spec: accessModes: - ReadOnlyMany storageClassName: "" # Required for static provisioning resources: requests: storage: 5Gi # Ignored, match PV capacity volumeName: s3-model-pv # Bind to the PV created above EOF
部署應用程式
將推論應用程式部署為 Kubernetes 部署、掛載 S3-backed持久性磁碟區以進行模型存取、套用 GPU 節點選擇器和容錯,以及設定模型路徑的環境變數。此部署會設定模型路徑 (env var of "/models/gpunet-0.pth"
),因此我們的應用程式 (在 中app.py
) 預設會使用此路徑。在 部署磁碟區掛載於 /models
(唯讀) 時,如果檔案已透過 PVC 存在,則不會觸發模型下載。
cat <<EOF | envsubst | kubectl apply -f - apiVersion: apps/v1 kind: Deployment metadata: name: gpunet-inference-app spec: replicas: 1 selector: matchLabels: app: gpunet-inference-app template: metadata: labels: app: gpunet-inference-app spec: tolerations: - key: "nvidia.com/gpu" operator: "Exists" effect: "NoSchedule" nodeSelector: role: gpu-worker containers: - name: inference image: ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/gpunet-inference-app:latest ports: - containerPort: 80 env: - name: MODEL_PATH value: "/models/gpunet-0.pth" resources: limits: nvidia.com/gpu: 1 requests: nvidia.com/gpu: 1 volumeMounts: - name: model-volume mountPath: /models readOnly: true volumes: - name: model-volume persistentVolumeClaim: claimName: s3-model-pvc EOF
如果 GPU 節點尚未提供,Karpenter 需要幾分鐘的時間來佈建 GPU 節點。確認推論 Pod 處於「執行中」狀態:
kubectl get pods -l app=gpunet-inference-app
預期的輸出應如下所示:
NAME READY STATUS RESTARTS AGE gpunet-inference-app-5d4b6c7f8-abcde 1/1 Running 0 2m
使用輸入和Load Balancer公開服務
建立 ClusterIP 服務以針對應用程式的連接埠,在 EKS 叢集內部公開推論部署:
cat <<EOF | envsubst | kubectl apply -f - apiVersion: v1 kind: Service metadata: name: gpunet-model-service spec: type: ClusterIP ports: - port: 80 targetPort: 80 selector: app: gpunet-inference-app EOF
建立輸入資源,透過 AWS LBC 佈建面向網際網路的 Application Load Balancer (ALB),將外部流量路由到推論服務:
cat <<EOF | envsubst | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: gpunet-model-ingress annotations: alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: ip spec: ingressClassName: alb rules: - http: paths: - path: / pathType: Prefix backend: service: name: gpunet-model-service port: number: 80 EOF
給它幾分鐘的時間,讓 Application Load Balancer (ALB) 完成佈建。監控傳入資源狀態,以確認已佈建 ALB:
kubectl get ingress gpunet-model-ingress
預期的輸出應如下所示 (填入 ADDRESS 欄位):
NAME CLASS HOSTS ADDRESS PORTS AGE gpunet-model-ingress alb * k8s-default-gpunetmo-183de3f819-516310036.us-east-1.elb.amazonaws.com 80 6m58s
從輸入狀態擷取和匯出 ALB 主機名稱,以供後續測試使用:
export ALB_HOSTNAME=$(kubectl get ingress gpunet-model-ingress -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
測試模型服務
透過使用範例映像 URL (例如,從 COCO 資料集) 傳送 POST 請求,模擬即時預測來驗證公開的推論端點:
curl -X POST "http://${ALB_HOSTNAME}/predict?image_url=http://images.cocodataset.org/test-stuff2017/000000024309.jpg"
預期的輸出應該是具有前 5 個預測的 JSON 回應,與此類似 (實際標籤和機率可能會根據影像和模型精確度而略有不同):
{"predictions":[{"label":"desk","probability":0.2888975441455841},{"label":"laptop","probability":0.2464350312948227},{"label":"notebook","probability":0.08554483205080032},{"label":"library","probability":0.030612602829933167},{"label":"monitor","probability":0.029896672815084457}]}
您可以選擇性地在新的 POST 請求中繼續測試其他映像。例如:
http://images.cocodataset.org/test-stuff2017/000000024309.jpg http://images.cocodataset.org/test-stuff2017/000000028117.jpg http://images.cocodataset.org/test-stuff2017/000000006149.jpg http://images.cocodataset.org/test-stuff2017/000000004954.jpg
結論
在本指南中,您會設定針對 GPU 加速即時推論工作負載最佳化的 Amazon EKS 叢集。您已使用 G5 EC2 執行個體
清除
若要避免未來產生費用,您需要手動刪除相關聯的 CloudFormation 堆疊,以刪除本指南期間建立的所有資源,包括 VPC 網路。
使用 --wait
旗標搭配 eksctl 刪除 CloudFormation 堆疊:
eksctl delete cluster --region ${AWS_REGION} --name ${EKS_CLUSTER_NAME} --wait
完成後,您應該會看到下列回應輸出:
2025-07-29 13:03:55 [✔] all cluster resources were deleted
使用 Amazon S3 主控台刪除本指南期間建立的 Amazon S3