This example shows how a TPU cluster with v5p machines can be created. The example also includes a consolidated tpu-kueue-jax-sample.yaml that creates a Kueue-managed Kubernetes job to run JAX and print the active TPU device count.
Key parameters when working with TPUs:
num_slices: Number of TPU slices required. A slice is a collection of chips all located inside the same TPU Pod connected by high-speed inter-chip interconnects (ICI).tpu_topology: The TPU topology desired. Topology is the number and physical arrangement of the TPU chips in a TPU slice.
Before you start, make sure you have performed the following tasks:
- Enable the Google Kubernetes Engine API.
- If you want to use the Google Cloud CLI for this task, install and then initialize the gcloud CLI.
- Ensure that you have enough quota for TPUs (
tpu-v5p-slicequota in your region). - Ensure that you have the following roles enabled on your deployment identity:
roles/editorroles/container.clusterAdminroles/iam.serviceAccountAdmin
This section guides you through the cluster creation process, ensuring that your project follows best practices.
NOTE: If you would like to create more than one cluster in a project, make sure you update the deployment name.
-
Launch Cloud Shell. You can use a different environment; however, we recommend Cloud Shell because the dependencies are already pre-installed for Cluster Toolkit. If you don't want to use Cloud Shell, follow the instructions to install dependencies to prepare a different environment.
-
Install the Cluster Toolkit: Download and extract the pre-built
gclusterbinary bundle. Follow the download commands for your operating system in the Install the Cluster Toolkit section of the setup guide. -
Once extracted, verify that the installation is ready:
./gcluster --version
-
Create a Cloud Storage bucket to store the state of the Terraform deployment:
gcloud storage buckets create gs://BUCKET_NAME \ --default-storage-class=STANDARD \ --location=COMPUTE_REGION \ --uniform-bucket-level-access gcloud storage buckets update gs://BUCKET_NAME --versioning
Replace the following variables:
BUCKET_NAME: the name of the new Cloud Storage bucket.COMPUTE_REGION: the compute region where you want to store the state of the Terraform deployment.
-
In the
examples/gke-tpu-v5p/gke-tpu-v5p-deployment.yamlfile, replace the following variables in theterraform_backend_defaultsandvarssections to match the specific values for your deployment:bucket: the name of the Cloud Storage bucket you created in the previous step.project_id: your Google Cloud project ID.region: the compute region for the cluster.zone: the compute zone for the TPUs.num_slices: the number of TPU slices to create.machine_type: the machine type of the TPU (e.g.,ct5p-hightpu-4t).tpu_topology: the TPU placement topology for the pod slice node pool (e.g.,2x2x2).authorized_cidr: The IP address range that you want to allow to connect with the cluster. This CIDR block must include the IP address of the machine running Terraform.reservation: the name of the Compute Engine reservation of TPU v5p nodes (if any).user_namespace: The Kubernetes service account namespace where your TPU workloads will run (defaults todefault).
Note: The
static_node_countis automatically calculated frommachine_type,num_slicesandtpu_topology. It is derived using the formula:(total_chips_in_topology / chips_per_machine).To modify advanced settings, edit
examples/gke-tpu-v5p/gke-tpu-v5p.yaml. -
To use on-demand capacity, you can remove the reservation usage by making the following changes.
- Remove the
reservationvariable from thegke-tpu-v5p-deployment.yaml. - Remove the
reservation_affinityblock from the nodepool module.
- Remove the
-
To utilize spot instances, remove the reservation variable from
gke-tpu-v5p-deployment.yamland addspot: true. Ingke-tpu-v5p.yaml, replace the reservation_affinity block undergke-tpu-v5p-poolmodule withspot: $(vars.spot). -
Generate Application Default Credentials (ADC) to provide access to Terraform.
-
Deploy the blueprint to provision the GKE infrastructure:
./gcluster deploy -d \ examples/gke-tpu-v5p/gke-tpu-v5p-deployment.yaml \ examples/gke-tpu-v5p/gke-tpu-v5p.yaml
This blueprint installs and configures Kueue by default to manage TPU quotas and queue job submissions. The provided tpu-kueue-jax-sample.yaml file creates a Kubernetes Job that integrates both Kueue queue routing and JAX TPU device count validation.
NOTE: By default, the toolkit dynamically applies an embedded kueue configuration based on your Pathways and Dynamic Slicing settings.
-
Custom Configurations:
- If you explicitly disable both Pathways and Dynamic Slicing, the toolkit will still install the Kueue engine/controllers, but it leaves them unconfigured(no default queues or resource flavors are created).
- If you want to override the default embedded configurations, or apply configuration in the scenario above, you can uncomment and set
config_pathin thekueuesection of theworkload-manager-installmodule in the blueprint. - For more details on default configurations and variables, see the
kubectl-applydocumentation.
-
Quota Allocation: The blueprint automatically calculates and sets a
google.com/tpuquota in Kueue'sClusterQueue. The node count is derived from yourmachine_typeandtpu_topology, and the quota is calculated using the formula:num_slices×(total_chips_in_topology / chips_per_machine)×chips_per_machine.
-
Connect to your cluster:
gcloud container clusters get-credentials gke-tpu-v5p --region=REGION --project=PROJECT_ID
Replace
REGIONandPROJECT_IDwith your deployment region and project ID. -
Update the Node Selector: Open
examples/gke-tpu-v5p/tpu-kueue-jax-sample.yamland verify that thenodeSelectorvalues match the TPU accelerator and topology configured in your deployment:nodeSelector: cloud.google.com/gke-tpu-accelerator: tpu-v5p-slice cloud.google.com/gke-tpu-topology: 2x2x1
-
Submit the Job: Submit the workload to GKE. The job contains the label
kueue.x-k8s.io/queue-name: user-queuewhich automatically routes it through Kueue:kubectl apply -f examples/gke-tpu-v5p/tpu-kueue-jax-sample.yaml
-
Verify Workload Admission & Status: Check if Kueue successfully admitted and queued the workload:
kubectl get workloads
Monitor the job and pod execution:
kubectl get jobs -
Verify JAX Logs: Print the logs of the pod to confirm JAX successfully detected the TPU devices:
kubectl logs -l job-name=tpu-kueue-jax-sample -c jax-tpu
A successful execution will output:
Global device count: 4
(Depending on your topology, e.g., 4 chips for
2x2x1).
This blueprint supports Pathways-on-Cloud orchestration, allowing you to run JAX workloads distributed across remote TPU workers coordinated by a CPU-based head node. The sections below guide you through deploying and verifying a sample job that prints the active TPU device count over the coordinated Pathways network.
NOTE: Refer to the GCluster Job Submission Guide for detailed instructions on job submission.
Before deploying, ensure Pathways is enabled in examples/gke-tpu-v5p/gke-tpu-v5p.yaml:
vars:
# Enable Pathways for TPUs (provisions CPU node pool and configures Kueue quotas)
enable_pathways_for_tpus: trueNote: Enabling Pathways statically provisions a cpu-np node pool (utilizing n4-standard-64 by default) to host the coordinator head pod, while GKE autoscaling dynamically manages its size between 0 and 100 based on job load.
Before submitting the job to the live GKE cluster, it is highly recommended to perform a dry-run to generate and inspect the Kubernetes JobSet manifest:
./gcluster job submit \
--name pathways-job \
--compute-type v5p-8 \
--pathways \
--pathways-gcs-location gs://YOUR_COORDINATION_BUCKET/pathways-scratch \
--image us-docker.pkg.dev/cloud-tpu-images/jax-ai-image/tpu:latest \
--command "pip install pathwaysutils && python -c 'import pathwaysutils; pathwaysutils.initialize(); import jax; print(\"JAX Device count:\", jax.device_count())'" \
--dry-run-out my-manifest.yamlThis generates a local file my-manifest.yaml containing the complete Kubernetes JobSet manifest. You can inspect this file to verify that:
- The coordinator pod is assigned to the CPU node pool (
nodeSelector: cpu-np). - The worker pods target the TPU node pool.
- The pathways sidecar containers (
pathways-proxy,pathways-worker) are correctly injected with correct ports and variables.
--name: A unique identifier for your job (keep it under 10 characters to avoid Kubernetes label length constraints).--compute-type: The TPU slice topology configuration (e.g.,v5p-8maps to a2x2x1TPU v5p topology).--pathways: Flags GCluster to generate a Pathways JobSet coordination architecture.--pathways-gcs-location: A Cloud Storage bucket URI used by the coordinator and worker pods to synchronize network state and parameters.--image: The container image for the workload (using the standard TPU JAX image).--command: The entrypoint command. It dynamically installspathwaysutilsand executes the python command verifying JAX initialization. Callingpathwaysutils.initialize()is required since it registers the virtual'proxy'backend inside JAX, allowing it to delegate computations over the network to the TPU workers--dry-run-out: Outputs the generated Kubernetes manifests to the file path specified as the argument to this flag (e.g.,my-manifest.yamlin the example above) instead of submitting them to GKE.
Once you have verified the manifest, submit the job to your live GKE cluster by running the command without the --dry-run-out flag:
./gcluster job submit \
--name pathways-job \
--compute-type v5p-8 \
--pathways \
--pathways-gcs-location gs://YOUR_COORDINATION_BUCKET/pathways-scratch \
--image us-docker.pkg.dev/cloud-tpu-images/jax-ai-image/tpu:latest \
--command "pip install pathwaysutils && python -c 'import pathwaysutils; pathwaysutils.initialize(); import jax; print(\"JAX Device count:\", jax.device_count())'"-
Monitor and Check Logs: Use the
gclusterCLI to track the execution status and view the workload logs:gcluster job logs pathways-job
A successful execution will output logs ending with:
JAX Device count: 4
-
Cancel and Clean Up Job: To terminate a running job or clean up resources once finished, run:
gcluster job cancel pathways-job
To avoid recurring charges for the resources used on this page, clean up the resources provisioned by Cluster Toolkit, including the VPC networks and GKE cluster:
./gcluster destroy DEPLOYMENT_NAME