Azure Databricks en LaLiga
Hace dos años que empezamos a utilizar Azure Databricks en LaLiga. En este tiempo hemos evolucionado nuestra Arquitectura de Big Data, sus procesos automáticos de despliegue e integración continua, hemos implementado con éxito varios proyectos y cuadros de mando utilizando esta tecnología, pero sobre todo ha sido un periodo del que hemos obtenido varias lecciones aprendidas, algunas de las cuales aprovechamos esta ocasión para compartirlas con vosotros.
Intro
Hace ya tres años de la creación del departamento de BI & Analytics en LaLiga, a través del cual, hemos intentado convertirnos en una compañía conducida por los datos (Data Driven Company), que nos permitiese tomar las mejores decisiones de la forma más rápida y con la menor incertidumbre, en cualquier ámbito de negocio.
Para andar este camino, es muy importante la elección de una plataforma tecnológica apropiada, ya que te va a condicionar tus costes, tus capacidades futuras, y otros muchos detalles, como la agilidad y rapidez de entrega de productos y proyectos, o la dificultad para encontrar perfiles en mercado.
Si bien, para los proyectos de dato pequeño, nos encontrábamos cómodos utilizando Azure SQL Database, Azure Data Factory y ejecutando código Python en Kubernetes, necesitábamos seleccionar una plataforma para los proyectos de dato grande, que pudiera ir más allá del propio Azure Datalake Store y Azure Datalake Analytics, ya no sólo por las capacidades de procesamiento, sino también pensando en la implementación de algoritmos predictivos y en el Real-Time. Otro punto importante a tener en cuenta era y es nuestra inmersión en el Cloud, en particular en Azure pero sin perder de viata a AWS, así como el uso de Power BI Premium como herramienta de visualización (nuestra carta de presentación a negocio y clubes).
Spark: la palabra mágica
Antes de continuar, es importante introducir Spark, una de las tecnologías más populares hoy en día para el procesamiento de dato grande, que se ha convertido en un estándar de facto, además de ser una solución Open Source dentro del proyecto Apache.
Spark es una tecnología Big Data para el procesamiento distribuido en memoria, que nos va a permitir cargar en memoria grandes cantidades de datos, para poder procesar y/o transformar dichos datos de forma eficaz, pudiendo utilizar lenguajes como Scala y Python (PySpark).
Cuando hablamos de procesamiento distribuido, nos referimos a poder tener un Cluster con 16 Nodos, 32 Nodos, 128 Nodos, o los que necesitemos (quizás cientos o miles). Aquí es donde está la ventaja de Spark, cuando tenemos cargas de trabajo tan grandes que no es operativo enfrentarse a ellas con una única máquina, y necesitamos apoyarnos en tecnologías distribuidas que nos permitan escalar horizontalmente.
Pero además, Spark ofrece otras ventajas, como por ejemplo SparkSQL, que nos permite ejecutar consultas SQL contra los ficheros de nuestro Datalake, simplificando el acceso a los mismos, y aumentando la productividad de los equipos de Analistas y Científicos de Datos (tan sólo hay que saber hacer simples consultas SQL).
Otra ventaja de Spark es la posibilidad de utilizar Spark Streaming, para poder realizar procesamiento a gran escala sobre un Stream de datos en Real-Time ó Near Real-Time.
¿Por qué elegimos Azure Databricks?
Teníamos que elegir una solución que pudiera satisfacer nuestras necesidades, siendo nuestros principales requisitos los siguientes:
- Poder ejecutar código Spark, para ingesta (batch y real-time) y exploración, entrenamiento y ejecución de modelos.
- Ejecución en Cloud, actualmente Azure, pero evitando vendor-locking, abiertos a oportunidades de negocio en AWS o en otros proveedores Cloud, que pudiera surgir en un futuro.
- Acceso a datos en Azure Datalake Store y Azure SQL Database.
- Poder utilizar GPUs para la ejecución de cálculos complejos.
- Bajo coste de mantenimiento. La plataforma tecnológica debe ser el medio, no el fin, nuestro esfuerzo debe estar totalmente focalizado en los objetivos de negocio.
Identificados los requisitos, pasamos a identificar y analizar las principales alternativas (Productos), para ver cuál de ellas se adaptaba mejor a nuestras necesidades.
Finalmente, la elección más razonable parecía ser Azure Databricks, que cumplía nuestros anteriores requisitos, y además valoramos algunos detalles adicionales como los siguientes:
- Databricks es de los creadores de Spark, siendo un producto que actualmente está en continua evolución y crecimiento.
- Azure Databricks está muy integrado con Azure y con Azure AD.
- Auto-escalado. Se puede configurar un Cluster para que escale automáticamente en función de la carga de trabajo, ajustando tanto su capacidad de procesamiento como optimizando su coste.
- Pago por uso. Los Clusters sólo generen consumo en Azure cuando están encendidos, siendo posible configurar su auto-apagado tras un periodo de inactividad (ej: 1 hora). Para la ejecución de trabajos en producción se pueden utilizar Jobs, que crean Clusters efímeros para la ejecución de tareas, que se destruyen a la finalización, optimizando el coste.
- Databricks ofrece su propio entorno de exploración basado en notebooks, que permite la colaboración y trabajo en equipo entre varias personas a la vez sobre el mismo notebook, y almacenando el histórico de cambios e incluso comentarios. Lo único que necesitas es Internet y un navegador.
Quizás, la principal duda sobre la elección de Azure Databricks, era hasta qué punto estábamos apostando por una solución propietaria, que nos pudiera posicionar a futuro en una situación de debilidad frente al proveedor de Cloud (Azure) o frente al fabricante (Databricks).
En este sentido, la principal tranquilidad es apostar por Spark. Adaptar los procesos de Spark sobre Databricks a otra forma de ejecutar código Spark, no debería ser un esfuerzo especialmente alto, y se podría hacer de forma gradual. Además, también es posible ejecutar Databricks en Amazon AWS.
Arquitectura de Azure Databricks en LaLiga
Antes de comenzar a explicar cómo hemos organizado la Arquitectura de Azure Databricks en LaLiga, es importante introducir y aclarar algunos conceptos, que nos ayudaran a comprender mejor los detalles posteriores.
- Workspace. Un Workspace es el recurso de Azure que deberemos crear para empezar a trabajar en Databricks. Dentro del Workspace, crearemos usuarios y grupos, los diferentes Clusters y Jobs, habilitaremos ciertas configuraciones, almacenaremos y ejecutaremos nuestros Notebooks, etc. No tiene coste. Realmente el coste lo tendremos cuando empecemos a arrancar Clusters y ejecutar Jobs.
- Cluster Interactivo. Permite definir el tamaño de máquina, para el Master y los Workers, el número de Workers que deseamos utilizar (podemos configurar Auto-escalado), así como las librerías que deseamos utilizar, qué usuarios tendrán qué permisos sobre dicho Cluster, y otras configuraciones. Solo tiene coste cuando está arrancado. Son necesarios para desarrollar, para tener un Cluster al que conectar (attach) nuestros Notebooks, y construir sobre ellos nuestro desarrollo, ejecutar consultas SparkSQL, etc.
- Job Cluster. La forma de productivizar nuestro código (Notebooks) es mediante Jobs, en los cuales damos la definición de un Cluster (tamaño de Nodos, librerías, etc.) y del Notebook que se ejecutará bajo demanda o mediante una planificación CRON. Para su ejecución utilizará un Cluster efímero, que se creará y se destruirá automáticamente a la finalización de la ejecución.
- Máquinas Virtuales. Una de las ventajas de Databricks, es que nos olvidamos de administrar las máquinas virtuales que se utilizan para prestar el servicio. No tenemos que provisionarlas, ni configurarlas, ni parchearlas, es casi como si no existieran, prácticamente sólo tenemos que decidir su tamaño (memoria, CPUs, y GPUs). Sin embargo, siguen existiendo, y pagaremos por ellas. De hecho, nos llegarán dos costes de Azure, el coste propio de Databricks, y el coste de los recursos de Azure que utilicemos (ej: Máquinas Virtuales, Discos, tráfico de red, etc.).
- VNET Injection. Podemos elegir que Databricks cree una red aislada en la que provisionará las máquinas virtuales que necesite cada vez que arrancamos un Cluster, o bien, podemos especificar la Virtual Network y segmentos de red que deseamos utilizar para la creación de las máquina virtuales de Databricks. Este es un detalle importante, que sólo podemos indicar en el momento de creación de cada Workspace, y no podemos cambiar después. Si deseamos poder acceder a recursos internos de la compañía, necesitaremos realizar esta configuración.
- Parquet. En el datalake, aunque en muchos casos utilizaremos como entrada ficheros en formatos CSV o JSON, crearemos una capa con el dato preparado, que almacenaremos con un formato diferente, optimizado para su consulta. Para ello utilizaremos el formato Parquet, un formato de almacenamiento columnar, que permitirá almacenarse de forma comprimida, y permitirá el particionamiento para evitar acceder a todos los datos cuando sólo necesitamos una parte de los mismos.
- Metastore. Podemos definir nuestros datos en el Metastore, es decir, si tenemos en Datalake un directorio que almacena los datos de nuestros clientes en formato Parquet, podemos definir en el Metastore una tabla externa referenciando a dicha ruta del Datalake e indicando que usa el formato Parquet, para de este modo, poder ejecutar consultas SQL de forma sencilla contra los ficheros de nuestro Datalake, como si fuera una simple tabla de una base de datos relacional. Sin embargo, las ventajas del Metastore van mucho más allá, pues podemos crear como tablas externas los datos maestros que tenemos en nuestro SQL Database, y así desde Azure Databricks, ser capaces de acceder con SQL tanto a SQL Server como a Datalake, y cruzar datos, de una forma muy sencilla.
- Table ACLs. Es una opción que se configura en cada Cluster o Job. El hecho de tener el Metastore, nos permite definir tablas externas de diferentes orígenes de datos, pero nos encontramos con un nuevo problema, que es poder securizar el acceso a dichos datos. Para ellos, es posible habilitar la funcionalidad de Table ACLs, mediante la cual, podemos especificar qué usuarios o grupos pueden acceder a qué tablas del Metastore, y así poder cumplir los requisitos de seguridad de los diferentes proyectos, y que los datos sólo sean accesibles para quienes realmente deben poder acceder a ellos.
- Credential Passthrough. Es una opción que se configura en cada Cluster o Job. Cuando accedemos a Azure Databricks, realmente estamos accediendo con un usuario de Azure AD, por ejemplo, cuando nos conectamos para ejecutar un Notebook, o unas celdas del mismo. Para simplificar el acceso al Datalake, en el caso de Azure Datalake Store, es posible utilizar la funcionalidad de Credential Passthrough, la cual permite acceder al Datalake desde Databricks utilizando las credenciales con las que accedemos a Databricks, de forma transparente. Esto simplifica el acceso, y además ayuda a garantizar, que los usuarios sólo acceden a los datos para los cuales tienen acceso.
- Azure Key Vaults y Secret Scopes. Habitualmente necesitamos almacenar secretos (ej: contraseñas, API Tokens, etc) y configuraciones (ej: nombre de servidores o bases de datos, URLs de acceso a APIs, etc.), que además pueden cambiar de un entorno (ej: Desarrollo) a otro (ej: Producción). Para ello, en el caso de Azure, podemos utilizar Azure Key Vault, para almacenar dichos valores, pudiendo utilizar un Key Vault por proyecto y entorno. Seguidamente, desde Databricks, podemos crear un Secret Scope que referieza al Key Vault que deseamos acceder, y dar acceso sólo a los usuarios que lo necesiten. Esto permite desacoplar configuraciones y secretos del propio código fuente (imporante: no guardar nunca contraseñas en Git), a la vez que facilita su almacenamiento seguro.
Una vez explicados estos conceptos, estamos en situación de poder explicar mejor cómo está organizada la Arquitectura de Azure Databricks en LaLiga.
- Entornos. En función del proyecto, utilizamos hasta cuatro entornos, que serían de Desarrollo, Test o Integración, QA o Pre-Producción y Producción. Cada entorno se materializa en un Workspace diferente.
- Workspaces de Productivización y de Exploración. Decidimos separar Workspaces de Productivización y de Exploración, para facilitar ciertos detalles de configuración que realizamos a nivel global mediante el uso de Global Init Scripts, y también por seguridad, para que los usuarios no tengan acceso al entorno de Producción, al separar el Cluster de Exploración en un Workspace independiente.
- VNET Injection. Hemos configurado nuestros Workspaces dentro de una de nuestras Virtual Networks de Azure, lo cual, nos permite poder acceder desde Databricks a recursos internos o servicios IaaS, como podría ser nuestro servicio de Nexus, para el almacenamiento de Modelos y librarías Python.
- Metastore compartido entre los Workspaces. Aunque en cada entorno (por ejemplo, en el entorno de Desarrollo) utilizamos dos Workspaces separados, uno para Productivización y otro para Exploración, se usa un único Metastore compartido, para minimizar el esfuerzo del mantenimiento del mismo. Se trata de una pequeña base de datos en Azure SQL Database.
- Cluster interactivo de Power BI con Auto-escalado. Tiene que estar levantado 24x7, para poder dar servicio a los refrescos de datos de los informes en Power BI. Para ello, se utiliza un único Worker con auto-escalado, para minimizar el coste en momentos de menor actividad.
- Cluster interactivo de Administración con Auto-escalado. Habitualmente está parado. Lo usa el equipo que administra el entorno, bajo demanda, para realizar tareas administrativas. Tiene un único Worker con auto-escalado.
- Clusters interactivos de Desarrollo. Los utilizan los equipos de desarrollo, para poder construir el código que necesitan ejecutar en sus proyectos, afinar las librerías necesarias, lanzar consultas SparkSQL, depurar el código de sus Notebooks, etc. Este tipo de Clusters interactivos, sólo existen en el entorno de Desarrollo, ya que en el resto de entornos, se ejecutan Jobs.
- Job Clusters para los diferentes proyectos. La forma de productivizar el código de los proyectos, es mediante un Job Cluster, que al final no deja de ser únicamente la definición de una Cluster (definición del tamaño de los nodos, librerías necesarias, etc.) y del Notebook que deseamos poder ejecutar de forma desatendida, ya sea bajo una planificación CRON o invocado desde Azure Data Factory.
- Acceso al Datalake desde Clusters Interactivos y Job Clusters. El acceso a Datalake se realiza utilizando un Service Principal para cada proyecto y entorno, que permite el acceso única y exclusivamente a los datos necesarios que le corresponde. Por seguridad, nadie conoce su contraseña, que se recicla de forma automática cada día. Cada Service Principal, se comparte tanto en el Cluster intertactivo de Desarrollo del proyecto, como en los Job Clusters utilizados por el proyecto.
- Clusters interactivos de Exploración, con Passthrough, Auto-escalado. Para labores de Análisis y Exploración de Datos se utiliza un Cluster específico por entorno, de tipo interactivo. Los usuarios tienen permiso de Restart, para poder arrancarlo cuando lo necesiten de forma autónoma, y tiene apagado automático a los 60 minutos de inactividad, para contener el coste. Tiene un único Worker con Auto-escalado, igualmente para contener el coste. El acceso a Datalake se realiza utilizando Credential Passthough, de tal modo, que las mismas credenciales de acceso que utilizamos para acceder a Azure Databricks (nuestro usuario de Azure AD) se utilizan para acceder al Datalake, de tal modo que los usuarios sólo puedan acceder a los datos a los que tiene permisos.
Ejemplo de un proyecto real
Veamos el caso de uso de un proyecto real. La siguiente imagen representa uno de los primeros proyectos que arrancamos con Azure Databricks en LaLiga, el proyecto del Sandbox.
El objetivo de este proyecto, es ingestar la información de tracking y eventing de los partidos de futbol de LaLiga, siendo el tracking la posición X e Y de los jugadores y pelota 25 veces por segundo, y el eventing la información de los eventos que ocurren en un partido (ej: pase, tiro a puerta, saque, etc.). De este modo, somos capaces de cruzar el tracking y el eventing, crear métricas adicionales relacionadas con los diferentes eventos y situaciones que ocurren en un partido, y ofrecerlo en un formato sencillo para su consulta y explotación, tanto a los equipos técnicos de los clubes como a terceros equipos de investigación.
El proceso de ingesta del histórico de todos los partidos, cruzando el tracking y el eventing, y la construcción de todas las métricas adicionales, es un ejercicio computacionalmente complejo en términos de memoria y CPU, que realizamos utilizando Azure Databricks.
Se pueden obtener muchas conclusiones, unas más evidentes y otras más complejas. En la siguiente imagen, mostramos el ejemplo del Convex Hull (una línea imaginaria que envuelve a todos los jugadores del equipo excepto al portero), cuya forma y tamaño tiene una clara relación con las situaciones de ataque y defensa de los equipos durante un partido. Este un ejemplo bastante visual. Hay muchos más.
Un vistazo atrás
Hace ya bastante tiempo, quizás un par de años, que empezamos a trabajar con Azure Databricks en LaLiga. No ha sido un camino fácil, tanto su implantación, como su puesta en marcha, y su adopción y divulgación con los diferentes equipos técnicos, han requerido de bastante esfuerzo.
Sin embargo, tras este tiempo, nuestra reflexión es que tomamos una buena decisión, y que con el resto de alternativas que teníamos sobre la mesa, nos habríamos encontrado al menos con las mismas dificultades, probablemente incluso con alguna más.
Actualmente tenemos diferentes proyectos en producción de diferente naturaleza, informes en Power BI Premium, procesos de ingesta en Batch, procesos de ingesta en Real-Time con Spark Streaming, entornos de exploración para Analistas y Científicos de Datos, etc., y seguimos creciendo sobre esta plataforma.
Hemos creado nuestros propios procesos de integración y despliegue continuo con Jenkins, pruebas unitarias automáticas Dockerizando Spark en standalone sobre un agente de Jenkins integrado con Sonarqube, tanto para el resultado de las pruebas unitarias como para el análisis estático de código, hemos automatizado diferentes tareas administrativas para minimizar el coste de mantenimiento y aumentar la productividad del equipo de Arquitectura y DevOps, y seguimos avanzando.
En general, aunque no ha sido fácil, nuestro feedback es positivo.
Esperamos que este artículo pueda haber sido de vuestro interés, que os ayude a valorar algunas de nuestras lecciones aprendidas, y a tener una perspectiva de cómo trabaja una empresa como LaLiga en este tipo de retos tecnológicos.