안녕하세요?
오늘은 AEWS 11주차 과제로 생성형 AI를 vLLM으로 배포해서 사용하는 테스트를 해볼 예정입니다.
이 과정에서 vLLM은 EKS 클러스터에 배포되고 mistral 7B 모델을 사용하며, AWS 의 고성능 파일 시스템인 FSx를 사용할겁니다.
또한 추론은 AWS Inferentia 를 통해 수행합니다.
이 가속기는 딥 러닝(DL) 및 생성형 AI 추론 애플리케이션을 위해 Amazon EC2에서 가장 낮은 비용으로 고성능을 제공하도록 설계되었습니다.
개요
모든 것이 배포 완료되고 난 이후의 구조는 아래와 같이 됩니다.
S3에 보관된 Mistral 7B 모델을 Provisioning된 FSx PVC로 vLLM Pod에 마운트하여 사용합니다.
OpenWebUI 프론트는 vLLM 에서 제공하는 OpenAI 호환 엔드포인트를 사용하도록 설계되어 있습니다.
이를 통해 채팅 기반 인터페이스를 통해 LLM 모델과 상호 작용할 수 있습니다.
결과는 아래와 같이 됩니다.
OpenWebUI 프론트에서 vLLM으로 배포된 Mistral-7B 모델을 사용할 수 있습니다.
vLLM에 대해서
vLLM은 사용하기 쉬운 오픈 소스 LLM 추론 및 서비스를 위한 라이브러리입니다.
이 라이브러리는 Mistral-7B-Instruct와 같은 LLM 모델을 배포하여 텍스트 생성 추론을 제공할 수 있는 프레임워크를 제공합니다. vLLM은 OpenAI API와 호환되는 API를 제공하므로 LLM 애플리케이션을 쉽게 통합할 수 있습니다.
아래와 같은 특징이 있습니다.
vLLM is fast with:
- State-of-the-art serving throughput
- Efficient management of attention key and value memory with PagedAttention
- Continuous batching of incoming request
- Fast model execution with CUDA/HIP graph
vLLM is flexible and easy to use with:
- Seamless integration with popular HuggingFace models
- OpenAI-compatible API server
- Prefix caching support
- Supports chipsets such as: AWS Neuron, NVIDIA GPUs and others,
환경 준비
이 모든 작업은 사전에 준비된 AWS 워크샵에서 진행합니다.
OpenAI 호환 엔드포인트로 텍스트 생성 추론 기능을 제공하기 위해 Amazon Elastic Kubernetes Service(EKS)에서 vLLM 프레임워크를 사용하여 Mistral-7B-Instruct 모델을 배포할 것입니다.
그리고 Karpenter를 사용해 AWS inferentia2 EC2 노드(생성형 AI용으로 설계된 추론용 노드)위의 컨테이너 이미지에서 vLLM 파드를 실행할 것입니다.
Cloud9를 통해 AWS 랩 환경에 연결
AWS의 Cloud9 IDE 터미널해서 작업합니다.
이렇게 Cloud9 환경이 준비되어있고
접근하면 아래와 같은 모습이 됩니다.
IAM 역할 검증
AWS IAM 역할을 사용하도록 하겠습니다.
aws cloud9 update-environment --environment-id ${C9_PID} --managed-credentials-action DISABLE
rm -vf ${HOME}/.aws/credentials
aws sts get-caller-identity
{
"UserId": "AROA5IGSAA4RHBHZUXYBD:i-0c0021218f19e5fdf",
"Account": "910973470498",
"Arn": "arn:aws:sts::910973470498:assumed-role/genaifsxworkshoponeks-C9Role-mZMtBmNGZEuc/i-0c0021218f19e5fdf"
}
WSParticipantRole:~/environment $
랩 지역 이름을 설정 합니다.
TOKEN=`curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`
export AWS_REGION=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/placement/region)
Amazon EKS 클러스터 변수를 설정합니다.
export CLUSTER_NAME=eksworkshop
지역 및 클러스터 이름이 올바르게 설정되었는지 확인합니다.
echo $AWS_REGION
us-west-2
echo $CLUSTER_NAME
eksworkshop
kube-config 파일을 업데이트
쿠버네티스 명령을 실행하기 위해 클러스터에 액세스할 수 있는 적절한 자격 증명으로 kube-config 파일을 업데이트해야 합니다.
aws eks update-kubeconfig --name $CLUSTER_NAME --region $AWS_REGION
Added new context arn:aws:eks:us-west-2:910973470498:cluster/eksworkshop to /home/ec2-user/.kube/config
kubectl 명령이 잘 작동하는지 확인해봅니다.
kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-10-0-127-249.us-west-2.compute.internal Ready <none> 46h v1.30.9-eks-5d632ec
ip-10-0-71-196.us-west-2.compute.internal Ready <none> 46h v1.30.9-eks-5d632ec
노드들을 확인할 수 있습니다.
Karpenter 살펴보기
Karpenter를 사용해서 노드를 씁니다.
Karpenter 구성은 아래와 같습니다.
kubectl -n karpenter get deploy/karpenter -o yaml
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "1"
meta.helm.sh/release-name: karpenter
meta.helm.sh/release-namespace: karpenter
generation: 1
labels:
app.kubernetes.io/instance: karpenter
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: karpenter
app.kubernetes.io/version: 1.0.1
helm.sh/chart: karpenter-1.0.1
name: karpenter
namespace: karpenter
spec:
progressDeadlineSeconds: 600
replicas: 2
revisionHistoryLimit: 10
selector:
matchLabels:
app.kubernetes.io/instance: karpenter
app.kubernetes.io/name: karpenter
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
app.kubernetes.io/instance: karpenter
app.kubernetes.io/name: karpenter
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: karpenter.sh/nodepool
operator: DoesNotExist
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app.kubernetes.io/instance: karpenter
app.kubernetes.io/name: karpenter
topologyKey: kubernetes.io/hostname
...
kubernetes.io/os: linux
priorityClassName: system-cluster-critical
restartPolicy: Always
schedulerName: default-scheduler
securityContext:
fsGroup: 65532
serviceAccount: karpenter
serviceAccountName: karpenter
terminationGracePeriodSeconds: 30
tolerations:
- key: CriticalAddonsOnly
operator: Exists
topologySpreadConstraints:
- labelSelector:
matchLabels:
app.kubernetes.io/instance: karpenter
app.kubernetes.io/name: karpenter
maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
Karpenter 내용을 확인해보면 다음과 같은 환경 변수가 설정되어 있는 것을 볼 수 있습니다.
- CLUSTER_ENDPOINT - 새 노드가 연결할 외부 쿠버네티스 클러스터 엔드포인트입니다. 엔드포인트가 지정되지 않으면 노드는 DescribeCluster API를 사용하여 클러스터 엔드포인트를 검색합니다.
- INTERRUPTION_QUEUE - EKS Terraform 블루프린트의 일부로 생성된 SQS 대기열의 엔드포인트입니다. 이 SQS 대기열은 Spot 중단 알림 및 AWS 상태 이벤트를 저장하는 데 사용됩니다.
Karpenter가 Amazon EKS 환경에서 실행 중인지 확인해봅니다
kubectl get pods --namespace karpenter
NAME READY STATUS RESTARTS AGE
karpenter-68c76c9cb6-dl6hh 1/1 Running 0 46h
karpenter-68c76c9cb6-zlxcc 1/1 Running 0 46h
스토리지 구성 - Amazon FSx for Lustre에 모델 데이터 호스팅
이 워크샵에서는 Mistral-7B-Instruct 모델을 Amazon S3 버킷에 저장합니다.
이 버킷은 Amazon FSx for Lustre 파일 시스템과 연결되며, vLLM 컨테이너는 Generative AI ChatBot 애플리케이션에 이를 사용합니다.
CSI 드라이버 배포
account-id 환경 변수를 설정합니다.
ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text)
CSI 드라이버가 사용자를 대신하여 AWS API 호출을 수행할 수 있도록 하는 IAM 정책 및 서비스 계정을 만듭니다.
cat << EOF > fsx-csi-driver.json
{
"Version":"2012-10-17",
"Statement":[
{
"Effect":"Allow",
"Action":[
"iam:CreateServiceLinkedRole",
"iam:AttachRolePolicy",
"iam:PutRolePolicy"
],
"Resource":"arn:aws:iam::*:role/aws-service-role/s3.data-source.lustre.fsx.amazonaws.com/*"
},
{
"Action":"iam:CreateServiceLinkedRole",
"Effect":"Allow",
"Resource":"*",
"Condition":{
"StringLike":{
"iam:AWSServiceName":[
"fsx.amazonaws.com"
]
}
}
},
{
"Effect":"Allow",
"Action":[
"s3:ListBucket",
"fsx:CreateFileSystem",
"fsx:DeleteFileSystem",
"fsx:DescribeFileSystems",
"fsx:TagResource"
],
"Resource":[
"*"
]
}
]
}
EOF
아래와 같이 IAM 정책을 만듭니다
aws iam create-policy \
> --policy-name Amazon_FSx_Lustre_CSI_Driver \
> --policy-document file://fsx-csi-driver.json
{
"Policy": {
"PolicyName": "Amazon_FSx_Lustre_CSI_Driver",
"PolicyId": "ANPA5IGSAA4RIUDH2DWR7",
"Arn": "arn:aws:iam::910973470498:policy/Amazon_FSx_Lustre_CSI_Driver",
"Path": "/",
"DefaultVersionId": "v1",
"AttachmentCount": 0,
"PermissionsBoundaryUsageCount": 0,
"IsAttachable": true,
"CreateDate": "2025-04-19T01:18:11+00:00",
"UpdateDate": "2025-04-19T01:18:11+00:00"
}
}
드라이버에 대한 Kubernetes 서비스 계정을 만들고 정책을 서비스 계정에 연결합니다.
eksctl create iamserviceaccount \
> --region $AWS_REGION \
> --name fsx-csi-controller-sa \
> --namespace kube-system \
> --cluster $CLUSTER_NAME \
> --attach-policy-arn arn:aws:iam::$ACCOUNT_ID:policy/Amazon_FSx_Lustre_CSI_Driver \
> --approve
2025-04-19 01:18:54 [ℹ] 1 iamserviceaccount (kube-system/fsx-csi-controller-sa) was included (based on the include/exclude rules)
2025-04-19 01:18:54 [!] serviceaccounts that exist in Kubernetes will be excluded, use --override-existing-serviceaccounts to override
2025-04-19 01:18:54 [ℹ] 1 task: {
2 sequential sub-tasks: {
create IAM role for serviceaccount "kube-system/fsx-csi-controller-sa",
create serviceaccount "kube-system/fsx-csi-controller-sa",
} }2025-04-19 01:18:54 [ℹ] building iamserviceaccount stack "eksctl-eksworkshop-addon-iamserviceaccount-kube-system-fsx-csi-controller-sa"
2025-04-19 01:18:54 [ℹ] deploying stack "eksctl-eksworkshop-addon-iamserviceaccount-kube-system-fsx-csi-controller-sa"
2025-04-19 01:18:54 [ℹ] waiting for CloudFormation stack "eksctl-eksworkshop-addon-iamserviceaccount-kube-system-fsx-csi-controller-sa"
2025-04-19 01:19:24 [ℹ] waiting for CloudFormation stack "eksctl-eksworkshop-addon-iamserviceaccount-kube-system-fsx-csi-controller-sa"
2025-04-19 01:19:24 [ℹ] created serviceaccount "kube-system/fsx-csi-controller-sa"
kubectl get sa -A | grep fsx
kube-system fsx-csi-controller-sa 0 75s
생성된 역할 ARN을 변수에 저장합니다.
export ROLE_ARN=$(aws cloudformation describe-stacks --stack-name "eksctl-${CLUSTER_NAME}-addon-iamserviceaccount-kube-system-fsx-csi-controller-sa" --query "Stacks[0].Outputs[0].OutputValue" --region $AWS_REGION --output text)
echo $ROLE_ARN
arn:aws:iam::910973470498:role/eksctl-eksworkshop-addon-iamserviceaccount-ku-Role1-2XZN7Hw0tnHV
Lustre용 FSx의 CSI 드라이버를 배포합니다.
kubectl apply -k "github.com/kubernetes-sigs/aws-fsx-csi-driver/deploy/kubernetes/overlays/stable/?ref=release-1.2"
# Warning: 'bases' is deprecated. Please use 'resources' instead. Run 'kustomize edit fix' to update your Kustomization automatically.
Warning: resource serviceaccounts/fsx-csi-controller-sa is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
serviceaccount/fsx-csi-controller-sa configured
serviceaccount/fsx-csi-node-sa created
clusterrole.rbac.authorization.k8s.io/fsx-csi-external-provisioner-role created
clusterrole.rbac.authorization.k8s.io/fsx-csi-node-role created
clusterrole.rbac.authorization.k8s.io/fsx-external-resizer-role created
clusterrolebinding.rbac.authorization.k8s.io/fsx-csi-external-provisioner-binding created
clusterrolebinding.rbac.authorization.k8s.io/fsx-csi-node-getter-binding created
clusterrolebinding.rbac.authorization.k8s.io/fsx-csi-resizer-binding created
deployment.apps/fsx-csi-controller created
daemonset.apps/fsx-csi-node created
csidriver.storage.k8s.io/fsx.csi.aws.com created
CSI 드라이버가 성공적으로 설치되었는지 확인해봅니다.
kubectl get pods -n kube-system -l app.kubernetes.io/name=aws-fsx-csi-driver
NAME READY STATUS RESTARTS AGE
fsx-csi-controller-6f4c577bd4-7mhw2 4/4 Running 0 20s
fsx-csi-controller-6f4c577bd4-f55zq 4/4 Running 0 20s
fsx-csi-node-6lftx 3/3 Running 0 20s
fsx-csi-node-8vfcr 3/3 Running 0 20s
서비스 계정에 IAM 역할을 추가합니다.
kubectl annotate serviceaccount -n kube-system fsx-csi-controller-sa \
> eks.amazonaws.com/role-arn=$ROLE_ARN --overwrite=true
serviceaccount/fsx-csi-controller-sa annotated
서비스 계정 내용을 확인하여 성공했는지 확인할 수 있습니다.
kubectl get sa/fsx-csi-controller-sa -n kube-system -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::910973470498:role/eksctl-eksworkshop-addon-iamserviceaccount-ku-Role1-2XZN7Hw0tnHV
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{},"labels":{"app.kubernetes.io/name":"aws-fsx-csi-driver"},"name":"fsx-csi-controller-sa","namespace":"kube-system"}}
creationTimestamp: "2025-04-19T01:19:24Z"
labels:
app.kubernetes.io/managed-by: eksctl
app.kubernetes.io/name: aws-fsx-csi-driver
name: fsx-csi-controller-sa
namespace: kube-system
resourceVersion: "855243"
uid: cf65f214-4962-4f2c-81c0-b2cb0a5a44b7
EKS 클러스터에 Persistent Volume 생성
FSx Lustre 인스턴스 세부 정보로 변수를 채웁니다.
FSXL_VOLUME_ID=$(aws fsx describe-file-systems --query 'FileSystems[].FileSystemId' --output text)
DNS_NAME=$(aws fsx describe-file-systems --query 'FileSystems[].DNSName' --output text)
MOUNT_NAME=$(aws fsx describe-file-systems --query 'FileSystems[].LustreConfiguration.MountName' --output text)
1200GiB FSx for Lustre 인스턴스는 이미 생성되어 있습니다.
따라서 아래 Persistent Volume에서는 해당 1200GiB FSx for Lustre 인스턴스가 'fsx-pv'라는 이름을 사용하여 EKS 클러스터 리소스로 등록되도록 구성합니다.
# fsxL-persistent-volume.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: fsx-pv
spec:
persistentVolumeReclaimPolicy: Retain
capacity:
storage: 1200Gi
volumeMode: Filesystem
accessModes:
- ReadWriteMany
mountOptions:
- flock
csi:
driver: fsx.csi.aws.com
volumeHandle: FSXL_VOLUME_ID
volumeAttributes:
dnsname: DNS_NAME
mountname: MOUNT_NAME
FSXL_VOLUME_ID, DNS_NAME, MOUNT_NAME을 FSx Lustre 인스턴스의 실제 값으로 바꿉니다.
sed -i'' -e "s/FSXL_VOLUME_ID/$FSXL_VOLUME_ID/g" fsxL-persistent-volume.yaml
sed -i'' -e "s/DNS_NAME/$DNS_NAME/g" fsxL-persistent-volume.yaml
sed -i'' -e "s/MOUNT_NAME/$MOUNT_NAME/g" fsxL-persistent-volume.yaml
FSx 인스턴스 세부 정보와 함께 영구 볼륨 정의의 출력을 볼 수 있습니다.
1200GiB FSx for Lustre 파일 시스템이 생성되었으며, 인스턴스 ID와 DNS 이름이 함께 표시됩니다.
cat fsxL-persistent-volume.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: fsx-pv
spec:
persistentVolumeReclaimPolicy: Retain
capacity:
storage: 1200Gi
volumeMode: Filesystem
accessModes:
- ReadWriteMany
mountOptions:
- flock
csi:
driver: fsx.csi.aws.com
volumeHandle: fs-0c2a4fec841bd4d58
volumeAttributes:
dnsname: fs-0c2a4fec841bd4d58.fsx.us-west-2.amazonaws.com
mountname: j42dbb4v
이 PersistentVolume을 EKS 클러스터에 배포해 보겠습니다.
kubectl apply -f fsxL-persistent-volume.yaml
persistentvolume/fsx-pv created
kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
fsx-pv 1200Gi RWX Retain Available <unset> 11s
pvc-d321380b-cd2a-4d34-8028-7ca7d5a0ffbd 50Gi RWO Delete Bound kube-prometheus-stack/data-prometheus-kube-prometheus-stack-prometheus-0 gp3 <unset> 47h
PersistentVolume과 바인딩할 PersistentVolumeClaim을 생성합니다.
아래와 같은 매니페스트를 사용합니다.
# fsxL-claim.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: fsx-lustre-claim
spec:
accessModes:
- ReadWriteMany
storageClassName: ""
resources:
requests:
storage: 1200Gi
volumeName: fsx-pv
이 PersistentVolumeClaim을 EKS 클러스터에 배포합니다.
kubectl apply -f fsxL-claim.yaml
잘 바인딩 되었는지 확인해봅니다.
kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
persistentvolume/fsx-pv 1200Gi RWX Retain Bound default/fsx-lustre-claim <unset> 62s
persistentvolume/pvc-d321380b-cd2a-4d34-8028-7ca7d5a0ffbd 50Gi RWO Delete Bound kube-prometheus-stack/data-prometheus-kube-prometheus-stack-prometheus-0 gp3 <unset> 47h
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/fsx-lustre-claim Pending fsx-pv 0 <unset> 11s
생성형 AI 채팅 애플리케이션 배포
모델 추론을 위해 AWS Inferentia 노드에 vLLM 배포
vLLM을 배포할 AWS Inferentia INF2 Accelerated Compute 노드를 위한 새로운 Karpenter NodePool을 생성합니다.
cat inferentia_nodepool.yaml
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: inferentia
labels:
intent: genai-apps
NodeGroupType: inf2-neuron-karpenter
spec:
template:
spec:
taints:
- key: aws.amazon.com/neuron
value: "true"
effect: "NoSchedule"
requirements:
- key: "karpenter.k8s.aws/instance-family"
operator: In
values: ["inf2"]
- key: "karpenter.k8s.aws/instance-size"
operator: In
values: [ "xlarge", "2xlarge", "8xlarge", "24xlarge", "48xlarge"]
- key: "kubernetes.io/arch"
operator: In
values: ["amd64"]
- key: "karpenter.sh/capacity-type"
operator: In
values: ["spot", "on-demand"]
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: inferentia
limits:
cpu: 1000
memory: 1000Gi
disruption:
consolidationPolicy: WhenEmpty
# expireAfter: 720h # 30 * 24h = 720h
consolidateAfter: 180s
weight: 100
---
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
name: inferentia
spec:
amiFamily: AL2
amiSelectorTerms:
- alias: al2@v20240917
blockDeviceMappings:
- deviceName: /dev/xvda
ebs:
deleteOnTermination: true
volumeSize: 100Gi
volumeType: gp3
role: "Karpenter-eksworkshop"
subnetSelectorTerms:
- tags:
karpenter.sh/discovery: "eksworkshop"
securityGroupSelectorTerms:
- tags:
karpenter.sh/discovery: "eksworkshop"
tags:
intent: apps
managed-by: karpenter
Karpenter NodePool을 배포합니다.
kubectl apply -f inferentia_nodepool.yaml
nodepool.karpenter.sh/inferentia created
ec2nodeclass.karpenter.k8s.aws/inferentia created
NodePool과 EC2NodeClass를 확인합니다.
kubectl get nodepool,ec2nodeclass inferentia
NAME NODECLASS NODES READY AGE
nodepool.karpenter.sh/inferentia inferentia 0 True 27s
NAME READY AGE
ec2nodeclass.karpenter.k8s.aws/inferentia True 27s
Neuron 장치 플러그인 및 스케줄러 설치
AWS Neuron SDK를 사용하여 Mistral-7B 모델을 이미 다운로드하고 컴파일한 상태라서
AWS Inferentia Accelerated Computes 노드에 바로 배포할 수 있습니다.
Neuron 장치 플러그인을 설치합니다.
kubectl apply -f https://raw.githubusercontent.com/aws-neuron/aws-neuron-sdk/master/src/k8/k8s-neuron-device-plugin-rbac.yml
Warning: resource clusterroles/neuron-device-plugin is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
clusterrole.rbac.authorization.k8s.io/neuron-device-plugin configured
Warning: resource serviceaccounts/neuron-device-plugin is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
serviceaccount/neuron-device-plugin configured
Warning: resource clusterrolebindings/neuron-device-plugin is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
clusterrolebinding.rbac.authorization.k8s.io/neuron-device-plugin configured
WSParticipantRole:~/environment/eks/genai $ kubectl apply -f https://raw.githubusercontent.com/aws-neuron/aws-neuron-sdk/master/src/k8/k8s-neuron-device-plugin.yml
daemonset.apps/neuron-device-plugin-daemonset created
Neuron 스케줄러는 두 개 이상의 Neuron 코어 또는 디바이스 리소스가 필요한 Pod를 스케줄링하는 데 필요합니다.
kubectl apply -f https://raw.githubusercontent.com/aws-neuron/aws-neuron-sdk/master/src/k8/k8s-neuron-scheduler-eks.yml
clusterrole.rbac.authorization.k8s.io/k8s-neuron-scheduler created
serviceaccount/k8s-neuron-scheduler created
clusterrolebinding.rbac.authorization.k8s.io/k8s-neuron-scheduler created
Warning: spec.template.metadata.annotations[scheduler.alpha.kubernetes.io/critical-pod]: non-functional in v1.16+; use the "priorityClassName" field instead
deployment.apps/k8s-neuron-scheduler created
service/k8s-neuron-scheduler created
WSParticipantRole:~/environment/eks/genai $ kubectl apply -f https://raw.githubusercontent.com/aws-neuron/aws-neuron-sdk/master/src/k8/my-scheduler.yml
serviceaccount/my-scheduler created
clusterrolebinding.rbac.authorization.k8s.io/my-scheduler-as-kube-scheduler created
clusterrolebinding.rbac.authorization.k8s.io/my-scheduler-as-volume-scheduler created
clusterrole.rbac.authorization.k8s.io/my-scheduler created
clusterrolebinding.rbac.authorization.k8s.io/my-scheduler created
configmap/my-scheduler-config created
deployment.apps/my-scheduler created
vLLM 애플리케이션 Pod 배포
이제 모델 추론 엔드포인트를 제공하는 vLLM Pod를 배포합니다.
vLLM 이 배포되면 FSx for Lustre PV에서 Mistral-7B 모델(29GB)을 메모리에 로드하여 사용할 수 있습니다.
cat mistral-fsxl.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: vllm-mistral-inf2-deployment
spec:
replicas: 1
selector:
matchLabels:
app: vllm-mistral-inf2-server
template:
metadata:
labels:
app: vllm-mistral-inf2-server
spec:
tolerations:
- key: "aws.amazon.com/neuron"
operator: "Exists"
effect: "NoSchedule"
containers:
- name: inference-server
image: public.ecr.aws/u3r1l1j7/eks-genai:neuronrayvllm-100G-root
resources:
requests:
aws.amazon.com/neuron: 1
limits:
aws.amazon.com/neuron: 1
args:
- --model=$(MODEL_ID)
- --enforce-eager
- --gpu-memory-utilization=0.96
- --device=neuron
- --max-num-seqs=4
- --tensor-parallel-size=2
- --max-model-len=10240
- --served-model-name=mistralai/Mistral-7B-Instruct-v0.2-neuron
env:
- name: MODEL_ID
value: /work-dir/Mistral-7B-Instruct-v0.2/
- name: NEURON_COMPILE_CACHE_URL
value: /work-dir/Mistral-7B-Instruct-v0.2/neuron-cache/
- name: PORT
value: "8000"
volumeMounts:
- name: persistent-storage
mountPath: "/work-dir"
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: fsx-lustre-claim
---
apiVersion: v1
kind: Service
metadata:
name: vllm-mistral7b-service
spec:
selector:
app: vllm-mistral-inf2-server
ports:
- protocol: TCP
port: 80
targetPort: 8000
kubectl apply -f mistral-fsxl.yaml
deployment.apps/vllm-mistral-inf2-deployment created
service/vllm-mistral7b-service created
콘솔에서 확인하면 새로운 AWS Inferentia inf2.xlarge 컴퓨팅 노드가 있는 것을 볼 수 있습니다.
모델과 상호 작용하기 위한 WebUI 채팅 애플리케이션 배포
Open WebUI 애플리케이션을 사용하여 추론 서비스에 연결할 수 있습니다.
이 애플리케이션은 워크숍에서 배포할 vLLM 호스팅 Mistral-7B-Instruct 모델에서 제공하는 OpenAI 호환 엔드포인트를 사용하도록 설계되었습니다.
cat open-webui.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: open-webui-deployment
spec:
replicas: 1
selector:
matchLabels:
app: open-webui-server
template:
metadata:
labels:
app: open-webui-server
spec:
containers:
- name: open-webui
image: kopi/openwebui
env:
- name: WEBUI_AUTH
value: "False"
- name: OPENAI_API_KEY
value: "xxx"
- name: OPENAI_API_BASE_URL
value: "http://vllm-mistral7b-service/v1"
---
apiVersion: v1
kind: Service
metadata:
name: open-webui-service
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: external
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: instance
spec:
selector:
app: open-webui-server
# type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: open-webui-ingress
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/healthcheck-path: /
alb.ingress.kubernetes.io/healthcheck-interval-seconds: '10'
alb.ingress.kubernetes.io/healthcheck-timeout-seconds: '9'
alb.ingress.kubernetes.io/healthy-threshold-count: '2'
alb.ingress.kubernetes.io/unhealthy-threshold-count: '10'
alb.ingress.kubernetes.io/success-codes: '200-302'
alb.ingress.kubernetes.io/load-balancer-name: open-webui-ingress
labels:
app: open-webui-ingress
spec:
ingressClassName: alb
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: open-webui-service
port:
number: 80
kubectl apply -f open-webui.yaml
deployment.apps/open-webui-deployment created
service/open-webui-service created
ingress.networking.k8s.io/open-webui-ingress created
Open WebUI Chat 인터페이스의 URL 주소를 확인합니다.
kubectl get ing
NAME CLASS HOSTS ADDRESS PORTS AGE
open-webui-ingress alb * open-webui-ingress-xxxxx.us-west-2.elb.amazonaws.com 80 20s
접근하면 아래와 같은 화면을 확인할 수 있습니다.
채팅으로 모델을 호출해봅니다.
'MLOps' 카테고리의 다른 글
KServe ML Model monitoring 해보기 : Knative, Loki (0) | 2023.04.01 |
---|---|
KServe로 하는 Model Serving 이해하기 (6) | 2022.10.08 |
이미지 예측 모델을 쿠버네티스에 배포하기 A to Z (Kubernetes Kubeflow KFServing InferenceService Custom Image) (0) | 2022.01.05 |