GKE Heterogeneous Node Pools, Taints, Tolerations, and Accelerator Topologies 🖥️⚡
In modern enterprise Kubernetes clusters, a single uniform machine type cannot economically or efficiently run diverse workloads. A production GKE cluster frequently executes lightweight web microservices, memory-intensive caching layers, high-throughput I/O databases, and massive AI/ML distributed training jobs simultaneously. Heterogeneous Node Pools enable co-locating diverse Compute Engine hardware profiles—including cost-effective Tau Arm (T2A), compute-optimized C3 Sapphire Rapids, local NVMe SSD RAID arrays, and NVIDIA H100 / Google Cloud TPU v5e accelerators—governed by Kubernetes Taints, Tolerations, and Node Affinity.
1. Heterogeneous Node Topology & Scheduling Flow
GKE uses Kubernetes admission and scheduling primitives to ensure that pods execute only on the hardware best suited for their performance and cost profile.
INCOMING WORKLOAD DEPLOYMENTS
│
┌──────────────────────────────┼──────────────────────────────┐
│ │ │
Stateless Web App Distributed Redis LLM Inference Service
(Tolerates Arm64) (Requires High Memory) (Requires NVIDIA L4 GPU)
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ NodeSelector:│ │ NodeSelector:│ │ Toleration: │
│ arch=arm64 │ │ tier=cache │ │ gpu=present │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
═══════╪══════════════════════════════╪══════════════════════════════╪════════
GKE SCHEDULING FILTER (Taints & Tolerations / Node Affinity Evaluation)
│ │ │
▼ ▼ ▼
┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
│ NODE POOL 1: ARM64 │ │ NODE POOL 2: MEMORY │ │ NODE POOL 3: GPU │
│ Machine: t2a-std-8 │ │ Machine: n2-highmem-16│ │ Machine: g2-std-16 │
│ OS: Container-Optimized│ │ Disks: Local NVMe SSD│ │ Accel: 1x NVIDIA L4 │
│ Cost: ~$0.15/hr │ │ Cost: ~$0.85/hr │ │ Taint: dedicated=gpu│
└─────────────────────┘ └─────────────────────┘ └─────────────────────┘
Scheduling Mechanisms
- Taints and Tolerations:
- A Taint on a node repels pods:
dedicated=gpu:NoSchedule. - A pod with a matching Toleration is permitted (but not required) to schedule on the tainted node. Taints protect expensive specialized hardware (GPUs, Spot VMs, high-memory nodes) from being overrun by generic web pods.
- A Taint on a node repels pods:
- Node Affinity / Anti-Affinity:
- NodeAffinity (
requiredDuringSchedulingIgnoredDuringExecution): Forces a pod to schedule strictly on nodes bearing specific labels (e.g.,kubernetes.io/arch: arm64orcloud.google.com/gke-gpu: "true").
- NodeAffinity (
- Topology Spread Constraints: Distributes pods evenly across failure domains (Availability Zones or Node Pools) to ensure high availability during zonal disruptions.
2. Advanced Hardware Configurations: Local NVMe SSDs & Arm
Local NVMe SSD Scratch Arrays
Compute Engine instances can attach physical local NVMe SSDs (375 GB per partition) that deliver millions of IOPS with sub-millisecond latencies:
- Ephemeral Storage RAID: GKE can format and strip multiple local SSDs into a single high-performance
ext4orxfsRAID 0 volume. - Workloads mount this high-speed disk using
emptyDir: { medium: Memory }or by requesting ephemeral storage backed by the local SSD.
Tau Arm (T2A Series) for Microservices
- Built on ARM Neoverse N1 cores, Google Cloud Tau T2A instances deliver up to 40% better price-performance than comparable x86 VMs.
- Multi-architecture container images (OCI manifests supporting
linux/amd64andlinux/arm64) run natively on Arm node pools without code changes.
3. Production Deployment & CLI Operations (gcloud & kubectl)
1. Provision a Tau Arm64 Node Pool for General Microservices
gcloud container node-pools create arm-microservices-pool \
--cluster=prod-regional-cluster \
--region=us-central1 \
--machine-type=t2a-standard-8 \
--image-type=COS_CONTAINERD \
--num-nodes=2 \
--enable-autoscaling \
--min-nodes=1 \
--max-nodes=10 \
--node-labels="workload-type=stateless,architecture=arm64" \
--project=core-infrastructure-prod2. Provision High-Memory Local NVMe SSD Node Pool (Redis / Kafka)
gcloud container node-pools create highmem-ssd-pool \
--cluster=prod-regional-cluster \
--region=us-central1 \
--machine-type=n2-highmem-16 \
--local-nvme-ssd-block=count=2 \
--ephemeral-storage-local-ssd=count=2 \
--num-nodes=1 \
--node-taints="workload=datastores:NoSchedule" \
--node-labels="storage-type=local-nvme" \
--project=core-infrastructure-prod3. Provision an NVIDIA L4 GPU Node Pool for LLM Inference
gcloud container node-pools create gpu-inference-pool \
--cluster=prod-regional-cluster \
--region=us-central1 \
--machine-type=g2-standard-16 \
--accelerator=type=nvidia-l4,count=1,gpu-driver-version=default \
--num-nodes=0 \
--enable-autoscaling \
--min-nodes=0 \
--max-nodes=8 \
--node-taints="nvidia.com/gpu=present:NoSchedule" \
--node-labels="workload=ai-inference" \
--project=core-infrastructure-prod4. Deploy AI Inference Pod with GPU Toleration and Node Affinity
Create vllm-inference.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: vllm-llama-service
namespace: ai-workloads
spec:
replicas: 2
selector:
matchLabels:
app: vllm-inference
template:
metadata:
labels:
app: vllm-inference
spec:
# 1. Toleration to bypass the GPU node taint
tolerations:
- key: "nvidia.com/gpu"
operator: "Equal"
value: "present"
effect: "NoSchedule"
# 2. Node Affinity to force placement onto the GPU node pool
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: "workload"
operator: "In"
values:
- "ai-inference"
containers:
- name: vllm
image: vllm/vllm-openai:latest
resources:
limits:
nvidia.com/gpu: "1"
cpu: "12"
memory: "48Gi"
requests:
nvidia.com/gpu: "1"
cpu: "8"
memory: "32Gi"
ports:
- containerPort: 8000Apply deployment:
kubectl apply -f vllm-inference.yaml4. Hardware Sizing & Accelerator Matrix
| Node Pool Category | Recommended GCE Machine Series | Typical vCPU / RAM | Specialized Capability |
|---|---|---|---|
| Cost-Optimized Microservices | t2a-standard (Arm64) | 4-16 vCPUs / 16-64 GiB | 40% price-performance boost on Go/Node/Java |
| Compute-Intensive APIs | c3-standard (Sapphire Rapids) | 4-44 vCPUs / 16-176 GiB | DDR5 RAM, Intel AMX, ultra-fast P99 |
| In-Memory Caches & DBs | n2-highmem or m1-megamem | 16-96 vCPUs / 128-1433 GiB | Multi-terabyte in-memory caching |
| Fast I/O & Streaming | c2-standard + Local NVMe SSD | 8-30 vCPUs / 32-120 GiB | Physical PCIe Gen4 NVMe arrays (millions IOPS) |
| AI Inference | g2-standard (NVIDIA L4) | 4-96 vCPUs / 16-384 GiB | FP8 precision, optimized for vLLM & Triton |
| AI Large Training | a3-highgpu (NVIDIA H100) | 208 vCPUs / 1872 GiB | 8x H100 80GB GPUs, 3.2 Tbps GPUDirect RDMA |
| Cloud TPU Slices | ct5lp (Cloud TPU v5e) | Sliced chip topologies | Cost-efficient Gemini/JAX/PyTorch training |
5. Official References & Documentation
- GKE Node Pools Overview
- Using Taints and Tolerations in GKE
- Running Arm Workloads on GKE Tau T2A
- NVIDIA GPUs on GKE Setup & Operations
- Local SSD Storage on GKE
6. Realistic Pricing Scenarios
Heterogeneous node pools allow aggressive optimization by segregating cheap compute from expensive hardware:
- T2A Arm64 (8 vCPU / 32 GiB): ~$0.308 per hour.
- N2 Standard (8 vCPU / 32 GiB): ~$0.388 per hour.
- G2 Standard with 1x NVIDIA L4 GPU: ~$0.842 per hour.
Scenario A: Mixed Enterprise Estate (Homogeneous vs Heterogeneous)
- Homogeneous Approach (All N2 Standard):
- 30 nodes running generic
n2-standard-8($0.388/hr each). - Cost: 30 × 8,497.20 / month**.
- 30 nodes running generic
- Heterogeneous Approach (Segregated Pools):
- Pool 1 (20 nodes Tau Arm T2A for web services): 20 × 4,496.80.
- Pool 2 (8 nodes N2-Highmem for Redis): 8 × 3,036.80.
- Pool 3 (2 nodes G2 GPU with autoscaling 0 to 4): 2 × 505.20.
- Net Monthly Cost: $8,038.80 / month (Delivers dedicated GPU inference capabilities and high-memory performance while reducing total spend).
Scenario B: High-Throughput Database Scratch Storage (Local SSD vs Persistent Disk)
- Architecture:
- 4 nodes hosting distributed clickhouse analytics requiring 100,000 IOPS scratch buffers.
- Option A: Hyperdisk Extreme 1,000 GB provisioned for 100,000 IOPS (0.035/IOPS = $3,620.00/month).
- Option B: 2 physical Local NVMe SSDs attached to
n2-standard-16(60.00/month).
- Net Monthly Savings: $3,560.00 / month by leveraging local NVMe SSD hardware on dedicated node pools.
7. Battle-Tested Nuggets & Production Gotchas
- The Missing Toleration Node Pool Lockout: If you apply a taint like
dedicated=gpu:NoSchedulewhen creating a node pool, Kubernetes system daemonsets (kube-dns, fluentbit, calico) might not possess the matching toleration. While GKE automatically injects tolerations into core system daemonsets, third-party DaemonSets (e.g., Datadog, Dynatrace, Falco) will fail to schedule on the tainted nodes. Always verify that your enterprise DaemonSets include tolerations for all custom node pool taints (operator: Exists). - Arm64 Architecture Multi-Arch Container Image Failures: When scheduling microservices onto Tau T2A Arm64 node pools, your container images must be compiled for the
linux/arm64architecture. If a deployment schedules onto an Arm node with anx86_64container image, the pod will crash immediately withexec format errororCrashLoopBackOff. Always usedocker buildx build --platform linux/amd64,linux/arm64in your CI/CD pipelines. - Local NVMe SSD Data Loss on Stop/Deallocate: Local NVMe SSDs are physically attached to the motherboard of the host server rack. If a GCE instance is stopped, pre-empted, or encounters a hardware maintenance event that triggers host recreation, all data stored on the Local SSD is permanently erased. Never store persistent database state (Postgres
data/directory) on a Local SSD without active distributed multi-node replication (e.g., Cassandra or Elasticsearch with replica count ). - GPU Driver Installation DaemonSet Failures: When creating GPU node pools, GKE can automatically install NVIDIA drivers using
--accelerator=...,gpu-driver-version=default. However, if you specify--accelerator=...,gpu-driver-version=disabledor run custom kernels, the NVIDIA driver daemonset will not initialize, and pods requestingnvidia.com/gpu: 1will sit inPendingforever with0/10 nodes available: Insufficient nvidia.com/gpu. - Autoscaler Scale-to-Zero on Tainted Pools: When creating specialized node pools (such as GPU or batch Spot pools) configured to autoscale from 0 to nodes (
--min-nodes=0), the Cluster Autoscaler inspects pending pod specs. If a pending pod does not possess the exact matching Toleration AND NodeAffinity, the autoscaler will consider the pool ineligible and refuse to spin up the nodes. - Topology Spread Constraints Overriding Node Pool Affinity: If you define a
topologySpreadConstraintmatchingtopologyKey: topology.kubernetes.io/zonewithwhenUnsatisfiable: DoNotSchedule, and one of your heterogeneous node pools is pinned to only 2 zones (while the cluster spans 3 zones), the scheduler may fail to place pods because it cannot satisfy the 3-zone spread constraint. Ensure topology constraints align with the zonal availability of your heterogeneous node pools.
Across the wiki
- Managed Node Groups — node pools and spot capacity (Kubernetes)
- AKS Heterogeneous Node Pools — System vs User, Ephemeral OS Disks, and Azure Linux — node pools and spot capacity (Azure)
- AWS Pricing Models — node pools and spot capacity (AWS)
- Spot Instances on EKS — node pools and spot capacity (Kubernetes)