This tutorial shows you how to deploy and serve
DeepSeek-R1
(deepseek-ai/DeepSeek-R1), a 671 billion-parameter Mixture-of-Experts
model with 37 billion active parameters, on Google Kubernetes Engine (GKE) on
Google Cloud. The deployment uses Cluster Toolkit, two multi-host
A4 (a4-highgpu-8g)
virtual machine (VM) instances with a total of 16 NVIDIA B200 GPUs, and the
vLLM serving framework.
In the deployment configuration that's described in this tutorial, you use
Cluster Toolkit to provision a GKE cluster in
Standard mode. The cluster has two a4-highgpu-8g nodes connected over
GPUDirect Remote Direct Memory Access (RDMA) networking. You then stage the
model weights in Cloud Storage and use the Kubernetes JobSet API and Ray to
serve the model across all 16 NVIDIA B200 GPUs with tensor parallelism, expert
parallelism, and an FP8 KV cache.
Objectives
- Install Cluster Toolkit and provision a multi-host A4 GKE cluster in Standard mode.
- Stage the DeepSeek-R1 model weights in a Cloud Storage bucket.
- Deploy a multi-host vLLM inference server with JobSet and Ray across two A4 nodes, with a total of 16 NVIDIA B200 GPUs.
- Send a chat completion request to the vLLM server.
- Run a serving benchmark workload.
- Clean up the deployed resources.
Costs
In this document, you use the following billable components of Google Cloud:
To generate a cost estimate based on your projected usage, use the pricing calculator.
Before you begin
Set up your environment for the tutorial:
- Install and initialize the
Google Cloud CLI, and install
kubectl. If you use Cloud Shell, both tools are preinstalled. - If you're running this tutorial from a Linux or macOS workstation rather than Cloud Shell, install the required Cluster Toolkit dependencies (such as Terraform and Git).
Enable the Kubernetes Engine API, Compute Engine API, Cloud Storage API, Artifact Registry API, and Service Usage API in your Google Cloud project:
Get a Hugging Face access token
You need a Hugging Face access token to use this tutorial. You can sign up for a free account at Hugging Face. After you have an account, generate an access token:
- On the Welcome to Hugging Face page, click your account avatar and select Access tokens.
- On the Access tokens page, click Create new token.
- Select the Read token type and enter a name for your token.
- Your access token is displayed. Save the token in a safe place.
Required roles
To get the permissions that you need to complete this tutorial, ask your administrator to grant you the following IAM roles on your project:
- Kubernetes Engine Admin (
roles/container.admin) - Compute Admin (
roles/compute.admin) - Storage Admin (
roles/storage.admin) - Project IAM Admin (
roles/resourcemanager.projectIamAdmin) - Service Account Admin (
roles/iam.serviceAccountAdmin) - Service Account User (
roles/iam.serviceAccountUser) - Service Usage Consumer (
roles/serviceusage.serviceUsageConsumer)
For more information about granting roles, see Manage access to projects, folders, and organizations.
You might also be able to get the required permissions through custom roles or other predefined roles.
Set up your environment variables
Set up your authentication and environment variables for the tutorial:
Set up your authentication:
Export the environment variables that you use throughout this tutorial:
Replace the following:
YOUR_PROJECT_ID: the ID of your Google Cloud project.YOUR_REGION: the compute region where your A4 capacity is located and where you want to deploy your cluster, such asus-central1.YOUR_ZONE: the compute zone where your A4 capacity is located, such asus-central1-b.YOUR_CLUSTER_NAME: the name for your GKE cluster and Cluster Toolkit deployment. Use a name of 20 characters or fewer so that the generated node pool service account ID (YOUR_CLUSTER_NAME-gke-np-sa) stays within the 30-character Identity and Access Management (IAM) limit.YOUR_RESERVATION_NAME: the name of your A4 reservation, or the extended reservation block path in the formatRESERVATION_NAME/reservationBlocks/RESERVATION_BLOCK_NAME.YOUR_HF_TOKEN: your Hugging Face access token.YOUR_GCS_BUCKET: a globally unique name for the Cloud Storage bucket where you stage the model weights. The script also setsTERRAFORM_BUCKETtoYOUR_GCS_BUCKET-tf-stateto store the Terraform state for Cluster Toolkit.
The script also sets
CLUSTER_TOOLKIT_VERSIONtov1.104.0, which specifies the Cluster Toolkit version to install. Cluster Toolkit versionv1.104.0and later include built-in capacity consumption options forexamples/gke-a4/gke-a4-deployment.yaml. In versions earlier thanv1.104.0, you must add these capacity options manually.
Create the GKE cluster
Download Cluster Toolkit and provision a GKE cluster in
Standard mode with two a4-highgpu-8g nodes, GPUDirect RDMA
networking, and the JobSet controller:
Download and extract the prebuilt Cluster Toolkit bundle for the version specified in
CLUSTER_TOOLKIT_VERSION(v1.104.0), change your working directory to thecluster-toolkitdirectory, and verify the binary version:Optionally, view the available commands and flags in Cluster Toolkit:
Create the Cloud Storage bucket that stores the Terraform state for your Cluster Toolkit deployment:
Create the
examples/gke-a4/gke-a4-deployment.yamldeployment configuration file for twoa4-highgpu-8gnodes:By default, the
examples/gke-a4/gke-a4.yamlblueprint is configured to use a specific reservation. If you don't have a specific reservation, you can editexamples/gke-a4/gke-a4.yamlbefore you deploy the cluster to use one of the following consumption options:- Spot capacity: ensure that your project has sufficient
preemptible quota for your GPUs,
disks, and CPUs. At the bottom of
examples/gke-a4/gke-a4.yaml, comment out the--- Option 1: Specific Reservation ---section and uncomment the--- Option 4: Spot ---section. - Flex-start VMs: ensure that your project has sufficient
preemptible quota
for your GPUs, disks, and CPUs. At the bottom of
examples/gke-a4/gke-a4.yaml, comment out the--- Option 1: Specific Reservation ---section and uncomment the--- Option 2: DWS Flex Start ---section.
- Spot capacity: ensure that your project has sufficient
preemptible quota for your GPUs,
disks, and CPUs. At the bottom of
Deploy the GKE cluster by using the
gcluster deploycommand:Cluster creation takes approximately 30 to 45 minutes. If you run
./gcluster deploywithout the--auto-approveflag, then to apply the proposed changes, enterawhen you're prompted to. When deployment finishes, the output ends with a message similar to the following:Apply complete! Resources: 80 added, 0 changed, 0 destroyed.Configure
kubectlcredentials to communicate with your cluster:
Stage the model weights in Cloud Storage
Download the DeepSeek-R1 weights onto the Local SSD of one of your A4 nodes and transfer them to a Cloud Storage bucket for fast streaming across nodes:
Download the Kubernetes manifest files for the model download Job, the vLLM inference server, and the benchmark Pods:
Create the Cloud Storage bucket with uniform bucket-level access to store the model weights:
Grant IAM permissions on the bucket to the default Kubernetes service account, the default Compute Engine service account, and the cluster node pool service account (
${CLUSTER_NAME}-gke-np-sa):The IAM permissions granted by the preceding script include the Storage Legacy Bucket Reader role (
roles/storage.legacyBucketReader), which is required for--load-format runai_streamer.Create a Kubernetes Secret named
hf-secretto store your Hugging Face access token:Review the
download_model.yamlmanifest, or create the file locally if you didn't download it earlier:Substitute your Cloud Storage bucket name into the
download_model.yamlmanifest and start the model download Job:Wait for the download Pod to start, and then stream the Job logs:
It takes approximately 30 to 40 minutes to download the model to the node's Local NVMe SSD and upload the weights to your Cloud Storage bucket.
After the Job has completed, verify that all 163
.safetensorsshards are present in your Cloud Storage bucket:Delete the completed staging Job:
Deploy the multi-host vLLM server
The workload manifest (deepseek_r1_server.yaml) defines a ClusterIP Service
(vllm-r1-leader) and a Kubernetes JobSet (vllm-r1) that coordinate
multi-host serving across the two a4-highgpu-8g nodes:
- Leader (
leader): runs one replica on an A4 node with eight NVIDIA B200 GPUs, sources the NCCL/gIB environment (/usr/local/gib/scripts/set_nccl_env.sh), starts the Ray head node on port6379for a two-node cluster, and launchesvllm serveon HTTP port8080. The server streams weights directly fromgs://$MODEL_BUCKET/DeepSeek-R1by using--load-format runai_streamerand loads the tokenizer from the mounted Cloud Storage FUSE volume at/models/DeepSeek-R1. It runs with--tensor-parallel-size 16,--pipeline-parallel-size 1,--distributed-executor-backend ray,--enable-expert-parallel,--kv-cache-dtype fp8_e4m3,--async-scheduling, and CUDA graphs (FULL_AND_PIECEWISE). - Worker (
worker): runs one headless replica on the second A4 node with eight NVIDIA B200 GPUs, configures NCCL/gIB, and joins the leader's Ray cluster atvllm-r1-leader:6379.
Deploy and monitor the multi-host vLLM server:
Review the
deepseek_r1_server.yamlmanifest, or create the file locally if you didn't download it earlier:To scale this deployment to more hosts, update the parameters in
deepseek_r1_server.yaml. For example, to scale to four A4 nodes, with a total of 32 NVIDIA B200 GPUs, setreplicas: 3under theworkerreplicated Job, and update the leader arguments as follows:--ray_cluster_size=4--tensor-parallel-size=16--pipeline-parallel-size=2
Substitute your Cloud Storage bucket name into
deepseek_r1_server.yamland apply the manifest to your cluster:Monitor the leader and worker Pod logs until server startup has completed:
Because
kubectl logs -fstreams logs in the foreground, run the worker logs command in a separate terminal session if you want to monitor both Pods simultaneously. To stop streaming logs at any time, press Control+C.Initial weight streaming, model loading, and CUDA graph capture take approximately 15 to 20 minutes. Before you send requests, wait until the leader Pod logs output the following messages:
(APIServer pid=1) INFO: Started server process [1] (APIServer pid=1) INFO: Waiting for application startup. (APIServer pid=1) INFO: Application startup complete.
Send inference requests
Send a chat completion request to the deployed vLLM server:
Forward local port
8080to thevllm-r1-leaderService in the background:Send a chat completion request to the
/v1/chat/completionsendpoint:
Run benchmark workloads
Benchmark the deployed DeepSeek-R1 server by using vllm bench serve with a
configuration of 8,192 input tokens and 1,024 output tokens (8k/1k):
Review the
deepseek_r1_8k_1k.yamlmanifest, or create the file locally if you didn't download it earlier:The manifest specifies 8,192 input tokens, 1,024 output tokens, 1,280 prompts, and a maximum concurrency of 112.
Apply the
deepseek_r1_8k_1k.yamlmanifest:Wait for the
vllm-bench-8k-1kPod to start, and then stream the benchmark logs:The benchmark takes approximately 15 minutes to complete. After the benchmark has completed, look for output that contains metrics such as the following:
Output token throughputTotal token throughputTTFTTPOT
Clean up
To avoid incurring additional ongoing charges to your Google Cloud account for the resources used in this tutorial, delete the Kubernetes workloads, the GKE cluster, and your Cloud Storage buckets:
Delete the benchmark Pods, the vLLM server JobSet and Service, the model download Job, and the Hugging Face Secret:
Delete the Cloud Storage bucket that stores the staged model weights:
From the
cluster-toolkitdirectory, destroy the GKE cluster and its associated infrastructure by using thegcluster destroycommand:Delete the Cloud Storage bucket that stores the Terraform state:
What's next
- Learn more about vLLM.
- Learn more about A4 VMs with NVIDIA B200 GPUs.
- Learn more about Cluster Toolkit.
- Learn more about managing AI-optimized GKE clusters.