Cloud Intelligence™
La funzionalità GKE che la maggior parte dei cluster ancora non usa: le custom ComputeClasses
Questa pagina è disponibile anche in English, Deutsch, Español, Français, 日本語 e Português.
About Chimbu Chinnadurai
Senior Cloud Architect II
I've probably debugged a Kubernetes issue in more time zones than I care to count. Based in London, I help engineering teams across EMEA get their clusters to behave — and actually understand why they misbehaved in the first place.
I write, speak, and guest on podcasts about all things cloud-native. Away from the terminal: I enjoy cooking almost as much as simplifying overly complex systems.
My personal pageVedo lo stesso schema in quasi tutte le review di cluster GKE che conduco: un lungo elenco di node pool statici, ciascuno legato a un singolo tipo di macchina. Un pool n2-standard-8 per i workloads generici, un pool n2-highmem-16 per i servizi più esigenti in termini di memoria, un pool Spot creato da qualcuno durante una campagna di riduzione dei costi l'anno scorso e magari un pool GPU. I workloads vengono schedulati sui nodi tramite nodeSelector, taint, toleration e regole di node affinity stratificate negli anni.
Tutto regge finché qualcosa non cambia, e di solito si tratta di una di queste tre situazioni.
Stockout zonali: una zona esaurisce la serie di macchine che avete scelto. Il Cluster Autoscaler può scalare solo i pool specifici che gli avete assegnato, lasciando i pod bloccati in
Pendinganche quando famiglie di macchine simili restano inutilizzate nella stessa zona.Trappole da preemption Spot: preemption Spot massicce riversano i workloads su pool di fallback on-demand. Una volta ripristinata la capacità, non c'è nulla che riporti attivamente i workloads al punto di partenza, quindi i risparmi pianificati non compaiono mai in fattura.
Upgrade generazionali: passare da N2 a N4 o C4 diventa un calvario operativo di diverse settimane: provisioning di nuovi pool, aggiornamento dei valori Helm in decine di repository, drain dei vecchi nodi e la speranza che nessun manifest abbia le vecchie label hardcoded.
GKE ha una risposta a tutti e tre i problemi già da un po': le custom ComputeClasses. I clienti con cui parlo o non ne hanno mai sentito parlare o danno per scontato che siano riservate ad Autopilot. Non è così. Le custom ComputeClasses sono disponibili sia in modalità Autopilot sia Standard a partire da GKE 1.30.3-gke.1451000 e sono abilitate di default in tutti i cluster idonei.
Che cos'è una custom ComputeClass?
Una ComputeClass è una custom resource di Kubernetes (computeclasses.cloud.google.com) che specifica quali profili di nodo preferiscono i workloads, ordinati per priorità. Come spiega la documentazione di Google, fornisce a GKE un profilo di attributi di nodo da usare durante gli eventi di autoscaling.
Node pool tradizionali: dichiarate prima l'infrastruttura, poi indirizzate i workloads su di essa.
ComputeClasses: dichiarate l'intento ("Prova prima N4 Spot; se non disponibile, prova C4 Spot; altrimenti ripiega su N4 on-demand") e l'autoscaler esegue automaticamente il provisioning dei nodi giusti.
Le responsabilità restano così separate: i team di piattaforma definiscono i profili hardware e gli sviluppatori applicativi scelgono un profilo per nome.
Cosa risolve nella pratica
- Gli stockout diventano fallback automatizzati: se una regola ad alta priorità fallisce per quota o disponibilità zonale, GKE tenta immediatamente la regola successiva dell'elenco.
- La capacità di fallback non resta attiva più del dovuto: con la migrazione attiva abilitata, GKE riporta i workloads sui nodi a priorità più alta non appena la capacità preferita torna disponibile, recuperando automaticamente i risparmi.
- Gli upgrade restano lato piattaforma: spostare i workloads da N2 a N4, o testare C4, significa aggiornare un solo manifest ComputeClass. I team applicativi non toccano mai i loro selector.
Ci sono anche alcuni vantaggi minori. Potete decidere a livello centrale quali reservation di Compute Engine vengono consumate per prime, regolare quanto aggressivamente ogni classe consolida i nodi sottoutilizzati e impostare una classe di default per un namespace o per l'intero cluster, così i team ottengono hardware adeguato senza scrivere un selector.
Le principali leve di configurazione
Sotto spec.priorities, ogni regola definisce un target hardware primario: machineFamily (ad esempio n4), machineType (ad esempio n4-standard-32), gpu o tpu. Per i cluster Standard una regola può puntare a node pool esistenti con nodepools. Le regole accettano flag per spot, minCores, minMemoryGb, impostazioni del boot disk, SSD locali, boot disk secondari, reservation e zone.
whenUnsatisfiable: controlla il comportamento se tutti i livelli di priorità falliscono.ScaleUpAnywaycrea un nodo con la configurazione di default del cluster, mentreDoNotScaleUplascia i Pod in pending finché non è possibile creare un nodo corrispondente.DoNotScaleUpè il default da GKE 1.33 (prima eraScaleUpAnyway), quindi impostatelo esplicitamente per evitare sorprese quando cluster su versioni diverse condividono gli stessi manifest.autoscalingPolicy: configura la sensibilità dello scale-down conconsolidationDelayMinutes, unaconsolidationThresholdper CPU e memoria e unagpuConsolidationThresholdseparata.activeMigration: la migrazione attiva ha due switch.optimizeRulePriority: trueriporta attivamente i pod sui nodi a priorità più alta man mano che la capacità si libera.ensureAllDaemonSetPodsRunning: truesostituisce con nodi più grandi quelli troppo piccoli per eseguire tutti i Pod DaemonSet richiesti.
nodePoolAutoCreation.enabled: true: consente a GKE di creare ed eliminare node pool per soddisfare le vostre priorità. Da GKE1.33.3-gke.1136000non serve più avere abilitato il node auto-provisioning a livello di cluster.
I vostri pool esistenti continuano a funzionare. Aggiungete la label cloud.google.com/compute-class e un taint NoSchedule corrispondente a un pool creato manualmente e questo entra a far parte della classe, così potete adottare le classi gradualmente.
Potete anche applicare una ComputeClass di default a un namespace etichettandolo, o all'intero cluster eseguendo il deployment di una classe chiamata default e abilitando la funzionalità sul cluster. GKE include anche ComputeClasses integrate, tra cui autopilot, che permette ai cluster Standard di eseguire determinati Pod sulla piattaforma di compute container-optimized di Autopilot.
Due pattern per iniziare
Pattern 1: sostituire i pool Spot statici con una catena di fallback resiliente
Ecco una compute class general-purpose che dà priorità a N4 Spot, ripiega su C4 Spot e usa N4 on-demand come valvola di sicurezza. Consolida i nodi sottoutilizzati dopo 5 minuti e fa risalire i pod lungo la catena quando la capacità Spot si ripristina:
apiVersion: cloud.google.com/v1kind: ComputeClassmetadata: name: general-cost-optimizedspec: nodePoolAutoCreation: enabled: true priorities: - machineFamily: n4 spot: true minCores: 4 - machineFamily: c4 spot: true minCores: 4 - machineFamily: n4 spot: false activeMigration: optimizeRulePriority: true autoscalingPolicy: consolidationDelayMinutes: 5 consolidationThreshold: 70 whenUnsatisfiable: DoNotScaleUpCon optimizeRulePriority: true, GKE riporta i Pod sulla regola Spot preferita appena la capacità torna disponibile, poi rimuove il nodo di fallback.
I workloads consumano questa classe tramite un singolo node selector:
apiVersion: apps/v1kind: Deploymentmetadata: name: checkout-apispec: replicas: 3 selector: matchLabels: app: checkout-api template: metadata: labels: app: checkout-api spec: nodeSelector: cloud.google.com/compute-class: general-cost-optimized containers: - name: api image: registry.k8s.io/pause resources: requests: cpu: "1" memory: "2Gi"Non c'è nessun selector cloud.google.com/gke-spot, nessuna toleration e nessuna affinity di famiglia di macchine, ed è intenzionale. GKE rifiuta i Pod che selezionano una ComputeClass e al tempo stesso selezionano label di sistema con un campo ComputeClass corrispondente, come cloud.google.com/gke-spot, cloud.google.com/machine-family o node.kubernetes.io/instance-type. Quelle scelte vanno definite dentro la classe. Se state migrando workloads esistenti, cercate prima queste label nei vostri manifest e chart Helm.
Pattern 2: sovrapporre le classi a pool custom esistenti
Se gestite cluster Standard con pool specializzati, pre-warmed o coperti da reservation, non dovete eliminarli. Configurate la classe in modo che usi prima i pool esistenti e ne crei di nuovi solo quando questi non possono scalare:
apiVersion: cloud.google.com/v1kind: ComputeClassmetadata: name: hybrid-fallback-classspec: nodePoolAutoCreation: enabled: true priorities: - nodepools: [core-workload-pool] - machineFamily: n4 - machineFamily: c4Etichettate e applicate un taint al pool esistente perché la classe lo riconosca:
gcloud container node-pools update core-workload-pool \ --cluster=example-cluster \ --node-labels="cloud.google.com/compute-class=hybrid-fallback-class"
gcloud container node-pools update core-workload-pool \ --cluster=example-cluster \ --node-taints="cloud.google.com/compute-class=hybrid-fallback-class:NoSchedule"Attenzione: --node-labels e --node-taints sostituiscono tutte le label e i taint del pool, quindi includete quelli esistenti quando eseguite questi comandi.
La guida alle best practice raccomanda lo stesso ordinamento: i pool creati manualmente ricevono la priorità più alta e i pool auto-creati fungono da fallback.
Cloud bill shouldn't be a mystery
One platform for AI and Cloud optimization.
Le novità recenti più rilevanti
La funzionalità si è evoluta rapidamente nelle ultime minor version di GKE. Questi sono i cambiamenti che segnalerei a chi aveva valutato le ComputeClasses un anno fa ed era passato oltre.
Punteggi di priorità ponderati (1.35.2-gke.1842000+)
Invece di affidarsi solo all'ordine dell'elenco, le regole possono definire un priorityScore esplicito da 1 a 1000. È tutto o niente: o ogni regola della classe ha un punteggio o nessuna lo ha. Fino a tre regole possono condividere lo stesso punteggio e GKE valuta insieme le regole a pari merito, lasciando al Cluster Autoscaler la scelta tra di esse.
apiVersion: cloud.google.com/v1kind: ComputeClassmetadata: name: priority-rule-score-classspec: nodePoolAutoCreation: enabled: true priorities: - machineFamily: e2 spot: true priorityScore: 30 - machineFamily: n2 spot: true priorityScore: 20 - machineFamily: t2d spot: true priorityScore: 20 - machineFamily: n1 spot: true priorityScore: 10 whenUnsatisfiable: ScaleUpAnywayQui N2 e T2D Spot contano come seconde scelte equivalenti. Le regole raggruppate possono aggiungere latenza all'autoscaling, più per le regole machineFamily ampie che per quelle machineType ristrette, perché GKE deve eseguire più simulazioni.
Selezione automatica dello storage (1.35.3-gke.1290000+)
Mescolare generazioni di macchine comportava un rischio lato storage, perché ogni serie supporta tipi di disco specifici (ad esempio, N4 supporta Hyperdisk, mentre N2 si basa sui PD standard). Ora potete usare una StorageClass con selezione automatica del tipo di disco e GKE crea un tipo di disco compatibile con qualunque tipo di macchina su cui finisce il Pod. Questo rende le catene di fallback multi-generazione molto più sicure per i workloads che usano il provisioning dinamico. I dischi preesistenti richiedono comunque un tipo di disco supportato da tutte le serie di macchine della classe.
Shape di macchina custom (1.33.2-gke.1111000+)
Potete definire shape custom come n4-custom-8-20480 direttamente in machineType, per i workloads il cui rapporto CPU/memoria non rientra nelle shape predefinite.
Impostazioni del boot disk sulle regole machineFamily (1.34.1-gke.1431000+)
Definite storage.bootDiskType e storage.bootDiskSize sulle regole flessibili machineFamily invece che solo sulle regole machineType specifiche:
priorities:- machineFamily: n4 spot: true minCores: 16 minMemoryGb: 64 storage: bootDiskType: hyperdisk-balanced bootDiskSize: 100Label e taint nella classe (da 1.33.2 a 1.34.1)
Dichiarate nodeLabels e taints per singola regola di priorità (le label da 1.33.2-gke.1111000, i taint da 1.33.4-gke.1350000) o globalmente sotto nodePoolConfig (da 1.34.1-gke.3084002). I valori a livello di regola e quelli globali non devono sovrapporsi. Così etichettatura e isolamento restano nello stesso oggetto della decisione hardware.
Classi di default per i workloads di sistema (1.33.1-gke.1788000+)
I namespace possono ricevere una classe di default che si applica solo ai Pod non DaemonSet, tramite la label cloud.google.com/default-compute-class-non-daemonset. GKE la usa per eseguire i Pod di sistema in namespace come kube-system e gmp-system separatamente dai vostri workloads, mentre ai Pod DaemonSet non viene assegnato il selector della classe.
Il compute di Autopilot accanto a un default custom (1.34.1-gke.1829001+)
Se impostate una classe custom come default del cluster in un cluster Autopilot, i Pod smettono di girare di default sulla piattaforma container-optimized di Autopilot. Ora potete far girare Pod specifici sulla piattaforma di compute di Autopilot aggiungendo a quei Pod il node selector cloud.google.com/compute-class: autopilot.
Alcune aggiunte minori
priorityDefaults (1.32.1-gke.1729000) permette di impostare valori condivisi una sola volta invece di ripeterli in ogni regola. nodeSystemConfig porta il tuning di kubelet e sysctl dentro la classe. Compact placement e workload policy sono disponibili per Autopilot da 1.33.2-gke.1335000.
Caveat di produzione e casi limite
Le ComputeClasses hanno dei limiti, ed è meglio scoprirli in una design review, non in produzione.
kube-scheduler non conosce le regole di priorità: le ComputeClasses influenzano l'autoscaling, ma kube-scheduler non le considera. Se i nodi di fallback hanno già capacità disponibile, i nuovi Pod possono finirci prima che sui nodi preferiti. Abilitare
activeMigration.optimizeRulePrioritysostituisce quei nodi di fallback nel tempo, e unaconsolidationThresholdpiù restrittiva aiuta a svuotare prima i nodi di fallback sottoutilizzati.Le modifiche valgono solo per il futuro: modificare una ComputeClass non altera i nodi in esecuzione. Eseguite cordon e drain dei nodi esistenti durante una finestra di manutenzione, così vengono sostituiti secondo le nuove regole.
Tenete i workloads stateful lontani dalla migrazione attiva: la migrazione attiva esegue l'eviction dei Pod e non sposta i dati. La guida alle best practice indica di disabilitarla per le classi usate da workloads stateful. Mettete quei workloads in una classe dedicata e usate la selezione automatica del tipo di disco oppure limitate la classe alle serie di macchine che supportano tutte il vostro tipo di disco.
I default di namespace non migrano i Pod esistenti: GKE inietta il selector della classe solo nei Pod creati dopo aver etichettato il namespace, quindi i Pod già in esecuzione non vengono toccati. Riavviate i workloads nel namespace dopo aver impostato un default, così i Pod ricreati ricevono il selector e la migrazione attiva. Un default a livello di cluster può invece migrare direttamente i Pod esistenti.
Attenzione al churn elevato di Pod: nei cluster con tassi molto alti di creazione ed eliminazione di Pod, il Cluster Autoscaler potrebbe non riuscire a valutare le regole inferiori prima che scada il backoff su quelle superiori, e i Pod restano in
Pending. Se incappate in questo caso limite, riducete la frequenza di consolidamento o disabilitate la migrazione attiva.Comportamento di fallback delle reservation: con
AnyBestEffort, se nessuna reservation corrispondente ha capacità, GKE prova un nodo on-demand per la stessa regola prima di passare alla successiva. Per passare direttamente alla regola successiva quando la capacità riservata è esaurita, usateaffinity: Specific(per le reservation con nome) oaffinity: AnyThenFail.
Vincoli minori
Un workload può selezionare una sola ComputeClass e ogni node pool creato manualmente può appartenere a una sola classe. Su Autopilot, bootDiskType supporta solo pd-balanced. I nomi delle classi non possono iniziare con gke o autopilot. Molte delle funzionalità descritte sopra richiedono patch version recenti, quindi verificate il vostro release channel prima di basarvi il vostro design.
Conclusione
Se i vostri cluster girano ancora su node pool fissi legati a tipi di macchina, iniziate con un namespace stateless. Assegnategli una catena di fallback Spot-first, poi osservate quanto spesso i Pod finiscono sulle regole di fallback e se la migrazione attiva li riporta indietro.
Tenete presente che una ComputeClass decide che tipo di nodo creare, ma la dimensione del nodo dipende dalle resource request dei vostri Pod. Per le regole machineFamily, il Cluster Autoscaler sceglie una dimensione di macchina in base alle request aggregate dei Pod in pending. Request sovradimensionate significano macchine più grandi, bin packing peggiore e un footprint di fallback più ampio.
È qui che il right-sizing ripaga. PerfectScale esegue in modo continuo il right-sizing delle request dei workloads, così i nodi creati dalle vostre ComputeClasses corrispondono a ciò che i Pod usano davvero. La sua funzionalità Node Recommendations vi aiuta a scegliere le famiglie di macchine più convenienti da inserire nelle regole di priorità. Se state analizzando la spesa dei cluster e volete vedere come questi setup di fallback si comportano nella pratica, prenotate una demo con il team.