MLOps

vLLM을 활용한 Mistral 7B 모델 배포 및 생성형 AI 사용 테스트

mokpolar 2025. 4. 19. 14:13
반응형

안녕하세요?
오늘은 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

접근하면 아래와 같은 화면을 확인할 수 있습니다.

채팅으로 모델을 호출해봅니다.

반응형