AI 워크로드에 대한 컨테이너 사용
머신러닝과 딥러닝이 기업의 핵심 경쟁력으로 부상하면서, 데이터 과학자들은 점점 더 복잡하고 계산 집약적인 모델을 개발하고 있다. 이러한 모델을 효율적으로 학습하고 배포하기 위해서는 강력한 컴퓨팅 인프라가 필수적이다. 특히 GPU를 사용하는 것은 병렬 처리 능력으로 딥러닝 워크로드를 가속화하는 데 있어 중요한 역할을 한다.
전통적으로 ML 엔지니어들은 베어메탈 서버에 직접 GPU 드라이버와 라이브러리를 설치하여 작업하였다. 이 접근 방식은 몇 가지 심각한 문제점을 가지고 있었다:
- 환경 구성의 복잡성: CUDA, cuDNN 등 복잡한 드라이버 스택 설치 및 관리가 필요. 특히 버전 호환성 문제로 인해 특정 프레임워크(TensorFlow, PyTorch)가 특정 CUDA/cuDNN 버전만 지원하는 경우가 많았음.
- 재현성 부족: 동일한 실험 환경을 다른 시스템에서 재현하기 어려움. '내 컴퓨터에서는 잘 작동하는데요?.'와 같은 이야기를 많이 들을 수 있었음.
- 리소스 비효율성: 고가의 GPU 리소스가 특정 사용자나 프로젝트에 고정되어 활용도가 저하. (베어메탈 GPU 서버의 활용률은 30% 미만인 경우가 많았음)
- 확장성 제한: 대규모 분산 학습을 위한 인프라 확장이 어려웠음. 새로운 GPU 서버를 추가할 때마다 동일한 환경 구성 과정을 반복 필요.
컨테이너 기술은 주로 두 가지 핵심 Linux 커널 기능에 의존한다:
- Cgroups (Control Groups): 프로세스 그룹의 리소스 사용(CPU, 메모리, 디스크 I/O, 네트워크 등)을 제한하고 격리하는 메커니즘
- Namespaces: 프로세스가 시스템의 특정 리소스만 볼 수 있도록 격리하는 기능 Linux는 여러 유형의 네임스페이스(PID, 네트워크, 마운트, UTS, IPC, 사용자 등)를 제공
이러한 기술들은 CPU와 메모리 같은 리소스를 효과적으로 관리할 수 있게 해주지만, GPU와 같은 특수 하드웨어 장치에 대해서는 몇 가지 근본적인 제약이 있었음:
- 물리적으로 분할하기 어려운 GPU: CPU 코어나 메모리와 달리, 초기 GPU는 물리적으로 분할하여 여러 컨테이너에 할당하기 어려웠음 (최근에는 MIG 기술로 개선됨 - Nvidia GPU A100에서 새롭게 생긴 기능, 2020년 등장)
- GPU 드라이버 복잡성: GPU 접근은 복잡한 사용자 공간 라이브러리와 커널 드라이버를 통해 이루어짐
- 장치 파일 접근 제어: /dev/nvidia* 과 같은 장치 파일에 대한 접근을 안전하게 관리 필요
MIG(Multi-Instance GPU)
MIG는 GPU를 독립적인 여러 인스턴스로 분할하여 사용하는 가상화 기술이다.
NVIDIA의 A100은 Ampere 아키텍처로 설계되어, MIG 가상화를 지원한다.
MIG로 최대 7배 많은 GPU 인스턴스를 제공하기 때문에 자원 활용률이 중요한 CSP에서 특히 활용하기 좋다.
각 인스턴스는 메모리, 캐시, 컴퓨팅 코어가 하드웨어 수준에서 완전 격리/보호된다.
따라서 각 인스턴스 별로 다른 유형의 워크로드를 수행할 수 있다.
또한 각 인스턴스는 필요에 따라 프로비저닝 까지 가능하다.
예를 들어 NVIDIA A100 40GB는 20GB 메모리의 인스턴스 2개를 생성하거나 10GB인 인스턴스 2개와 5GB 인스턴스 4개를 생성할 수 있다.
특히 동적으로 GPU 리스소스를 전환할 수 있으므로 다양한 수요에 대처할 수 있다.
vGPU(Virtual GPU)
vGPU는 방식은 CPU 가상화와 유사하다.
하이퍼바이저들은 VM 별로 CPU 사용 시간을 나누어 제공하는 time-shared 방식으로 vCPU를 제공한다.
이때 GPU 메모리를 작업 전환마다 다시 적제한다면 오버헤드가 굉장히 많이 발생할 것이다.
따라서 GPU 메모리는 각 vGPU마다 전용 구간을 나누어 가지고, 컴퓨팅 리소스는 time-shared 방식으로 시간을 나누어 실행하게 된다.
vGPU 소프트웨어는 하이퍼바이저와 함께 가상화 계층에 설치되어 vCPU처럼 vGPU를 제공한다.
MIG는 하이퍼바이저 뿐만 아니라 베어메탈 서버에서도 GPU를 나누어 쓸 수 있다.
그러므로 활용성 자체는 MIG가 좋지만, NVIDIA GPU에서만 지원되므로 의존성이 높다.
참고: https://brunch.co.kr/@f38b64b143b343c/19 / https://everenew.tistory.com/491
컨테이너 환경에서의 GPU 리소스 사용 진화 (단일 GPU)
초기 단계 (2016-2018)
- 초기에는 GPU 장치 파일을 컨테이너에 직접 마운트하고 필요한 라이브러리를 볼륨으로 공유해야 했음.
- 아래는 Tensorflow에서 CUDA를 통해 NVIDIA GPU 장치에 액세스하도록 Docker 명령어를 수동으로 실행하는 예시임
docker run --device=/dev/nvidia0:/dev/nvidia0 \
--device=/dev/nvidiactl:/dev/nvidiactl \
-v /usr/local/cuda:/usr/local/cuda \
tensorflow/tensorflow:latest-gpu
- 문제점
- 모든 장치 파일을 수동으로 지정해야 함
- 호스트와 컨테이너 간 라이브러리 버전 충돌 가능성
- 여러 컨테이너 간 GPU 공유 메커니즘 부재
- 오케스트레이션 환경에서 자동화하기 어려움
NVIDIA Container Runtime 활용 (2018-2020)
NVIDIA는 이러한 문제를 해결하기 위해 NVIDIA Container Runtime을 개발하였음. NVIDIA Container Runtime: Docker, CRI-O 등 컨테이너 기술에서 사용하는 Open Containers Initiative (OCI) 스펙과 호환되는 GPU 인식 컨테이너 런타임임.
이 런타임은 다음과 같은 기능을 자동화함:
- GPU 장치 파일 마운트
- NVIDIA 드라이버 라이브러리 주입
- CUDA 호환성 검사
- GPU 기능 감지 및 노출
# Docker 19.03 이전 버전 사용
docker run --runtime=nvidia nvidia/cuda:11.0-base nvidia-smi
# Docker 19.03 이후부터는 더 간단하게 --gpus 플래그를 사용
docker run --gpus '"device=0,1"' nvidia/cuda:11.0-base nvidia-smi
이 방식을 통해 다음과 같은 개선점을 얻음
- 자동화된 GPU 검출 및 설정
- 호스트-컨테이너 간 드라이버 호환성 자동 관리
- 컨테이너 이미지 이식성 향상
Kubernetes 장치 플러그인 등장 (2020-현재)
Kubernetes 오픈 소스에 Device Plugin이라는 제안이 2017년 9월 처음으로 이루어짐
NVIDIA Device Plugin: Kubernetes 클러스터에서 NVIDIA GPU 리소스를 노출하고 관리하는 플러그인으로, 이를 통해 Pod 스펙에서 선언적으로 GPU 리소스를 요청
apiVersion: v1
kind: Pod
metadata:
name: gpu-pod
spec:
containers:
- name: gpu-container
image: nvidia/cuda:11.0-base
command: ["nvidia-smi"]
resources:
limits:
nvidia.com/gpu: 2 # 2개의 GPU 요청
이 방식을 통해 다음과 같은 장점을 얻음
- 선언적 리소스 관리
- 클러스터 수준의 GPU 리소스 스케줄링
- 자동화된 GPU 할당 및 격리
- 다중 테넌트 환경에서의 리소스 공정성
멀티 GPU 활용을 위한 AI/ML 인프라 with EKS
컨테이너 환경에서의 GPU 리소스 사용 진화 (멀티 GPU)
분산 학습에서 발생하는 네트워크 병목 현상
- 대규모 모델이 등장하면서, 이제 단일 GPU로는 학습이 불가능해짐.
- GPT-3: 1,750억 파라미터
- Megatron-Turing NLG: 5,300억 파라미터
- PaLM: 5,400억 파라미터
- AWS에서도 이러한 추세에 맞춰 대규모 모델 개발과 학습을 위한 인프라를 지속적으로 발전시키고 있음.
- Amazon Bedrock: 100개 이상의 LLM을 지원하며 Trainium/Inferentia 칩을 통한 모델 성능 향상과 같은 AI 인프라 혁신에 투자
- 2025년 현재 AWS는 이러한 대규모 모델의 학습과 추론을 위해 분산 컴퓨팅 기술을 적극적으로 활용하고 있으며, 단일 GPU로는 처리할 수 없는 규모의 모델 개발을 지원하고 있음.
- 이러한 대규모 모델은 필연적으로 여러 GPU와 여러 노드에 걸친 분산 학습이 필요
- 분산 학습에서 이루어지는 주요 통신 패턴 - AllReduce, AllGather, ReduceScatter 등
- AllReduce: 딥러닝 연산시 그래디언트 (Gradient) 동기화를 위해 모든 GPU가 데이터를 교환하고 집계
- 이러한 병목 현상에 따라 발생하는 이슈 예시
- 8개 GPU 노드 간 AllReduce 작업에서 최대 70%의 시간이 통신에 소비
- GPU 활용률 저하 (60-70% 수준)
- 학습 시간 증가 (최대 2-3배)
NCCL: GPU 간 통신을 위한 NVIDIA의 해결책
분산 학습에서 GPU 간 통신 성능이 중요해짐에 따라, NVIDIA는 NCCL(NVIDIA Collective Communications Library)을 개발
NCCL은 여러 GPU 간의 집합 통신 연산을 최적화하기 위해 설계된 라이브러리임.
- NCCL 주요 특징
- 고성능 집합 통신 연산: AllReduce, Broadcast, Reduce, AllGather, ReduceScatter 등 딥러닝에 필수적인 통신 패턴을 최적화
- 다양한 토폴로지 지원: PCIe, NVLink, NVSwitch 등 GPU 간 연결 토폴로지를 자동으로 감지하고 최적화
- 멀티 노드 확장성: InfiniBand, RoCE, IP 소켓 등을 통한 노드 간 통신 지원
- CUDA 통합: CUDA 스트림과 통합되어 계산과 통신의 오버랩 가능
NCCL은 딥러닝 프레임워크(PyTorch, TensorFlow 등)에 통합되어 사용자가 명시적으로 통신 코드를 작성하지 않아도 효율적인 분산 학습이 가능하게 함
그러나 NCCL만으로는 노드 간 네트워크 병목 현상을 완전히 해결할 수 없음.
AWS EFA (Elastic Fabric Adapter) 기술
GPU 네트워크 병목 문제를 해결하기 위해 설계된 혁신적인 네트워크 인터페이스
기반 핵심 기술:
- OS 바이패스 기술: EFA는 운영 체제 커널을 우회하여 사용자 공간 애플리케이션이 네트워크 어댑터와 직접 통신 가능
- 커널 컨텍스트 스위칭 제거
- 시스템 콜 오버헤드 제거
- 인터럽트 처리 최소화
- RDMA (Remote Direct Memory Access): 원격 시스템의 메모리에 CPU 개입 없이 직접 접근할 수 있는 기술
- 원격 시스템의 CPU를 사용하지 않고 메모리 읽기/쓰기 가능
- 제로 카피(Zero-copy) 데이터 전송
- DMA(Direct Memory Access)를 통한 효율적인 데이터 이동
- NCCL과 EFA와의 통합: NVIDIA Collective Communications Library(NCCL)는 EFA와 최적화된 통합을 제공
- GPU 직접 통신 최적화
- 토폴로지 인식 통신 알고리즘
- 자동 튜닝 메커니즘
EFA를 지원하는 인스턴스 유형 https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/efa.html
Amazon EKS에 EFA를 통합함으로써 얻을 수 있는 주요 이점:
- 인프라 관리 간소화
- Kubernetes를 통한 선언적 인프라 관리
- GPU 및 EFA 리소스의 자동 검출 및 할당
- 자동 확장 및 축소 기능
- 비용 최적화
- 리소스 활용도 향상 (GPU 활용률 90% 이상)
- 학습 시간 단축으로 인한 인스턴스 비용 절감
- Spot 인스턴스와의 통합 가능성
- 성능 최적화
- 노드 간 초저지연 통신
- 분산 학습 확장성 향상
- 대규모 모델 학습 가속화
- 개발자 경험 개선
- 일관된 개발 및 배포 환경
- 재현 가능한 실험 설정
- CI/CD 파이프라인과의 통합 용이성
아래 내용은 GenAI with Inferentia & FSx Workshop 워크샵 내용을 기반으로 작성됐습니다.
이 워크숍에서는 vLLM(모델 서빙용), 오픈 소스 Foundation Model, 그리고 다음 AWS 서비스 스택을 사용해 Generative AI 기반의 인터랙티브 Chat 애플리케이션을 구축합니다.
Amazon Elastic Kubernetes Service (EKS) - 오케스트레이션 레이어
Amazon FSx for Lustre & Amazon S3 - AI 모델과 데이터 호스팅
AWS Inferentia - 가속 컴퓨팅 레이어
Amazon EKS, Amazon FSx, Amazon S3, AWS Inferentia에 걸쳐 구성 요소를 설정하고 배포하는 실습을 진행하며, Generative AI Chat 애플리케이션을 구축합니다.
이 패턴을 활용해 Generative AI 및 ML 워크로드를 쉽게 구축하고 테스트하는 방법을 배웁니다. 또한 데이터 레이어에서 성능, 스케일, 통합과 관련된 테스트를 수행하고, 생성된 데이터를 AWS 리전 간에 원활하게 공유하는 방법을 익힙니다.
Inferentia 및 FSx를 활용한 GenAI workshop
참고 솔루션/SDK
- AWS Neuron SDK: 컴파일러, 런타임 및 프로파일링 도구로 구성된 SDK로, 고성능 및 비용 효율적인 딥러닝 가속화를 제공
- Amazon FSx for Lustre
- 속도가 중요한 워크로드(예: 머신 러닝, 분석, 고성능 컴퓨팅)를 위한 고성능 병렬 파일 시스템을 제공하는 완전 관리형 서비스
- 밀리초 미만의 대기 시간으로 데이터에 액세스하고 TB/s의 처리량과 수백만 개의 IOPS로 확장 가능
- Amazon S3와도 통합되어 있어 클라우드 데이터의 대량을 쉽게 저장, 액세스 및 처리
- S3 버킷과 연결되면 FSx for Lustre 파일 시스템은 S3 객체를 파일로 투명하게 제공하며, 파일이 Lustre 파일 시스템에 추가, 수정 또는 삭제되면 연결된 S3 버킷의 내용을 자동으로 업데이트 가능
Kubernetes 스토리지 개념 및 FSx for Lustre 통합
CSI 드라이버
- 컨테이너 스토리지 인터페이스(CSI)는 Kubernetes 같은 컨테이너 오케스트레이션 시스템에서 블록/파일 스토리지를 관리하기 위한 표준으로 Kubernetes가 컨테이너화된 애플리케이션의 Persistent Storage를 기본적으로 관리할 수 있도록 해줌.
- FSx for Lustre CSI 드라이버는 Amazon EKS에서 FSx for Lustre 파일 시스템의 Persistent Volume을 관리하도록 지원하며, 고성능/저지연 스토리지를 컨테이너 워크로드에 통합.
StorageClass
- EKS에서 스토리지 유형(예: FSx, EBS, EFS)과 정책을 정의하는 클래스.
- Kubernetes가 스토리지 클래스를 통해 스토리지 특성을 인식.
- StorageClass마다 다른 스토리지 유형이나 백업 정책에 매핑될 수 있음.
Persistent Volume(PV)
- 관리자가 EKS 클러스터에 매핑한 스토리지 볼륨.
- Pod 수명보다 오래 지속되어 공유 데이터 접근에 적합.
Persistent Volume Claim(PVC)
- 사용자가 특정 크기와 액세스 모드(예: ReadWriteOnce, ReadWriteMany)로 스토리지를 요청하는 방식.
정적 vs 동적 프로비저닝
- 정적 프로비저닝
- 관리자가 FSx for Lustre 인스턴스와 PV를 수동 생성.
- 개발자가 Pod에서 Persistent Volume을 사용하기 위해 PVC로 PV를 요청해 사용.
- 동적 프로비저닝
- PVC 요청 시 PV와 FSx for Lustre 인스턴스가 자동 생성.
- 관리자가 미리 스토리지를 프로비저닝할 필요 없음.
CSI 드라이버 배포
- account-id 환경 변수 설정
ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text)
IAM 정책 및 서비스 계정 생성
CSI 드라이버가 사용자를 대신해 AWS API 호출할 수 있기 위함.
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
생성된 역할 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
FSx for Lustre CSI 드라이버 배포
# 배포
kubectl apply -k "github.com/kubernetes-sigs/aws-fsx-csi-driver/deploy/kubernetes/overlays/stable/?ref=release-1.2"
# 확인
kubectl get pods -n kube-system -l app.kubernetes.io/name=aws-fsx-csi-driver
서비스 계정에 주석 추가
# 서비스 계정에 IAM 역할 추가
kubectl annotate serviceaccount -n kube-system fsx-csi-controller-sa \
eks.amazonaws.com/role-arn=$ROLE_ARN --overwrite=true
# 확인
kubectl get sa/fsx-csi-controller-sa -n kube-system -o yaml
EKS 클러스터에 Persistent Volume 생성
Persistent Volume을 생성하는 두 가지 방법
- 정적 프로비저닝 - 관리자가 백엔드 스토리지 엔티티를 만들고 PV를 생성하며, 사용자는 이 PV가 자신의 Pod에서 사용되도록 클레임(PVC)을 만듭니다.
- 동적 프로비저닝 - 사용자가 PVC를 요청하면 CSI 드라이버가 사용자 요구 사항에 따라 PV(및 해당 백업 스토리지 엔티티)를 자동으로 생성합니다. 이 방법은 관리자가 미리 생성해야 하는 별도의 프로세스가 필요하지 않습니다.
이번 실습에서는 정적 프로비저닝 방식을 사용해 PV 설정.
워크샵 환경에서 이미 프로비저닝된 FSx for Lustre 인스턴스를 제공하며 Mistral-7B 모델을 저장하고 있는 Amazon S3 버킷과 연결돼 있음.
PV, PVC를 생성해 vLLM Pod에서 이 스토리지 볼륨을 사용해 Mistral-7B 모델 데이터에 접근할 수 있도록 진행.
cd /home/ec2-user/environment/eks/FSxL
# 사전 생성된 FSx for Lustre Instance 세부 정보 변수 선언
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)
PV 생성
# 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
# 실제 환경 값으로 치환
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
# 확인
cat fsxL-persistent-volume.yaml
# EKS 클러스터에 PV 배포
kubectl apply -f fsxL-persistent-volume.yaml
# PV 확인
kubectl get pv
PVC 생성
PV와 바인딩할 PVC 생성
# fsxL-claim.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: fsx-lustre-claim
spec:
accessModes:
- ReadWriteMany
storageClassName: ""
resources:
requests:
storage: 1200Gi
volumeName: fsx-pv
# EKS 클러스터에 PVC 배포
kubectl apply -f fsxL-claim.yaml
# PV,PVC 확인
kubectl get pv,pvc
Amazon FSx console에서 옵션 및 성능 세부 정보 확인
- Persistent, SSD
- 고성능 SSD 기반 스토리지
- 데이터 자동 복제, 내구성 및 가용성 높음
- Persistent, HDD
- HDD 기반 스토리지
- 장기 워크로드에 적합, SSD보다 저렵하지만 성능 낮음
- Scratch, SSD
- 임시 워크로드에 적합
- 데이터 복제 없음
- 처리량은 스토리지 용량에 비례하여 증가 (ex. 1.5 TiB 용량에 500 MB/s/TiB를 선택하면 총 처리량은 750 MB/s.)
콘솔에서 아래와 같이 Summary와 Performance도 확인 가능
생성형 AI 채팅 애플리케이션 배포
이번 실습에서는 Amazon EKS 클러스터에 vLLM Pod와 WebUI Pod를 배포
Kubernetes에서 생성 AI 챗봇 애플리케이션을 구성 및 배포
Amazon FSx for Lustre와 Amazon S3를 사용하여 Mistral-7B 모델을 저장하고 액세스하고, 생성 AI 워크로드에 대한 가속 컴퓨팅으로 AWS Inferentia Accelerators를 활용 함.
vLLM 이란?
LLM 추론 및 제공을 위한 오픈 소스의 사용하기 쉬운 라이브러리. Mistral-7B-Instruct와 같은 LLM 모델을 배포하여 텍스트 생성 추론을 제공할 수 있는 프레임워크를 제공합니다. vLLM은 OpenAI API와 호환되는 API를 제공하여 LLM 애플리케이션을 쉽게 통합 가능.
AWS Inferentia Accelerators 란?
Amazon Web Services(AWS)에서 딥 러닝 및 생성적 AI 추론 애플리케이션을 가속화하도록 설계한 맞춤형 머신 러닝 칩
AWS Inferentia Accelerators는 TensorFlow, PyTorch, MXNet과 같은 널리 사용되는 머신 러닝 프레임워크를 지원하는 Amazon EC2에서 최저 비용으로 고성능을 제공
AWS Inferentia2 Accelerators는 머신 러닝 모델을 대규모로 배포하도록 특별히 최적화되어 있으며, 딥 러닝 모델을 대규모로 실행하도록 설계
AWS Inferentia 노드에 vLLM 배포
AWS Inferentia Accelerators를 위한 Karpenter NodePool 및 EC2 NodeClass 생성
# 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
# 확인
kubectl get nodepool,ec2nodeclass inferentia
Neuron device plugin & scheduler 설치
Neuron Scheduler는 두 개 이상의 Neuron Core 또는 Device 리소스가 필요한 Pod를 스케줄링할 때 필
# Neuron Device Plugin 설치
kubectl apply -f https://raw.githubusercontent.com/aws-neuron/aws-neuron-sdk/master/src/k8/k8s-neuron-device-plugin-rbac.yml
kubectl apply -f https://raw.githubusercontent.com/aws-neuron/aws-neuron-sdk/master/src/k8/k8s-neuron-device-plugin.yml
# Neuron Scheduler 설치
kubectl apply -f https://raw.githubusercontent.com/aws-neuron/aws-neuron-sdk/master/src/k8/k8s-neuron-scheduler-eks.yml
kubectl apply -f https://raw.githubusercontent.com/aws-neuron/aws-neuron-sdk/master/src/k8/my-scheduler.yml
vLLM 애플리케이션 Pod 배포
# 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
# vLLM Pod 배포
kubectl apply -f mistral-fsxl.yaml
# 확인
kubectl get pod
WebUI 채팅 애플리케이션 배포
배포한 vLLM 모델을 사용하기 위해 Open WebUI 채팅 기반 앱 배포
# 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
# Open WebUI 배포
kubectl apply -f open-webui.yaml
# URL 주소 확인
kubectl get ingress
Mistral-7B 데이터 구조 확인, 생성된 데이터 공유 및 복제
이번 실습에서는 vLLM Pod에 로그인하여 Persistent Volume에 저장된 Mistral-7B 모델 데이터 구조를 확인하고 FSx for Lustre로 지원되는 Persistent Volume을 통해 S3 버킷의 모든 데이터에 접근하는 방법을 학습
S3 버킷 간 S3 크로스 리전 복제 구성
FSx for Lustre에서 사용 중인 버킷을 클릭하여 복제 규칙을 생성
버킷 버전 관리 활성화
Source Bucket에서 'Limit the scope of this rule using one or more filters' 선택
Prefix 값으로 test/ 입력
Destination 버킷으로 다른 리전에 있는 S3를 선택 후 버전 관리 활성화
S3가 사용자 대신 S3 버킷 간 객체 복제를 할 수 있도록 IAM 지정
객체 복제 시 AWS KMS를 통한 암호화 활성화 및 KMS 키 선택
복제 규칙 Save 후 생성 확인
데이터 자동 내보내기 및 복제 확인
Pod 로그인 및 복제 테스트 파일 생성
cd /home/ec2-user/environment/eks/FSxL
# pod 확인
kubectl get pods
# pod 접속
kubectl exec -it <YOUR-vLLM-POD-NAME> -- bash
# PVC 마운트 위치 확인
df -h
마운트 경로 안에서 테스트 파일 생성
# 마운트 경로 진입 및 확인
cd /work-dir/
ls -ll
# Mistral 모델 데이터 구조 확인
cd Mistral-7B-Instruct-v0.2/
ls -ll
# 복제 테스트용 폴더 및 파일 생성
cd /work-dir
mkdir test
cd test
cp /work-dir/Mistral-7B-Instruct-v0.2/README.md /work-dir/test/testfile
ls -ll /work-dir/test
Data Layer 테스트를 위한 자체 환경 구성
이번 실습에서는 Pod를 배포하는 자체 테스트 환경을 구성하고 사용자가 동적 프로비저닝 기능을 사용해 온디맨드 PV와 PVC를 배포하는 방법 학습
백엔드에 연결된 FSx Lustre 인스턴스가 사전 프로비저닝 필요 없이 자동으로 생성되도록 하는 방법 학습
Storage Class 정의
# 환경 변수 설정
VPC_ID=$(aws eks describe-cluster --name $CLUSTER_NAME --region $AWS_REGION --query "cluster.resourcesVpcConfig.vpcId" --output text)
SUBNET_ID=$(aws eks describe-cluster --name $CLUSTER_NAME --region $AWS_REGION --query "cluster.resourcesVpcConfig.subnetIds[0]" --output text)
SECURITY_GROUP_ID=$(aws ec2 describe-security-groups --filters Name=vpc-id,Values=${VPC_ID} Name=group-name,Values="FSxLSecurityGroup01" --query "SecurityGroups[*].GroupId" --output text)
# 확인
echo $SUBNET_ID
echo $SECURITY_GROUP_ID
# fsxL-storage-class.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: fsx-lustre-sc
provisioner: fsx.csi.aws.com
parameters:
subnetId: SUBNET_ID
securityGroupIds: SECURITY_GROUP_ID
deploymentType: SCRATCH_2
fileSystemTypeVersion: "2.15"
mountOptions:
- flock
# 실제 환경 값으로 치환
sed -i'' -e "s/SUBNET_ID/$SUBNET_ID/g" fsxL-storage-class.yaml
sed -i'' -e "s/SECURITY_GROUP_ID/$SECURITY_GROUP_ID/g" fsxL-storage-class.yaml
# 확인
cat fsxL-storage-class.yaml
StorageClass 생성
# StorageClass 생성
kubectl apply -f fsxL-storage-class.yaml
# 확인
kubectl get sc
PVC 생성
# fsxL-dynamic-claim.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: fsx-lustre-dynamic-claim
spec:
accessModes:
- ReadWriteMany
storageClassName: fsx-lustre-sc
resources:
requests:
storage: 1200Gi
# PVC 생성
kubectl apply -f fsxL-dynamic-claim.yaml
# PVC 확인
kubectl describe pvc/fsx-lustre-dynamic-claim
kubectl get pvc
성능 테스트
FSx for Lustre 파일 시스템의 스토리지 성능, IOPS, 처리량 및 지연 시간과 관련된 중요한 매개변수 확인
테스트 도구
- FIO(Flexible I/O): 스토리지 벤치마킹 도구
- IOping: 실시간 I/O 지연 모니터링, 드라이브 성능 테스트
테스트 Pod 프로비저닝
cd /home/ec2-user/environment/eks/FSxL
# 인스턴스 가용영역 확인
aws ec2 describe-subnets --subnet-id $SUBNET_ID --region $AWS_REGION | jq .Subnets[0].AvailabilityZone
# Pod 배포
kubectl apply -f pod_performance.yaml
# Pod 확인
kubectl get pods
FIO, IOping 테스트
# 컨테이너 접속
kubectl exec -it fsxl-performance -- bash
# FIO, IOping 설치
apt-get update
apt-get install fio ioping -y
# IOping 실행
ioping -c 20 .
# FIO 실행
mkdir -p /data/performance
cd /data/performance
fio --randrepeat=1 --ioengine=libaio --direct=1 --gtod_reduce=1 --name=fiotest --filename=testfio8gb --bs=1MB --iodepth=64 --size=8G --readwrite=randrw --rwmixread=50 --numjobs=8 --group_reporting --runtime=10
FIO 옵션 값에 따라 결과가 다르게 나올 수 있을 것으로 보인다.
'k8s > AWS EKS' 카테고리의 다른 글
[AWS] Amazon VPC Lattice for Amazon EKS (0) | 2025.04.26 |
---|---|
[AWS] EKS Upgrade (0) | 2025.04.02 |
[AWS] EKS Mode/Nodes (1) | 2025.03.22 |
[AWS] EKS Security (0) | 2025.03.16 |
[AWS] EKS AutoScaling (0) | 2025.03.08 |