Accogliere il cambiamento in una startup che corre veloce
Almeno una volta all'anno vado in barca a vela con la mia famiglia. Nulla di particolare: una barca che possiamo manovrare da soli per goderci la natura e stare in famiglia. Sorprendentemente, un elemento chiave di quei viaggi è il vento – direzione e intensità – e la sua caratteristica è proprio questa: cambia di continuo!
Come il vento, anche la vita è in costante mutamento. E questo vale ancora di più in una startup che corre veloce. Ciò che funziona bene oggi potrebbe non essere la scelta giusta domani. Saper riconoscere quando è il momento di cambiare rotta, ottimizzare o addirittura rivoluzionare un sistema è fondamentale. Noi ingegneri tendiamo a innamorarci delle nostre decisioni e dei sistemi che costruiamo. Ma restare aggrappati alle scelte del passato può diventare un peso.
In Attribute™ ci innamoriamo delle persone, non di ciò che costruiamo, quindi quando è arrivato il momento di rimettere in discussione il nostro stack di data processing non ci ha colto di sorpresa. All'inizio abbiamo fatto le migliori scelte infrastrutturali possibili dati i nostri vincoli. Ma con la crescita, quelle stesse scelte sono diventate un limite, costringendoci a ripensare e ad adattarci.
La scelta iniziale: Serverless Dataproc per Spark
Quando abbiamo iniziato a costruire la nostra infrastruttura di data processing, abbiamo scelto Serverless Dataproc per eseguire i nostri job Spark. All'epoca, era una decisione perfettamente sensata. Con un team ristretto e una capacità ingegneristica limitata, non potevamo permetterci il lusso di costruire e mantenere un cluster Spark gestito internamente. Serverless Dataproc ci ha permesso di concentrarci sulla generazione di valore senza doverci preoccupare dell'overhead operativo. Potevamo scrivere i nostri job Spark, distribuirli con facilità e lasciare che Google Cloud pensasse al resto.
I dolori della crescita: costi e mancanza di controllo
Con la crescita del business sono cresciute anche le nostre esigenze di data processing. Più dati significavano più job, una frequenza di esecuzione più alta e, alla lunga, costi alle stelle.
Abbiamo anche iniziato a scontrarci con colli di bottiglia operativi. La natura black-box di Dataproc ci lasciava poca visibilità sui dettagli di esecuzione dei job, rendendo difficile ottimizzare le performance e fare il debug degli errori in modo efficiente.
A quel punto, gli svantaggi superavano i benefici. Era chiaro: dovevamo riprenderci il controllo.
Il "perché" dietro la migrazione
Ho una regola semplice quando affronto qualsiasi progetto di ingegneria: deve partire da un "perché" solido. Se non c'è una ragione convincente per fare qualcosa, non dovremmo sprecare la nostra risorsa più preziosa, il tempo. (Piccolo suggerimento: provare una nuova tecnologia solo perché è "figa" non è mai un motivo abbastanza valido).
Per la nostra migrazione da Dataproc, il "perché" era chiaro:
- Efficienza dei costi: Serverless Dataproc era diventato troppo costoso.
- Trasparenza operativa: ci servivano più controllo e visibilità sui nostri job Spark.
- Scalabilità e flessibilità: volevamo poter mettere a punto l'infrastruttura in base alle nostre esigenze in evoluzione.
Con queste motivazioni a guidarci, abbiamo avviato un progetto mirato per migrare i nostri workloads Spark da Dataproc a una soluzione self-managed basata su Kubernetes.
La migrazione: portare Spark su Kubernetes
Abbiamo deciso di deployare i nostri job Spark utilizzando Spark-on-Kubernetes, che consente di eseguire le applicazioni Spark come workloads Kubernetes nativi. Ecco una panoramica ad alto livello del nostro approccio:
Setup dell'infrastruttura:
- Abbiamo predisposto un cluster Kubernetes su Google Kubernetes Engine (GKE) con l'autoscaling abilitato per gestire workloads variabili.
- Abbiamo configurato un namespace dedicato ai nostri job Spark, garantendo una netta separazione dagli altri servizi.
- Utilizziamo istanze Spot per ridurre ulteriormente i costi.
Esecuzione dei job con SparkApplication CRD:
Ci siamo affidati allo Spark Operator per gestire i workloads Spark come risorse Kubernetes native.
Questo ci ha permesso di definire i job Spark in modo dichiarativo tramite le Custom Resources (CR) di Kubernetes, rendendoli più semplici da gestire e scalare.
Osservabilità e monitoraggio:
Abbiamo integrato Victoria Metrics e Grafana per il monitoraggio in tempo reale dell'esecuzione dei job.
Il risultato: più controllo, costi più bassi e monitoraggio migliore
La migrazione ha prodotto miglioramenti significativi:
- Riduzione dei costi: eliminando l'overhead gestito di Dataproc e ottimizzando l'uso delle risorse, abbiamo tagliato di oltre il 70% i costi di elaborazione Spark.
- Maggiore trasparenza: abbiamo ottenuto una visibilità molto più profonda sull'esecuzione dei job, con benefici concreti su debugging e ottimizzazione. Ne è derivata anche una maggiore ownership da parte del team di Engineering: eliminata la black box, siamo spinti a fare un lavoro ingegneristico migliore.
- Scalabilità: girare su Kubernetes ci permette di scalare i workloads in modo dinamico, senza vincoli imposti dal vendor. Migliorare le performance di Spark e ridurre i tempi di esecuzione ci ha inoltre permesso di aumentare l'efficienza nello sviluppo delle feature, generando più valore per il prodotto.
Riflessioni finali
L'ingegneria significa risolvere i problemi nel modo più efficiente possibile. Scegliere Serverless Dataproc è stata la decisione giusta all'inizio, e passare a Kubernetes è stata la decisione giusta man mano che crescevamo.
La lezione chiave? Restare adattabili. Non innamorarsi delle proprie decisioni e accogliere il cambiamento, perché arriverà comunque, che ci piaccia o no.
Mi chiedo quando inizieremo a sentire l'attrito con la nostra attuale infrastruttura per applicazioni Spark…