Implementación de Plataforma de Datos con Azure Data Factory, Databricks y CI/CD en Azure DevOps

1. Arquitectura de la Solución

https://www.databricks.com/blog/best-practices-kicking-databricks-workflows-natively-azure-data-factory
Figura 1: Arquitectura general de la plataforma de datos. Azure Data Factory (izquierda) orquesta pipelines de integración que ejecutan procesos en Azure Databricks (centro), donde se llevan a cabo las transformaciones de datos y se publican resultados para su consumo en herramientas analíticas como Power BI (derecha).

Para el caso de un banco con necesidades de analítica avanzada sobre ~1 TB de datos provenientes de múltiples fuentes, se propone una arquitectura de referencia basada en Azure. Las fuentes de datos incluyen sistemas heterogéneos: archivos planos (CSV, JSON) depositados en almacenamiento, bases de datos relacionales on-premises (por ejemplo, IBM DB2 de core bancario) y bases de datos SQL Server operacionales. Azure Data Factory (ADF) actúa como el servicio de orquestación ETL/ELT, extrayendo datos de estas fuentes de origen hacia un data lake en Azure (por ejemplo, un almacenamiento Azure Data Lake Gen2). ADF utiliza actividades de copia y movimientos de datos con integración a runtime auto hospedado cuando es necesario (e.g. para acceder a DB2 on-premises) y Linked Services configurados con autenticación segura (credenciales almacenadas en Azure Key Vault). Los datos crudos se almacenan en la capa Bronze del data lake (formato parquet o delta)【1†】.

Una vez aterrizados los datos, Azure Databricks toma el rol de procesamiento avanzado utilizando notebooks de PySpark y SQL. Siguiendo el patrón de medallion architecture, los notebooks transforman los datos desde la capa Bronze (raw data) hacia capas refinadas: Silver (datos limpiados y consolidados) y Gold (datos agregados y listos para analítica)【1†】【2†】. Por ejemplo, diariamente se ingieren transacciones del día (increméntales) y se combinan con dimensiones estáticas para producir reportes diarios, mientras que mensualmente se ejecutan notebooks de cierre que consolidan ~1 TB de información histórica. Azure Databricks provee el poder de Apache Spark para manejar estos volúmenes, optimizando el almacenamiento mediante formatos como Delta Lake (con características ACID y esquema evolutivo). Adicionalmente, se aprovechan frameworks de calidad de datos (expectativas, validaciones) para garantizar la confiabilidad de los datos en cada fase.

En cuanto a seguridad y gobernanza, la solución incorpora control de acceso a nivel de datos. Los datos sensibles (por ejemplo, información personal de clientes) se enmascaran o tokenizan antes de su uso en entornos de desarrollo y QA. Esto se logra aplicando funciones de mascaramiento dentro de los notebooks de Databricks (p. ej., sustituyendo nombres reales por alias, cifrando o hasheando identificadores) de modo que los ingenieros nunca vean datos reales en entornos no productivos. Asimismo, Azure Databricks puede integrar Unity Catalog para una capa adicional de control de acceso a datos y manejo de metadatos centralizado. Todos los datos en reposo se almacenan cifrados (Azure Storage encryption, y opciones de cifrado a nivel de columna para datos muy sensibles) y el tránsito se asegura vía TLS【2†】. Para aislar entornos, cada ambiente (Dev, QA, Prod) reside en su propio resource group o suscripción, con redes virtuales segregadas y, de ser un requerimiento bancario, integración con Private Endpoints y Azure VNET para eliminar exposición pública de los servicios.

2. Desarrollo, Integración y Despliegue (Dev, QA, Prod)

Figura 2: Flujo de CI/CD con Azure DevOps. El código de Azure Data Factory reside en un repositorio Git (ramas de desarrollo y colaboración), y al publicarse cambios se generan artefactos (plantillas ARM) que son desplegados mediante pipelines de Release hacia las instancias de ADF en QA y Prod【32†】【4†】

El proceso de Integración Continua y Despliegue Continuo (CI/CD) se implementa utilizando Azure DevOps (Azure Repos para control de versiones y Azure Pipelines para automatización). A continuación se describe el flujo típico adaptado al proyecto:

  1. Control de código fuente: El código de infraestructura (Terraform, scripts) y de datos (pipelines ADF en formato JSON, notebooks Databricks en Python/SQL) se versiona en Azure Repos Git (o GitHub). Azure Data Factory está conectado a este repositorio en el entorno de desarrollo, permitiendo a los desarrolladores trabajar en una rama de colaboración (por ejemplo main) con la integración GUI de ADF【32†】. Los desarrolladores crean ramas feature para desarrollar nuevas pipelines o modificaciones, asegurando aislamiento hasta que la funcionalidad esté lista.
  2. Integración continua: Al completar una tarea en ADF o notebooks, se realiza un Pull Request hacia la rama principal main. Esto desencadena validaciones automáticas en Azure DevOps: ejecución de pruebas unitarias de datos (por ejemplo, utilizando frameworks como Nutter para notebooks【4†】), linters de código PySpark, y verificación de estándares. Solo tras aprobación de PR, los cambios se integran en main.
  3. Publicación de artefactos: Cuando los cambios se fusionan en main, Azure Data Factory (en el entorno Dev) genera automáticamente un paquete de despliegue. En ADF, al hacer Publish desde la interfaz (o mediante API) se actualiza la rama especial adf_publish con la plantilla ARM de fábrica (ARM Template) y un archivo de parámetros con configuraciones de Linked Services, etc【32†】. Azure DevOps Pipeline de Build aprovecha esto para empaquetar los artefactos: la plantilla ARM de ADF, los scripts SQL/PySpark (por ejemplo, notebooks exportados como archivos .py), y los archivos de definición de infraestructura (Terraform) necesarios.
  4. Despliegue a Desarrollo (Infraestructura): Un primer stage de la pipeline de Release despliega la infraestructura base requerida en Dev usando Terraform (si no existe ya). Esto incluye instanciar servicios como Azure Databricks workspace, la cuenta de Data Lake, Key Vault, etc., según código IaC. Este despliegue puede ejecutarse automáticamente en cada cambio de IaC o manualmente cuando hay cambios infraestructurales.
  5. Despliegue a QA: Tras el build exitoso, la pipeline puede realizar despliegues automáticos hacia el entorno de QA. Primero se aplican las plantillas ARM para crear/actualizar la instancia de Azure Data Factory de QA con las nuevas pipelines/datasets (Azure DevOps ofrece tareas para desplegar Resource Manager templates). En paralelo, los notebooks actualizados se implementan en el workspace de Azure Databricks QA (por ejemplo, usando el CLI de Databricks para importar notebooks al workspace target). Variables de ambiente o parámetros permiten que, durante el despliegue, se reemplacen conexiones o rutas de datos apropiadas para QA (p. ej., base de datos de QA, contenedor de storage de QA). Antes de proceder a producción, se ejecutan pruebas de integración en QA: correr pipelines completos con datos de prueba o un subconjunto de datos real enmascarado, validando que todo funcione correctamente.
  6. Promoción a Producción: Con aprobación manual (gates de aprobación en Azure Pipelines) se autoriza la liberación a Prod una vez que QA ha sido validado. La pipeline entonces despliega de manera similar la plantilla ARM en la fábrica de Prod y los notebooks al workspace Databricks de Prod. Adicionalmente, se pueden ejecutar scripts de post-despliegue (PowerShell o CLI) para reactivar triggers en la instancia de ADF de Prod que hubieran sido pausados durante la actualización (es una buena práctica detener triggers programados antes de actualizar pipelines para evitar ejecuciones en mitad del despliegue)【32†】.
  7. Manejo de secretos y configuración: Ningún secreto (credenciales de BD, keys) se almacena en el código. En su lugar, se usan Linked Services configurados para obtener credenciales desde Azure Key Vault, y variables de pipeline/variable groups en Azure DevOps para credenciales de Databricks (personal access tokens) o variables de Terraform. Esto garantiza que los pipelines puedan ejecutarse sin intervención manual y sin exponer datos sensibles.

Al final, el resultado es un proceso totalmente automatizado: cualquier cambio de código (pipeline de datos o notebook) sigue un camino controlado desde Dev hasta Prod, con trazabilidad y aprobación. De este modo, se asegura la consistencia entre entornos y se minimizan errores humanos en la configuración. Vale destacar que solo el entorno de Desarrollo tiene integración Git activa; las fábricas de datos de QA y Prod no se editan manualmente ni están conectadas a Git, sino que reciben los cambios exclusivamente vía despliegues automatizados (ARM templates)【32†】. Esto previene desvíos de configuración entre ambientes. Finalmente, la supervisión se configura integrando ADF y Databricks con Azure Monitor y Application Insights para recibir alertas de fallos en pipelines o jobs, componente clave de operaciones en un ambiente bancario crítico.

3. Ejemplos de Scripts y Configuraciones

3.1 Configuración de Azure Data Factory y vinculación con Databricks

Azure Data Factory permite orquestar la ejecución de notebooks de Azure Databricks mediante actividades dedicadas. En la definición JSON de un pipeline, esto se representa con type: DatabricksNotebook y una referencia a un Linked Service de Databricks. Por ejemplo, el siguiente fragmento de JSON corresponde a una actividad de ADF que invoca un notebook de Databricks, pasando parámetros de entrada y salida:

{ "name": "RunDatabricksNotebook", "type": "DatabricksNotebook", "linkedServiceName": { "referenceName": "AzureDatabricks_LinkedService", "type": "LinkedServiceReference" }, "typeProperties": { "notebookPath": "/Users/analyst/Procesos/MaskingNotebook", "baseParameters": { "input_path": "bronze/datos_sensibles/", "output_path": "silver/datos_enmascarados/" } } }

En la configuración anterior, AzureDatabricks_LinkedService define la conexión al workspace de Databricks (incluyendo la URL del workspace y un token de autenticación). El notebookPath apunta al notebook PySpark que realizará la transformación (en este caso, aplicar enmascaramiento de datos sensibles), y se envían parámetros como rutas de entrada/salida. Al ejecutar este pipeline, ADF desencadenará el cluster/job de Databricks apropiado para correr el notebook【58†】.

3.2 Automatización de Infraestructura con Terraform

Para asegurar consistencia entre entornos, la infraestructura se define como código utilizando Terraform. A continuación se muestra un ejemplo simplificado de recursos Terraform para crear una instancia de Azure Data Factory y un workspace de Azure Databricks en el entorno de Desarrollo:

resource "azurerm_data_factory" "datafactory_dev" { name = "bank-adf-dev" resource_group_name = azurerm_resource_group.dev.name location = azurerm_resource_group.dev.location identity { type = "SystemAssigned" } } resource "azurerm_databricks_workspace" "workspace_dev" { name = "bank-dbx-dev" resource_group_name = azurerm_resource_group.dev.name location = azurerm_resource_group.dev.location sku = "premium" managed_resource_group_name = "${azurerm_resource_group.dev.name}-databricks-managed" }

En el script, se definen los recursos principales con sus respectivos nombres y configuraciones. La fábrica de datos recibe una identidad administrada para permitirle acceder a Key Vault u otros servicios sin credenciales explícitas. El workspace de Databricks se crea en SKU premium (necesaria para funciones avanzadas como Git integration y Jobs), y se especifica un resource group administrado (que Azure crea para almacenar recursos del workspace). Mediante Terraform, similares definiciones se replican (parametrizadas) para QA y Prod, cambiando nombres, por lo que crear nuevos entornos es consistente y rápido【5†】. Este enfoque IaC también cubre otros componentes: cuentas de Storage, Key Vaults, instancias de Azure SQL (si se usan para staging), redes, etc., garantizando que todos los entornos se construyan con la misma configuración base.

3.3 Pipeline de CI/CD en YAML (Azure DevOps)

La siguiente es una muestra de pipeline YAML en Azure DevOps que compila y despliega la solución de datos. Consta de dos stages simplificados: uno de construcción (que podría ejecutar pruebas y empaquetar artefactos) y stages de despliegue para cada entorno. Se ilustran aquí Dev y Prod por brevedad:

trigger: - main stages: - stage: Build jobs: - job: BuildArtifacts steps: - script: echo "Ejecutando pruebas unitarias y empaquetando artefactos..." - task: PublishBuildArtifacts@1 inputs: pathToPublish: "$(Pipeline.Workspace)/drop" artifactName: "drop" - stage: DeployDev dependsOn: Build jobs: - job: DeployInfrastructure steps: - script: terraform init && terraform apply -auto-approve -var env=dev - script: | pip install databricks-cli databricks configure --token <

En este pipeline, cuando hay un commit en la rama main, se inicia la etapa de Build. Luego, el stage DeployDev aplica Terraform para asegurar que la infraestructura de Dev esté actualizada (ej. crear o actualizar el ADF y Databricks workspace) y después utiliza la CLI de Databricks para importar los notebooks actualizados al workspace de Databricks en Dev. (En una pipeline real, también se incluiría la implementación de la plantilla ARM de ADF en este punto). Una vez validado en Dev, el stage de Prod se dispara (normalmente con una aprobación manual previa) y repite el proceso en el entorno de producción. Obsérvese que las credenciales sensibles (como DATABRICKS_TOKEN_PROD) se pasan de forma segura vía Azure DevOps Variable Groups o Azure Key Vault integration, no se exponen en texto plano.

3.4 Ejemplo de Tokenización y Enmascaramiento en Databricks

A continuación se muestra un ejemplo de cómo implementar enmascaramiento de datos sensibles usando PySpark en un notebook de Databricks. El código toma columnas como nombre completo, teléfono y email, y les aplica funciones de ofuscación:

from pyspark.sql.functions import regexp_replace, sha2, col # DataFrame original 'df' con columnas: FullName, Phone, Email df_masked = df.withColumn("FullName", regexp_replace(col("FullName"), r"\w+$", "****")) \ .withColumn("Phone", regexp_replace(col("Phone"), r"(\d{3})\d{4}(\d+)", r"\1****\2")) \ .withColumn("Email", regexp_replace(col("Email"), r"(?<=^.).(?=[^@]*@)", "*")) df_masked.show()

En este ejemplo, la columna FullName se transforma reemplazando el último token (presumiblemente el apellido) por ****, de modo que “Juan Pérez” se convierte en “Juan ****”. La columna Phone reemplaza 4 dígitos luego de los 3 iniciales por asteriscos (e.g., 3121234567 → 312****567). Para el Email, se utiliza una expresión regular con lookahead/lookbehind que mantiene la primera letra y el dominio, pero remplaza el resto del usuario por asteriscos (e.g., maria.gomez@bank.comm*****@bank.com). Alternativamente, para requerimientos más estrictos, se podría aplicar hashing o cifrado: por ejemplo, reemplazar el correo completo por un hash SHA-256 (usando sha2(col("Email"), 256))【4†】, o cifrar datos con una clave simétrica almacenada en Azure Key Vault (usando una librería como Fernet). La decisión entre enmascaramiento reversible (tokenización) o irreversible (hash) dependerá de si en entornos no productivos se necesita revertir los valores ofuscados para alguna prueba. En todos los casos, la clave es realizar la ofuscación en la capa de procesamiento (notebooks) y nunca exponer datos reales fuera de producción.

4. Mejores Prácticas de CI/CD y DataOps en Azure

  • Separación de ambientes y control de acceso: Mantener entornos aislados (Dev, QA, Prod) con sus propias suscripciones o resource groups. Restringir los permisos: los desarrolladores trabajan con datos enmascarados en Dev/QA y solo un grupo muy limitado accede a Prod. Usar roles RBAC y ACLs (p.ej. Unity Catalog) para limitar acceso a datos sensibles【2†】.
  • Infraestructura como código y plantillas: Versionar toda la infraestructura (Terraform, ARM Templates, Bicep) y los pipelines de datos. Esto garantiza reproducibilidad y trazabilidad de cambios. Automatizar la creación de entornos efímeros si se requiere (por ejemplo, crear entornos temporales para pruebas de integración end-to-end con datos sintetizados).
  • Git flow y políticas de código: Implementar una estrategia de branching (Git flow) adecuada: rama principal protegida, PRs con revisión obligatoria, ejecución automática de pipelines de prueba en cada PR (validación continua). Esto aplica tanto a proyectos de ADF (que deben usar integración Git solo en Dev) como al código de Databricks (almacenable en repos Git mediante Databricks Repos o exportado como archivos Python). Automatizar también verificaciones de calidad de código (linting) y formateo consistente (por ejemplo, black para Python).
  • Desacoplar configuración por ambiente: Utilizar parámetros y variables para diferencias entre entornos. En Data Factory, aprovechar Global Parameters o archivos de parámetros ARM para cambiar, por ejemplo, strings de conexión o nombres de rutas de blob según el ambiente【32†】. En Databricks, usar archivos de configuración (como .env o tablas de parámetros) que la pipeline lea para decidir qué data lake o clave usar en cada entorno.
  • Scripts pre/post despliegue: Automatizar tareas auxiliares en los despliegues de Data Factory: detener triggers antes de desplegar (para evitar ejecuciones concurrentes) y reanudarlos al final; limpiar o archivar ejecuciones fallidas antiguas; actualizar registros de versionado. Microsoft proporciona scripts PowerShell de ejemplo para estas tareas comunes【32†】.
  • Pruebas automatizadas de datos: Incorporar pruebas tanto unitarias (p.ej., funciones PySpark individuales) como de integración (validar que un pipeline de ADF produce los resultados esperados dado un input conocido). Frameworks como Nutter permiten escribir pruebas para notebooks Databricks, y en ADF se pueden hacer ejecuciones de pipeline en modo testing verificando conteos, sumas, etc. Estas pruebas pueden ejecutarse en los pipelines CI/CD (por ejemplo, en stage de Build) para impedir que un cambio rompa el flujo de datos【4†】.
  • Monitoreo y alertas: Configurar integración de logs y métricas hacia Azure Monitor/Application Insights. ADF envía métricas de pipeline (ejecuciones, fallos) y Databricks puede enviar logs de clusters y métricas de jobs. Establecer alertas proactivas (por ejemplo, si una pipeline clave no corre antes de cierta hora, o si el tiempo de ejecución excede umbral). Implementar dashboards de observabilidad que combinen información de ADF, Databricks y el estado del data lake (espacio utilizado, retrasos, etc.)【4†】.
  • Seguridad end-to-end: Además del enmascaramiento de datos, usar principios de Zero Trust: autenticación mediante identidades gestionadas para que Data Factory acceda a fuentes/targets sin embebido de credenciales, almacenamiento de secretos solo en Key Vault, uso de https siempre, y cifrado de datos sensible a nivel columna cuando sea necesario (como describió Northwestern Mutual en su implementación de cifrado en Databricks【4†】). Realizar revisiones periódicas de acceso y pruebas de penetración en la plataforma de datos, especialmente dado el carácter sensible de datos bancarios.

5. Bibliografía

[1] Azure Data Factory – Guía de integración continua y entrega continua (CI/CD)

[2] Azure Architecture Center – DataOps para data warehouse moderno (Contoso Parking Sensors Demo)

[3] Microsoft – Ejemplo de CI/CD en Azure Databricks con Azure DevOps

[4] Databricks Blog – Encriptación de columna y protección de PII en Databricks (Keyuri Shah, 2020)

[5] Microsoft – Ejecutar notebooks de Azure Databricks desde Azure Data Factory (tutorial)

[6] Terraform Registry – Azure Databricks Workspace (configuración Terraform)

Comentarios

Entradas más populares de este blog

Desarrollo de un Nodo Python en ROS Humble para Captura de Imágenes de Cámara, Detección de Objetos y Diseminación de Información en Gazebo