Cloud Run: concurrency, scaling e cold start
Cloud Run esegue container stateless che scalano automaticamente, anche fino a zero istanze. Le tre leve principali che l’esame testa sono concurrency, min-instances e CPU allocation, perché governano insieme latenza, throughput e costo.
La concurrency definisce quante richieste una singola istanza serve in parallelo. Un valore alto (default elevato) massimizza l’uso della CPU per istanza e riduce il numero di container attivi, quindi il costo, ma richiede codice thread-safe e non è adatto a workload CPU-bound per richiesta. Un valore basso (fino a 1) isola le richieste — utile per carichi pesanti o non thread-safe — ma fa scalare orizzontalmente prima, aumentando le istanze e la spesa. Regolare la concurrency è quasi sempre il primo intervento per ottimizzare costo e latenza sotto carico.
Il cold start è la latenza di avvio quando una nuova istanza parte da zero: pull dell’immagine, boot del container, inizializzazione dell’app. Per eliminarlo su percorsi latency-sensitive si impostano le min-instances a un valore maggiore di zero, tenendo container “caldi” sempre pronti — al prezzo di pagare istanze idle. Le max-instances invece pongono un tetto alla scalabilità, proteggendo backend fragili (per esempio un database con connessioni limitate) da un fan-out eccessivo.
La CPU allocation ha due modalità. Di default la CPU è allocata solo durante la richiesta: fuori dal ciclo request/response il processo non ottiene CPU, quindi task in background, callback asincroni o processing dopo l’invio della response non vengono eseguiti in modo affidabile. Con always-on CPU (CPU sempre allocata) il container mantiene CPU tra le richieste, necessaria per background work, e cambia il modello di fatturazione. Completano il quadro il request timeout (limite massimo di durata di una richiesta) e la memoria allocata per istanza, che va dimensionata sul working set reale.
Configurazione, secret e revision
Configurazioni non sensibili passano come environment variables; i dati sensibili (password, chiavi API, connection string) vanno in Secret Manager e montati come secret nel servizio — mai hardcoded nel codice né esposti come env in chiaro. Per l’identità, si assegna una service account dedicata con ruoli minimi e si usa Workload Identity invece delle service account key esportate.
Ogni deploy crea una nuova revision immutabile. Il traffic splitting tra revision abilita rilasci canary e blue/green e il rollback istantaneo ripristinando il traffico alla revision precedente.
Workload su GKE: Deployment, HPA e resource
Su GKE il deploy applicativo standard è un Deployment, che gestisce un set di Pod repliche tramite un ReplicaSet e supporta rolling update controllati. Lo scaling orizzontale automatico è compito dell’Horizontal Pod Autoscaler (HPA), che aggiusta il numero di repliche in base a metriche come utilizzo di CPU/memoria o metriche custom.
L’HPA funziona correttamente solo se ogni container dichiara i resource requests e limits. Le requests determinano lo scheduling (quanta CPU/memoria il Pod prenota su un nodo) e sono la base su cui l’HPA calcola l’utilizzo percentuale; i limits impongono il tetto oltre il quale il container viene throttlato (CPU) o terminato per OOM (memoria). Request troppo alte sprecano capacità e impediscono lo scheduling; troppo basse causano contesa e OOMKill.
Le probe governano il ciclo di vita del Pod: la readiness probe decide quando il Pod può ricevere traffico dal Service (fondamentale per non instradare richieste a un’app non ancora pronta), mentre la liveness probe rileva un processo bloccato e ne forza il restart. Una startup probe protegge app a boot lento dai falsi restart della liveness.
Trappole tipiche d’esame
- Latenza di primo byte inaccettabile su Cloud Run → soluzione: imposta min-instances > 0 per tenere istanze calde; non aumentare la concurrency, che non riduce il cold start.
- Task in background dopo la response non completano su Cloud Run → soluzione: abilita always-on CPU; con la CPU allocata solo durante la richiesta il lavoro post-response non è garantito.
- Costo troppo alto sotto carico su Cloud Run → soluzione: alza la concurrency per servire più richieste per istanza prima di aggiungere container, invece di scalare a molte istanze a concurrency bassa.
- Backend con connessioni limitate saturato dallo scaling → soluzione: imposta max-instances su Cloud Run o un tetto all’HPA su GKE per limitare il fan-out.
- HPA non scala i Pod su GKE → soluzione: definisci i resource requests di CPU/memoria; senza requests l’HPA non può calcolare l’utilizzo target.
- Traffico instradato a Pod non pronti dopo il deploy → soluzione: configura una readiness probe corretta; la liveness da sola riavvia i Pod ma non li esclude dal Service finché non sono pronti.
- Secret esposti in chiaro → soluzione: monta i valori sensibili da Secret Manager e usa Workload Identity, mai chiavi hardcoded o service account key esportate.