|
| 1 | +# Single Host Model Serving with NVIDIA TensorRT-LLM (TRT-LLM) on A3mega GKE Node Pool |
| 2 | + |
| 3 | +This document outlines the steps to serve and benchmark various Large Language Models (LLMs) using the [NVIDIA TensorRT-LLM](https://github.com/NVIDIA/TensorRT-LLM) framework on a single [A3-Mega GKE Node pool](https://cloud.google.com/kubernetes-engine). |
| 4 | + |
| 5 | +This guide walks you through setting up the necessary cloud infrastructure, configuring your environment, and deploying a high-performance LLM for inference. |
| 6 | + |
| 7 | +<a name="table-of-contents"></a> |
| 8 | +## Table of Contents |
| 9 | + |
| 10 | +* [1. Test Environment](#test-environment) |
| 11 | +* [2. High-Level Architecture](#architecture) |
| 12 | +* [3. Environment Setup (One-Time)](#environment-setup) |
| 13 | + * [3.1. Clone the Repository](#clone-repo) |
| 14 | + * [3.2. Configure Environment Variables](#configure-vars) |
| 15 | + * [3.3. Connect to your GKE Cluster](#connect-cluster) |
| 16 | + * [3.4. Get Hugging Face Token](#get-hf-token) |
| 17 | + * [3.5. Create Hugging Face Kubernetes Secret](#setup-hf-secret) |
| 18 | +* [4. Run the Recipe](#run-the-recipe) |
| 19 | + * [4.1. Supported Models](#supported-models) |
| 20 | + * [4.2. Deploy and Benchmark a Model](#deploy-model) |
| 21 | +* [5. Monitoring and Troubleshooting](#monitoring) |
| 22 | + * [5.1. Check Deployment Status](#check-status) |
| 23 | + * [5.2. View Logs](#view-logs) |
| 24 | +* [6. Cleanup](#cleanup) |
| 25 | + |
| 26 | +<a name="test-environment"></a> |
| 27 | +## 1. Test Environment |
| 28 | + |
| 29 | +[Back to Top](#table-of-contents) |
| 30 | + |
| 31 | +The recipe uses the following setup: |
| 32 | + |
| 33 | +* **Orchestration**: [Google Kubernetes Engine (GKE)](https://cloud.google.com/kubernetes-engine) |
| 34 | +* **Deployment Configuration**: A [Helm chart](https://helm.sh/) is used to configure and deploy a [Kubernetes Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/). This deployment encapsulates the inference of the target LLM using the TensorRT-LLM framework. |
| 35 | + |
| 36 | +This recipe has been optimized for and tested with the following configuration: |
| 37 | + |
| 38 | +* **GKE Cluster**: |
| 39 | + * A [regional standard cluster](https://cloud.google.com/kubernetes-engine/docs/concepts/configuration-overview) version: `1.33.4-gke.1036000` or later. |
| 40 | + * A GPU node pool with 1 [a3-megagpu-8g](https://docs.cloud.google.com/compute/docs/accelerator-optimized-machines#a3-mega-vms) machine. |
| 41 | + * [Workload Identity Federation for GKE](https://cloud.google.com/kubernetes-engine/docs/concepts/workload-identity) enabled. |
| 42 | + * [Cloud Storage FUSE CSI driver for GKE](https://cloud.google.com/kubernetes-engine/docs/concepts/cloud-storage-fuse-csi-driver) enabled. |
| 43 | + * [DCGM metrics](https://cloud.google.com/kubernetes-engine/docs/how-to/dcgm-metrics) enabled. |
| 44 | + * [Kueue](https://kueue.sigs.k8s.io/docs/reference/kueue.v1beta1/) and [JobSet](https://jobset.sigs.k8s.io/docs/overview/) APIs installed. |
| 45 | + * Kueue configured to support [Topology Aware Scheduling](https://kueue.sigs.k8s.io/docs/concepts/topology_aware_scheduling/). |
| 46 | +* A regional Google Cloud Storage (GCS) bucket to store logs generated by the recipe runs. |
| 47 | + |
| 48 | +> [!IMPORTANT] |
| 49 | +> To prepare the required environment, see the [GKE environment setup guide](../../../../docs/configuring-environment-gke-a3-mega.md). |
| 50 | +> Provisioning a new GKE cluster is a long-running operation and can take **20-30 minutes**. |
| 51 | +
|
| 52 | +<a name="architecture"></a> |
| 53 | +## 2. High-Level Flow |
| 54 | + |
| 55 | +[Back to Top](#table-of-contents) |
| 56 | + |
| 57 | +Here is a simplified diagram of the flow that we follow in this recipe: |
| 58 | + |
| 59 | +```mermaid |
| 60 | +--- |
| 61 | +config: |
| 62 | + layout: dagre |
| 63 | +--- |
| 64 | +flowchart TD |
| 65 | + subgraph workstation["Client Workstation"] |
| 66 | + T["Cluster Toolkit"] |
| 67 | + B("Kubernetes API") |
| 68 | + A["helm install"] |
| 69 | + end |
| 70 | + subgraph huggingface["Hugging Face Hub"] |
| 71 | + I["Model Weights"] |
| 72 | + end |
| 73 | + subgraph gke["GKE Cluster (A3-Mega)"] |
| 74 | + C["Deployment"] |
| 75 | + D["Pod"] |
| 76 | + E["TensorRT-LLM container"] |
| 77 | + F["Service"] |
| 78 | + end |
| 79 | + subgraph storage["Cloud Storage"] |
| 80 | + J["Bucket"] |
| 81 | + end |
| 82 | +
|
| 83 | + %% Logical/actual flow |
| 84 | + T -- Create Cluster --> gke |
| 85 | + A --> B |
| 86 | + B --> C & F |
| 87 | + C --> D |
| 88 | + D --> E |
| 89 | + F --> C |
| 90 | + E -- Downloads at runtime --> I |
| 91 | + E -- Write logs --> J |
| 92 | +
|
| 93 | +
|
| 94 | + %% Layout control |
| 95 | + gke |
| 96 | +``` |
| 97 | + |
| 98 | +* **helm:** A package manager for Kubernetes to define, install, and upgrade applications. It's used here to configure and deploy the Kubernetes Deployment. |
| 99 | +* **Deployment:** Manages the lifecycle of your model server pod, ensuring it stays running. |
| 100 | +* **Service:** Provides a stable network endpoint (a DNS name and IP address) to access your model server. |
| 101 | +* **Pod:** The smallest deployable unit in Kubernetes. The Triton server container with TensorRT-LLM runs inside this pod on a GPU-enabled node. |
| 102 | +* **Cloud Storage:** A Cloud Storage bucket to store benchmark logs and other artifacts. |
| 103 | + |
| 104 | +<a name="environment-setup"></a> |
| 105 | +## 3. Environment Setup (One-Time) |
| 106 | + |
| 107 | +[Back to Top](#table-of-contents) |
| 108 | + |
| 109 | +First, you'll configure your local environment. These steps are required once before you can deploy any models. |
| 110 | + |
| 111 | +<a name="clone-repo"></a> |
| 112 | +### 3.1. Clone the Repository |
| 113 | + |
| 114 | +```bash |
| 115 | +git clone https://github.com/ai-hypercomputer/gpu-recipes.git |
| 116 | +cd gpu-recipes |
| 117 | +export REPO_ROOT=$(pwd) |
| 118 | +export RECIPE_ROOT=$REPO_ROOT/inference/a3mega/llama3.1-70b/trtllm-gke |
| 119 | +``` |
| 120 | + |
| 121 | +<a name="configure-vars"></a> |
| 122 | +### 3.2. Configure Environment Variables |
| 123 | + |
| 124 | +This is the most critical step. These variables are used in subsequent commands to target the correct resources. |
| 125 | + |
| 126 | +```bash |
| 127 | +export PROJECT_ID=<PROJECT_ID> |
| 128 | +export CLUSTER_REGION=<REGION_of_your_cluster> |
| 129 | +export CLUSTER_NAME=<YOUR_GKE_CLUSTER_NAME> |
| 130 | +export KUEUE_NAME=<YOUR_KUEUE_NAME> |
| 131 | +export GCS_BUCKET=<your-gcs-bucket-for-logs> |
| 132 | +export TRTLLM_VERSION=1.3.0rc3 |
| 133 | + |
| 134 | +# Set the project for gcloud commands |
| 135 | +gcloud config set project $PROJECT_ID |
| 136 | +``` |
| 137 | + |
| 138 | +Replace the following values: |
| 139 | + |
| 140 | +| Variable | Description | Example | |
| 141 | +| --------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | |
| 142 | +| `PROJECT_ID` | Your Google Cloud Project ID. | `gcp-project-12345` | |
| 143 | +| `CLUSTER_REGION` | The GCP region where your GKE cluster is located. | `us-central1` | |
| 144 | +| `CLUSTER_NAME` | The name of your GKE cluster. | `a3-mega` | |
| 145 | +| `KUEUE_NAME` | The name of the Kueue local queue. The default queue created by the cluster toolkit is `a3mega`. Verify the name in your cluster. | `a3mega` | |
| 146 | +| `ARTIFACT_REGISTRY` | Full path to your Artifact Registry repository. | `us-central1-docker.pkg.dev/gcp-project-12345/my-repo` | |
| 147 | +| `GCS_BUCKET` | Name of your GCS bucket (do not include `gs://`). | `my-benchmark-logs-bucket` | |
| 148 | +| `TRTLLM_VERSION` | The tag/version for the Docker image. Other verions can be found at [NGC Catalog](https://catalog.ngc.nvidia.com/orgs/nvidia/teams/tensorrt-llm/containers/release) | `1.3.0rc3` | |
| 149 | + |
| 150 | + |
| 151 | +<a name="connect-cluster"></a> |
| 152 | +### 3.3. Connect to your GKE Cluster |
| 153 | + |
| 154 | +Fetch credentials for `kubectl` to communicate with your cluster. |
| 155 | + |
| 156 | +```bash |
| 157 | +gcloud container clusters get-credentials $CLUSTER_NAME --region $CLUSTER_REGION |
| 158 | +``` |
| 159 | + |
| 160 | +<a name="get-hf-token"></a> |
| 161 | +### 3.4. Get Hugging Face token |
| 162 | + |
| 163 | +To access models through Hugging Face, you'll need a Hugging Face token. |
| 164 | + 1. Create a [Hugging Face account](https://huggingface.co/) if you don't have one. |
| 165 | + 2. For **gated models** like Llama 4, ensure you have requested and been granted access on Hugging Face before proceeding. |
| 166 | + 3. Generate an Access Token: Go to **Your Profile > Settings > Access Tokens**. |
| 167 | + 4. Select **New Token**. |
| 168 | + 5. Specify a Name and a Role of at least `Read`. |
| 169 | + 6. Select **Generate a token**. |
| 170 | + 7. Copy the generated token to your clipboard. You'll use this later. |
| 171 | + |
| 172 | + |
| 173 | +<a name="setup-hf-secret"></a> |
| 174 | +### 3.5. Create Hugging Face Kubernetes Secret |
| 175 | + |
| 176 | +Create a Kubernetes Secret with your Hugging Face token to enable the pod to download model checkpoints from Hugging Face. |
| 177 | + |
| 178 | +```bash |
| 179 | +# Paste your Hugging Face token here |
| 180 | +export HF_TOKEN=<YOUR_HUGGINGFACE_TOKEN> |
| 181 | + |
| 182 | +kubectl create secret generic hf-secret \ |
| 183 | +--from-literal=hf_api_token=${HF_TOKEN} \ |
| 184 | +--dry-run=client -o yaml | kubectl apply -f - |
| 185 | +``` |
| 186 | + |
| 187 | +<a name="run-the-recipe"></a> |
| 188 | +## 4. Run the recipe |
| 189 | + |
| 190 | +[Back to Top](#table-of-contents) |
| 191 | + |
| 192 | +> [!NOTE] |
| 193 | +> After running the recipe with `helm install`, it can take **up to 30 minutes** for the deployment to become fully available. This is because the GKE node must first pull the Docker image and then download the model weights from Hugging Face. |
| 194 | +
|
| 195 | +> [!TIP] |
| 196 | +> You can use the [NVIDIA Model Optimizer](https://github.com/NVIDIA/Model-Optimizer/tree/main/examples/llm_ptq) to quantize these models to FP8. |
| 197 | +
|
| 198 | +<a name="supported-models"></a> |
| 199 | +### 4.1. Supported Models |
| 200 | + |
| 201 | +[Back to Top](#table-of-contents) |
| 202 | + |
| 203 | +This recipe supports the following models. Running TRTLLM inference benchmarking on these models are only tested and validated on A3-Mega GKE nodes with certain combination of TP, PP, EP, number of GPU chips, input & output sequence length, precision, etc. |
| 204 | + |
| 205 | +As the PyTorch backend requires pre-quantized models for optimal performance, we use the FP8 quantized version for Llama 3.1 70B. |
| 206 | + |
| 207 | +| Model Name | Hugging Face ID | Configuration File | Release Name Suffix | |
| 208 | +| :--- | :--- | :--- | :--- | |
| 209 | +| **Llama 3.1 70B (FP8)** | `nvidia/Llama-3.1-70B-Instruct-FP8` | `llama3.1-70b.yaml` | `llama-3-1-70b` | |
| 210 | + |
| 211 | +> [!TIP] |
| 212 | +> You can use the NVIDIA Model Optimizer to quantize these models to FP8. |
| 213 | +
|
| 214 | +<a name="deploy-model"></a> |
| 215 | +### 4.2. Deploy and Benchmark a Model |
| 216 | + |
| 217 | +[Back to Top](#table-of-contents) |
| 218 | + |
| 219 | +The recipe uses [`trtllm-bench`](https://github.com/NVIDIA/TensorRT-LLM/blob/main/docs/source/legacy/performance/perf-benchmarking.md), a command-line tool from NVIDIA to benchmark the performance of TensorRT-LLM engine. |
| 220 | + |
| 221 | +1. **Configure model-specific variables.** Choose a model from the [table above](#supported-models) and set the variables: |
| 222 | + |
| 223 | + ```bash |
| 224 | + # Example for Llama 3.1 70B (FP8) |
| 225 | + export HF_MODEL_ID="nvidia/Llama-3.1-70B-Instruct-FP8" |
| 226 | + export CONFIG_FILE="llama3.1-70b.yaml" |
| 227 | + export RELEASE_NAME="$USER-llama-3-1-70b" |
| 228 | + ``` |
| 229 | + |
| 230 | +2. **Install the helm chart.** You can run a single benchmark configuration or a sequence of multiple experiments by indexing the `experiments` list. |
| 231 | + |
| 232 | + ```bash |
| 233 | + cd $RECIPE_ROOT |
| 234 | + helm install -f values.yaml \ |
| 235 | + --set workload.benchmarks.experiments[0].isl=128 \ |
| 236 | + --set workload.benchmarks.experiments[0].osl=128 \ |
| 237 | + --set workload.benchmarks.experiments[0].num_requests=1000 \ |
| 238 | + --set-file workload_launcher=$REPO_ROOT/src/launchers/trtllm-launcher.sh \ |
| 239 | + --set-file serving_config=$REPO_ROOT/src/frameworks/a3mega/trtllm-configs/${CONFIG_FILE} \ |
| 240 | + --set queue=${KUEUE_NAME} \ |
| 241 | + --set "volumes.gcsMounts[0].bucketName=${GCS_BUCKET}" \ |
| 242 | + --set workload.model.name=${HF_MODEL_ID} \ |
| 243 | + --set workload.image=nvcr.io/nvidia/tensorrt-llm/release:${TRTLLM_VERSION} \ |
| 244 | + --set workload.framework=trtllm \ |
| 245 | + ${RELEASE_NAME} \ |
| 246 | + $REPO_ROOT/src/helm-charts/a3mega/trtllm-inference/single-node |
| 247 | + ``` |
| 248 | + > [!NOTE] |
| 249 | + > You can modify the benchmark configuration at runtime by changing the values for `isl`, `osl`, and `num_requests` (number of prompts) in the Helm command to test different scenarios. |
| 250 | + |
| 251 | +3. **Check the deployment status:** |
| 252 | + |
| 253 | + ```bash |
| 254 | + kubectl get deployment/${RELEASE_NAME}-serving |
| 255 | + ``` |
| 256 | + |
| 257 | + Wait until the `READY` column shows `1/1`. See the [Monitoring and Troubleshooting](#monitoring) section to view the deployment logs. |
| 258 | + |
| 259 | +<a name="monitoring"></a> |
| 260 | +## 5. Monitoring and Troubleshooting |
| 261 | + |
| 262 | +[Back to Top](#table-of-contents) |
| 263 | + |
| 264 | +After the model is deployed via Helm as described in the sections [above](#run-the-recipe), use the following steps to monitor the deployment and interact with the model. Replace `<deployment-name>` and `<service-name>` with the appropriate names from the model-specific deployment instructions (e.g., `$USER-llama3.1-70b` and `$USER-llama3.1-70b-svc`). |
| 265 | + |
| 266 | +<a name="check-status"></a> |
| 267 | +### 5.1. Check Deployment Status |
| 268 | + |
| 269 | +Check the status of your deployment. Replace the name if you deployed a different model. |
| 270 | + |
| 271 | +```bash |
| 272 | +kubectl get deployment/$RELEASE_NAME-serving |
| 273 | +``` |
| 274 | + |
| 275 | +Wait until the `READY` column shows `1/1`. If it shows `0/1`, the pod is still starting up. |
| 276 | + |
| 277 | +> [!NOTE] |
| 278 | +> In the GKE UI on Cloud Console, you might see a status of "Does not have minimum availability" during startup. This is normal and will resolve once the pod is ready. |
| 279 | + |
| 280 | +<a name="view-logs"></a> |
| 281 | +### 5.2. View Logs |
| 282 | + |
| 283 | +To see the logs from the TRTLLM server (useful for debugging), use the `-f` flag to follow the log stream: |
| 284 | + |
| 285 | +```bash |
| 286 | +kubectl logs -f deployment/$RELEASE_NAME-serving |
| 287 | +``` |
| 288 | + |
| 289 | +You should see logs indicating preparing the model, and then running the throughput benchmark test, similar to this: |
| 290 | + |
| 291 | +```bash |
| 292 | +Running benchmark for nvidia/Llama3.1-70b with ISL=128, OSL=128, TP=8, EP=1, PP=1 |
| 293 | +
|
| 294 | +=========================================================== |
| 295 | +PYTORCH BACKEND |
| 296 | +=========================================================== |
| 297 | +Model: nvidia/Llama-3.1-70B-Instruct-FP8 |
| 298 | +Model Path: /ssd/nvidia/Llama-3.1-70B-Instruct-FP8 |
| 299 | +TensorRT LLM Version: 1.2 |
| 300 | +Dtype: bfloat16 |
| 301 | +KV Cache Dtype: FP8 |
| 302 | +Quantization: FP8 |
| 303 | +
|
| 304 | +=========================================================== |
| 305 | +REQUEST DETAILS |
| 306 | +=========================================================== |
| 307 | +Number of requests: 1000 |
| 308 | +Number of concurrent requests: 985.9849 |
| 309 | +Average Input Length (tokens): 128.0000 |
| 310 | +Average Output Length (tokens): 128.0000 |
| 311 | +=========================================================== |
| 312 | +WORLD + RUNTIME INFORMATION |
| 313 | +=========================================================== |
| 314 | +TP Size: 8 |
| 315 | +PP Size: 1 |
| 316 | +EP Size: 1 |
| 317 | +Max Runtime Batch Size: 2304 |
| 318 | +Max Runtime Tokens: 4608 |
| 319 | +Scheduling Policy: GUARANTEED_NO_EVICT |
| 320 | +KV Memory Percentage: 85.00% |
| 321 | +Issue Rate (req/sec): 8.3913E+13 |
| 322 | +
|
| 323 | +=========================================================== |
| 324 | +PERFORMANCE OVERVIEW |
| 325 | +=========================================================== |
| 326 | +Request Throughput (req/sec): X.XX |
| 327 | +Total Output Throughput (tokens/sec): X.XX |
| 328 | +Total Token Throughput (tokens/sec): X.XX |
| 329 | +Total Latency (ms): X.XX |
| 330 | +Average request latency (ms): X.XX |
| 331 | +Per User Output Throughput [w/ ctx] (tps/user): X.XX |
| 332 | +Per GPU Output Throughput (tps/gpu): X.XX |
| 333 | +
|
| 334 | +-- Request Latency Breakdown (ms) ----------------------- |
| 335 | +
|
| 336 | +[Latency] P50 : X.XX |
| 337 | +[Latency] P90 : X.XX |
| 338 | +[Latency] P95 : X.XX |
| 339 | +[Latency] P99 : X.XX |
| 340 | +[Latency] MINIMUM: X.XX |
| 341 | +[Latency] MAXIMUM: X.XX |
| 342 | +[Latency] AVERAGE: X.XX |
| 343 | +
|
| 344 | +=========================================================== |
| 345 | +DATASET DETAILS |
| 346 | +=========================================================== |
| 347 | +Dataset Path: /ssd/token-norm-dist_llama3.1-70b_128_128_tp8.json |
| 348 | +Number of Sequences: 1000 |
| 349 | +
|
| 350 | +-- Percentiles statistics --------------------------------- |
| 351 | +
|
| 352 | + Input Output Seq. Length |
| 353 | +----------------------------------------------------------- |
| 354 | +MIN: 128.0000 128.0000 256.0000 |
| 355 | +MAX: 128.0000 128.0000 256.0000 |
| 356 | +AVG: 128.0000 128.0000 256.0000 |
| 357 | +P50: 128.0000 128.0000 256.0000 |
| 358 | +P90: 128.0000 128.0000 256.0000 |
| 359 | +P95: 128.0000 128.0000 256.0000 |
| 360 | +P99: 128.0000 128.0000 256.0000 |
| 361 | +=========================================================== |
| 362 | +``` |
| 363 | + |
| 364 | +<a name="cleanup"></a> |
| 365 | +## 6. Cleanup |
| 366 | + |
| 367 | +To avoid incurring further charges, clean up the resources you created. |
| 368 | + |
| 369 | +1. **Uninstall the Helm Release:** |
| 370 | + |
| 371 | + First, list your releases to get the deployed models: |
| 372 | + |
| 373 | + ```bash |
| 374 | + # list deployed models |
| 375 | + helm list --filter $USER |
| 376 | + ``` |
| 377 | + |
| 378 | + Then, uninstall the desired release: |
| 379 | + |
| 380 | + ```bash |
| 381 | + helm uninstall <release_name> |
| 382 | + ``` |
| 383 | + Replace `<release_name>` with the helm release names listed. |
| 384 | + |
| 385 | +2. **Delete the Kubernetes Secret:** |
| 386 | + |
| 387 | + ```bash |
| 388 | + kubectl delete secret hf-secret --ignore-not-found=true |
| 389 | + ``` |
| 390 | + |
| 391 | +3. (Optional) Delete the built Docker image from Artifact Registry if no longer needed. |
| 392 | +4. (Optional) Delete Cloud Build logs. |
| 393 | +5. (Optional) Clean up files in your GCS bucket if benchmarking was performed. |
| 394 | +6. (Optional) Delete the [test environment](#test-environment) provisioned including GKE cluster. |
0 commit comments