Abrazar el cambio en una startup que se mueve rápido
Al menos una vez al año salgo a navegar con mi familia. Nada sofisticado: un velero que podamos manejar nosotros mismos para disfrutar de la naturaleza y del tiempo juntos. Curiosamente, un elemento clave en esos viajes es el viento —su dirección y su fuerza— y lo particular es que ¡nunca deja de cambiar!
Igual que el viento, la vida cambia todo el tiempo. Y esto se cumple todavía más en una startup que se mueve rápido. Lo que hoy funciona bien, mañana puede dejar de encajar. Saber reconocer cuándo toca pivotar, optimizar o incluso rehacer un sistema por completo es fundamental. Como ingenieros, tendemos a enamorarnos de nuestras decisiones y de los sistemas que construimos. Pero aferrarnos a decisiones pasadas puede convertirse en un lastre.
En Attribute™ nos enamoramos de las personas, no de lo que construimos, así que cuando llegó el momento de revisar nuestro stack de procesamiento de datos no fue ninguna sorpresa. Al principio tomamos las mejores decisiones de infraestructura posibles dadas nuestras restricciones. Pero conforme escalamos, esas mismas decisiones se volvieron limitaciones y nos obligaron a replantear y adaptarnos.
La decisión inicial: Serverless Dataproc para Spark
Cuando empezamos a armar nuestra infraestructura de procesamiento de datos, elegimos Serverless Dataproc para ejecutar nuestros jobs de Spark. En ese momento, la decisión tenía todo el sentido. Al ser un equipo pequeño y con capacidad de ingeniería limitada, no podíamos darnos el lujo de construir y mantener un clúster de Spark autoadministrado. Serverless Dataproc nos permitió enfocarnos en entregar valor sin preocuparnos por la carga operativa. Podíamos escribir nuestros jobs de Spark, desplegarlos con facilidad y dejar que Google Cloud se encargara del resto.
Los dolores del crecimiento: costo y falta de control
A medida que el negocio crecía, también crecían nuestras necesidades de procesamiento de datos. Más datos significaban más jobs, mayor frecuencia de ejecución y, con el tiempo, costos disparados.
También empezamos a chocar con cuellos de botella operativos. La naturaleza de caja negra de Dataproc nos daba muy poca visibilidad sobre los detalles de ejecución de los jobs, lo que dificultaba optimizar el rendimiento y depurar fallas de forma eficiente.
A esa altura, las desventajas pesaban más que los beneficios. Estaba claro que teníamos que recuperar el control.
El "por qué" detrás de la migración
Tengo una regla simple para abordar cualquier proyecto de ingeniería: debe partir de un "por qué" sólido. Si no hay una razón contundente para hacer algo, no vale la pena gastar en ello nuestro recurso más valioso: el tiempo. (Pista: probar una tecnología nueva solo porque es "cool" nunca es razón suficiente).
Para nuestra migración desde Dataproc, el "por qué" estaba claro:
- Eficiencia en costos: Serverless Dataproc se había vuelto demasiado caro.
- Transparencia operativa: necesitábamos mejor control y visibilidad sobre nuestros jobs de Spark.
- Escalabilidad y flexibilidad: queríamos poder afinar la infraestructura según nuestras necesidades cambiantes.
Con esas razones como motor, arrancamos un proyecto enfocado en migrar nuestros workloads de Spark de Dataproc a una solución autoadministrada basada en Kubernetes.
La migración: llevar Spark a Kubernetes
Decidimos desplegar nuestros jobs de Spark con Spark-on-Kubernetes, que permite ejecutar aplicaciones Spark como workloads nativos de Kubernetes. Este es un resumen general de nuestro enfoque:
Configuración de la infraestructura:
- Aprovisionamos un clúster de Kubernetes en Google Kubernetes Engine (GKE) con autoscaling habilitado para manejar workloads variables.
- Configuramos un namespace dedicado para nuestros jobs de Spark, garantizando una separación clara del resto de servicios.
- Se usan instancias Spot para reducir aún más los costos.
Ejecución de jobs con el CRD SparkApplication:
Nos apoyamos en el Spark Operator para gestionar los workloads de Spark como recursos nativos de Kubernetes.
Esto nos permitió definir los jobs de Spark de forma declarativa mediante Custom Resources (CRs) de Kubernetes, lo que facilita administrarlos y escalarlos.
Observabilidad y monitoreo:
Integramos Victoria Metrics y Grafana para el monitoreo en tiempo real de la ejecución de los jobs.
El resultado: más control, menores costos y mejor monitoreo
La migración se tradujo en mejoras significativas:
- Reducción de costos: al eliminar la sobrecarga administrada de Dataproc y optimizar el uso de recursos, redujimos en más del 70% los costos de procesamiento con Spark.
- Mayor transparencia: ganamos visibilidad mucho más profunda sobre la ejecución de los jobs, lo que se tradujo en mejor debugging y optimización. Esto también generó mayor sentido de propiedad en el equipo de Engineering; eliminar la caja negra nos obliga a hacer mejor trabajo de ingeniería.
- Escalabilidad: correr sobre Kubernetes nos permite escalar workloads de forma dinámica sin las limitaciones que impone un proveedor. Al mejorar el rendimiento de Spark y acortar los tiempos de ejecución, también ganamos eficiencia en el desarrollo de features, lo que se traduce en más valor de producto.
Reflexiones finales
La ingeniería consiste en resolver problemas de la manera más eficiente posible. Usar Serverless Dataproc fue la decisión correcta al principio, y movernos a Kubernetes fue la decisión correcta a medida que escalamos.
¿La gran lección? Mantente flexible. No te enamores de tus decisiones; abraza el cambio, porque va a llegar te guste o no.
Me pregunto cuándo empezaremos a sentir la fricción con nuestra actual infraestructura de aplicaciones Spark…