이 튜토리얼에서는 Ray Serve LLM, vLLM, vLLM-TorchTPU를 사용하여 Google Kubernetes Engine (GKE)의 멀티 호스트 Cloud TPU 슬라이스에서 텐서 동시 로드 및 전문가 동시 로드를 통해 전문가 혼합 (MoE) Gemma 4 26B-A4B-it 대규모 언어 모델 (LLM)을 서빙하는 방법을 보여줍니다.
이 튜토리얼은 Kubernetes 컨테이너 조정을 사용하여 분산 멀티 호스트 TPU 슬라이스에서 AI 및 ML 워크로드를 서빙하는 머신러닝 (ML) 엔지니어, 플랫폼 관리자 및 운영자, 데이터 및 AI 전문가를 대상으로 합니다. Google Cloud 콘텐츠에서 언급된 일반적인 역할 및 예시 태스크에 대해 자세히 알아보려면 일반 GKE 사용자 역할 및 태스크를 참조하세요.
이 튜토리얼을 시작하기 전에 다음 사항을 숙지해야 합니다.
배경
이 섹션에서는 이 튜토리얼에서 사용되는 주요 기술을 설명합니다.
TPU
Tensor Processing Unit (TPU)은 머신러닝 워크로드를 위해 Google에서 커스텀 설계한 가속기입니다. 이 튜토리얼은 TPU Trillium (v6e) 및 Ironwood (TPU7x)를 모두 지원합니다. 멀티 호스트 TPU 슬라이스에서는 여러 TPU 호스트가 고속 칩 간 상호 연결 (ICI) 네트워크를 통해 직접 통신하여 단일 칩 또는 TPU 호스트의 메모리 용량을 초과하는 모델을 서빙하는 데 필요한 집계 고대역폭 메모리 (HBM) 및 짧은 지연 시간의 집합 통신을 제공합니다.
vLLM 및 TorchTPU
vLLM은 LLM을 위한 오픈소스 추론 및 서빙 엔진입니다. TorchTPU는 ICI를 통한 기기 관리 및 집합 통신을 제공하는 Cloud TPU용 오픈소스 PyTorch 백엔드입니다.
vLLM-TorchTPU는 vLLM에 TPU 플랫폼 플러그인을 등록하고 TorchTPU 및 가속 선형 대수 (XLA) 컴파일러를 통해 Cloud TPU의 PyTorch 모델 정방향 전달을 컴파일합니다. Ray를 사용하여 멀티 호스트 TPU 슬라이스 전반에서 분산 텐서 동시 로드 및 전문가 동시 로드를 지원합니다.
GKE의 Ray Serve LLM
Ray Serve LLM은 /v1/chat/completions, /v1/completions, /v1/models 엔드포인트에 OpenAI 호환 HTTP 인그레스를 제공하고 GKE에서 분산 vLLM 배포를 조정합니다. GKE 기반 Ray 부가기능을 사용 설정하면 KubeRay 연산자와 TPU 웹훅이 슬라이스 토폴로지 메타데이터, 워커 호스트 이름, TorchTPU 조정 변수를 각 멀티 호스트 TPU 워커 포드에 삽입합니다.
Ray Serve LLM은 멀티 호스트 TPU 서빙을 위해 다음과 같은 기능을 제공합니다.
- 토폴로지 인식 슬라이스 배치는
accelerator_config필드에 TPU 토폴로지를 지정할 때 각 모델 복제본에 대해 손상되지 않은 멀티 호스트 TPU 슬라이스를 예약하여 슬라이스 전반에서 TPU 기기당 하나의 vLLM 작업자를 예약합니다. - 프리픽스 인식 라우팅은 여러 TPU 슬라이스 복제본에서 서빙할 때 공유 시스템 프롬프트 또는 멀티턴 대화 기록이 있는 요청을 TPU HBM에 해당 프리픽스 키-값 (KV) 블록을 이미 캐시하는 복제본으로 전달합니다.
- 직접 토큰 스트리밍은 동시 요청 전반에서 아웃바운드 토큰 스트림을 일괄 처리하고 모델 복제본에서 직접 응답을 스트리밍하여 첫 번째 토큰까지의 시간 (TTFT)과 토큰 간 지연 시간 (ITL)을 줄입니다.
- 동시 실행 기반 복제본 자동 확장은 진행 중인 요청 동시 실행을 기반으로 전체 슬라이스 단위로 모델 복제본을 확장합니다.
목표
이 튜토리얼에서는 다음 작업을 처리하는 방법을 보여줍니다.
- 사전 빌드된 vLLM-TorchTPU 기본 이미지 위에 Ray Serve를 레이어링하는 컨테이너 이미지를 빌드하고 Artifact Registry에 푸시합니다.
- TPU Trillium (v6e) 또는 Ironwood (TPU7x)에서 Autopilot 또는 Standard로 GKE 클러스터를 만듭니다.
- 멀티 호스트 RayCluster 커스텀 리소스를 배포하고 TPU 토폴로지 구성을 확인합니다.
- Ray Serve LLM 애플리케이션을 배포하여 멀티 호스트 TPU 슬라이스에서 텐서 동시 로드 및 전문가 동시 로드를 통해
google/gemma-4-26B-A4B-it모델을 서빙하거나 RayService 커스텀 리소스를 사용하여 클러스터와 애플리케이션을 모두 선언적으로 배포합니다. - OpenAI 호환 모델 검색 및 채팅 완료 요청을 서빙된 모델로 전송합니다.
비용
이 튜토리얼에서는 비용이 청구될 수 있는 Google Cloud의 구성요소를 사용합니다.
프로젝트 사용량을 기준으로 예상 비용을 산출하려면 가격 계산기를 사용합니다.
이 튜토리얼에 설명된 태스크를 완료했으면 만든 리소스를 삭제하여 청구가 계속되는 것을 방지할 수 있습니다. 자세한 내용은 삭제를 참조하세요.
시작하기 전에
시작하기 전에 다음 태스크를 수행했는지 확인합니다.
- Artifact Registry API 및 Google Kubernetes Engine API를 사용 설정합니다. API 사용 설정
- 이 태스크에 Google Cloud CLI를 사용하려면 gcloud CLI를 설치한 후 초기화합니다. 이전에 gcloud CLI를 설치했으면
gcloud components update명령어를 실행하여 최신 버전을 가져옵니다. 이전 gcloud CLI 버전에서는 이 문서의 명령어를 실행하지 못할 수 있습니다.
- 선택한 영역에서 TPU Trillium (v6e) 또는 Ironwood (TPU7x) 용량에 대한 프로젝트 할당량이 충분한지 확인합니다. 자세한 내용은 Cloud TPU 할당량을 참조하세요.
- Hugging Face 계정이 있는지 확인하고,
google/gemma-4-26B-A4B-it모델 페이지에서 라이선스 약관에 동의하고,Read권한이 있는 Hugging Face 액세스 토큰을 생성합니다. - 계정에 다음 IAM 역할이 있는지 확인합니다.
- Kubernetes Engine 관리자(
roles/container.admin) - Artifact Registry 관리자 (
roles/artifactregistry.admin)
- Kubernetes Engine 관리자(
개발 환경 준비
이 튜토리얼에서는 Cloud Shell 또는 로컬 워크스테이션을 사용하여 Google Cloud에서 호스팅되는 리소스를 관리합니다. Cloud Shell에는 kubectl, docker, python3, gcloud CLI를 비롯해 이 튜토리얼에 사용되는 명령줄 도구가 사전 설치되어 있습니다.
Cloud Shell로 환경을 설정하려면 다음 단계를 따르세요.
Google Cloud 콘솔의 경우 Google Cloud 콘솔에서
Cloud Shell 활성화를 클릭하여 Cloud Shell 세션을 시작합니다. 이 작업을 수행하면 Google Cloud 콘솔의 하단 창에서 세션이 실행됩니다.
kubernetes-engine-samples저장소를 클론하고 이 튜토리얼의 샘플 디렉터리로 변경합니다.git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples.git cd kubernetes-engine-samples/ai-ml/gke-ray/rayserve/llm/torchtpuRay CLI가 설치된 로컬 Python 가상 환경을 만들고 활성화합니다.
python3 -m venv ~/ray-env source ~/ray-env/bin/activate pip install "ray[default]==2.58.0"Google Cloud 프로젝트 및 클러스터의 환경 변수를 설정합니다.
export PROJECT_ID=PROJECT_ID export CLUSTER_NAME=CLUSTER_NAME export REGION=REGION export ZONE=ZONE export ARTIFACT_REGISTRY=ARTIFACT_REGISTRY export HF_TOKEN=HF_TOKEN export IMAGE_URI=${REGION}-docker.pkg.dev/${PROJECT_ID}/${ARTIFACT_REGISTRY}/ray-torchtpu-vllm:latest gcloud config set project ${PROJECT_ID}다음을 바꿉니다.
PROJECT_ID: Google Cloud 프로젝트 ID입니다.CLUSTER_NAME: GKE 클러스터의 이름입니다(예:ray-serve-torchtpu-cluster).REGION: Artifact Registry 저장소와 TPU 용량이 있는 리전(예:us-east5)ZONE: TPU 용량을 사용할 수 있는 영역입니다(예:us-east5-b). 자세한 내용은 GKE에서 TPU 가용성 확인을 참조하세요.ARTIFACT_REGISTRY: Artifact Registry Docker 저장소의 이름입니다(예:ray-torchtpu).HF_TOKEN:Read권한이 있는 Hugging Face 액세스 토큰입니다.
커스텀 컨테이너 이미지 빌드
TorchTPU, vLLM, vLLM-TorchTPU 플러그인이 포함된 사전 빌드된 vLLM-TorchTPU 기본 이미지 위에 ray[default,serve] 패키지를 설치하는 컨테이너 이미지를 빌드합니다.
kubernetes-engine-samples/ai-ml/gke-ray/rayserve/llm/torchtpu디렉터리에서Dockerfile를 검토합니다.Ray Serve LLM을 사용한 멀티 호스트 TPU 서빙에는 TPU Trillium (v6e)의 경우 Ray 버전 2.56.0 이상, Ironwood (TPU7x)의 칩렛별 예약의 경우 Ray 버전 2.58.0 이상이 필요합니다.
Artifact Registry Docker 저장소를 만들고 컨테이너 이미지를 빌드한 후 Artifact Registry에 푸시합니다.
gcloud artifacts repositories create ${ARTIFACT_REGISTRY} \ --repository-format=docker \ --location=${REGION} \ --description="Ray and vLLM-TorchTPU container images" gcloud auth configure-docker ${REGION}-docker.pkg.dev,us-docker.pkg.dev docker build -t ${IMAGE_URI} . docker push ${IMAGE_URI}
GKE 클러스터 만들기
관리형 Ray 연산자 부가기능이 사용 설정된 GKE Autopilot 또는 Standard 클러스터의 멀티 호스트 TPU 슬라이스에서 LLM을 서빙할 수 있습니다.
GKE 클러스터를 만들고
kubectl명령줄 도구의 사용자 인증 정보를 구성합니다.Autopilot
Ray Operator 부가기능을 사용 설정하여 리전 GKE Autopilot 클러스터를 만듭니다.
gcloud container clusters create-auto ${CLUSTER_NAME} \ --project=${PROJECT_ID} \ --region=${REGION} \ --cluster-version=1.36 \ --enable-ray-operator이후 단계에서 RayCluster 또는 RayService 커스텀 리소스를 배포하면 GKE Autopilot은 포드 리소스 요청 및 노드 선택기와 일치하는 CPU 헤드 노드와 멀티 호스트 TPU 슬라이스를 자동으로 프로비저닝합니다.
클러스터와 통신하도록
kubectl명령줄 도구를 구성합니다.gcloud container clusters get-credentials ${CLUSTER_NAME} \ --project=${PROJECT_ID} \ --region=${REGION}
표준
Ray 헤드 포드, 이미지 스트리밍, GKE Dataplane V2가 사용 설정된 CPU 노드 풀이 있는 영역 GKE Standard 클러스터를 만듭니다.
gcloud container clusters create ${CLUSTER_NAME} \ --project=${PROJECT_ID} \ --zone=${ZONE} \ --cluster-version=1.36 \ --machine-type=n2-standard-8 \ --num-nodes=1 \ --addons=RayOperator \ --enable-image-streaming \ --enable-dataplane-v2클러스터와 통신하도록
kubectl명령줄 도구를 구성합니다.gcloud container clusters get-credentials ${CLUSTER_NAME} \ --project=${PROJECT_ID} \ --zone=${ZONE}TPU Trillium (v6e)의 호스트 4개에 걸친
4x4토폴로지 또는 Ironwood (TPU7x)의 호스트 2개에 걸친2x2x2토폴로지를 사용하여 16개 기기 멀티 호스트 TPU 슬라이스 노드 풀을 만듭니다.Trillium(v6e)
gcloud container node-pools create tpu-multi-host-pool \ --project=${PROJECT_ID} \ --cluster=${CLUSTER_NAME} \ --zone=${ZONE} \ --machine-type=ct6e-standard-4t \ --tpu-topology=4x4 \ --num-nodes=4Ironwood (TPU7x)
gcloud container node-pools create tpu-multi-host-pool \ --project=${PROJECT_ID} \ --cluster=${CLUSTER_NAME} \ --zone=${ZONE} \ --machine-type=tpu7x-standard-4t \ --tpu-topology=2x2x2 \ --num-nodes=2
모델 가중치를 다운로드할 때 Ray 포드가 Hugging Face로 인증할 수 있도록 Hugging Face 토큰으로 Kubernetes 보안 비밀을 만듭니다.
kubectl create secret generic hf-secret \ --from-literal=hf_api_token=${HF_TOKEN}
멀티 호스트 RayCluster 커스텀 리소스 배포
CPU 헤드 포드, Ray 공유 메모리 객체 스토어용 메모리 내 /dev/shm 볼륨, 멀티 호스트 TPU 워커 그룹을 프로비저닝하는 RayCluster 커스텀 리소스를 배포합니다.
샘플 저장소에서 TPU 버전에 대한 RayCluster 매니페스트를 검토합니다.
Trillium(v6e)
Ironwood (TPU7x)
Ironwood (TPU7x)에서는 각 물리적 칩에 2개의 칩렛이 포함되어 있어 4칩 호스트당 8개의 논리적 TPU 코어를 제공합니다. 매니페스트는
resources섹션에서TPU필드의 값을"8"로 설정하고RAY_TPU_RESOURCE_PER_CHIP환경 변수를"2"로 설정하여 호스트당 8개의 논리적TPU리소스를 공지하고 예약합니다.매니페스트를 적용하여 RayCluster 커스텀 리소스를 만듭니다.
Trillium(v6e)
envsubst < ray-cluster.tpu-v6e-multihost.yaml | kubectl apply -f -Ironwood (TPU7x)
envsubst < ray-cluster.tpu-v7x-multihost.yaml | kubectl apply -f -헤드 포드와 모든 TPU 워커 포드가
Running및1/1 READY상태가 될 때까지 기다립니다.kubectl get pods -l ray.io/cluster=ray-serve-torchtpu출력은 다음과 비슷합니다.
Trillium(v6e)
NAME READY STATUS RESTARTS AGE ray-serve-torchtpu-head-5cj7g 1/1 Running 0 35s ray-serve-torchtpu-v6e-16-worker-6x8cg 1/1 Running 0 35s ray-serve-torchtpu-v6e-16-worker-79j5l 1/1 Running 0 35s ray-serve-torchtpu-v6e-16-worker-psxbl 1/1 Running 0 35s ray-serve-torchtpu-v6e-16-worker-rz7dv 1/1 Running 0 35sIronwood (TPU7x)
NAME READY STATUS RESTARTS AGE ray-serve-torchtpu-head-w7b2v 1/1 Running 0 2m30s ray-serve-torchtpu-tpu7x-16-worker-6zlnj 1/1 Running 0 2m30s ray-serve-torchtpu-tpu7x-16-worker-cb4zz 1/1 Running 0 2m30sKubeRay TPU 웹훅이 멀티 호스트 슬라이스, 워커 조정, TorchTPU 환경 변수를 워커 포드에 삽입했는지 확인합니다.
kubectl get pods -l ray.io/node-type=worker \ -o=jsonpath='{range .items[0].spec.containers[0].env[*]}{.name}={.value}{"\n"}{end}' \ | grep -E "TPU_WORKER_ID|TPU_WORKER_HOSTNAMES|TPU_NAME|TORCH_TPU_TOPOLOGY|TORCH_TPU_SLICEBUILDER_ADDRESSES"출력은 다음과 비슷합니다.
Trillium(v6e)
TPU_WORKER_HOSTNAMES=v6e-16-0-0.ray-serve-torchtpu-headless,v6e-16-0-1.ray-serve-torchtpu-headless,v6e-16-0-2.ray-serve-torchtpu-headless,v6e-16-0-3.ray-serve-torchtpu-headless TORCH_TPU_TOPOLOGY=4,4,1 TORCH_TPU_SLICEBUILDER_ADDRESSES=v6e-16-0-0.ray-serve-torchtpu-headless:8471,v6e-16-0-0.ray-serve-torchtpu-headless:8472,v6e-16-0-0.ray-serve-torchtpu-headless:8473,v6e-16-0-0.ray-serve-torchtpu-headless:8474,v6e-16-0-1.ray-serve-torchtpu-headless:8471,v6e-16-0-1.ray-serve-torchtpu-headless:8472,v6e-16-0-1.ray-serve-torchtpu-headless:8473,v6e-16-0-1.ray-serve-torchtpu-headless:8474,v6e-16-0-2.ray-serve-torchtpu-headless:8471,v6e-16-0-2.ray-serve-torchtpu-headless:8472,v6e-16-0-2.ray-serve-torchtpu-headless:8473,v6e-16-0-2.ray-serve-torchtpu-headless:8474,v6e-16-0-3.ray-serve-torchtpu-headless:8471,v6e-16-0-3.ray-serve-torchtpu-headless:8472,v6e-16-0-3.ray-serve-torchtpu-headless:8473,v6e-16-0-3.ray-serve-torchtpu-headless:8474 TPU_WORKER_ID=1 TPU_NAME=v6e-16-0Ironwood (TPU7x)
TPU_WORKER_HOSTNAMES=tpu7x-16-0-0.ray-serve-torchtpu-headless,tpu7x-16-0-1.ray-serve-torchtpu-headless TORCH_TPU_TOPOLOGY=2,2,2,2 TORCH_TPU_SLICEBUILDER_ADDRESSES=tpu7x-16-0-0.ray-serve-torchtpu-headless:8471,tpu7x-16-0-0.ray-serve-torchtpu-headless:8472,tpu7x-16-0-0.ray-serve-torchtpu-headless:8473,tpu7x-16-0-0.ray-serve-torchtpu-headless:8474,tpu7x-16-0-0.ray-serve-torchtpu-headless:8475,tpu7x-16-0-0.ray-serve-torchtpu-headless:8476,tpu7x-16-0-0.ray-serve-torchtpu-headless:8477,tpu7x-16-0-0.ray-serve-torchtpu-headless:8478,tpu7x-16-0-1.ray-serve-torchtpu-headless:8471,tpu7x-16-0-1.ray-serve-torchtpu-headless:8472,tpu7x-16-0-1.ray-serve-torchtpu-headless:8473,tpu7x-16-0-1.ray-serve-torchtpu-headless:8474,tpu7x-16-0-1.ray-serve-torchtpu-headless:8475,tpu7x-16-0-1.ray-serve-torchtpu-headless:8476,tpu7x-16-0-1.ray-serve-torchtpu-headless:8477,tpu7x-16-0-1.ray-serve-torchtpu-headless:8478 TPU_WORKER_ID=1 TPU_NAME=tpu7x-16-0Ray 대시보드의 클러스터 탭에서 TPU 워커 노드와 자동 감지된
ray.io/tpu-*라벨을 검사할 수 있습니다.로컬 포트를 포트 8265의 Ray 대시보드와 포트 8000의 Ray Serve HTTP 엔드포인트로 전달합니다.
kubectl port-forward svc/ray-serve-torchtpu-head-svc 8265:8265 8000:8000 >/dev/null 2>&1 &
Ray Serve LLM 애플리케이션 만들기 및 배포
RayCluster 커스텀 리소스에 Ray Serve LLM 애플리케이션을 배포하여 멀티 호스트 TPU 슬라이스에서 google/gemma-4-26B-A4B-it 모델을 서빙합니다.
샘플 저장소에서
serve_vllm_torchtpu.py스크립트를 검토합니다.스크립트는 멀티 호스트 TPU 서빙을 위해 다음 설정으로
LLMConfig객체를 구성합니다.accelerator_config및placement_group_config필드는 단일 TPU 슬라이스에 배치 그룹을 예약하고 슬라이스 전체에서 vLLM 워커당 TPU 코어 하나를 할당합니다.engine_kwargs필드는 텐서 동시 로드 및 전문가 동시 로드를 사용하여 16개의 TPU 코어 모두에서 모델을 샤딩하고 프리픽스 캐싱을 사용 설정합니다. 또한 텍스트 추론을 위해 TPU HBM을 예약하도록 멀티모달 입력을 사용 중지하고, Gemma 4 어텐션 레이어에 통합 KV 캐시 레이아웃을 사용하며, 컨텍스트 길이를 8,192 토큰으로, 프리필 배치 크기를 4,096 토큰으로 제한합니다.experimental_configs필드는 10ms마다 아웃바운드 토큰 스트림을 일괄 처리하여 동시 요청 전반에서 스트리밍 오버헤드를 줄입니다.runtime_env필드는 vLLM-TorchTPU (TPU_MULTIHOST_BACKEND: "ray")에 대해 Ray 멀티 호스트 백엔드를 사용 설정하고, SparseCore 대신 TPU TensorCore에서 Gemma 4 MoE 전문가 레이어를 실행하며, 이 설정을 모든 16개 워커 액터에 전파합니다.
serve run명령어를 사용하여 RayCluster 커스텀 리소스에 Ray Serve 애플리케이션을 배포합니다. 이 명령어는 모델 가중치 로드가 완료되고, 16개의 모든 TPU 기기에서 XLA 그래프 컴파일이 완료되고, 서빙 애플리케이션이RUNNING상태에 도달할 때까지 배포 로그를 스트리밍합니다.Trillium(v6e)
ray job submit \ --address http://localhost:8265 \ --working-dir . \ --runtime-env-json '{"excludes": ["ray-env", ".git", "*.whl"]}' \ -- serve run --non-blocking serve_vllm_torchtpu:app accelerator_type=TPU-V6E topology=4x4Ironwood (TPU7x)
ray job submit \ --address http://localhost:8265 \ --working-dir . \ --runtime-env-json '{"excludes": ["ray-env", ".git", "*.whl"]}' \ -- serve run --non-blocking serve_vllm_torchtpu:app accelerator_type=TPU-V7X topology=2x2x2출력은 다음과 비슷합니다.
Trillium(v6e)
(ServeController pid=1637) INFO 2026-10-13 10:23:05,160 controller 1637 -- Deploying new version of Deployment(name='LLMServer:google--gemma-4-26B-A4B-it', app='default') (initial target replicas: 1). (ServeController pid=1637) INFO 2026-10-13 10:23:05,161 controller 1637 -- Deploying new version of Deployment(name='OpenAiIngress', app='default') (initial target replicas: 1). (ServeReplica:default:LLMServer:google--gemma-4-26B-A4B-it pid=515, ip=10.72.3.21) INFO 10-13 10:24:06 [__init__.py:318] TPU info: node_name=v6e-16-0 | tpu_type=v6e-16 | worker_id=1 | num_chips=4 | num_cores_per_chip=1 (ServeReplica:default:LLMServer:google--gemma-4-26B-A4B-it pid=515, ip=10.72.3.21) INFO 10-13 10:24:06 [__init__.py:238] Platform plugin torchtpu is activated (ServeReplica:default:LLMServer:google--gemma-4-26B-A4B-it pid=515, ip=10.72.3.21) (EngineCore pid=1041) INFO 10-13 10:24:08 [core.py:112] Initializing a V1 LLM engine (v0.22.1) with config: model='google/gemma-4-26B-A4B-it', speculative_config=None, tokenizer='google/gemma-4-26B-A4B-it', skip_tokenizer_init=False, tokenizer_mode=auto, revision=None, tokenizer_revision=None, trust_remote_code=False, dtype=torch.bfloat16, max_seq_len=8192, download_dir=None, load_format=auto, tensor_parallel_size=16, pipeline_parallel_size=1, data_parallel_size=1, decode_context_parallel_size=1, dcp_comm_backend=ag_rs, disable_custom_all_reduce=True, quantization=None, quantization_config=None, enforce_eager=False, enable_return_routed_experts=False, kv_cache_dtype=auto, device_config=tpu, ... (ServeReplica:default:LLMServer:google--gemma-4-26B-A4B-it pid=515, ip=10.72.3.21) (EngineCore pid=1041) (RayWorkerWrapper pid=1017, ip=10.72.14.21) INFO 10-13 10:24:30 [expert_map_manager.py:245] [EP Rank 4/16] Expert parallelism is enabled. Expert placement strategy: linear. Local/global number of experts: 8/128. Experts local to global index map: 0->32, 1->33, 2->34, 3->35, 4->36, 5->37, 6->38, 7->39. (ServeReplica:default:LLMServer:google--gemma-4-26B-A4B-it pid=515, ip=10.72.3.21) (EngineCore pid=1041) (RayWorkerWrapper pid=856, ip=10.72.6.21) INFO 10-13 10:27:55 [default_loader.py:397] Loading weights took 12.76 seconds (ServeReplica:default:LLMServer:google--gemma-4-26B-A4B-it pid=515, ip=10.72.3.21) (EngineCore pid=1041) (RayWorkerWrapper pid=856, ip=10.72.6.21) INFO 10-13 10:32:53 [tpu_worker.py:376] Memory statistics | total_hbm_limit_gb=31.25GiB | total_hbm_limit_cap_gb=28.75GiB | total_hbm_used_gb=3.69GiB | kv_cache_headroom_gb=5.0GiB | kv_connector_hbm_reserve_gb=0.0GiB | total_hbm_avail_gb=20.06GiB (ServeReplica:default:LLMServer:google--gemma-4-26B-A4B-it pid=515, ip=10.72.3.21) (EngineCore pid=1041) INFO 10-13 10:32:54 [kv_cache_utils.py:1733] GPU KV cache size: 600,832 tokens (ServeReplica:default:LLMServer:google--gemma-4-26B-A4B-it pid=515, ip=10.72.3.21) (EngineCore pid=1041) INFO 10-13 10:32:54 [kv_cache_utils.py:1734] Maximum concurrency for 8,192 tokens per request: 73.34x (ServeReplica:default:LLMServer:google--gemma-4-26B-A4B-it pid=515, ip=10.72.3.21) INFO 2026-10-13 10:36:59,851 default_LLMServer:google--gemma-4-26B-A4B-it b2grwv9c -- Started vLLM engine. INFO 2026-10-13 10:37:05,997 serve 1456 -- Application 'default' is ready at http://127.0.0.1:8000/. 2026-10-13 10:37:11,447 SUCC cli.py:66 -- ------------------------------------------ 2026-10-13 10:37:11,447 SUCC cli.py:67 -- Job 'raysubmit_94DTwVzR2pvGQVwR' succeeded 2026-10-13 10:37:11,447 SUCC cli.py:68 -- ------------------------------------------Ironwood (TPU7x)
(ServeController pid=2042) INFO 2026-10-13 10:24:38,092 controller 2042 -- Deploying new version of Deployment(name='LLMServer:google--gemma-4-26B-A4B-it', app='default') (initial target replicas: 1). (ServeController pid=2042) INFO 2026-10-13 10:24:38,093 controller 2042 -- Deploying new version of Deployment(name='OpenAiIngress', app='default') (initial target replicas: 1). (ServeReplica:default:LLMServer:google--gemma-4-26B-A4B-it pid=316, ip=172.16.7.5) INFO 10-13 10:24:49 [__init__.py:318] TPU info: node_name=tpu7x-16-0 | tpu_type=tpu7x-16 | worker_id=0 | num_chips=8 | num_cores_per_chip=2 (ServeReplica:default:LLMServer:google--gemma-4-26B-A4B-it pid=316, ip=172.16.7.5) INFO 10-13 10:24:49 [__init__.py:238] Platform plugin torchtpu is activated (ServeReplica:default:LLMServer:google--gemma-4-26B-A4B-it pid=316, ip=172.16.7.5) (EngineCore pid=719) INFO 10-13 10:25:23 [core.py:112] Initializing a V1 LLM engine (v0.22.1) with config: model='google/gemma-4-26B-A4B-it', speculative_config=None, tokenizer='google/gemma-4-26B-A4B-it', skip_tokenizer_init=False, tokenizer_mode=auto, revision=None, tokenizer_revision=None, trust_remote_code=False, dtype=torch.bfloat16, max_seq_len=8192, download_dir=None, load_format=auto, tensor_parallel_size=16, pipeline_parallel_size=1, data_parallel_size=1, decode_context_parallel_size=1, dcp_comm_backend=ag_rs, disable_custom_all_reduce=True, quantization=None, quantization_config=None, enforce_eager=False, enable_return_routed_experts=False, kv_cache_dtype=auto, device_config=tpu, ... (ServeReplica:default:LLMServer:google--gemma-4-26B-A4B-it pid=316, ip=172.16.7.5) (EngineCore pid=719) (RayWorkerWrapper pid=602, ip=172.16.10.6) INFO 10-13 10:25:44 [expert_map_manager.py:245] [EP Rank 8/16] Expert parallelism is enabled. Expert placement strategy: linear. Local/global number of experts: 8/128. Experts local to global index map: 0->64, 1->65, 2->66, 3->67, 4->68, 5->69, 6->70, 7->71. (ServeReplica:default:LLMServer:google--gemma-4-26B-A4B-it pid=316, ip=172.16.7.5) (EngineCore pid=719) (RayWorkerWrapper pid=762) INFO 10-13 10:29:06 [default_loader.py:397] Loading weights took 11.69 seconds (ServeReplica:default:LLMServer:google--gemma-4-26B-A4B-it pid=316, ip=172.16.7.5) (EngineCore pid=719) (RayWorkerWrapper pid=604, ip=172.16.10.6) INFO 10-13 10:34:51 [tpu_worker.py:376] Memory statistics | total_hbm_limit_gb=94.75GiB | total_hbm_limit_cap_gb=87.17GiB | total_hbm_used_gb=3.74GiB | kv_cache_headroom_gb=5.0GiB | kv_connector_hbm_reserve_gb=0.0GiB | total_hbm_avail_gb=78.43GiB (ServeReplica:default:LLMServer:google--gemma-4-26B-A4B-it pid=316, ip=172.16.7.5) (EngineCore pid=719) INFO 10-13 10:34:58 [kv_cache_utils.py:1733] GPU KV cache size: 2,349,568 tokens (ServeReplica:default:LLMServer:google--gemma-4-26B-A4B-it pid=316, ip=172.16.7.5) (EngineCore pid=719) INFO 10-13 10:34:58 [kv_cache_utils.py:1734] Maximum concurrency for 8,192 tokens per request: 286.81x (ServeReplica:default:LLMServer:google--gemma-4-26B-A4B-it pid=316, ip=172.16.7.5) INFO 2026-10-13 10:40:13,586 default_LLMServer:google--gemma-4-26B-A4B-it a15k5cv3 -- Started vLLM engine. INFO 2026-10-13 10:40:18,415 serve 1953 -- Application 'default' is ready at http://127.0.0.1:8000/. 2026-10-13 10:40:24,372 SUCC cli.py:66 -- ------------------------------------------ 2026-10-13 10:40:24,372 SUCC cli.py:67 -- Job 'raysubmit_mzWQDQGDM9QSgcMN' succeeded 2026-10-13 10:40:24,372 SUCC cli.py:68 -- ------------------------------------------
서빙된 모델 쿼리
Ray Serve 애플리케이션이 RUNNING 상태에 도달하면 등록된 모델 엔드포인트를 확인하고, 포트 8000을 통해 OpenAI 호환 /v1/chat/completions 요청을 전송하고, 원하는 경우 처리량과 지연 시간을 벤치마킹합니다.
모델이
/v1/models엔드포인트에 등록되어 있는지 확인합니다.curl http://localhost:8000/v1/models출력은 다음과 비슷합니다.
{"data":[{"id":"google/gemma-4-26B-A4B-it","object":"model","owned_by":"organization-owner","permission":[],"metadata":{"model_id":"google/gemma-4-26B-A4B-it","input_modality":"text","max_request_context_length":8192}}],"object":"list"}/v1/chat/completions엔드포인트에 채팅 완성 프롬프트를 전송합니다.curl -X POST http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "google/gemma-4-26B-A4B-it", "messages": [ {"role": "system", "content": "You are a helpful AI assistant."}, {"role": "user", "content": "Explain in one sentence how tensor parallelism and expert parallelism accelerate Mixture-of-Experts LLM inference on TPUs."} ], "max_tokens": 128, "temperature": 0.0 }'The output on Ironwood (TPU7x) is similar to the following:
{"id":"chatcmpl-ec30e61c-011d-49cd-989a-9ae9f59448a8","object":"chat.completion","created":1791887860,"model":"google/gemma-4-26B-A4B-it","choices":[{"index":0,"message":{"role":"assistant","content":"Tensor parallelism accelerates inference by partitioning large weight matrices across TPU cores to reduce per-device memory and computation, while expert parallelism distributes the specialized MoE layers across different devices to enable massive model scaling and parallelize the processing of different tokens through different experts.","refusal":null,"annotations":null,"audio":null,"function_call":null,"tool_calls":[],"reasoning":null},"logprobs":null,"finish_reason":"stop","stop_reason":106,"token_ids":null,"routed_experts":null}],"service_tier":null,"system_fingerprint":"vllm-0.22.1-tp16-ep-aaf76e1a","usage":{"prompt_tokens":49,"total_tokens":100,"completion_tokens":51,"prompt_tokens_details":null},"prompt_logprobs":null,"prompt_token_ids":null,"prompt_text":null,"kv_transfer_params":null}선택사항:
vllm bench serve명령어를 사용하여 서빙 처리량과 지연 시간을 벤치마킹합니다. 다음 명령어는 Ray TPU 워커 포드에서vllm bench serveHTTP 부하 생성기 클라이언트를 실행하고 포트 8000의 Ray Serve HTTP 프록시에 동시 요청 64개를 전송합니다.WORKER_POD=$(kubectl get pods -l ray.io/node-type=worker -o jsonpath='{.items[0].metadata.name}') kubectl exec -it ${WORKER_POD} -- vllm bench serve \ --backend openai-chat \ --endpoint /v1/chat/completions \ --model google/gemma-4-26B-A4B-it \ --dataset-name random \ --random-input-len 1024 \ --random-output-len 512 \ --max-concurrency 32 \ --num-prompts 64 \ --num-warmups 4 \ --temperature 0.0 \ --ignore-eos \ --host ray-serve-torchtpu-head-svc \ --port 8000출력은 다음과 비슷합니다.
Trillium(v6e)
============ Serving Benchmark Result ============ Successful requests: 64 Failed requests: 0 Maximum request concurrency: 32 Benchmark duration (s): 8.41 Total input tokens: 66392 Total generated tokens: 32768 Request throughput (req/s): 7.61 Output token throughput (tok/s): 3894.26 Peak output token throughput (tok/s): 4409.00 Peak concurrent requests: 64.00 Total token throughput (tok/s): 11784.50 ---------------Time to First Token---------------- Mean TTFT (ms): 414.57 Median TTFT (ms): 348.94 P99 TTFT (ms): 640.26 -----Time per Output Token (excl. 1st token)------ Mean TPOT (ms): 7.37 Median TPOT (ms): 7.29 P99 TPOT (ms): 8.24 ---------------Inter-token Latency---------------- Mean ITL (ms): 7.37 Median ITL (ms): 0.00 P99 ITL (ms): 25.94 ==================================================Ironwood (TPU7x)
============ Serving Benchmark Result ============ Successful requests: 64 Failed requests: 0 Maximum request concurrency: 32 Benchmark duration (s): 9.65 Total input tokens: 66392 Total generated tokens: 32768 Request throughput (req/s): 6.64 Output token throughput (tok/s): 3397.33 Peak output token throughput (tok/s): 4574.00 Peak concurrent requests: 64.00 Total token throughput (tok/s): 10280.73 ---------------Time to First Token---------------- Mean TTFT (ms): 654.11 Median TTFT (ms): 522.30 P99 TTFT (ms): 1288.23 -----Time per Output Token (excl. 1st token)------ Mean TPOT (ms): 8.13 Median TPOT (ms): 8.08 P99 TPOT (ms): 9.69 ---------------Inter-token Latency---------------- Mean ITL (ms): 8.12 Median ITL (ms): 9.31 P99 ITL (ms): 26.85 ==================================================
RayService 커스텀 리소스를 사용하여 선언적으로 배포
프로덕션 배포의 경우 작업을 수동으로 제출하는 대신 멀티 호스트 Ray 클러스터와 Ray Serve LLM 애플리케이션을 모두 단일 선언적 RayService 커스텀 리소스로 관리할 수 있습니다. Ray Serve LLM 빌더는 서빙 구성에서 직접 모델 구성을 허용하므로 별도의 Python 스크립트가 필요하지 않습니다. KubeRay는 클러스터를 프로비저닝하고, Serve 애플리케이션을 배포하고, 복제본 상태를 모니터링하고, 애플리케이션이 트래픽을 처리할 준비가 되면 포트 8000에서 Kubernetes ClusterIP 서비스를 만듭니다.
이전 섹션의 RayCluster 커스텀 리소스가 아직 실행 중인 경우 포트 전달을 중지하고 RayCluster 커스텀 리소스를 삭제하여 TPU 슬라이스를 해제합니다.
pkill -f "kubectl port-forward" || true kubectl delete raycluster ray-serve-torchtpu --ignore-not-found샘플 저장소에서 TPU 버전에 대한 RayService 매니페스트를 검토합니다.
Trillium(v6e)
Ironwood (TPU7x)
RayService 매니페스트를 적용합니다.
Trillium(v6e)
envsubst < ray-service.tpu-v6e-multihost.yaml | kubectl apply -f -Ironwood (TPU7x)
envsubst < ray-service.tpu-v7x-multihost.yaml | kubectl apply -f -RayService 커스텀 리소스가 TPU 슬라이스에서 모델 가중치 로드 및 XLA 그래프 컴파일을 완료하고
Running상태에 도달할 때까지 기다립니다.kubectl get rayservice ray-serve-torchtpu kubectl get svc ray-serve-torchtpu-serve-svc출력은 다음과 비슷합니다.
NAME SERVICE STATUS NUM SERVE ENDPOINTS ray-serve-torchtpu Running 3 NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ray-serve-torchtpu-serve-svc ClusterIP 34.118.239.213 <none> 8000/TCP 29s로컬 포트 8000을
ray-serve-torchtpu-serve-svc서비스로 전달하고 서빙된 모델 쿼리의curl명령어를 사용하여 모델을 쿼리합니다.kubectl port-forward svc/ray-serve-torchtpu-serve-svc 8000:8000 >/dev/null 2>&1 &
모델 성능 관찰
Cloud Monitoring에서 TPU TensorCore 사용률, HBM 사용량, Ray 클러스터 측정항목을 모니터링하려면 GKE의 Ray 클러스터에 대한 로그 및 측정항목 수집 및 보기 및 Ray 대시보드에서 TPU 측정항목 보기를 참조하세요.
Ray 대시보드 액세스
기본 제공 Ray 대시보드에서 Ray Serve 배포, 복제본 행위자, 노드 라벨, 쿼리 지연 시간의 상태를 검사하려면 다음 단계를 따르세요.
Ray 헤드 서비스에 대한 포트 전달이 아직 실행되고 있지 않으면 백그라운드에서 시작합니다.
kubectl port-forward svc/ray-serve-torchtpu-head-svc 8265:8265 8000:8000 >/dev/null 2>&1 &Cloud Shell에서
웹 미리보기를 클릭하고 포트 8265에서 미리보기를 선택하거나 로컬 브라우저에서
http://localhost:8265를 엽니다.서빙, 클러스터, 측정항목 탭을 클릭하여 vLLM 배포, 복제본 상태, 자동 감지된
ray.io/tpu-*노드 라벨, 요청 지연 시간 측정항목, 노드별 TPU 사용률 그래프를 확인합니다.
삭제
이 튜토리얼에서 사용한 리소스 비용이 Google Cloud 계정에 청구되지 않도록 하려면 RayService 또는 RayCluster 커스텀 리소스, GKE 클러스터, Artifact Registry 저장소를 삭제합니다.
백그라운드 포트 전달 프로세스를 중지하고 Ray 커스텀 리소스와 Kubernetes 보안 비밀을 삭제합니다.
pkill -f "kubectl port-forward" || true kubectl delete rayservice ray-serve-torchtpu --ignore-not-found kubectl delete raycluster ray-serve-torchtpu --ignore-not-found kubectl delete secret hf-secret --ignore-not-foundGKE 클러스터를 삭제합니다.
Autopilot
gcloud container clusters delete ${CLUSTER_NAME} \ --region=${REGION}표준
gcloud container clusters delete ${CLUSTER_NAME} \ --zone=${ZONE}Artifact Registry 저장소를 삭제합니다.
gcloud artifacts repositories delete ${ARTIFACT_REGISTRY} \ --location=${REGION}
다음 단계
- Cloud TPU의 PyTorch에 대해 자세히 알아보세요.
- TorchTPU 문서 살펴보기
- Ray 및 TorchTPU를 사용하여 멀티 호스트 TPU에서 모델을 미세 조정하는 방법 알아보기
- Ray를 사용하여 GKE에서 멀티 호스트 TPU를 통해 Gemma 개방형 모델을 제공하는 방법 알아보기
- KubeRay RayService 커스텀 리소스를 사용하여 선언적으로 Ray Serve 배포를 관리하는 방법을 알아봅니다.
- GKE의 TPU에 대하여를 읽어보세요.
- Ray Serve LLM 문서 살펴보기