[EKS] LLM 서빙과 최적화: vLLM on Trainium 워크샵 - 8.0. 개요와 워크샵 아키텍처
서종호(가시다)님의 Hands-On LLM Serving and Optimization Study (LLMSO) 6주차 학습 내용을 기반으로 합니다.
TL;DR
- 6주차는 AWS가 제공하는 “Scaling LLM Inference with vLLM and AWS Trainium” 워크샵을 따라간다.
trn1.2xlarge인스턴스 위에 vLLM을 올려TinyLlama-1.1B-Chat-v1.0을 서빙하는 구성이다 - 워크샵 환경에 접속하면 EKS 1.33 컨트롤 플레인까지는 이미 배포돼 있고 워커 노드 그룹이 비어 있다. 실습은 노드 그룹(
neuron-trn1-2x)을 만드는 것부터 시작한다 - Trainium을 파드가 쓰려면 자원 노출과 배치가 따로 필요하다. 노출은 Neuron device plugin이, 배치는 Neuron scheduler extension이 맡는다
- 모델 컴파일 산출물은 init container가 S3에 올려 두고, S3 CSI Driver로 마운트한 PV를 통해 이후 파드가 재사용한다
워크샵 개요
이번 주차는 AWS가 제공하는 “Scaling LLM Inference with vLLM and AWS Trainium” 워크샵을 따라간다. 감사하게도 스터디 멤버 한 분이 워크샵 환경을 열어 준 덕분에, 개인 계정으로는 준비하기 번거로운 Trainium 인스턴스를 직접 다뤄 볼 수 있었다.
목표와 산출물
vLLM과 AWS Trainium(trn1.2xlarge)을 Amazon EKS 위에서 조합해, 운영 환경을 가정한 LLM 추론 서빙 인프라를 구축하는 핸즈온 워크샵이다. 서빙 대상은 TinyLlama-1.1B-Chat-v1.0이고, 이 모델을 NeuronX Distributed(NxD)로 컴파일해 배포한 뒤 모니터링과 오토스케일링, 부하 테스트까지 이어 간다.
워크샵이 목표로 하는 최종 산출물은 일곱 개다. 각 항목이 이 시리즈의 어디에 해당하는지 함께 적는다.
- Trainium(
trn1.2xlarge) 기반 EKS 클러스터 구성 — 8.2.1편 - vLLM과 NxD로 TinyLlama-1.1B 모델 서빙 배포 — 8.3.1편
- init container 기반 모델 컴파일과 S3 캐싱 패턴 구현 — 8.3.1편
- ingress-nginx로 외부 접근과 로드밸런싱 구성 — 이후 Lab
- Prometheus와 Grafana(및 CloudWatch)로 모니터링 구축 — 이후 Lab
- CPU 사용률 기반 HPA 구성 — 이후 Lab
- llmperf 등으로 처리량과 지연 시간 검증 — 이후 Lab
기술 스택
워크샵에서 사용하는 기술은 네 계층으로 나뉜다. 각 기술의 원리는 이미 다룬 곳이 있어, 여기서는 이 워크샵이 어느 조합을 고르는지만 정리한다.
| 계층 | 이 워크샵의 선택 | 참고 |
|---|---|---|
| 서빙 엔진 | vLLM. continuous batching, PagedAttention, OpenAI 호환 API, Neuron 백엔드 | 6.1편 |
| 모델 병렬화 | NeuronX Distributed(NxD). 텐서/파이프라인/데이터/컨텍스트 병렬, speculative decoding, 양자화, multi-LoRA | 7.1편 |
| 오케스트레이션 | Amazon EKS 1.33. 관리형 컨트롤 플레인, HPA, 로드밸런싱, IAM 통합 | EKS 개요 |
| AWS 서비스 | EC2(trn1.2xlarge), EKS, S3, IAM, VPC, CloudWatch, Neuron SDK |
8.1편 |
단일 가속기 메모리 한계와 텐서 병렬 처리
이 워크샵이 텐서 병렬 처리를 기본 전제로 깔고 가는 이유는 모델 크기와 가속기 메모리의 관계 때문이다. Llama 3.1 8B를 FP32로 올리면 가중치만 32GB다.

파라미터 80억 개에 FP32 4바이트를 곱하면 32GB이고, 여기에 KV cache와 부가 메모리 오버헤드가 더 얹힌다. 정밀도를 FP16이나 BF16으로 낮추면 절반, INT8이면 4분의 1로 줄지만, 모델이 커질수록 단일 가속기의 HBM 용량을 넘어서는 지점이 온다는 구조 자체는 그대로다. 모델 크기와 가속기 메모리 용량의 관계는 5.2편에서 정리했다.
한 장에 안 들어가면 여러 장에 나눠 올려야 한다. 텐서 병렬 처리(tensor parallelism)는 가중치 텐서 자체를 여러 가속기에 쪼개 올려 두고, 각 가속기가 자기 몫을 계산한 뒤 결과를 합치는 병렬화 기법이다.

동작은 세 단계다.
- 가중치 텐서를 여러 Neuron 디바이스에 분할한다
- 각 디바이스가 할당받은 부분을 병렬로 계산한다
- 부분 출력을 결합해 최종 결과를 만든다
이 워크샵의 배포 매니페스트에 tensor-parallel-size=2가 등장하는 근거가 여기에 있다. 값이 왜 2인지는 vLLM 서빙 구성에서 다시 본다. 병렬화 방식을 어느 인터커넥트 계층에 배치하느냐에 따라 통신 비용이 달라지는 문제는 5.3편에서 다뤘다.
AWS 추론 스택과 Amazon EKS
AWS는 Trainium 하드웨어 위에 대형 모델을 올리기 위한 스택을 네 층으로 제공한다.

맨 아래가 Trainium과 Inferentia 인스턴스, 그 위가 AWS Neuron SDK, 그 위가 이 가속기를 실제로 굴리는 AWS 클라우드 서비스들(Parallel Cluster, SageMaker, Batch, ECS, EKS, Neuron DLC, Neuron DLAMI), 맨 위가 모델 서버(TGI, vLLM, SageMaker LMI, Triton, Ray Serve, TorchServe)다. 이 워크샵은 세 번째 층에서 Amazon EKS를, 네 번째 층에서 vLLM을 고른 조합이다.
AWS 가속기 계열과 Neuron SDK 자체에 대한 설명은 8.1편에서 다룬다.
가속기 지형 안에서 AWS 칩이 놓이는 위치는 5.5편에 서 다룬 적이 있다.
EKS 쪽은 AWS가 컨트롤 플레인만 관리하고 데이터 플레인은 사용자가 관리하는 표준 모드다. Auto Mode와의 관리 경계 차이는 EKS 개요에 그림과 함께 정리해 두었다. 이 워크샵에서 노드 그룹을 직접 만들어야 하는 것도 표준 모드이기 때문이다.
실제로 클러스터에 붙어 보면 컨트롤 플레인 구성 요소가 파드로 보이지 않는다. CoreDNS까지는 보이지만 API 서버나 etcd는 사용자 계정의 노드 위에 있지 않다. 이 가시성 차이는 EKS와 바닐라 K8s 비교에서 다뤘다.
워크샵 아키텍처
전체 구성
워크샵이 제시하는 전체 아키텍처는 다음과 같다.

왼쪽부터 따라가면, 외부 클라이언트의 요청이 인터넷 게이트웨이를 지나 ELB로 들어오고, ELB가 워커 노드의 ingress-nginx 컨트롤러로 전달한다. 컨트롤러는 vLLM Service를 거쳐 vLLM 파드로 요청을 보내고, 그 파드가 노드에 붙은 Trn1 가속기를 쓴다. 파드 안에는 vLLM 컨테이너와 init container가 함께 들어 있고, 모델 캐시는 S3를 PV로 마운트한 볼륨에 있다. 오른쪽의 EKS 애드온 5종과 AWS 관리형 서비스 5종은 아래 표에서 하나씩 확인한다.
| 레이어 | 구성 요소 | 접속 시점 상태 | 만드는 시점 |
|---|---|---|---|
| 인프라 | VPC, 퍼블릭 서브넷, 보안 그룹, 워크샵 인스턴스(t3.2xlarge) |
사전 배포 | 해당 없음 |
| 컨트롤 플레인 | EKS 1.33, VPC CNI, OIDC | 사전 배포 | 해당 없음 |
| 데이터 플레인 | 관리형 노드 그룹 neuron-trn1-2x(trn1.2xlarge) |
미배포 | 8.2.1편 |
| 가속기 통합 | Neuron device plugin, Neuron scheduler extension | 미배포 | 8.2.2편 |
| 서빙 | vLLM Deployment와 init container, NxD | 미배포 | 8.3.1편 |
| 스토리지 | S3 모델 캐시, S3 CSI Driver, PV/PVC | 일부 사전 배포 | 8.2.1편 |
| 네트워크 | vLLM Service (LoadBalancer) |
미배포 | 8.3.2편 |
| 네트워크 | ingress-nginx 컨트롤러 | 미배포 | 이후 Lab |
| 관측 | Prometheus, Grafana, CloudWatch | 미배포 | 이후 Lab |
표에서 상태 열이 중요하다. 워크샵 환경에 접속해 EKS 콘솔을 열어 보면 클러스터는 이미 활성 상태인데 컴퓨팅(노드 그룹)이 비어 있다. 즉 실습의 출발점은 클러스터 생성이 아니라 Trainium 인스턴스를 쓰는 노드 그룹을 붙이는 것이다. 이 경계가 이후 Lab들의 순서를 결정한다.
AWS 인프라 레이어
워크샵 인스턴스는 그림에서 Workshop Instance로 표시된 t3.2xlarge EC2다. 가속기가 없는 일반 인스턴스이고, 실습 명령은 전부 여기에 SSH로 붙어서 실행한다. 클러스터의 워커 노드가 아니라, kubectl과 eksctl을 돌리는 작업 노드 겸 배스천 역할이다.
VPC와 서브넷은 CloudFormation 스택이 미리 만들어 둔다. 워크샵 인스턴스가 뜬 퍼블릭 서브넷은 10.0.1.0/24이고, VPC 대역은 10.0.0.0/16이다. 스택의 Outputs를 보면 서브넷이 하나가 아니라 4개 AZ에 걸친 퍼블릭/프라이빗 세트로 만들어진다. 각 서브넷의 CIDR은 Outputs에 나오지 않으므로 여기서 확정할 수 있는 대역은 워크샵 인스턴스가 속한 10.0.1.0/24뿐이다.
보안 그룹은 22, 8000, 8080 인바운드를 연다. SSH 22번의 소스가 0.0.0.0/0으로 열려 있는데, 짧게 쓰고 버리는 워크샵 환경이라 이렇게 구성된 것이고 운영 환경에 그대로 옮길 설정은 아니다.
IAM Role은 EKS, ECR, S3, CloudFormation 접근용으로 미리 준비된다. 노드 그룹에 붙는 정책 구성은 8.2.1편의 매니페스트에서 확인하는데, 이 글 범위에서 기억해 둘 것은 모델 캐시 버킷을 읽고 쓰기 위한 S3 접근 권한이 워커 노드에 붙는다는 점이다.
EKS 클러스터와 노드 그룹
컨트롤 플레인은 Kubernetes 1.33이고, VPC CNI와 OIDC 공급자가 활성화된 상태로 이미 배포돼 있다. VPC CNI가 파드에 VPC 주소를 직접 부여하는 방식은 EKS VPC CNI에서 다뤘다.
직접 만드는 쪽은 관리형 노드 그룹 neuron-trn1-2x다. 주요 설정은 다음과 같다.
- 인스턴스 타입
trn1.2xlarge,desiredCapacity: 1 - 루트 볼륨
volumeSize: 100,volumeType: gp2 - AMI 계열
AmazonLinux2023. eksctl 로그에는[AmazonLinux2023/1.33]로 찍히고, 자동 설치 메시지는 이를 EKS 최적화 가속 컴퓨팅 AMI라고 부른다. “Neuron 전용 AMI”라는 별도 제품이 있는 것은 아니다 - 서브넷은
us-west-2b,us-west-2d두 AZ의 퍼블릭 서브넷을 지정한다
서브넷이 2개 AZ에 걸쳐 선언돼 있지만 desiredCapacity가 1이므로 이번 실습에서 실제로 뜨는 노드는 한 대, 즉 한 AZ에만 존재한다. 서브넷을 두 AZ로 열어 둔 것은 trn1.2xlarge의 제공 AZ가 제한적이어서 용량 확보에 실패할 가능성을 줄이려는 구성으로 보인다. 이 부분에서 실제로 막혔던 과정은 8.2.1편에 있다.
디스크 용량은 워크샵 설명 문서와 실제 매니페스트가 다르다. 문서 요약에는 500GB로 적혀 있지만 실행되는 eksctl 설정 파일에는 volumeSize: 100이 박혀 있다. 실제 배포되는 값은 100GB다.
관리형 노드 그룹이 ASG 위에서 어떻게 동작하는지는 EKS 데이터 플레인 컴퓨팅에 정리돼 있다.
Trainium 자원 노출
Trainium을 파드가 쓰려면 두 가지가 따로 필요하다. 노출은 Neuron device plugin이, 배치는 Neuron scheduler extension이 맡는다. 일반적인 NVIDIA GPU 환경이라면 device plugin 하나로 끝나는데, Neuron은 칩과 코어를 둘 다 광고하기 때문에 기본 스케줄러가 같은 하드웨어를 이중으로 세는 문제가 생긴다. 그래서 스케줄러 쪽 확장이 추가로 붙는다.
하드웨어 사실부터 정리하면, trn1.2xlarge에는 Trainium 칩이 1개 있고 그 칩 안에 NeuronCore-v2가 2개, HBM이 32GB 들어 있다. 칩당 연산 성능과 메모리 대역폭 수치는 8.1편에서 공식 문서와 대조해 정리한다.
자원이 파드까지 닿는 경로는 다음과 같다.
flowchart TD
subgraph node["trn1.2xlarge 워커 노드"]
ami["EKS 최적화 가속 AMI<br/>Neuron 드라이버 + /dev 디바이스 노드"]
dp["neuron-device-plugin<br/>DaemonSet Pod"]
kubelet["kubelet"]
ami --> dp
dp -- "gRPC 소켓 등록" --> kubelet
end
kubelet -- "allocatable 광고<br/>aws.amazon.com/neuron: 1<br/>aws.amazon.com/neuroncore: 2" --> api["kube-apiserver"]
api --> sched["k8s-neuron-scheduler<br/>+ my-scheduler"]
sched -- "schedulerName: my-scheduler" --> pod["vLLM Pod<br/>requests: neuroncore 2"]
Neuron device plugin
Neuron device plugin은 노드의 Neuron 디바이스를 Kubernetes 확장 자원으로 광고하는 DaemonSet이다. NVIDIA 환경의 device plugin과 같은 자리에 놓인다. device plugin이 kubelet에 소켓을 등록하고 kubelet이 노드의 allocatable에 자원을 싣는 흐름은 GPU 자원과 K8s 할당에서 정리했고, 이 플러그인이 없을 때 파드가 Pending에 걸리는 양상은 GPU 파드 Pending에서 다뤘다.
특이한 점은 광고하는 자원이 하나가 아니라는 것이다. aws.amazon.com/neuron(칩 단위)과 aws.amazon.com/neuroncore(코어 단위)를 동시에 올린다. trn1.2xlarge 노드에서는 각각 1과 2로 잡힌다. 칩을 통째로 잡을지 코어 단위로 쪼갤지를 파드가 선택할 수 있게 하려는 설계인데, 같은 하드웨어가 두 이름으로 세어지므로 기본 스케줄러만으로는 자원 계산이 어긋난다.
이 플러그인이 노드 AMI에 들어 있는지 궁금했는데, 그렇지 않다. AMI가 제공하는 범위는 Neuron 드라이버와 /dev 디바이스 노드까지고, device plugin은 API 서버에 등록되는 DaemonSet이라 부팅하는 워커 노드가 스스로 만들 수 없다. kubelet이 쓰는 system:node:<name> 권한에 DaemonSet을 만들 권한이 없기 때문이다.
그런데도 워크샵에서 자동으로 떠 있는 것처럼 보이는 이유는 eksctl이다. 노드 그룹을 만들 때 eksctl이 가속 AMI와 Neuron 인스턴스 타입 조합을 감지해 ClusterRole, ServiceAccount, ClusterRoleBinding, DaemonSet을 함께 생성한다. 실제 생성 로그와 RBAC 근거는 8.2.2편에 있다.
플러그인 컨테이너 이미지가 AMI에 미리 받아져 있을 가능성은 따로 확인이 필요하다. 노드에서 아래 명령으로 pull 여부를 볼 수 있는데, 이 글에서는 확인하지 않았다.
# 노드에 이미 받아져 있는 컨테이너 이미지 중 neuron 관련 이미지 확인
~$ sudo ctr -n k8s.io images ls | grep -i neuron
Neuron scheduler extension
배치를 맡는 쪽은 k8s-neuron-scheduler와 my-scheduler 두 Deployment다. 파드가 이 스케줄러를 타려면 스펙에 schedulerName: my-scheduler를 명시해야 한다. 기본 kube-scheduler를 그대로 두고 확장 스케줄러를 나란히 띄운 뒤, 필요한 워크로드만 이름으로 지정해 보내는 형태다.
이 확장이 필요한 이유는 두 가지다. 앞에서 본 칩과 코어의 이중 회계를 정확히 처리해야 하고, 텐서 병렬로 여러 코어를 묶어 쓸 때 연속된 코어를 할당해야 한다. 스케줄러 파드의 실제 로그와 배치 결과는 8.2.2편에서 확인한다.
vLLM 서빙 구성
서빙은 Deployment 하나로 배포되고, 파드 안에 컨테이너가 둘이다. init container가 먼저 돌아 모델을 준비하고, 그다음 vLLM 컨테이너가 서버를 띄운다.
컨테이너 이미지는 AWS가 공개 ECR에 올려 둔 Neuron용 vLLM 추론 이미지를 쓴다.
# vLLM Deployment가 쓰는 이미지 태그
# 태그 안에 vLLM 버전(0.9.1), Python 버전(py310), Neuron SDK 버전(sdk2.25.0), 베이스 OS가 모두 박혀 있다
image: public.ecr.aws/neuron/pytorch-inference-vllm-neuronx:0.9.1-neuronx-py310-sdk2.25.0-ubuntu22.04
버전 조합이 태그에 그대로 노출되는 형태라, Neuron SDK 버전과 vLLM 버전을 따로 맞출 필요 없이 이미지 하나로 고정된다.
init container가 하는 일은 모델 다운로드, NxD 컴파일, S3 업로드 세 가지다. 이 흐름은 모델 캐시 스토리지에서 도식으로 정리한다.
tensor-parallel-size=2가 나오는 근거는 하드웨어 구성이다. trn1.2xlarge에는 Trainium 칩이 1개 있고 그 안에 NeuronCore-v2가 2개다. vLLM은 NeuronCore 하나를 디바이스 하나로 잡으므로, 이 인스턴스에서 쓸 수 있는 디바이스 수가 2가 되고 텐서 병렬 크기도 2가 된다. 앞 절에서 본 aws.amazon.com/neuroncore: 2가 같은 사실의 Kubernetes 쪽 표현이다.
워크샵 문서는 이 구성이 쓰는 최적화 기법으로 continuous batching, OpenAI 호환 API, 텐서/파이프라인 병렬, 메모리 풀링, speculative decoding을 든다. 각 기법의 원리는 이 시리즈에서 이미 정리했다. continuous batching은 6.1편, 병렬화 전반은 7.1편, speculative decoding은 7.2.1편에 있다.
모델 캐시 스토리지
Neuron 백엔드는 모델을 그대로 올려 쓰는 것이 아니라 NxD로 컴파일한 산출물을 쓴다. 이 컴파일이 느려서, 파드가 뜰 때마다 다시 컴파일하면 기동 시간이 그만큼 늘어난다. 워크샵이 init container와 S3를 묶어 쓰는 이유가 여기에 있다.
flowchart TD
start(["vLLM Pod 기동"]) --> check{S3 캐시에 컴파일 산출물 존재}
check -- 없음 --> hf["Hugging Face에서<br/>TinyLlama-1.1B-Chat-v1.0 다운로드"]
hf --> compile["NxD로 Neuron 컴파일<br/>tensor-parallel-size=2"]
compile --> upload["S3 버킷에 산출물 업로드"]
upload --> serve
check -- 있음 --> mount["S3 CSI Driver로<br/>PV(100Gi, RWX) 마운트"]
mount --> serve["vLLM 컨테이너 기동<br/>OpenAI 호환 API 서빙"]
캐시 버킷 이름은 vllm-models-cache-<ACCOUNT_ID> 형태다. 이 버킷을 Mountpoint for Amazon S3 CSI Driver로 100Gi, ReadWriteMany PV로 마운트한다. ReadWriteMany라서 여러 파드가 같은 캐시를 동시에 읽을 수 있고, HPA로 파드가 늘어나는 Lab 4 시나리오에서 이 구조가 의미를 갖는다. 새로 뜬 파드가 컴파일을 건너뛰고 바로 서버를 올릴 수 있기 때문이다.
외부 접근 경로
외부 요청은 인터넷 게이트웨이와 ELB를 거쳐 클러스터로 들어온다. 클러스터 안에서는 ingress-nginx 컨트롤러가 경로 기반 라우팅(/)으로 vLLM Service에 연결하고, Service가 vLLM 파드로 전달한다. vLLM 서버가 듣는 포트는 8080이다.
아키텍처 그림에서 ELB의 화살표는 vLLM Service가 아니라 워커 노드의 ingress-nginx 쪽으로 들어간다. 그렇다면 LoadBalancer 타입 Service는 vLLM이 아니라 ingress-nginx 컨트롤러 쪽일 가능성이 높은데, 매니페스트를 확인하지 않아 단정하지 않는다. 실제 매니페스트를 확인한 결과는 8.3.2편에 있다 — Lab 2 시점의 외부 진입점은 vLLM Service 자신이고, ingress-nginx는 이후 Lab에서 올라간다.
모니터링과 오토스케일링
vLLM Deployment에는 readiness probe와 liveness probe가 붙고, 지표는 Prometheus와 Grafana, CloudWatch로 모은다. 오토스케일링은 CPU 사용률 기반 HPA다.
가속기 워크로드인데 스케일 기준이 가속기 사용률이 아니라 CPU 사용률이라는 점은 이 구성의 특징으로 기억해 둘 만하다. 실제 동작과 지표 수집 구성은 Lab 4에서 확인한다.
실습 환경 접속
여기서부터는 워크샵 환경에 실제로 붙는 과정이다. 이후 Lab은 모두 워크샵 인스턴스에 접속한 상태를 전제로 시작한다.
사전 준비
워크샵에 Join하면 AWS 콘솔을 열 수 있다. 리전은 us-west-2이고, 콘솔에서 EKS로 들어가면 클러스터 하나가 이미 활성 상태로 떠 있다.

버전이 1.33, 상태가 활성으로 표시된다. 컴퓨팅 탭을 열어 보면 노드 그룹이 비어 있는데, 앞에서 정리한 대로 여기서부터가 실습 범위다.
그다음 워크샵 포털에서 SSH 개인 키를 내려받는다. 좌측 메뉴의 Get EC2 SSH key를 누르면 키 다운로드 창이 열린다.

로컬 PC에 VS Code와 Remote-SSH 확장을 깔아 두면 편하지만 필수는 아니다. 터미널에서 ssh로 붙어도 실습 진행에는 차이가 없다.
내려받은 키는 권한이 열려 있으면 ssh가 거부하므로 소유자 읽기 전용으로 낮춘다.
# 내려받은 개인 키 확인
~$ ls -al
total 8
drwxr-xr-x 3 user staff 96 9 11 20:55 .
drwxr-xr-x@ 12 user staff 384 9 11 20:55 ..
-rw-r--r--@ 1 user staff 1678 9 11 20:55 ws-default-keypair.pem
# 소유자 읽기 전용(400)으로 권한 축소
~$ chmod 400 ws-default-keypair.pem
~$ ls -al ws-default-keypair.pem
-r--------@ 1 user staff 1678 9 11 20:55 ws-default-keypair.pem
워크샵 인스턴스 접속
접속할 IP는 CloudFormation 스택의 Outputs에 있다. 워크샵 첫 페이지에서 바로 볼 수 있는 화면이다.

Key 열을 보면 이 스택이 EKS 클러스터 ARN과 이름, 엔드포인트, 인터넷 게이트웨이 ID, 워크샵 인스턴스 ID와 Public IP, 그리고 4개 AZ의 퍼블릭/프라이빗 서브넷 ID를 출력한다. 앞서 인프라 레이어에서 서브넷이 하나가 아니라고 적은 근거가 이 목록이다.
PublicIP 값을 복사해 SSH로 붙는다.
# 다운로드한 개인 키로 워크샵 인스턴스에 접속
~$ MYEC2IP=203.0.113.20 # Outputs의 PublicIP 값으로 교체
~$ ssh -i ws-default-keypair.pem ubuntu@$MYEC2IP
붙은 뒤 인스턴스가 어떤 장비인지 확인한다.
ubuntu@ip-10-0-1-100:~$ whoami
ubuntu
ubuntu@ip-10-0-1-100:~$ hostnamectl
# 실행 결과 (식별자 행은 제외)
Static hostname: ip-10-0-1-100
Icon name: computer-vm
Chassis: vm
Virtualization: amazon
Operating System: Ubuntu 22.04.5 LTS
Kernel: Linux 6.8.0-1035-aws
Architecture: x86-64
Hardware Vendor: Amazon EC2
Hardware Model: t3.2xlarge # 아키텍처 그림의 Workshop Instance와 같은 타입
ubuntu@ip-10-0-1-100:~$ df -hT
# 실행 결과 (루트 파일 시스템만 발췌)
Filesystem Type Size Used Avail Use% Mounted on
/dev/root ext4 117G 2.9G 114G 3% /
htop으로 보면 vCPU 8개, 메모리 31GB다. t3.2xlarge 스펙과 일치한다.

네트워크 인터페이스를 보면 ens5(VPC 인터페이스)와 함께 docker0 브리지가 이미 올라와 있고, Docker Engine도 설치돼 있다. 이 인스턴스가 클러스터 워커 노드가 아니라 이미지 빌드와 도구 실행을 위한 작업 노드라는 뜻이다.
ip -c addr 전체 출력
ubuntu@ip-10-0-1-100:~$ ip -c addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
link/ether 02:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
altname enp0s5
inet 10.0.1.100/24 metric 100 brd 10.0.1.255 scope global dynamic ens5
valid_lft 3384sec preferred_lft 3384sec
inet6 fe80::xx:xxff:fexx:xxxx/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
docker info 전체 출력
ubuntu@ip-10-0-1-100:~$ docker info
Client: Docker Engine - Community
Version: 29.8.0
Context: default
Debug Mode: false
Plugins:
buildx: Docker Buildx (Docker Inc.)
Version: v0.37.0
Path: /usr/libexec/docker/cli-plugins/docker-buildx
compose: Docker Compose (Docker Inc.)
Version: v5.5.1
Path: /usr/libexec/docker/cli-plugins/docker-compose
model: Docker Model Runner (Docker Inc.)
Version: v1.2.6
Path: /usr/libexec/docker/cli-plugins/docker-model
Server:
Containers: 0
Running: 0
Paused: 0
Stopped: 0
Images: 0
Server Version: 29.8.0
Storage Driver: overlayfs
driver-type: io.containerd.snapshotter.v1
Logging Driver: json-file
Cgroup Driver: systemd
Cgroup Version: 2
Plugins:
Volume: local
Network: bridge host ipvlan macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local splunk syslog
CDI spec directories:
/etc/cdi
/var/run/cdi
Swarm: inactive
Runtimes: io.containerd.runc.v2 runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 1294c24a7da8e5a793ed378161673abe94118892
runc version: v1.5.1-0-g8f2685a4
init version: de40ad0
Security Options:
apparmor
Profile: default
seccomp
Profile: builtin
cgroupns
Kernel Version: 6.8.0-1035-aws
Operating System: Ubuntu 22.04.5 LTS
OSType: linux
Architecture: x86_64
CPUs: 8
Total Memory: 30.98GiB
Name: ip-10-0-1-100
ID: <docker-id>
Docker Root Dir: /var/lib/docker
Debug Mode: false
Experimental: false
Insecure Registries:
::1/128
127.0.0.0/8
Live Restore Enabled: false
Firewall Backend: iptables
EnableUserlandProxy: true
UserlandProxyPath: /usr/bin/docker-proxy
홈 디렉터리에는 workshop/ 디렉터리가 하나 있고, 그 안에 .env 파일 하나만 들어 있다. 실습에 쓸 도구로 tree와 jq를 설치해 두면 이후 확인 작업이 편하다.
# 실습용 도구 설치
ubuntu@ip-10-0-1-100:~$ sudo apt update && sudo apt install tree jq -y
# 워크샵 디렉터리 구조 확인
ubuntu@ip-10-0-1-100:~$ tree -a workshop/
workshop/
└── .env
0 directories, 1 file
Hugging Face 토큰 설정
workshop/.env에는 HF_TOKEN이 미리 들어 있다. 다만 만료된 값이라 그대로 쓰면 모델 다운로드 단계에서 실패한다. Hugging Face에서 토큰을 직접 발급받아 교체해 둔다.

# 미리 들어 있는 토큰 확인 (만료된 값)
ubuntu@ip-10-0-1-100:~$ cat workshop/.env
HF_TOKEN="hf_xxxxxxxxxxxxxxxxxxxx"
# 직접 발급받은 토큰으로 교체
ubuntu@ip-10-0-1-100:~$ rm -f /home/ubuntu/workshop/.env
ubuntu@ip-10-0-1-100:~$ echo 'HF_TOKEN="hf_xxxxxxxxxxxxxxxxxxxx"' > /home/ubuntu/workshop/.env
정리
- 워크샵은
trn1.2xlarge위에 vLLM과 NxD로TinyLlama-1.1B-Chat-v1.0을 서빙하고, 모니터링과 HPA, 부하 테스트까지 이어 간다 - 접속 시점에 이미 만들어져 있는 것은 VPC와 서브넷, 보안 그룹, 워크샵 인스턴스, 그리고 EKS 1.33 컨트롤 플레인이다. 직접 만드는 것은 노드 그룹부터다
- 가속기 통합은 두 축으로 나뉜다. Neuron device plugin이 칩과 코어를 확장 자원으로 광고하고, Neuron scheduler extension이 이중 회계와 연속 코어 할당을 처리한다
- 서빙 파드는 init container로 모델을 컴파일해 S3에 올려 두고, 이후 파드는 S3 CSI Driver로 마운트한 PV에서 산출물을 재사용한다
- 이 글에서 확정하지 않은 항목이 둘 있다. vLLM Service의 타입과 노드 AMI의 Neuron 이미지 pre-pull 여부는 각각 해당 Lab에서 매니페스트와 노드 상태를 보고 확인한다
- 가속기와 Neuron SDK 배경은 8.1편, 노드 그룹 구축과 트러블슈팅은 8.2.1편, Neuron 자원 노출 검증은 8.2.2편에서 이어 간다
참고 링크
- AWS Neuron Documentation
- neuronx-distributed (GitHub)
- vLLM Documentation
- Mountpoint for Amazon S3 CSI Driver (GitHub)
- Amazon EKS User Guide: What is Amazon EKS
- EKS 개요
- EKS 데이터 플레인 컴퓨팅
- EKS와 바닐라 K8s 비교
- EKS VPC CNI
- GPU 파드 Pending 트러블슈팅
- GPU 자원과 K8s 할당
댓글남기기