Está en la página 1de 100

Machine Translated by Google

bas harenslak
Julián de Ruiter

MANEJO
Machine Translated by Google

Representa una
tarea/operación Canalización como DAG

que queremos ejecutar

Tarea 2

Tarea 1 Tarea 4

Tarea 3
archivo DAG

(Pitón)
Intervalo de programación = @daily

Qué programa usar para Dependencia entre tareas, lo que


ejecutar el DAG indica que la tarea 3 debe ejecutarse
antes que la tarea 4
Machine Translated by Google

Canalizaciones de datos con Apache Airflow


Machine Translated by Google
Machine Translated by Google

Canalizaciones de
datos con Apache Airflow
BAS HARENSLAK
Y JULIÁN DE RUITER

MANEJO
ISLA REFUGIO
Machine Translated by Google

Para obtener información en línea y solicitar este y otros libros de Manning, visite
www.manning.com. La editorial ofrece descuentos en este libro cuando se pide en cantidad.
Para obtener más información, póngase en contacto

Departamento de Ventas Especiales


Manning Publications Co.
Calle Baldwin, 20
apartado de correos 761

Isla del refugio, NY 11964


Correo electrónico: orders@manning.com

©2021 por Manning Publications Co. Todos los derechos reservados.

Ninguna parte de esta publicación puede reproducirse, almacenarse en un sistema de recuperación o transmitirse de
ninguna forma o por medios electrónicos, mecánicos, fotocopiados o de otro modo, sin el permiso previo por escrito del
editor.

Muchas de las designaciones utilizadas por los fabricantes y vendedores para distinguir sus productos se reclaman
como marcas comerciales. Donde esas designaciones aparecen en el libro, y Manning Publications estaba al tanto de un
reclamo de marca registrada, las designaciones se han impreso en mayúsculas iniciales o en mayúsculas.

Reconociendo la importancia de preservar lo que se ha escrito, es política de Manning que los libros que publicamos se
impriman en papel libre de ácido, y realizamos nuestros mejores esfuerzos con ese fin.
Reconociendo también nuestra responsabilidad de conservar los recursos de nuestro planeta, los libros de Manning
están impresos en papel que es al menos 15 por ciento reciclado y procesado sin el uso de cloro elemental.

Editora de desarrollo: Tricia Louvar


Editor de desarrollo técnico: Arthur Zubarev
Manning Publications Co. Editor de reseñas: Aleks Dragosavljevic´
20 Baldwin Road PO Editora de producción: Deirdre S. Hiam
Box 761 Shelter Correctora: Michele Mitchell
Island, Nueva York 11964 Correctora: Keri Hales
Corrector técnico: Al Krinker
Tipógrafo: Dennis Dalinnik
Diseño de portada: Marija Tudor

ISBN: 9781617296901
Impreso en los Estados Unidos de América
Machine Translated by Google

contenidos breves
PARTE 1 COMENZANDO ............................................... ..........1
1 ÿ Conoce Apache Airflow 3 2 ÿ
Anatomía de un DAG de Airflow 20 3 ÿ
Programación en Airflow 40 4 ÿ Plantillas
de tareas usando el contexto de Airflow 60 5 ÿ Definición
de dependencias entre tareas 85

PARTE 2 MÁS ALLÁ DE LO BÁSICO ............................................... .....113


6 ÿ Activación de flujos de trabajo
115 7 ÿ Comunicación con sistemas externos 135 8
ÿ Creación de componentes personalizados 157 9 ÿ
Pruebas 186 10 ÿ Ejecución de tareas en contenedores
220

PARTE 3 FLUJO DE AIRE EN LA PRÁCTICA ............................................... ..253


11 ÿ Mejores prácticas 255
12 ÿ Caudal de aire operativo en producción 281

v
Machine Translated by Google

vi CONTENIDOS BREVES

13 ÿ Asegurar el flujo de aire 322 14 ÿ

Proyecto: Encontrar la manera más rápida de moverse por NYC 344

PARTE 4 EN LAS NUBES ............................................... ............365


15 ÿ Flujo de aire en las nubes 367

16 ÿ Flujo de aire en AWS 375

17 ÿ Flujo de aire en Azure 394

18 ÿ Flujo de aire en GCP 412


Machine Translated by Google

contenido
prefacio xv
agradecimientos xvii sobre
este libro xix
sobre los autores xxiii
sobre la ilustración de portada xxiv

PARTE 1 COMENZANDO ............................................... 1

1 Conozca Apache Airflow 3


1.1 Introducción a las canalizaciones de datos 4
Canalizaciones de datos como gráficos 4 ÿ Ejecución de un gráfico de canalización 6 Gráficos

de canalización frente a secuencias de comandos secuenciales 6 ÿ Ejecución de canalizaciones con

administradores de flujo de trabajo 9

1.2 Introducción a Airflow 10 Definición


flexible de canalizaciones en código (Python) 10 ÿ Programación y
ejecución de canalizaciones 11 ÿ Monitoreo y manejo de fallas 13 Carga
incremental y relleno 15

1.3 Cuándo usar Airflow 17


Razones para elegir Airflow 17 ÿ Razones para no elegir Airflow 17

1.4 El resto de este libro 18

viii
Machine Translated by Google

viii CONTENIDO

2 Anatomía de un Airflow DAG 20


2.1 Recopilación de datos de numerosas fuentes 21 Exploración
de los datos 21

2.2 Escribir su primer Airflow DAG 22 Tareas frente a


operadores 26 ÿ Ejecutar código Python arbitrario 27

2.3 Ejecutar un DAG en Airflow 29 Ejecutar Airflow


en un entorno de Python 29 ÿ Ejecutar Airflow en contenedores Docker 30 ÿ
Inspeccionar la interfaz de usuario de Airflow 31

2.4 Correr a intervalos regulares 33 2.5 Manejar


tareas fallidas 36

3 Programación en Airflow 40
3.1 Un ejemplo: procesamiento de eventos de usuario 41
3.2 Ejecución a intervalos regulares 42
Definición de intervalos de programación 42 ÿ Intervalos basados en cron
44 Intervalos basados en frecuencia 46 3.3 Procesamiento incremental

de datos 46
Obtener eventos de forma incremental 46 ÿ Referencias de tiempo dinámicas
usando fechas de ejecución 48 ÿ Partición de sus datos 50

3.4 Comprender las fechas de ejecución de Airflow 52 Ejecutar el


trabajo en intervalos de longitud fija 52 3.5 Usar el relleno

para llenar los vacíos del pasado 54 Ejecutar el trabajo en el tiempo 54


3.6 Las mejores prácticas para diseñar tareas 55 Atomicidad

55 ÿ Idempotencia 57

4 Plantillas de tareas usando el contexto de Airflow 60


4.1 Inspección de datos para su procesamiento con Airflow 61
Determinación de cómo cargar datos incrementales 61

4.2 Contexto de la tarea y plantillas Jinja 63 Argumentos del


operador de plantillas 64 ÿ ¿Qué hay disponible para la creación de
plantillas? 66 ÿ Plantillas de PythonOperator 68 Proporcionar variables
a PythonOperator 73 ÿ Inspeccionar argumentos de plantilla 75 4.3
Conectar otros sistemas 77
Machine Translated by Google

CONTENIDO ix

5 Definición de dependencias entre tareas 85


5.1 Dependencias básicas 86
Dependencias lineales 86 ÿ Dependencias de entrada/ salida 87

5.2 Bifurcación 90 Bifurcación dentro de tareas 90 ÿ Bifurcación dentro del


DAG 92

5.3 Tareas condicionales 97


Condiciones dentro de las tareas 97 ÿ Hacer que las tareas sean
condicionales 98 Usar operadores integrados 100 5.4 Más sobre las

reglas de activación 100 ¿Qué es una regla de activación? 101 ÿ El efecto de


las fallas 102 Otras reglas de activación 103 5.5 Compartir datos
entre tareas 104 Compartir datos usando XComs 104 ÿ Cuándo (no)

usar XComs 107 ÿ Usar backends personalizados de XCom 108

5.6 Encadenar tareas de Python con la API de Taskflow 108


Simplificar las tareas de Python con la API de Taskflow
109 Cuándo (no) usar la API de Taskflow 111

PARTE 2 MÁS ALLÁ DE LO BÁSICO .......................................113

6 Activación de flujos de trabajo 115


6.1 Sondeo de condiciones con sensores 116
Sondeo de condiciones personalizadas 119 ÿ
Sensores fuera del flujo feliz 120 6.2 Activación de

otros DAG 122 Relleno con TriggerDagRunOperator 126


Sondeo del estado de otros DAG 127 6.3 Inicio de
flujos de trabajo con REST/CLI 131

7 Comunicación con sistemas externos 135


7.1 Conexión a servicios en la nube 136 Instalación
de dependencias adicionales 137 ÿ Desarrollo de un modelo de
aprendizaje automático 137 ÿ Desarrollo local con sistemas
externos 143 7.2 Transferencia de datos entre sistemas 150

Implementación de un PostgresToS3Operator 151 ÿ Subcontratación del


trabajo pesado 155
Machine Translated by Google

X CONTENIDO

8 Creación de componentes personalizados 157


8.1 Comenzando con un PythonOperator 158
Simulación de una API de clasificación de películas 158 ÿ Obtención de
clasificaciones de la API 161 ÿ Creación del DAG real 164

8.2 Construyendo un gancho personalizado 166


Diseño de un gancho personalizado 166 ÿ Construcción de nuestro DAG
con MovielensHook 172

8.3 Construyendo un operador personalizado 173


Definición de un operador personalizado 174 ÿ Creación de un operador para
obtener calificaciones 175

8.4 Creación de sensores personalizados 178


8.5 Empaquetado de sus componentes 181
Arrancar un paquete de Python 182 ÿ Instalar su paquete 184

9 Prueba 186
9.1 Primeros pasos con las pruebas 187
Prueba de integridad de todos los DAG 187 ÿ Configuración de una
canalización de CI/ CD 193 ÿ Escritura de pruebas unitarias 195 ÿ
Estructura del proyecto Pytest 196 ÿ Prueba con archivos en el disco 201

9.2 Trabajar con DAG y contexto de tareas en pruebas 203


Trabajar con sistemas externos 208

9.3 Uso de pruebas para el desarrollo 215


Prueba de DAG completos 217

9.4 Emular entornos de producción con Whirl 218


9.5 Crear entornos DTAP 219

10 Ejecutar tareas en contenedores 220


10.1 Desafíos de muchos operadores diferentes 221
Interfaces de operador e implementaciones 221 ÿ Dependencias complejas y
conflictivas 222 ÿ Avanzando hacia un operador genérico 223

10.2 Introducción de contenedores 223


¿Qué son los contenedores? 223 ÿ Ejecutar nuestro primer contenedor
Docker 224 ÿ Crear una imagen Docker 225
Persistencia de datos usando volúmenes 227

10.3 Contenedores y flujo de aire 230

Tareas en contenedores 230 ÿ ¿Por qué usar contenedores? 231


Machine Translated by Google

CONTENIDO xi

10.4 Ejecutar tareas en Docker 232


Introducción a DockerOperator 232 ÿ Creación de imágenes de contenedor para tareas
233 ÿ Creación de un DAG con tareas de Docker 236
Flujo de trabajo basado en Docker 239

10.5 Ejecutar tareas en Kubernetes 240


Introducción a Kubernetes 240 ÿ Configuración de Kubernetes 242
Uso de KubernetesPodOperator 245 ÿ Diagnóstico de problemas relacionados con
Kubernetes 248 ÿ Diferencias con los flujos de trabajo basados en Docker 250

PARTE 3 EL FLUJO DE AIRE EN LA PRÁCTICA ..........................253

11 Mejores prácticas 255


11.1 Escritura de DAG limpios 256
Usar convenciones de estilo 256 ÿ Administrar credenciales de forma centralizada 260
Especifique los detalles de configuración de manera consistente 261 ÿ Evite realizar
cualquier cálculo en su definición de DAG 263 ÿ Use fábricas para generar patrones
comunes 265 ÿ Agrupe tareas relacionadas usando grupos de tareas 269 ÿ Cree nuevos
DAG para grandes cambios 270

11.2 Diseño de tareas reproducibles 270


Requerir siempre que las tareas sean idempotentes 271 ÿ Los resultados de
las tareas deben ser deterministas 271 ÿ Diseñar tareas usando paradigmas
funcionales 272

11.3 Manejo eficiente de datos 272


Limitar la cantidad de datos que se procesan 272 ÿ Carga/ procesamiento
incremental 274 ÿ Caché de datos intermedios 275
No almacene datos en sistemas de archivos locales 275 ÿ Descargue el
trabajo a sistemas externos/ de origen 276

11.4 Administrar sus recursos 276


Administrar la simultaneidad mediante grupos 276 ÿ Detectar tareas de ejecución
prolongada mediante SLA y alertas 278

12 Caudal de aire operativo en producción 281


12.1 Arquitecturas de flujo de aire 282
¿Qué albacea es el adecuado para mí? 284 ÿ Configurar un metastore para Airflow
284 ÿ Un vistazo más de cerca al programador 286

12.2 Instalación de cada ejecutor 290


Configuración de SequentialExecutor 291 ÿ Configuración de LocalExecutor
292 ÿ Configuración de CeleryExecutor 293
Configuración de KubernetesExecutor 296
Machine Translated by Google

xi CONTENIDO

12.3 Captura de registros de todos los procesos de Airflow 302


Captura de la salida del servidor web 303 ÿ Captura de la salida del programador
303 ÿ Captura de registros de tareas 304 ÿ Envío de registros a almacenamiento
remoto 305

12.4 Visualización y monitoreo de métricas de Airflow 305


Recopilación de métricas de Airflow 306 ÿ Configuración de Airflow para enviar
métricas 307 ÿ Configuración de Prometheus para recopilar métricas 308
Creación de cuadros de mando con Grafana 310 ÿ ¿Qué debe supervisar? 312

12.5 Cómo ser notificado de una tarea fallida 314


Alertas dentro de los DAG y operadores 314 ÿ Definición de acuerdos de nivel de
servicio 316 ÿ Escalabilidad y rendimiento 318 ÿ Control del número máximo de tareas en
ejecución 318 ÿ Configuraciones de rendimiento del sistema 319 ÿ Ejecución de varios
planificadores 320

13 Asegurar el flujo de aire 322


13.1 Protección de la interfaz web de Airflow 323
Adición de usuarios a la interfaz RBAC 324 ÿ Configuración de la interfaz RBAC 327

13.2 Cifrado de datos en reposo 327


Creación de una clave Fernet 328

13.3 Conexión con un servicio LDAP 330


Comprender LDAP 330 ÿ Obtención de usuarios de un servicio LDAP 333

13.4 Cifrado del tráfico al servidor web 333


Comprensión de HTTPS 334 ÿ Configuración de un certificado para HTTPS
336

13.5 Obtención de credenciales de sistemas de gestión secretos


339

14 Proyecto: encontrar la forma más rápida de moverse por NYC 344


14.1 Comprender los datos 347
Uso compartido de archivos de Yellow Cab 348 ÿ Citi Bike REST API 348
Decidir sobre un plan de aproximación 350

14.2 Extracción de los datos 350


Descarga de datos de Citi Bike 351 ÿ Descarga de datos de Yellow Cab 353

14.3 Aplicar transformaciones similares a los datos 356


Machine Translated by Google

CONTENIDO XIII

14.4 Estructuración de una canalización de datos


360 14.5 Desarrollo de canalizaciones de datos idempotentes 361

PARTE 4 EN LAS NUBES.................................................... ..365

15 Flujo de aire en las nubes 367


15.1 Diseño de estrategias de implementación (nube) 368 15.2 Operadores

y ganchos específicos de la nube 369 15.3 Servicios administrados 370

Astronomer.io 371 ÿ Google Cloud Composer 371 Amazon Managed

Workflows para Apache Airflow 372

15.4 Elección de una estrategia de implementación 372

16 Flujo de aire en AWS 375


16.1 Implementación de Airflow en AWS 375 Selección

de servicios en la nube 376 ÿ Diseño de la red 377 Adición de


sincronización de DAG 378 ÿ Escalado con CeleryExecutor 378 Pasos adicionales
380

16.2 Enlaces y operadores específicos de AWS 381 16.3


Caso de uso: clasificación de películas sin servidor con AWS Athena 383
Descripción general 383 ÿ Configuración de recursos 384 ÿ Creación del
DAG 387 ÿ Limpieza 393

17 Flujo de aire en Azure 394


17.1 Implementación de Airflow en Azure 394 Selección

de servicios 395 ÿ Diseño de la red 395 Escalado con


CeleryExecutor 397 ÿ Pasos adicionales 398 17.2 Enlaces/operadores

específicos de Azure 398 17.3 Ejemplo: clasificación de películas sin servidor con

Azure Synapse 400 Descripción general 400 ÿ Configuración de recursos 401 ÿ

Construcción el DAG 404 ÿ Limpieza 410

18 Flujo de aire en GCP 412


18.1 Implementación de Airflow en GCP 413 Selección

de servicios 413 ÿ Implementación en GKE con Helm 415 Integración


con los servicios de Google 417 ÿ Diseño de la red 419 ÿ Escalado con
CeleryExecutor 419
Machine Translated by Google

xiv CONTENIDO

18.2 Ganchos y operadores específicos de GCP 422


18.3 Caso de uso: clasificación de películas sin servidor en GCP 427
Subir a GCS 428 ÿ Obtener datos en BigQuery 429
Extracción de las mejores calificaciones 432

apéndice A Muestras de código en ejecución


436 apéndice B Estructuras de paquetes Airflow 1 y 2 439
apéndice C Mapeo de métricas de Prometheus 443

índice 445
Machine Translated by Google

prefacio
Ambos hemos tenido la suerte de ser ingenieros de datos en tiempos interesantes y desafiantes.
Para bien o para mal, muchas empresas y organizaciones se están dando cuenta de que los datos juegan un papel
papel clave en la gestión y mejora de sus operaciones. Desarrollos recientes en
El aprendizaje automático y la IA han abierto una gran cantidad de nuevas oportunidades para capitalizar.
Sin embargo, la adopción de procesos centrados en datos suele ser difícil, ya que generalmente requiere
coordinar trabajos a través de muchos sistemas heterogéneos diferentes y vincular todo
juntos de manera agradable y oportuna para el próximo análisis o implementación del producto.
En 2014, los ingenieros de Airbnb reconocieron los desafíos de administrar datos complejos
flujos de trabajo dentro de la empresa. Para hacer frente a esos desafíos, comenzaron a desarrollar
Airflow: una solución de código abierto que les permitió escribir y programar flujos de trabajo
y supervise las ejecuciones de flujo de trabajo utilizando la interfaz web integrada.
El éxito del proyecto Airflow condujo rápidamente a su adopción bajo Apache
Software Foundation, primero como un proyecto de incubadora en 2016 y luego como un proyecto de alto nivel en
2019. Como resultado, muchas grandes empresas ahora confían en Airflow para orquestar
numerosos procesos de datos críticos.
Trabajando como consultores en GoDataDriven, hemos ayudado a varios clientes a adoptar el flujo de aire como
un componente clave en proyectos que involucran la construcción de lagos/plataformas de datos,
modelos de aprendizaje automático, etc. Al hacerlo, nos dimos cuenta de que entregar estos
las soluciones pueden ser desafiantes, ya que las herramientas complejas como Airflow pueden ser difíciles de aprender
durante la noche. Por esta razón, también desarrollamos un programa de capacitación de Airflow en GoData Driven, y
organizamos y participamos con frecuencia en reuniones para compartir nuestros
conocimientos, vistas e incluso algunos paquetes de código abierto. Combinados, estos esfuerzos han

XV
Machine Translated by Google

xvi PREFACIO

nos ayudó a explorar las complejidades de trabajar con Airflow, que no siempre fueron fáciles
comprender utilizando la documentación que tenemos a nuestra disposición.
En este libro, nuestro objetivo es proporcionar una introducción completa a Airflow que cubra
todo, desde la creación de flujos de trabajo simples hasta el desarrollo de componentes personalizados y
diseñar/gestionar implementaciones de Airflow. Tenemos la intención de complementar muchos de los
excelentes blogs y otra documentación en línea al reunir varios temas en
un solo lugar, utilizando un formato conciso y fácil de seguir. Al hacerlo, esperamos impulsar
tus aventuras con Airflow construyendo sobre la experiencia que hemos ganado
a través de diversos desafíos en los últimos años.
Machine Translated by Google

expresiones de gratitud
Este libro no hubiera sido posible sin el apoyo de muchas personas increíbles.
Los colegas de GoDataDriven y amigos personales nos apoyaron y brindaron valiosas sugerencias
y puntos de vista críticos. Además, los lectores del Programa de acceso anticipado de Manning
(MEAP) publicaron comentarios útiles en el foro en línea.
Los revisores del proceso de desarrollo también contribuyeron con comentarios útiles: Al
Krinker, Clifford Thurber, Daniel Lamblin, David Krief, Eric Platon, Felipe Ortega, Jason Rendel,
Jeremy Chen, Jiri Pik, Jonathan Wood, Karthik Sirasanagandla, Kent R.
Spillner, Lin Chen, Philip Best, Philip Patterson, Rambabu Posa, Richard Meinsen, Robert G.
Gimbel, Roman Pavlov, Salvatore Campagna, Sebastián Palma Mardones, Thorsten Weber, Ursin
Stauss y Vlad Navitski.
En Manning, le debemos un agradecimiento especial a Brian Sawyer, nuestro editor de
adquisiciones, quien nos ayudó a dar forma a la propuesta inicial del libro y creyó en que
podríamos llevarlo a cabo; Tricia Louvar, nuestra editora de desarrollo, que fue muy paciente al
responder todas nuestras preguntas e inquietudes, brindó comentarios críticos sobre cada uno de
nuestros capítulos preliminares y fue una guía esencial para nosotros a lo largo de todo este viaje;
y al resto del personal también: Deirdre Hiam, nuestra editora de proyectos; Michele Mitchell,
nuestra correctora de estilo; Keri Hales, nuestra correctora; y Al Krinker, nuestro corrector técnico.

bas harenslak
Quisiera agradecer a mis amigos y familiares por su paciencia y apoyo durante esta aventura de
un año y medio que pasó de ser un proyecto paralelo a innumerables días, noches y fines de
semana. Stephanie, gracias por aguantarme siempre trabajando en

xvii
Machine Translated by Google

xviii EXPRESIONES DE GRATITUD

el ordenador. Miriam, Gerd y Lotte, gracias por su paciencia y confianza en mí.


mientras escribía este libro. También me gustaría agradecer al equipo de GoDataDriven por su
apoyo y dedicación para aprender y mejorar siempre, no podría haber imaginado ser
el autor de un libro cuando comencé a trabajar hace cinco años.

Julián de Ruiter
En primer lugar, quisiera agradecer a mi esposa, Anne Paulien, y a mi hijo, Dexter, por
su infinita paciencia durante las muchas horas que pasé haciendo “sólo un poco más
trabajo” en el libro. Este libro no hubiera sido posible sin su inquebrantable
apoyo. Del mismo modo, también me gustaría agradecer a nuestra familia y amigos por su
apoyo y confianza. Finalmente, me gustaría agradecer a nuestros colegas de GoDataDriven por su
consejos y aliento, de quien también he aprendido una cantidad increíble en el
años pasados.
Machine Translated by Google

sobre este libro


Las canalizaciones de datos con Apache Airflow se escribieron para ayudarlo a implementar flujos de
trabajo (o canalizaciones) orientados a datos mediante Airflow. El libro comienza con los conceptos y
la mecánica involucrados en la creación programática de flujos de trabajo para Apache Airflow
utilizando el lenguaje de programación Python. Luego, el libro cambia a temas más profundos, como
la extensión de Airflow mediante la creación de sus propios componentes personalizados y la prueba
exhaustiva de sus flujos de trabajo. La parte final del libro se centra en el diseño y la gestión de
implementaciones de Airflow, abordando temas como la seguridad y el diseño de arquitecturas para
varias plataformas en la nube.

Quién debería leer este libro


Data Pipelines con Apache Airflow está escrito tanto para científicos como para ingenieros que buscan
desarrollar flujos de trabajo básicos en Airflow, así como para ingenieros interesados en temas más
avanzados, como la creación de componentes personalizados para Airflow o la gestión de
implementaciones de Airflow. Dado que los flujos de trabajo y los componentes de Airflow se crean en
Python, esperamos que los lectores tengan una experiencia intermedia con la programación en Python
(es decir, que tengan un buen conocimiento práctico de la creación de funciones y clases de Python,
que comprendan conceptos como *args y **kwargs, etc. .). Cierta experiencia con Docker también es
beneficiosa, ya que la mayoría de nuestros ejemplos de código se ejecutan con Docker (aunque
también se pueden ejecutar localmente si lo desea).

xix
Machine Translated by Google

XX SOBRE ESTE LIBRO

Cómo está organizado este libro: una hoja de


ruta El libro consta de cuatro secciones que cubren un total de 18 capítulos.
La Parte 1 se centra en los conceptos básicos de Airflow, explica qué es Airflow y describe sus
conceptos básicos. ÿ El Capítulo 1 analiza el concepto de flujos de trabajo/canalizaciones de datos y

cómo se pueden crear utilizando Apache Airflow. También analiza las ventajas y desventajas de
Airflow en comparación con otras soluciones, incluidas las situaciones en las que es posible
que no desee usar Apache Airflow.

ÿ El Capítulo 2 aborda la estructura básica de las canalizaciones en Apache Airflow (también


conocidas como DAG), y explica los diferentes componentes involucrados y cómo encajan
entre sí.
ÿ El Capítulo 3 muestra cómo puede usar Airflow para programar sus canalizaciones para que se
ejecuten en intervalos de tiempo recurrentes, de modo que pueda (por ejemplo) crear
canalizaciones que carguen nuevos datos de forma incremental con el tiempo. El capítulo
también se sumerge en algunas complejidades del mecanismo de programación de Airflow,
que a menudo es una fuente de confusión. ÿ El Capítulo 4 demuestra cómo puede usar los
mecanismos de creación de plantillas en Airflow para incluir dinámicamente variables en sus
definiciones de canalización. Esto le permite hacer referencia a cosas como programar fechas
de ejecución dentro de sus canalizaciones. ÿ El Capítulo 5 demuestra diferentes enfoques para
definir relaciones entre tareas en sus canalizaciones, lo que le permite crear estructuras de
canalización más complejas con ramas, tareas condicionales y variables compartidas.

La Parte 2 profundiza en el uso de temas de Airflow más complejos, incluida la interfaz con sistemas
externos, la creación de sus propios componentes personalizados y el diseño de pruebas para sus
canalizaciones. ÿ El Capítulo 6 muestra cómo puede desencadenar flujos de trabajo de otras formas

que no impliquen
horarios fijos, como archivos que se cargan o mediante una llamada HTTP.
ÿ El Capítulo 7 demuestra los flujos de trabajo mediante el uso de operadores que organizan
varias tareas fuera de Airflow, lo que le permite desarrollar un flujo de eventos a través de
sistemas que no están conectados.

ÿ El Capítulo 8 explica cómo puede crear componentes personalizados para Airflow que le permitan
reutilizar la funcionalidad en todas las canalizaciones o integrarlos con sistemas que no son
compatibles con la funcionalidad integrada de Airflow. ÿ El Capítulo 9 analiza varias opciones
para probar los flujos de trabajo de Airflow, toca varias propiedades de los operadores y cómo
abordarlos durante la prueba. ÿ El Capítulo 10 demuestra cómo puede usar flujos de trabajo
basados en contenedores para ejecutar tareas de canalización dentro de Docker o Kubernetes y
analiza las ventajas y desventajas de estos enfoques basados en contenedores.
Machine Translated by Google

SOBRE ESTE LIBRO xxx

La parte 3 se centra en la aplicación de Airflow en la práctica y toca temas como la mejor


prácticas, ejecutar/asegurar Airflow y un caso de uso demostrativo final.

ÿ El Capítulo 11 destaca varias mejores prácticas para usar cuando se construyen tuberías, que
le ayudará a diseñar e implementar soluciones eficientes y mantenibles.
ÿ El Capítulo 12 detalla varios temas a tener en cuenta cuando se ejecuta Airflow en un entorno de
producción, como arquitecturas para escalar, monitorear, registrar y
alertando
ÿ El Capítulo 13 analiza cómo asegurar su instalación de Airflow para evitar
acceso y minimizar el impacto en caso de que se produzca una vulneración.
ÿ El Capítulo 14 muestra un ejemplo de proyecto Airflow en el que periódicamente
procesar viajes de Yellow Cab y Citi Bikes de la ciudad de Nueva York para determinar el
medio de transporte más rápido entre barrios.

La Parte 4 explora cómo ejecutar Airflow en varias plataformas en la nube e incluye temas como
como diseñar implementaciones de Airflow para las diferentes nubes y cómo usar
operadores para interactuar con diferentes servicios en la nube.

ÿ El Capítulo 15 brinda una introducción general al describir qué componentes de Airflow están involucrados
en las implementaciones (en la nube), presenta la idea detrás de los componentes específicos de la
nube integrados en Airflow y sopesa las opciones de implementación.
su propia implementación en la nube frente al uso de una solución administrada.
ÿ El Capítulo 16 se enfoca en la plataforma en la nube AWS de Amazon, y amplía el capítulo anterior al
diseñar soluciones de implementación para Airflow en AWS y demostrar cómo se pueden usar
componentes específicos para aprovechar los servicios de AWS. ÿ El Capítulo 17 diseña
implementaciones y demuestra componentes específicos de la nube
para la plataforma Azure de Microsoft.
ÿ El Capítulo 18 aborda las implementaciones y los componentes específicos de la nube para Google
plataforma GCP.

Las personas nuevas en Airflow deben leer los capítulos 1 y 2 para tener una buena idea de lo que Airflow
es y lo que puede hacer. Los capítulos 3 a 5 brindan información importante sobre la clave de Airflow
funcionalidad. El resto del libro trata temas como la creación de componentes personalizados, las pruebas, las
prácticas recomendadas y las implementaciones, y puede leerse desordenadamente, en función de
las necesidades particulares del lector.

sobre el código
Todo el código fuente en las listas o el texto está en una fuente de ancho fijo como esta para separarlo
del texto ordinario. A veces, el código también está en negrita para resaltar el código que ha cambiado
de pasos anteriores en el capítulo, como cuando se agrega una nueva función a una línea existente
de código

En muchos casos, se ha reformateado el código fuente original; hemos añadido línea


saltos y sangría reelaborada para acomodar el espacio de página disponible en el
libro. En casos raros, incluso esto no fue suficiente, y los listados incluyen continuación de línea
Machine Translated by Google

XXII SOBRE ESTE LIBRO

marcadores (ÿ). Además, los comentarios en el código fuente a menudo se eliminan de las listas cuando el
código se describe en el texto. Las anotaciones de código acompañan a muchos de los listados, destacando
conceptos importantes.
Referencias a elementos en el código, scripts o clases/variables/
los valores suelen estar en cursiva para ayudar a distinguirlos del texto que los rodea.
El código fuente de todos los ejemplos y las instrucciones para ejecutarlos con Docker y Docker
Compose están disponibles en nuestro repositorio de GitHub (https://github.com/BasPH/ data-pipelines-with-
apache-airflow) y se puede descargar a través del sitio web del libro (www.manning.com/books/data-
pipelines-with-apache-airflow).

NOTA El Apéndice A proporciona instrucciones más detalladas sobre cómo ejecutar los ejemplos
de código.

Todos los ejemplos de código se han probado con Airflow 2.0. La mayoría de los ejemplos también deberían
ejecutarse en versiones anteriores de Airflow (1.10), con pequeñas modificaciones. Siempre que fue posible,
hemos incluido indicadores en línea sobre cómo hacerlo. Para ayudarlo a tener en cuenta las diferencias
en las rutas de importación entre Airflow 2.0 y 1.10, el apéndice B proporciona una descripción general de
las rutas de importación modificadas entre las dos versiones.

Foro de discusión de LiveBook


La compra de Data Pipelines con Apache Airflow incluye acceso gratuito a un foro web privado administrado
por Manning Publications donde puede hacer comentarios sobre el libro, hacer preguntas técnicas y recibir
ayuda del autor y otros usuarios. Para acceder al foro y suscribirse, vaya a https://livebook.manning.com/#!/
book/data pipelines-with-apache-airflow/discussion. Esta página brinda información sobre cómo ingresar al
foro una vez que está registrado, qué tipo de ayuda está disponible y sus reglas de conducta.

El compromiso de Manning con nuestros lectores es proporcionar un lugar donde pueda tener lugar un
diálogo significativo entre lectores individuales y entre lectores y autores. No se trata de un compromiso de
participación específica por parte de los autores, cuya contribución al foro es voluntaria (y no remunerada).
¡Le sugerimos que intente hacerles algunas preguntas desafiantes a los autores para que su interés no se
desvíe!
El foro y los archivos de debates anteriores estarán accesibles desde el sitio web del editor mientras el
libro esté impreso.
Machine Translated by Google

Sobre los autores


BAS HARENSLAK es ingeniero de datos en GoDataDriven, una empresa que desarrolla soluciones
basadas en datos ubicada en Ámsterdam, Países Bajos. Con experiencia en software
ingeniería y ciencias de la computación, le gusta trabajar en software y datos como si fueran
son rompecabezas desafiantes. Prefiere trabajar en software de código abierto, es un commiter
en el proyecto Apache Airflow, y es coorganizador de la reunión de Amsterdam Airflow.

JULIAN DE RUITER es un ingeniero de aprendizaje automático con experiencia en informática y


ciencias de la vida y tiene un doctorado en biología computacional del cáncer. Como desarrollador de software
experimentado, disfruta unir los mundos de la ciencia de datos y la ingeniería al
uso de software en la nube y de código abierto para desarrollar aprendizaje automático listo para la producción
soluciones En su tiempo libre, disfruta desarrollando sus propios paquetes de Python, contribuyendo a
proyectos de código abierto y jugando con la electrónica.

XXIII
Machine Translated by Google

sobre la ilustración de la portada


La figura de la portada de Data Pipelines con Apache Airflow se titula "Femme de l'Isle de
Siphanto", o Mujer de la isla de Siphanto. La ilustración está tomada de una colección de
trajes de gala de varios países de Jacques Grasset de Saint Sauveur (1757–1810), titulada
Costumes de Différents Pays, publicada en Francia en 1797.
Cada ilustración está finamente dibujada y coloreada a mano. La rica variedad de la colección
de Grasset de Saint-Sauveur nos recuerda vívidamente cuán diferentes culturalmente eran
las ciudades y regiones del mundo hace apenas 200 años. Aislados unos de otros, las
personas hablaban diferentes dialectos e idiomas. En las calles o en el campo, era fácil
identificar dónde vivían y cuál era su oficio o posición en la vida solo por su vestimenta.
La forma de vestir ha cambiado desde entonces y la diversidad por regiones, tan rica en
la época, se ha desvanecido. Ahora es difícil distinguir a los habitantes de diferentes
continentes, y mucho menos de diferentes ciudades, regiones o países. Quizás hemos
cambiado la diversidad cultural por una vida personal más variada, ciertamente por una vida
tecnológica más variada y acelerada.
En una época en la que es difícil distinguir un libro informático de otro, Manning celebra
la inventiva y la iniciativa del negocio informático con portadas de libros basadas en la rica
diversidad de la vida regional de hace dos siglos, revividas por Grasset de Saint. -Cuadros de
Sauveur.

XXIV
Machine Translated by Google

Parte 1

Empezando

Este parte del libro preparará el escenario para su viaje hacia la creación de conductos para todo
tipo de maravillosos procesos de datos utilizando Apache Airflow. El primero
dos capítulos tienen como objetivo brindarle una descripción general de qué es Airflow y qué
puede hacer por ti.
Primero, en el capítulo 1, exploraremos los conceptos de canalizaciones de datos y esbozaremos los
El papel que juega Apache Airflow para ayudarlo a implementar estas canalizaciones. Para establecer
expectativas, también compararemos Airflow con varias otras tecnologías y discutiremos cuándo podría o
no ser una buena opción para su caso de uso específico. Próximo,
el capítulo 2 le enseñará cómo implementar su primera canalización en Airflow. Después
construyendo la canalización, también examinaremos cómo ejecutar esta canalización y monitorear
su progreso utilizando la interfaz web de Airflow.
Los capítulos 3 a 5 profundizan en los conceptos clave de Airflow para brindarle una sólida
comprensión de los fundamentos de Airflow.
El Capítulo 3 se centra en la semántica de programación, que le permite configurar el flujo de aire para
ejecutar sus canalizaciones a intervalos regulares. Esto le permite (por ejemplo) escribir
canalizaciones que cargan y procesan datos de manera eficiente en forma diaria, semanal o mensual
base. A continuación, en el capítulo 4, analizaremos los mecanismos de creación de plantillas en Airflow, que
le permite hacer referencia dinámicamente a variables como las fechas de ejecución en su
tuberías Finalmente, en el capítulo 5, nos sumergiremos en diferentes enfoques para definir
dependencias de tareas en sus canalizaciones, que le permiten definir tareas complejas
jerarquías, incluidas tareas condicionales, ramas, etc.
Si es nuevo en Airflow, le recomendamos que se asegure de comprender las
conceptos principales descritos en los capítulos 3 a 5, ya que estos son clave para usarlo de manera efectiva.
Machine Translated by Google

2 PARTE 1 Primeros pasos

La semántica de programación de Airflow (descrita en el capítulo 3) puede ser especialmente confusa


para los nuevos usuarios, ya que pueden ser algo contradictorios cuando se encuentran por primera vez.
Después de terminar la parte 1, debe estar bien equipado para escribir sus propias líneas de tubería
básicas en Apache Airflow y estar listo para sumergirse en algunos temas más avanzados en
partes 2–4.
Machine Translated by Google

Conozca Apache Airflow

Este capítulo cubre


ÿ Mostrar cómo se pueden representar canalizaciones de datos
en flujos de trabajo como gráficos de tareas

ÿ Comprender cómo Airflow encaja en el ecosistema


de los administradores de flujo de trabajo

ÿ Determinar si Airflow es una buena opción para usted

Las personas y las empresas se basan cada vez más en los datos y desarrollan canalizaciones de
datos como parte de sus actividades comerciales diarias. Volúmenes de datos involucrados en estos
Los procesos comerciales han aumentado sustancialmente a lo largo de los años, de megabytes por
día a gigabytes por minuto. Aunque manejar este diluvio de datos puede parecer una
desafío considerable, estos crecientes volúmenes de datos se pueden gestionar con el
utillaje apropiado.
Este libro se centra en Apache Airflow, un marco orientado a lotes para crear
canalizaciones de datos. La característica clave de Airflow es que le permite crear fácilmente
canalizaciones de datos utilizando un marco flexible de Python, al mismo tiempo que proporciona muchas construcciones
bloques que le permiten unir las diferentes tecnologías encontradas
en los paisajes tecnológicos modernos.

3
Machine Translated by Google

4 CAPÍTULO 1 Conozca Apache Airflow

Airflow se considera mejor como una araña en una red: se encuentra en medio de sus procesos de datos y
coordina el trabajo que se lleva a cabo en los diferentes sistemas (distribuidos). Como
Por lo tanto, Airflow no es una herramienta de procesamiento de datos en sí misma, sino que orquesta los
diferentes componentes responsables de procesar sus datos en canalizaciones de datos.
En este capítulo, primero le daremos una breve introducción a las canalizaciones de datos en Apache.
Flujo de aire. Luego, discutiremos varias consideraciones a tener en cuenta al evaluar si Airflow es adecuado
para usted y demostraremos cómo dar los primeros pasos con
Flujo de aire.

1.1 Introducción de canalizaciones de datos


Las canalizaciones de datos generalmente consisten en varias tareas o acciones que deben ejecutarse para
lograr el resultado deseado. Por ejemplo, supongamos que queremos construir un pequeño panel meteorológico
que nos diga cómo será el clima la próxima semana (figura 1.1). A
implementar este tablero de tiempo en vivo, necesitamos realizar algo como los siguientes pasos:

1 Obtener datos de pronóstico del tiempo de una API meteorológica.

2 Limpiar o transformar los datos obtenidos (p. ej., convertir temperaturas


de Fahrenheit a Celsius o viceversa), para que los datos se ajusten a nuestro propósito.
3 Envíe los datos transformados al panel meteorológico.

Obtener y
datos limpios Figura 1.1 Descripción general del caso de uso del
panel meteorológico, en el que los datos meteorológicos
API de DashboardWeather se obtienen de una API externa y se introducen en un panel dinámico

Como puede ver, esta tubería relativamente simple ya consta de tres tareas diferentes
que cada uno realice una parte del trabajo. Además, estas tareas deben ejecutarse en un
orden específico, ya que (por ejemplo) no tiene sentido intentar transformar los datos
antes de ir a buscarlo. Del mismo modo, no podemos enviar ningún dato nuevo al tablero hasta que haya
sufrido las transformaciones requeridas. Como tal, debemos asegurarnos de que este
el orden de tareas implícito también se aplica cuando se ejecuta este proceso de datos.

1.1.1 Canalizaciones de datos como gráficos

Una forma de hacer que las dependencias entre tareas sean más explícitas es dibujar la tubería de datos como
un gráfico. En esta representación basada en gráficos, las tareas se representan como nodos en
el gráfico, mientras que las dependencias entre tareas están representadas por bordes dirigidos
entre los nodos de tareas. La dirección del borde indica la dirección del
dependencia, con un borde que apunta de la tarea A a la tarea B, lo que indica que la tarea A necesita
debe completarse antes de que la tarea B pueda comenzar. Tenga en cuenta que este tipo de gráfico generalmente se llama
un gráfico dirigido, debido a las direcciones en los bordes del gráfico.
Al aplicar esta representación gráfica a nuestra canalización del tablero meteorológico, podemos ver
que el gráfico proporciona una representación relativamente intuitiva de la canalización general
Machine Translated by Google

Presentación de canalizaciones de datos 5

(figura 1.2). Con solo echar un vistazo rápido al gráfico, podemos ver que nuestra canalización consta de tres
tareas diferentes, cada una de las cuales corresponde a una de las tareas descritas. Otro
que esto, la dirección de los bordes indica claramente el orden en que las tareas necesitan
para ser ejecutado: simplemente podemos seguir las flechas para rastrear la ejecución.

Obtener tiempo para Enviar datos a dashboarecast Limpiar datos de pronóstico d

Leyenda

Dependencia de tareaNodo de tarea

Figura 1.2 Representación gráfica de la canalización de datos para el panel meteorológico.


Los nodos representan tareas y los bordes dirigidos representan dependencias entre
tareas (con un borde que apunta de la tarea A a la tarea B, lo que indica que la tarea A debe
ejecutarse antes que la tarea B).

Este tipo de gráfico generalmente se denomina gráfico acíclico dirigido (DAG), ya que el gráfico contiene bordes
dirigidos y no contiene bucles ni ciclos (acíclico). Esta propiedad acíclica es extremadamente importante, ya que
evita que nos topemos con dependencias circulares.
(figura 1.3) entre tareas (donde la tarea A depende de la tarea B y viceversa). Estas dependencias circulares se
vuelven problemáticas cuando intentamos ejecutar el gráfico, ya que ejecutamos
en una situación en la que la tarea 2 solo puede ejecutarse una vez que se ha completado la tarea 3, mientras que
la tarea 3 solo puede ejecutarse una vez que se haya completado la tarea 2. Esta contradicción lógica
conduce a un tipo de situación de interbloqueo, en la que ni la tarea 2 ni la 3 pueden ejecutarse, lo que impide
impedir que ejecutemos el gráfico.

Un gráfico de directorios (DAG) de tareas acíclicas seleccionadas

Tarea 1 Tarea 2 Tarea 3

Un gráfico dir de tareas cíclicas seleccionadas

Tarea 1 Tarea 2 Tarea 3 Figura 1.3 Los ciclos en los gráficos


impiden la ejecución de tareas debido a
las dependencias circulares. En los
gráficos acíclicos (arriba), hay un camino
claro para ejecutar las tres tareas
diferentes. Sin embargo, en los gráficos
La tarea 2 nunca podrá ejecutarse, cíclicos (abajo), ya no hay una ruta de
debido a su dependencia de la tarea 3, ejecución clara debido a la
que a su vez depende de la tarea 2. interdependencia entre las tareas 2 y 3.

Tenga en cuenta que esta representación es diferente de las representaciones gráficas cíclicas, que pueden
contienen ciclos para ilustrar partes iterativas de algoritmos (por ejemplo), como son comunes
Machine Translated by Google

6 CAPÍTULO 1 Conozca Apache Airflow

en muchas aplicaciones de aprendizaje automático. Sin embargo, Airflow (y muchos otros administradores
de flujo de trabajo) utilizan la propiedad acíclica de los DAG para resolver y ejecutar de manera eficiente
estos gráficos de tareas.

1.1.2 Ejecutando un gráfico de tubería

Una buena propiedad de esta representación DAG es que proporciona un algoritmo relativamente sencillo
que podemos usar para ejecutar la canalización. Conceptualmente, este algoritmo consta de los siguientes
pasos:

1 Para cada tarea abierta (= no completada) en el gráfico, haga lo siguiente:


– Para cada borde que apunte hacia la tarea, verifique si la tarea "aguas arriba" en el
se ha completado el otro extremo del borde.
– Si se han completado todas las tareas anteriores, agregue la tarea en consideración a una cola de
tareas para ejecutar.
2 Ejecute las tareas en la cola de ejecución, marcándolas como completadas una vez que
terminar de realizar su trabajo.
3 Vuelva al paso 1 y repita hasta que se hayan completado todas las tareas del gráfico.

Para ver cómo funciona esto, analicemos una pequeña ejecución de nuestro canal de control (figura 1.4). En
nuestro primer ciclo a través de los pasos de nuestro algoritmo, vemos que las tareas de limpieza y envío
aún dependen de las tareas anteriores que aún no se han completado. Como tal, las dependencias de estas
tareas no se han satisfecho, por lo que en este momento no se pueden agregar a la cola de ejecución. Sin
embargo, la tarea de búsqueda no tiene aristas entrantes, lo que significa que no tiene dependencias
ascendentes insatisfechas y, por lo tanto, se puede agregar a la cola de ejecución.

Después de completar la tarea de búsqueda , podemos iniciar el segundo ciclo examinando las
dependencias de las tareas de limpieza y envío . Ahora vemos que la tarea de limpieza se puede ejecutar
ya que su dependencia ascendente (la tarea de búsqueda ) se ha completado. Como tal, podemos agregar
la tarea a la cola de ejecución. La tarea de inserción no se puede agregar a la cola, ya que depende de la
tarea de limpieza , que aún no hemos ejecutado.
En el tercer ciclo, después de completar la tarea de limpieza , la tarea de inserción finalmente está lista
para su ejecución, ya que ahora se ha satisfecho su dependencia anterior de la tarea de limpieza . Como
resultado, podemos agregar la tarea a la cola de ejecución. Una vez que la tarea de inserción ha terminado
de ejecutarse, no nos quedan más tareas por ejecutar, por lo que finaliza la ejecución de la canalización
general.

1.1.3 Gráficos de canalización frente a secuencias de comandos secuenciales

Aunque la representación gráfica de una tubería proporciona una descripción general intuitiva de las tareas
en la tubería y sus dependencias, es posible que se pregunte por qué no usaríamos un script simple para
ejecutar esta cadena lineal de tres pasos. Para ilustrar algunas ventajas del enfoque basado en gráficos,
pasemos a un ejemplo un poco más grande.
En este nuevo caso de uso, se nos acercó el propietario de una empresa paraguas,
Machine Translated by Google

Presentación de canalizaciones de datos 7

Bucle 1 Tarea lista para ejecución;


no hay dependencias insatisfechas

Obtener pronóstico del tiempo Limpiar datos de pronóstico Enviar datos al tablero

Bucle 2 La tarea ha terminado Tarea ahora lista para ejecución, No está listo para la ejecución
ejecución como su dependencia aguas arriba aún; todavía tiene insatisfecho
Está satisfecho dependencias

Obtener pronóstico del tiempo Limpiar datos de pronóstico dEnviar datos al tablero

Bucle 3 La tarea ha terminado Tarea ya lista


ejecución para ejecución

Obtener pronóstico del tiempo Limpiar datos de pronóstico Enviar datos al tablero

Estado final

Obtener pronóstico del tiempo Enviar datos al panelLimpiar datos de pronóstico

Leyenda

Dependencia no satisfechaAbrir tarea

tarea completada dependencia satisfecha

Figura 1.4 Uso de la estructura DAG para ejecutar tareas en la canalización de datos en el orden
correcto: representa el estado de cada tarea durante cada uno de los bucles a través del algoritmo, lo
que demuestra cómo esto conduce a la ejecución completa de la canalización (estado final)

que se inspiró en nuestro panel meteorológico y le gustaría intentar usar la máquina


aprendizaje (ML) para aumentar la eficiencia de su operación. Para ello, la empresa
al propietario le gustaría que implementáramos una canalización de datos que cree un modelo ML que
correlacione las ventas generales con los patrones climáticos. Este modelo se puede utilizar para predecir cómo
mucha demanda habrá de los paraguas de la empresa en las próximas semanas, dependiendo de las
previsiones meteorológicas para esas semanas (figura 1.5).
Para construir una tubería para entrenar el modelo ML, necesitamos implementar algo
como los siguientes pasos:

1 Prepare los datos de ventas haciendo lo siguiente:


– Obtener los datos de ventas del sistema de origen
– Limpiar/transformar los datos de ventas para que se ajusten a los requisitos
Machine Translated by Google

8 CAPÍTULO 1 Conozca Apache Airflow

Obtener y
API meteorológica limpiar datos

Combinar modelo Predecir


Obtener y
conjuntos de datos de tren las ventas de paraguas
limpiar datos

Los datos de ventas

Figura 1.5 Descripción general del caso de uso de la demanda global, en el que se utilizan datos meteorológicos
y de ventas históricos para entrenar un modelo que predice demandas de ventas futuras en función de las
previsiones meteorológicas

2 Prepare los datos meteorológicos haciendo lo siguiente:


– Obtener los datos del pronóstico del tiempo desde una API

– Limpieza/transformación de los datos meteorológicos para adaptarse a los requisitos


3 Combine los conjuntos de datos meteorológicos y de ventas para crear el conjunto de datos combinado que

se puede usar como entrada para crear un modelo de aprendizaje automático predictivo.

4 Entrene el modelo de ML con el conjunto de datos combinado.


5 Implemente el modelo ML para que pueda ser utilizado por la empresa.

Esta canalización se puede representar usando la misma representación basada en gráficos que usamos antes, dibujando tareas

como nodos y dependencias de datos entre tareas como bordes.

Una diferencia importante con nuestro ejemplo anterior es que los primeros pasos de esta canalización (obtener y borrar los

datos meteorológicos/de ventas) son, de hecho, independientes entre sí, ya que involucran dos conjuntos de datos separados. Esto

se ilustra claramente con las dos ramas separadas en la representación gráfica de la tubería (figura 1.6), que se pueden ejecutar en

paralelo si aplicamos nuestro algoritmo de ejecución de gráficos, haciendo un mejor uso de los recursos disponibles y reduciendo

potencialmente el tiempo de ejecución de una tubería. en comparación con la ejecución de las tareas secuencialmente.

Obtener pronóstico del Limpiar


tiempo datos de pronóstico

Entrenar Implementar
Unir conjuntos de datos
modelo ML modelo ML

Obtener datos de Limpiar datos de


ventas ventas

Figura 1.6 Independencia entre las tareas de ventas y meteorológicas en la representación gráfica de la
canalización de datos para el modelo general de previsión de demanda. Los dos conjuntos de tareas de
recuperación/limpieza son independientes, ya que involucran dos conjuntos de datos diferentes (los conjuntos de datos meteorológicos y de ventas
Esta independencia está indicada por la falta de bordes entre los dos conjuntos de tareas.
Machine Translated by Google

Presentación de canalizaciones de datos 9

Otra propiedad útil de la representación basada en gráficos es que separa claramente


canalizaciones en pequeñas tareas incrementales en lugar de tener un script monolítico o
proceso que hace todo el trabajo. Aunque tener un solo script monolítico puede no parecer un gran problema al
principio, puede introducir algunas ineficiencias cuando
las tareas en la canalización fallan, ya que tendríamos que volver a ejecutar todo el script. En cambio, en el
representación gráfica, solo necesitamos volver a ejecutar cualquier tarea fallida (y cualquier
dependencias).

1.1.4 Ejecución de canalización mediante administradores de flujo de trabajo

Por supuesto, el desafío de ejecutar gráficos de tareas dependientes no es un problema nuevo en computación. A
lo largo de los años, muchas de las llamadas soluciones de "gestión del flujo de trabajo"
han sido desarrollados para abordar este problema, que generalmente le permiten definir y
ejecutar gráficos de tareas como flujos de trabajo o canalizaciones.
Algunos administradores de flujo de trabajo conocidos de los que puede haber oído hablar incluyen los que se enumeran
en la tabla 1.1.

Tabla 1.1 Descripción general de varios administradores de flujo de trabajo conocidos y sus características clave.

Ata Flujos de trabajo Instalación Escalable


Nombre Escrito en Programación Relleno
originadodefinido en Interfaz de usuariob
plataforma horizontalmente

Flujo de aire airbnb Pitón Pitón Sí Sí Sí En cualquier lugar Sí

Argó Applatix YAML Vamos


Terceroc Sí Kubernetes Sí

LinkedIn YAML Java Sí No Sí En cualquier sitio

Conductor Netflix JSON Java No Sí En cualquier lugar Sí

Luigi Spotify Pitón Pitón No Sí Sí En cualquier lugar Sí

Hacer Disfraz C No No No en cualquier lugar no


ADSL

Metaflujo Netflix Pitón Pitón No No En cualquier lugar Sí

Nifi NSA interfaz de usuario Java Sí No Sí En cualquier lugar Sí

Oozie XML Java Sí Sí Sí Hadoop Sí

una. Algunas herramientas fueron creadas originalmente por (ex) empleados de una empresa; sin embargo, todas las herramientas son de código abierto y no están representadas por una
sola empresa.
b. La calidad y las características de las interfaces de usuario varían ampliamente.

C. https://github.com/bitphy/argo-cron.

Aunque cada uno de estos administradores de flujo de trabajo tiene sus propias fortalezas y debilidades,
todos brindan una funcionalidad central similar que le permite definir y ejecutar canalizaciones que contienen
múltiples tareas con dependencias.
Una de las diferencias clave entre estas herramientas es cómo definen sus flujos de trabajo.
Por ejemplo, herramientas como Oozie usan archivos estáticos (XML) para definir flujos de trabajo, que
proporciona flujos de trabajo legibles pero flexibilidad limitada. Otras soluciones como Luigi y
Machine Translated by Google

10 CAPÍTULO 1 Conozca Apache Airflow

Airflow le permite definir flujos de trabajo como código, lo que proporciona una mayor flexibilidad pero
puede ser más difícil de leer y probar (dependiendo de las habilidades de codificación de la persona que
implementa el flujo de trabajo).
Otras diferencias clave radican en el alcance de las funciones proporcionadas por el administrador de flujo
de trabajo. Por ejemplo, herramientas como Make y Luigi no brindan soporte integrado para
programación de flujos de trabajo, lo que significa que necesitará una herramienta adicional como Cron si desea
ejecute su flujo de trabajo en un horario recurrente. Otras herramientas pueden proporcionar funciones
adicionales, como programación, supervisión, interfaces web fáciles de usar, etc.
la plataforma, lo que significa que no tiene que unir varias herramientas usted mismo
para obtener estas características.

En definitiva, elegir la solución de gestión de flujo de trabajo adecuada para sus necesidades
requieren una consideración cuidadosa de las características clave de las diferentes soluciones y
cómo se ajustan a sus necesidades. En la siguiente sección, nos sumergiremos en Airflow: el enfoque
de este libro, y explore varias características clave que lo hacen particularmente adecuado para
manejar flujos de trabajo o canalizaciones orientados a datos.

1.2 Introducción al flujo de aire


En este libro, nos enfocamos en Airflow, una solución de código abierto para desarrollar y monitorear flujos de
trabajo. En esta sección, proporcionaremos una vista de helicóptero de lo que hace Airflow,
después de lo cual pasaremos a un examen más detallado de si es una buena opción para
su caso de uso.

1.2.1 Definición flexible de canalizaciones en código (Python)

Al igual que otros administradores de flujos de trabajo, Airflow le permite definir canalizaciones o flujos de trabajo
como DAG de tareas. Estos gráficos son muy similares a los ejemplos esbozados en el
sección anterior, con tareas definidas como nodos en el gráfico y dependencias como
bordes dirigidos entre las tareas.
En Airflow, usted define sus DAG usando código de Python en archivos DAG, que son esencialmente
secuencias de comandos de Python que describen la estructura del DAG correspondiente. Como tal,
cada archivo DAG generalmente describe el conjunto de tareas para un DAG determinado y las dependencias
entre las tareas, que luego Airflow analiza para identificar la estructura DAG
(figura 1.7). Aparte de esto, los archivos DAG suelen contener algunos metadatos adicionales
sobre el DAG que le dice a Airflow cómo y cuándo debe ejecutarse, etc. Bien
profundice más en esta programación en la siguiente sección.
Una ventaja de definir Airflow DAG en código Python es que esta programática
El enfoque le brinda mucha flexibilidad para crear DAG. Por ejemplo, como nosotros
verá más adelante en este libro, puede usar el código de Python para generar dinámicamente opciones
tareas dependiendo de ciertas condiciones o incluso generar DAG completos basados en metadatos externos o
archivos de configuración. Esta flexibilidad brinda una gran cantidad de personalización.
en cómo construye sus tuberías, lo que le permite adaptar Airflow a sus necesidades de construcción
tuberías arbitrariamente complejas.
Machine Translated by Google

Introducción al flujo de aire 11

Representa una
tarea/operación Canalización como DAG

que queremos ejecutar

Tarea 2

Tarea 1 Tarea 4

Tarea 3
archivo DAG

(Pitón)
Intervalo de programación = @daily

Qué programa usar para Dependencia entre tareas, lo que


ejecutar el DAG indica que la tarea 3 debe ejecutarse
antes que la tarea 4

Figura 1.7 Las canalizaciones de flujo de aire se definen como DAG mediante código Python
en archivos DAG. Cada archivo DAG normalmente define un DAG, que describe las
diferentes tareas y sus dependencias. Además de esto, el DAG también define un intervalo
de programación que determina cuándo Airflow ejecuta el DAG.

Además de esta flexibilidad, otra ventaja de la base Python de Airflow es que las tareas pueden
ejecutar cualquier operación que pueda implementar en Python. Con el tiempo, esto ha llevado al
desarrollo de muchas extensiones de Airflow que le permiten ejecutar tareas en una amplia variedad
de sistemas, incluidas bases de datos externas, tecnologías de big data y varios servicios en la
nube, lo que le permite crear canalizaciones de datos complejas que unen procesos de datos. a
través de muchos sistemas diferentes.

1.2.2 Programación y ejecución de canalizaciones


Una vez que haya definido la estructura de su(s) tubería(s) como DAG(s), Airflow le permite definir
un intervalo de programación para cada DAG, que determina exactamente cuándo Airflow ejecuta
su tubería. De esta forma, puede decirle a Airflow que ejecute su DAG cada hora, cada día, cada
semana, etc., o incluso usar intervalos de programación más complicados basados en expresiones
similares a Cron.
Para ver cómo Airflow ejecuta sus DAG, veamos brevemente el proceso general involucrado
en el desarrollo y ejecución de Airflow DAG. A un alto nivel, Airflow está organizado en tres
componentes principales (figura 1.8):

ÿ El programador de Airflow: analiza los DAG, verifica su intervalo de programación y (si la


programación de los DAG ha pasado) comienza a programar las tareas de los DAG para su
ejecución al pasarlas a los trabajadores de Airflow. ÿ Los trabajadores de Airflow: seleccionan
las tareas que están programadas para su ejecución y las ejecutan. Como tal, los trabajadores
son responsables de “realizar el trabajo”. ÿ El servidor web Airflow : visualiza los DAG
analizados por el programador y proporciona
la interfaz principal para que los usuarios supervisen las ejecuciones de DAG y sus resultados.
Machine Translated by Google

12 CAPÍTULO 1 Conozca Apache Airflow

Supervise las ejecuciones Visualice DAGs + Almacenar

y los resultados de DAG servidor resultados de tareas resultados de tareas

web de flujo de aire Trabajadores

Usuarios de flujo de aire

Almacén de metadatos

Airflow (base de datos)

Tienda Ejecutar
serializada tareas
DAG

Programador Programar
de flujo de aire tareas
Cola

Leer
DAG

Escribir flujos de trabajo de


datos en Python como DAG de Airflow
carpeta DAG

Archivos DAG que describen


canalizaciones (en Python)

Figura 1.8 Descripción general de los principales componentes involucrados en Airflow (p. ej., el servidor web, el
programador y los trabajadores de Airflow)

Podría decirse que el corazón de Airflow es el programador, ya que aquí es donde sucede la mayor
parte de la magia que determina cuándo y cómo se ejecutan sus canalizaciones. En un nivel alto, el
planificador ejecuta los siguientes pasos (figura 1.9):

1 Una vez que los usuarios han escrito sus flujos de trabajo como DAG, el programador lee los
archivos que contienen estos DAG para extraer las tareas correspondientes, las dependencias
y el intervalo de programación de cada DAG.
2 Para cada DAG, el programador luego verifica si el intervalo de programación para el DAG ha
pasado desde la última vez que se leyó. Si es así, las tareas en el DAG están programadas
para su ejecución.
3 Para cada tarea programada, el programador verifica si las dependencias (= tareas previas)
de la tarea se han completado. Si es así, la tarea se agrega a la cola de ejecución.

4 El programador espera varios momentos antes de iniciar un nuevo ciclo saltando


volver al paso 1.

El lector astuto habrá notado que los pasos seguidos por el planificador son, de hecho, muy similares
al algoritmo presentado en la sección 1.1. Esto no es por accidente, ya que
Machine Translated by Google

Introducción al flujo de aire 13

2. El programador de flujo de aire analiza DAG y programa 3. Trabajadores de flujo de aire

tareas de acuerdo con la programación de DAG, teniendo ejecutar tareas

en cuenta las dependencias entre tareas. programadas.


1. El usuario escribe
Programador de flujo de aire Trabajador de flujo de aire
el flujo de trabajo
como DAG. Leer DAG de archivos (tareas, Si la programación de
dependencias + intervalo de DAG ha pasado, Ejecutar tarea
programación) programe las tareas de DAG
Ejecución
archivo DAG
cola
Usuario (Pitón)
Para cada
tarea programada
Espera X segundos Recuperar
resultados de tareas
Comprobar
dependencias de tareas

Si se satisfacen las
4. Monitores de usuario dependencias de la tarea
ejecución + tarea

resultados utilizando
Agregar tarea a
la interfaz web.
la cola de ejecución

Tienda
serializada
DAG

servidor Recuperar DAG Almacenar

web de flujo de aire + resultados de tareas resultados de tareas

Almacén de metadatos

Airflow (base de datos)

Figura 1.9 Resumen esquemático del proceso involucrado en el desarrollo y ejecución de canalizaciones como DAG
mediante Airflow

Airflow esencialmente sigue los mismos pasos, agregando algo de lógica adicional en la parte superior para manejar
su lógica de programación.
Una vez que las tareas se han puesto en cola para su ejecución, son recogidas por un grupo de trabajadores
de flujo de aire que ejecutan tareas en paralelo y realizan un seguimiento de sus resultados. Estos resultados se
comunican al metastore de Airflow para que los usuarios puedan realizar un seguimiento del progreso de las tareas
y ver sus registros mediante la interfaz web de Airflow (proporcionada por el servidor web de Airflow).

1.2.3 Monitoreo y manejo de fallas


Además de programar y ejecutar DAG, Airflow también proporciona una amplia interfaz web que se puede usar para
ver DAG y monitorear los resultados de las ejecuciones de DAG.
Después de iniciar sesión (figura 1.10), la página principal proporciona una amplia descripción general de los
diferentes DAG con vistas resumidas de sus resultados recientes (figura 1.11).
Por ejemplo, la vista de gráfico de un DAG individual proporciona una descripción general clara de las tareas y
dependencias del DAG (figura 1.12), similar a las descripciones generales esquemáticas que hemos estado dibujando
en este capítulo. Esta vista es particularmente útil para ver la estructura de un DAG (que brinda información detallada
sobre las dependencias entre tareas) y para ver los resultados de ejecuciones de DAG individuales.
Machine Translated by Google

14 CAPÍTULO 1 Conozca Apache Airflow

Tu nombre de usuario

+ contraseña

Figura 1.10 La página de inicio de sesión de la interfaz web de Airflow. En los ejemplos de código que acompañan
a este libro, se proporciona un usuario predeterminado "admin" con la contraseña "admin".

Nombres de Horarios de Estado de las tareas de flujo de

flujos de flujo de trabajo trabajo de ejecuciones recientes


trabajo registrados

Figura 1.11 La página principal de la interfaz web de Airflow, que muestra una descripción general de los DAG
disponibles y sus resultados recientes
Machine Translated by Google

Introducción al flujo de aire 15

Figura 1.12 La vista de gráfico en la interfaz web de Airflow, que muestra una descripción general de las tareas en
un DAG individual y las dependencias entre estas tareas

Además de esta vista de gráfico, Airflow también proporciona una vista de árbol detallada que muestra todas las
ejecuciones históricas y en ejecución para el DAG correspondiente (figura 1.13). Este es posiblemente el
vista más potente proporcionada por la interfaz web, ya que le brinda una descripción general rápida de
cómo se ha desempeñado un DAG con el tiempo y le permite profundizar en las tareas fallidas para ver
Qué salió mal.
De forma predeterminada, Airflow puede manejar fallas en las tareas al volver a intentarlas un par de veces.
(opcionalmente con algún tiempo de espera en el medio), lo que puede ayudar a que las tareas se recuperen de cualquier
fallas intermitentes. Si los reintentos no ayudan, Airflow registrará la tarea como fallida,
notificando opcionalmente sobre la falla si está configurado para hacerlo. La depuración de tareas fallidas es bastante
sencilla, ya que la vista de árbol le permite ver qué tareas fallaron y
cavar en sus registros. La misma vista también le permite borrar los resultados de
tareas para volver a ejecutarlas (junto con cualquier tarea que dependa de esa tarea), permitiéndole
vuelva a ejecutar fácilmente cualquier tarea después de realizar cambios en su código.

1.2.4 Carga incremental y relleno


Una característica poderosa de la semántica de programación de Airflow es que los intervalos de programación
no solo activa los DAG en puntos de tiempo específicos (similares a, por ejemplo, Cron), sino que también
proporcionar detalles sobre el último y el próximo (esperado) intervalo de programación. Esto esencialmente
Machine Translated by Google

dieciséis
CAPÍTULO 1 Conozca Apache Airflow

todas las corridas de

una tarea

Estado de un

sola tarea

una carrera de
un flujo de trabajo

Figura 1.13 Vista de árbol de Airflow, que muestra los resultados de varias ejecuciones del modelo de ventas generales
DAG (ejecuciones más recientes + históricas). Las columnas muestran el estado de una ejecución del DAG y las filas
muestran el estado de todas las ejecuciones de una sola tarea. Los colores (que puedes ver en la versión del libro
electrónico) indican el resultado de la tarea correspondiente. Los usuarios también pueden hacer clic en los "cuadrados"
de la tarea para obtener más detalles sobre una instancia de tarea determinada, o para restablecer el estado de una
tarea para que Airflow pueda volver a ejecutarla, si lo desea.

le permite dividir el tiempo en intervalos discretos (por ejemplo, todos los días, semanas, etc.), y ejecutar
su DAG para cada uno de estos intervalos.1
Esta propiedad de los intervalos de programación de Airflow es invaluable para implementar canalizaciones de datos
eficientes, ya que le permite crear canalizaciones de datos incrementales. En estos
canalizaciones incrementales, cada ejecución de DAG procesa solo datos durante el tiempo correspondiente
slot (el delta de los datos) en lugar de tener que volver a procesar todo el conjunto de datos cada vez.
Especialmente para conjuntos de datos más grandes, esto puede proporcionar importantes beneficios de tiempo y costo al
evitando costosos recálculos de los resultados existentes.
Los intervalos de programación se vuelven aún más poderosos cuando se combinan con el concepto
de reposición, que le permite ejecutar un nuevo DAG para intervalos de programación históricos
que ocurrió en el pasado. Esta característica le permite crear fácilmente (o rellenar) nuevos datos
conjuntos con datos históricos simplemente ejecutando su DAG para estos intervalos de programación anteriores.
Además, al borrar los resultados de ejecuciones anteriores, también puede usar esta función Airflow para
vuelva a ejecutar fácilmente cualquier tarea histórica si realiza cambios en su código de tarea, lo que le permite
para reprocesar fácilmente un conjunto de datos completo cuando sea necesario.

1
Si esto te suena un poco abstracto ahora, no te preocupes, ya que proporcionamos más detalles sobre estos conceptos más adelante en el
libro.
Machine Translated by Google

Cuándo usar el flujo de aire 17

1.3 Cuándo usar Airflow


Después de esta breve introducción a Airflow, esperamos que esté lo suficientemente entusiasmado con
conocer Airflow y aprender más sobre sus funciones clave. Sin embargo, antes
Yendo más allá, primero exploraremos varias razones por las que podría querer elegir trabajar
con Airflow (así como varias razones por las que podría no hacerlo), para asegurarse de que Airflow sea
de hecho, la mejor opción para usted.

1.3.1 Razones para elegir Airflow

En las secciones anteriores, ya describimos varias características clave que hacen que Airflow
ideal para implementar canalizaciones de datos orientadas a lotes. En resumen, estos incluyen la
siguiendo:

ÿ La capacidad de implementar canalizaciones usando el código de Python le permite crear canalizaciones


arbitrariamente complejas usando cualquier cosa que pueda imaginar en Python. ÿ La base Python de
Airflow facilita ampliar y agregar integraciones
con muchos sistemas diferentes. De hecho, la comunidad de Airflow ya ha desarrollado una rica colección
de extensiones que permiten que Airflow se integre con muchos
diferentes tipos de bases de datos, servicios en la nube, etc.
ÿ La rica semántica de programación le permite ejecutar sus canalizaciones a intervalos regulares
y construya canalizaciones eficientes que usen procesamiento incremental para evitar costosos
recálculo de los resultados existentes. ÿ
Funciones como la reposición le permiten (re)procesar fácilmente datos históricos,
lo que le permite volver a calcular cualquier conjunto de datos derivados después de realizar cambios en su
código.

ÿ La rica interfaz web de Airflow proporciona una vista fácil para monitorear los resultados de
su tubería se ejecuta y depura cualquier falla que pueda haber ocurrido.

Una ventaja adicional de Airflow es que es de código abierto, lo que garantiza que
puede construir su trabajo en Airflow sin quedarse bloqueado por ningún proveedor. Las soluciones de flujo de aire
administrado también están disponibles en varias compañías (si desea
algo de soporte técnico), lo que le brinda mucha flexibilidad en la forma en que ejecuta y administra
su instalación Airflow.

1.3.2 Razones para no elegir Airflow

Aunque Airflow tiene muchas características ricas, varias de las opciones de diseño de Airflow pueden hacer
es menos adecuado para ciertos casos. Por ejemplo, algunos casos de uso que no son adecuados para
El flujo de aire incluye lo siguiente:

ÿ Manejo de canalizaciones de transmisión, ya que Airflow está diseñado principalmente para ejecutarse
o tareas orientadas a lotes, en lugar de cargas de trabajo de transmisión.
ÿ Implementar pipelines altamente dinámicos, en los que se agregan/eliminan tareas
entre cada corrida de tubería. Aunque Airflow puede implementar este tipo de
comportamiento dinámico, la interfaz web solo mostrará tareas que todavía están definidas en
Machine Translated by Google

18 CAPÍTULO 1 Conozca Apache Airflow

la versión más reciente del DAG. Como tal, Airflow favorece las tuberías que no
no cambien de estructura cada vez que se ejecutan.
ÿ Equipos con poca o ninguna experiencia en programación (Python), como implementar
Los DAG en Python pueden ser desalentadores con poca experiencia en Python. En tales equipos,
usar un administrador de flujo de trabajo con una interfaz gráfica (como Azure Data Factory) o una
definición de flujo de trabajo estático puede tener más sentido.
ÿ Del mismo modo, el código de Python en los DAG puede volverse complejo rápidamente para un uso mayor
casos. Como tal, implementar y mantener los DAG de Airflow requiere
rigor de ingeniería para mantener las cosas mantenibles a largo plazo.

Además, Airflow es principalmente una plataforma de gestión de flujo de trabajo/canalización y no


(actualmente) incluyen características más amplias, como el mantenimiento de linajes de datos, datos
versiones, etc. Si necesita estas características, probablemente tendrá que buscar
en combinar Airflow con otras herramientas especializadas que brindan esas capacidades.

1.4 El resto de este libro


A estas alturas, debería (con suerte) tener una buena idea de qué es Airflow y cómo sus características pueden
ayudarlo a implementar y ejecutar canalizaciones de datos. En el resto de este libro,
Comenzaremos presentando los componentes básicos de Airflow con los que debe familiarizarse para comenzar
a crear sus propias canalizaciones de datos. Estos primeros capítulos deberían ser
ampliamente aplicable y atractivo para una amplia audiencia. Para estos capítulos, le esperamos
tener experiencia intermedia con la programación en Python (~un año de experiencia), lo que significa que debe
estar familiarizado con conceptos básicos como formato de cadenas, comprensiones, args/kwargs, etc. También
debe estar familiarizado con el
conceptos básicos de la terminal de Linux y tener un conocimiento básico de trabajo de bases de datos (incluido
SQL) y diferentes formatos de datos.
Después de esta introducción, profundizaremos en funciones más avanzadas de Airflow.
como generar DAG dinámicos, implementar sus propios operadores, ejecutar tareas en contenedores, etc. Estos
capítulos requerirán una mayor comprensión de
las tecnologías involucradas, incluida la escritura de sus propias clases de Python, Docker básico
conceptos, formatos de archivo y partición de datos. Esperamos que esta segunda parte sea de especial interés
para los ingenieros de datos de la audiencia.
Finalmente, varios capítulos hacia el final del libro se enfocan en temas relacionados con
la implementación de Airflow, incluidos los patrones de implementación, el monitoreo, la seguridad y
arquitecturas en la nube. Esperamos que estos capítulos sean de especial interés para las personas
interesados en implementar y administrar implementaciones de Airflow, como administradores de sistemas e
ingenieros de DevOps.

Resumen
ÿ Las canalizaciones de datos se pueden representar como DAG, que definen claramente las tareas y sus
dependencias Estos gráficos se pueden ejecutar de manera eficiente, aprovechando
cualquier paralelismo inherente a la estructura de dependencia.
Machine Translated by Google

Resumen 19

ÿ Aunque se han desarrollado muchos administradores de flujo de trabajo a lo largo de los años
para ejecutar gráficos de tareas, Airflow tiene varias características clave que lo hacen único
adecuado para implementar canalizaciones de datos eficientes y orientadas a lotes.
ÿ Airflow consta de tres componentes principales: el servidor web, el programador y
los procesos de trabajo, que trabajan juntos para programar tareas a partir de sus datos
canalizaciones y ayudarlo a monitorear sus resultados.
Machine Translated by Google

Anatomía de
un DAG de flujo de aire

Este capítulo cubre


ÿ Ejecutar Airflow en su propia máquina ÿ
Escribir y ejecutar su primer flujo de trabajo
ÿ Examinar la primera vista en la interfaz de Airflow
ÿ Manejar tareas fallidas en Airflow

En el capítulo anterior, aprendimos por qué trabajar con datos y las muchas herramientas en el
panorama de datos no es fácil. En este capítulo, comenzamos con Airflow y revisamos un flujo de
trabajo de ejemplo que utiliza componentes básicos que se encuentran en muchos flujos de trabajo.
Es útil tener algo de experiencia en Python al comenzar con Airflow, ya que los flujos de trabajo
se definen en el código de Python. La brecha en el aprendizaje de los conceptos básicos de Airflow
no es tan grande. En general, poner en marcha la estructura básica de un flujo de trabajo de Airflow
es fácil. Profundicemos en el caso de uso de un entusiasta de los cohetes para ver cómo Airflow
podría ayudarlo.

20
Machine Translated by Google

Recopilación de datos de numerosas fuentes 21

2.1 Recopilación de datos de numerosas fuentes


Los cohetes son una de las maravillas de la ingeniería de la humanidad, y cada lanzamiento de cohetes atrae
atención en todo el mundo. En este capítulo, cubrimos la vida de un entusiasta de los cohetes.
llamado John, que rastrea y sigue todos los lanzamientos de cohetes. Las noticias sobre el cohete
lanzamientos se encuentra en muchas fuentes de noticias de las que John realiza un seguimiento e, idealmente, John
le gustaría tener todas sus noticias sobre cohetes agregadas en una sola ubicación. Juan recientemente
Recogió la programación y le gustaría tener algún tipo de forma automatizada de recopilar
información de todos los lanzamientos de cohetes y, finalmente, algún tipo de información personal sobre
las últimas noticias sobre cohetes. Para empezar poco a poco, John decidió recopilar primero imágenes de cohetes.

2.1.1 Explorando los datos


Para los datos, hacemos uso de Launch Library 2 (https://thespacedevs.com/llapi),
un depósito en línea de datos sobre lanzamientos de cohetes históricos y futuros de
varias fuentes. Es una API gratuita y abierta para cualquier persona en el planeta (sujeto a tasa
límites).
John actualmente solo está interesado en los próximos lanzamientos de cohetes. Por suerte, el lanzamiento
Library proporciona exactamente los datos que está buscando (https://ll.thespacedevs.com/2.0.0/
lanzamiento/próximo). Proporciona datos sobre los próximos lanzamientos de cohetes, junto con
URL de dónde encontrar imágenes de los respectivos cohetes. Aquí hay un fragmento de los datos.
esta URL devuelve.

Listado 2.1 Ejemplo de solicitud de curl y respuesta a la API de Launch Library

$ curl -L "https://ll.thespacedevs.com/2.0.0/launch/upcoming"

{ La respuesta es un documento JSON, Inspeccionar la URL


... como puede ver en la estructura. respuesta con curl de
"resultados": la línea de comando
los
[{
cuadrado
soportes
"id": "528b72ff-e47e-46a3-b7ad-23b2ffcec2f2",
indicar "url": "https://.../528b72ff-e47e-46a3-b7ad-23b2ffcec2f2/",
una lista. "launch_library_id": 2103, "name":
Aquí vemos información como la
"Falcon 9 Block 5 | NROL-108", "net":
identificación del cohete y la hora de
"2020-12-19T14:00:00Z", "window_end": "2020-12-19T17:00:
Todos los valores inicio y finalización de la ventana de
dentro de
00Z", "window_start": "2020-12-19T14:00:00Z", ÿ "imagen":
lanzamiento del cohete.
"https://spacelaunchnow-prod east.nyc3.digitaloceanspaces.com/
estos rizados
tirantes media/launch_images/falcon2520925_image
Referirse a

uno solo _20201217060406.jpeg",


cohete "infografía": ".../falcon2520925_infographic_20201217162942.png",
lanzar. ...
},
{
Una URL a un
"id": "57c418cc-97ae-4d8e-b806-bb0e0345217f",
imagen de la
"url": "https://.../57c418cc-97ae-4d8e-b806-bb0e0345217f/",
lanzamiento
cohete "launch_library_id": nulo,
"name": "Largo 8 de marzo | XJY-7 y otros",
"net": "2020-12-22T04:29:00Z",
"window_end": "2020-12-22T05:03:00Z",
Machine Translated by Google

22 CAPÍTULO 2 Anatomía de un DAG de flujo de aire

"window_start": "2020-12-22T04:29:00Z", "imagen":


"https://.../long2520march_image_20201216110501.jpeg", "infografía": nulo,

...
},
...

]}

Como puede ver, los datos están en formato JSON y brindan información sobre el lanzamiento del cohete,
y para cada lanzamiento, hay información sobre el cohete específico, como la identificación, el nombre y la
URL de la imagen. Esto es exactamente lo que John necesita, e inicialmente dibuja el plan en la figura 2.1
para recopilar las imágenes de los próximos lanzamientos de cohetes (por ejemplo, para apuntar su
protector de pantalla al directorio que contiene estas imágenes):

Sistema de

Biblioteca de lanzamiento notificaciones


Internet

Obtener próximos Guardar Guardar imágenes

lanzamientos lanzamientos de cohetes de cohetes

Obtener imágenes
Notificar
de cohetes

la la
John
computadora de juan computadora de juan

Figura 2.1 Modelo mental de John para descargar imágenes de cohetes

Basándonos en el ejemplo de la figura 2.1, podemos ver que, al final del día, el objetivo de John es tener
un directorio lleno de imágenes de cohetes, como la imagen de la figura 2.2 del cohete Ariane 5 ECA.

2.2 Escribiendo tu primer Airflow DAG


El caso de uso de John tiene un alcance muy bueno, así que veamos cómo programar su plan. Son solo
unos pocos pasos y, en teoría, con un poco de Bash-fu, podría resolverlo en una sola línea. Entonces, ¿por
qué necesitaríamos un sistema como Airflow para este trabajo?
Lo bueno de Airflow es que podemos dividir un trabajo grande, que consta de uno o más pasos, en
"tareas" individuales que juntas forman un DAG. Se pueden ejecutar varias tareas en paralelo y las tareas
pueden ejecutar diferentes tecnologías. Por ejemplo, primero podríamos ejecutar un script de Bash y luego
ejecutar un script de Python. Desglosamos el modelo mental de John de su flujo de trabajo en tres tareas
lógicas en Airflow en la figura 2.3.
¿Por qué estas tres tareas, podrías preguntar? ¿Por qué no descargar los lanzamientos y las imágenes
correspondientes en una sola tarea? ¿O por qué no dividirlos en cinco tareas? Después de todo,
Machine Translated by Google

Escribiendo tu primer Airflow DAG 23

Figura 2.2 Imagen de ejemplo


del cohete Ariane 5 ECA

Notificación
Biblioteca de lanzamiento sistema
Internet

Guardar
Obtener próximos Guardar imágenes
lanzamientos de cohetes
lanzamientos Obtener imágenes de cohetes

de cohetes Notificar

la Juan
John
computadora de juan computadora

Figura 2.3 El modelo mental de John asignado a tareas en Airflow


Machine Translated by Google

24 CAPÍTULO 2 Anatomía de un DAG de flujo de aire

tenemos cinco flechas en el plan de Juan. Todas estas son preguntas válidas para formular mientras se
desarrolla un flujo de trabajo, pero la verdad es que no hay una respuesta correcta o incorrecta. Hay varios
puntos a tener en cuenta, sin embargo, y a lo largo de este libro trabajamos
muchos de estos casos de uso para tener una idea de lo que está bien y lo que está mal. El código para esto
el flujo de trabajo es el siguiente.

Listado 2.2 DAG para descargar y procesar datos de lanzamiento de cohetes

importar json
importar pathlib

flujo de aire de importación


solicitudes de importación
importar solicitudes.excepciones como solicitudes_excepciones
de importación de flujo de aire DAG
desde airflow.operators.bash importar BashOperator
desde airflow.operators.python importar PythonOperator Crea una instancia de un objeto
los DAG; este es el punto de partida
nombre de dag = dag( de cualquier flujo de trabajo.
el DAG
dag_id="download_rocket_launches", La fecha en que el
start_date=airflow.utils.dates.days_ago(14), DAG debe comenzar primero

en que
schedule_interval=Ninguno, correr
)
intervalo
el DAG Aplique Bash para descargar la
debería download_launches = respuesta URL con curl.
correr BashOperator( task_id="download_launches", El nombre de la tarea
bash_command="curl -o /tmp/launches.json -L
'https://ll.thespacedevs.com/2.0.0/launch/upcoming'",
dag = dag,
)
Una función de Python analizará
la respuesta y descargará todas
def _get_pictures(): # las imágenes del cohete.
Asegurarse de que existe el directorio
pathlib.Path("/tmp/images").mkdir(padres=Verdadero, exist_ok=Verdadero)

# Descargar todas las imágenes en launches.json


con open("/tmp/launches.json") como f:
lanzamientos = json.load(f)
image_urls = [lanzamiento["imagen"] para lanzamiento en lanzamientos["resultados"]]
para image_url en image_urls:
probar:
respuesta = solicitudes.get(image_url)
image_filename = image_url.split("/")[-1]
target_file = f"/tmp/images/{image_filename}"
con abierto(target_file, "wb") como f:
f.write(respuesta.contenido)
print(f"Descargado {image_url} a {target_file}")
excepto solicitudes_excepciones.MissingSchema:
print(f"{image_url} parece ser una URL no válida.")
excepto solicitudes_excepciones.ConnectionError:
print(f"No se pudo conectar a {image_url}").
Machine Translated by Google

Escribiendo tu primer Airflow DAG 25

get_pictures =
PythonOperator( task_id="get_pictures", Llame a la función de Python en
python_callable=_get_pictures, dag=dag, el DAG con un PythonOperator.

notificar = BashOperator(
task_id="notificar",
bash_command='echo "Ahora hay $(ls /tmp/images/ | wc -l) imágenes."',
dag = dag,
)
Establecer el orden

descargar_lanzamientos >> obtener_imágenes >> notificar de ejecución de las tareas.

Analicemos el flujo de trabajo. El DAG es el punto de partida de cualquier flujo de trabajo. Todos
las tareas dentro del flujo de trabajo hacen referencia a este objeto DAG para que Airflow sepa qué tareas
pertenecen a qué DAG.

Listado 2.3 Instanciando un objeto DAG

La clase DAG toma dos El nombre del DAG que

dag = dag ( argumentos requeridos. se muestra en la interfaz


dag_id="download_rocket_launches", de usuario (IU) de Airflow
start_date=airflow.utils.dates.days_ago(14),
La fecha y hora en la que el
schedule_interval=Ninguno, flujo de trabajo debe comenzar
)
a ejecutarse por primera vez.

Tenga en cuenta que el dag (en minúsculas) es el nombre asignado a la instancia del DAG (en mayúsculas)
clase. El nombre de la instancia podría tener cualquier nombre; puedes llamarlo cohete_dag o
el_nombre_que_te_guste. Haremos referencia a la variable ( dag en minúsculas) en todos los operadores, que le dice a
Airflow a qué DAG pertenece el operador.
También tenga en cuenta que establecemos schedule_interval en Ninguno. Esto significa que el DAG no se ejecutará
automáticamente. Por ahora, puede activarlo manualmente desde la interfaz de usuario de Airflow. llegaremos a
programación en el apartado 2.4.
A continuación, un script de flujo de trabajo de Airflow consta de uno o más operadores, que realizan
el trabajo real. En el listado 2.4, aplicamos BashOperator para ejecutar un comando Bash.

Listado 2.4 Instanciando un BashOperator para ejecutar un comando Bash

download_launches = BashOperator( El nombre de


task_id="download_launches", la tarea
bash_command="curl -o /tmp/launches.json 'https:// El comando Bash a
ll.thespacedevs.com/2.0.0/launch/upcoming'", dag=dag, ejecutar

) Referencia a la
variable DAG

Cada operador realiza una sola unidad de trabajo, y múltiples operadores juntos forman un
flujo de trabajo o DAG en Airflow. Los operadores funcionan independientemente unos de otros, aunque
puede definir el orden de ejecución, que llamamos dependencias en Airflow. Después de todo,
Machine Translated by Google

26 CAPÍTULO 2 Anatomía de un DAG de flujo de aire

El flujo de trabajo de John no sería útil si primero intentara descargar imágenes mientras no hay datos sobre la
ubicación de las imágenes. Para asegurarnos de que las tareas se ejecuten en el orden correcto, podemos
establecer dependencias entre tareas.

Listado 2.5 Definiendo el orden de ejecución de tareas

Las flechas establecen el


descargar_lanzamientos >> obtener_imágenes >> notificar orden de ejecución de las tareas.

En Airflow, podemos usar el operador binario de desplazamiento a la derecha (es decir, “rshift” [>>]) para
definir dependencias entre tareas. Esto garantiza que la tarea get_pictures se ejecute solo después de que la
descarga _launches se haya completado con éxito, y la tarea de notificación se ejecute solo después de que
get_pictures se haya completado con éxito.

NOTA En Python, el operador rshift (>>) se usa para cambiar bits, que es una operación común, por
ejemplo, en bibliotecas criptográficas. En Airflow, no hay caso de uso para el cambio de bits, y el
operador rshift se anuló para proporcionar una forma legible de definir dependencias entre tareas.

2.2.1 Tareas frente a operadores

Quizás se pregunte cuál es la diferencia entre tareas y operadores. Después de todo, ambos ejecutan un poco
de código. En Airflow, los operadores tienen una única responsabilidad: existen para realizar un único trabajo.
Algunos operadores realizan trabajos genéricos, como BashOperator (usado para ejecutar un script Bash) o
PythonOperator (usado para ejecutar una función de Python); otros tienen casos de uso más específicos, como
EmailOperator (usado para enviar un correo electrónico) o SimpleHTTPOperator (usado para llamar a un punto
final HTTP).
De cualquier manera, realizan una sola pieza de trabajo.
El rol de un DAG es orquestar la ejecución de una colección de operadores. Eso incluye el inicio y la
detención de los operadores, el inicio de tareas consecutivas una vez que finaliza un operador, garantizar que
se cumplan las dependencias entre los operadores, etc.
En este contexto y en toda la documentación de Airflow, vemos que los términos operador y tarea se usan
indistintamente. Desde la perspectiva del usuario, se refieren a lo mismo y, a menudo, los dos se sustituyen en
las discusiones. Los operadores proporcionan la implementación de un trabajo. Airflow tiene una clase llamada
BaseOperator y muchas subclases que heredan de BaseOperator, como PythonOperator, EmailOperator y
OracleOperator.

Sin embargo, hay una diferencia. Las tareas en Airflow gestionan la ejecución de un operador; se pueden
considerar como un pequeño envoltorio o administrador alrededor de un operador que garantiza que el
operador se ejecute correctamente. El usuario puede concentrarse en el trabajo a realizar mediante el uso de
operadores, mientras que Airflow asegura la correcta ejecución del trabajo a través de tareas (figura 2.4).
Machine Translated by Google

Escribiendo tu primer Airflow DAG 27

TROZO DE CUERO

Tarea Tarea Tarea

Operador Operador Operador

Figura 2.4 Los usuarios de Airflow utilizan DAG y operadores. Las tareas
son componentes internos para gestionar el estado del operador y
mostrar los cambios de estado (p. ej., iniciado/finalizado) al usuario.

2.2.2 Ejecutar código Python arbitrario


Obtener los datos para los próximos lanzamientos de cohetes fue un solo comando curl en Bash,
que se ejecuta fácilmente con BashOperator. Sin embargo, al analizar el resultado JSON,
seleccionar las URL de la imagen y descargar las imágenes respectivas requiere un poco
más esfuerzo. Aunque todo esto todavía es posible en una sola línea de Bash, a menudo es más fácil y
más legible con unas pocas líneas de Python o cualquier otro lenguaje de su elección. Ya que
El código de flujo de aire se define en Python, es conveniente mantener tanto el flujo de trabajo como
la lógica de ejecución en el mismo script. Para descargar las imágenes de los cohetes, implementamos
listado 2.6.

Listado 2.6 Ejecutando una función de Python usando PythonOperator

Crear directorio de imágenes


def _get_pictures(): # Función de Python para llamar si no existe.
Asegurarse de que existe el directorio
pathlib.Path("/tmp/images").mkdir(padres=Verdadero, exist_ok=Verdadero)

# Descargar todas las imágenes en launches.json Abra el resultado de la

con open("/tmp/launches.json") como f: launches = tarea anterior.

json.load(f)
image_urls = [lanzamiento["imagen"] para lanzamiento en lanzamientos["resultados"]]
para image_url en image_urls:
Descarga
probar:
cada imagen.
respuesta = solicitudes.get(image_url)
image_filename = image_url.split("/")[-1]
target_file = f"/tmp/images/{image_filename}"
Almacenar cada
con abierto(target_file, "wb") como f:
imagen.
f.write(respuesta.contenido)
Imprimir en salida estándar;
print(f"Descargado {image_url} a {target_file}") excepto
esto será solicitudes_excepciones.MissingSchema:
capturado en print(f"{image_url} parece ser una URL no válida.")
Registros de flujo de aire.
excepto solicitudes_excepciones.ConnectionError:
print(f"No se pudo conectar a {image_url}").

Cree una instancia de PythonOperator


para llamar a la función de Python.
get_pictures =
PythonOperator( task_id="get_pictures",
python_callable=_get_pictures, dag=dag, Apunte a la función de
Python para ejecutar.
)
Machine Translated by Google

28 CAPÍTULO 2 Anatomía de un DAG de flujo de aire

PythonOperator en Airflow es responsable de ejecutar cualquier código de Python. Al igual que


el BashOperator utilizado anteriormente, este y todos los demás operadores requieren un task_id. los
Se hace referencia a task_id cuando se ejecuta una tarea y se muestra en la interfaz de usuario. el uso de un
PythonOperator siempre es doble:

1 Definimos el propio operador (get_pictures).


2 El argumento python_callable apunta a una función invocable, típicamente
(_obtener_imágenes).

Al ejecutar el operador, se llama a la función de Python y ejecutará la función. Vamos a desglosarlo. El uso
básico de PythonOperator siempre se parece a la figura 2.5.

def _get_pictures():
# trabaja aquí... PythonOperator invocable

get_pictures = OperadorPython(
task_id="obtener_imágenes",
python_callable =_get_pictures, PythonOperador
dag=dag
)

Figura 2.5 El argumento python_callable en PythonOperator


apunta a una función para ejecutar.

Aunque no es obligatorio, por conveniencia mantenemos igual el nombre de la variable get_pictures


al task_id.

Listado 2.7 Asegura que el directorio de salida existe y lo crea si no existe

# Asegurarse de que exista el directorio


pathlib.Path("/tmp/images").mkdir(padres=Verdadero, exist_ok=Verdadero)

El primer paso en el invocable es asegurarse de que el directorio en el que se almacenarán las imágenes
almacenado existe, como se muestra en el listado 2.7. A continuación, abrimos el resultado descargado de la
Inicie la API de la biblioteca y extraiga las URL de las imágenes para cada lanzamiento.

Listado 2.8 Extrae URL de imágenes para cada lanzamiento de cohete

Abra el JSON de los lanzamientos de cohetes.


Lea como un dictado para
con open("/tmp/launches.json") como f: launches = que podamos mezclar los datos.
json.load(f) image_urls = [lanzamiento["imagen"] for
lanzamiento en lanzamientos["resultados"]]

Para cada lanzamiento, obtenga el elemento "imagen".

Se llama a cada URL de imagen para descargar la imagen y guardarla en /tmp/images.


Machine Translated by Google

Ejecutar un DAG en Airflow 29

Listado 2.9 Descarga todas las imágenes de las URL de las imágenes recuperadas

para image_url en image_urls: intente: Obtén la imagen. Obtenga solo el nombre del
bucle sobre
toda la imagen archivo seleccionando todo
URL. respuesta = solicitudes.get(image_url) después del último. Por ejemplo, https://
image_filename = image_url.split("/")[-1] target_file = f"/tmp/ anfitrión/RocketImages/Electron
Construir images/{image_filename}" with open(target_file, "wb") como f: .jpg_1440.jpg ÿ Electron.jpg

el objetivo
f.write (respuesta.contenido) _1440.jpg.

ruta de archivo.
print(f"Descargado {image_url} a {target_file}") excepto Imprimir resultado.

objetivo abierto solicitudes_excepciones.MissingSchema: print(f"{image_url} parece ser una


manejador de archivos
URL no válida.") excepto solicitudes_excepciones.ConnectionError: Detectar y procesar
posibles errores.
Escribir imagen
print(f"No se pudo conectar a {image_url}").
a la ruta del archivo.

2.3 Ejecutar un DAG en Airflow


Ahora que tenemos nuestro DAG básico de lanzamiento de cohetes, vamos a ponerlo en marcha y verlo.
en la interfaz de usuario de Airflow. El flujo de aire mínimo consiste en tres componentes básicos: un
planificador, un servidor web y una base de datos. Para poner en marcha Airflow, debe
puede instalar Airflow en su entorno Python o ejecutar un contenedor Docker.

2.3.1 Ejecutar Airflow en un entorno de Python


Hay varios pasos para instalar y ejecutar Airflow como un paquete de Python desde PyPi:

pip instalar apache-flujo de aire

Asegúrese de instalar apache-airflow y no solo airflow. Después de unirse a Apache


Foundation en 2016, el repositorio PyPi airflow pasó a llamarse apache-airflow.
Dado que muchas personas todavía estaban instalando Airflow en lugar de eliminar el repositorio anterior, se
mantuvo como un maniquí para proporcionar a todos un mensaje que apunta a la correcta
repositorio.
Algunos sistemas operativos vienen con una instalación de Python. Corriendo solo pip
install apache-airflow instalará Airflow en este entorno de "sistema". Cuando
trabajando en proyectos de Python, es deseable mantener cada proyecto en su propio entorno de Python para
crear un conjunto reproducible de paquetes de Python y evitar la dependencia
enfrentamientos Dichos entornos se crean con herramientas como estas:

ÿ
Pyenv: https://github.com/pyenv/pyenv

ÿ Conda: https://docs.conda.io

ÿ virtualenv: https://virtualenv.pypa.io

Después de instalar Airflow, inícielo inicializando el metastore (una base de datos en la que todos
El estado del flujo de aire se almacena), creando un usuario, copiando el lanzamiento del cohete DAG en los DAG
directorio e iniciando el programador y el servidor web:
1 inicio de base de datos de flujo de aire

2 usuarios de flujo de aire crean --nombre de usuario administrador --contraseña administrador --nombre Anónimo --apellido

Administrador --administrador de función --email admin@example.org


Machine Translated by Google

30 CAPÍTULO 2 Anatomía de un DAG de flujo de aire

3 cp download_rocket_launches.py ~/airflow/dags/
4 servidor web de flujo de aire

5 programador de flujo de aire

Tenga en cuenta que el programador y el servidor web son procesos continuos que mantienen su terminal
abierta, por lo tanto, ejecútelos en segundo plano con el servidor web airflow y/o abra un
segunda ventana de terminal para ejecutar el programador y el servidor web por separado. después de que estés
configure, vaya a http:/ /localhost:8080 e inicie sesión con el nombre de usuario "admin" y la contraseña
“admin” para ver Airflow.

2.3.2 Ejecución de Airflow en contenedores Docker


Los contenedores de Docker también son populares para crear entornos aislados para ejecutar un conjunto
reproducible de paquetes de Python y evitar conflictos de dependencia. Sin embargo, los contenedores Docker
crean un entorno aislado en el nivel del sistema operativo, mientras que Python
los entornos se aíslan solo en el nivel de tiempo de ejecución de Python. Como resultado, puede crear
Contenedores Docker que contienen no solo un conjunto de paquetes de Python, sino también otros
dependencias como controladores de base de datos o un compilador GCC. A lo largo de este libro nos
mostrará Airflow ejecutándose en contenedores Docker en varios ejemplos.
La ejecución de contenedores Docker requiere la instalación de un motor Docker en su
máquina. Luego puede ejecutar Airflow en Docker con el siguiente comando.

Listado 2.10 Ejecutando Airflow en Docker

ventana acoplable ejecutar \


Exposición en el
-ti \ puerto de host 8080.
-p 8080:8080 \ -v ÿ /
ruta/a/dag/download_rocket_launches.py:/opt/airflow/dags/ Monte el archivo
download_rocket_launches.py \ -- DAG en el contenedor.
entrypoint=/bin/bash \ Imagen de Airflow Docker
--nombre flujo de aire \
apache/flujo de aire:2.0.0-python3.8 \ -c '( \ Inicialice el metastore en
el contenedor.
airflow db init && \ ÿ los
Crear usuarios de airflow crean --username admin --password admin --firstname
un usuario. Anónimo --lastname Admin --role Admin --email admin@example.org \
); \
servidor web de flujo de aire
Inicie el servidor web.
& \ programador de flujo de aire \
'
Inicie el programador.

NOTA Si está familiarizado con Docker, probablemente argumentará que no es deseable ejecutar
múltiples procesos en un solo contenedor de Docker como se muestra en el listado 2.10. El comando
es un solo comando, destinado a la demostración.
propósitos para ponerse en marcha rápidamente. En un entorno de producción, debe
ejecutar el servidor web, el programador y la metatienda de Airflow en contenedores separados,
explicado en detalle en el capítulo 10.
Machine Translated by Google

Ejecutar un DAG en Airflow 31

Descargará y ejecutará la imagen apache/airflow de Airflow Docker. Una vez que se ejecuta, puede ver
Airflow en http: //localhost:8080 e iniciar sesión con el nombre de usuario "admin" y la contraseña "admin".

2.3.3 Inspección de la interfaz de usuario de Airflow

La primera vista de Airflow en http:/ /localhost:8080 que verá es la pantalla de inicio de sesión, que se
muestra en la figura 2.6.

Figura 2.6 Vista de inicio de sesión de Airflow

Después de iniciar sesión, puede inspeccionar el DAG download_rocket_launches, como se muestra en la


figura 2.7.
Este es el primer vistazo de Airflow que verá. Actualmente, el único DAG es download_rocket_launches,
que está disponible para Airflow en el directorio de DAG.
Hay mucha información en la vista principal, pero primero inspeccionemos el DAG download_rocket_launches.
Haga clic en el nombre del DAG para abrirlo e inspeccionar la llamada vista de gráfico (figura 2.8).
Machine Translated by Google

32 CAPÍTULO 2 Anatomía de un DAG de flujo de aire

Figura 2.7 Pantalla de inicio de flujo de aire

Operador leyenda del estado


tipos en DAG

Activar/desactivar DAG estructura DAG Activar DAG

Figura 2.8 Vista del gráfico de flujo de aire

Esta vista nos muestra la estructura del script DAG proporcionado a Airflow. Una vez colocado en
el directorio de DAG, Airflow leerá el script y extraerá los fragmentos que
juntos forman un DAG, por lo que se puede visualizar en la interfaz de usuario. La vista de gráfico nos muestra la
estructura del DAG, y cómo y en qué orden se conectan todas las tareas en el DAG
y se ejecutará. Esta es una de las vistas que probablemente usará más al desarrollar sus flujos de trabajo.

La leyenda del estado muestra todos los colores que puede ver cuando se ejecuta, así que veamos qué
sucede y ejecuta el DAG. Primero, el DAG debe estar "encendido" para poder ejecutarse; palanca
el botón al lado del nombre DAG para eso. A continuación, haga clic en el botón Reproducir para ejecutarlo.
Machine Translated by Google

Correr a intervalos regulares 33

Figura 2.9 Vista de gráfico que muestra un DAG en ejecución

Después de activar el DAG, comenzará a ejecutarse y verá el estado actual del flujo de trabajo representado por
colores (figura 2.9). Dado que establecemos dependencias entre nuestras tareas, las tareas consecutivas solo
comienzan a ejecutarse una vez que se han completado las tareas anteriores. Comprobemos el resultado de la
tarea de notificación . En un caso de uso real, probablemente desee enviar un correo electrónico o, por ejemplo,
una notificación de Slack para informar sobre las nuevas imágenes. En aras de la simplicidad, ahora imprime el
número de imágenes descargadas. Revisemos los registros.
Todos los registros de tareas se recopilan en Airflow, por lo que podemos buscar en la interfaz de usuario
resultados o posibles problemas en caso de falla. Haga clic en una tarea de notificación completa y verá una
ventana emergente con varias opciones, como se muestra en la figura 2.10.
Haga clic en el botón Registro superior central para inspeccionar los registros, como se muestra en la figura
2.11. Los registros son bastante detallados de forma predeterminada, pero muestran la cantidad de imágenes
descargadas en el registro. Finalmente, podemos abrir el directorio /tmp/images y verlos. Cuando se ejecuta en
Docker, este directorio solo existe dentro del contenedor de Docker y no en su sistema host. Por lo tanto, primero
debe ingresar al contenedor Docker:

docker exec -it flujo de aire /bin/bash

Después de eso, obtiene un terminal Bash en el contenedor y puede ver las imágenes en /tmp/images (figura
2.12).

2.4 Correr a intervalos regulares


El entusiasta de los cohetes, John, está feliz ahora que tiene un flujo de trabajo en funcionamiento en Airflow,
que puede activar de vez en cuando para recopilar las últimas imágenes de cohetes. Puede ver el estado de su
flujo de trabajo en la interfaz de usuario de Airflow, que ya es una mejora en comparación con un script en la línea
de comandos que estaba ejecutando antes. Pero todavía necesita activar su flujo de trabajo a mano
periódicamente, lo que podría automatizarse. Después de todo, a nadie le gusta hacer tareas repetitivas que las
computadoras son buenas para hacer por sí mismas.
Machine Translated by Google

34 CAPÍTULO 2 Anatomía de un DAG de flujo de aire

Figura 2.10 Opciones emergentes de tareas

Figura 2.11 Declaración de impresión que se muestra en los registros


Machine Translated by Google

Correr a intervalos regulares 35

largo2520marzo25202d_imagen_
20190222031211.jpeg

falcon2520heavy_image_
20190224025007.jpeg

largo2520marzo25203_imagen_
20200102181012.jpg

falcon25209_image_ h-iia2520202_image_
20190224025007.jpeg 20190222031201.jpeg

ariane252052520eca_image_
20190224012333.jpeg

kuaizhou_image_ soyuz25202.1b_image_ electron_image_ luciérnaga_alfa_imagen_


20191027094423.jpeg 20190520165337.jpg 20190705175640.jpeg 20200817170720.jpg

Figura 2.12 Imágenes de cohetes resultantes

En Airflow, podemos programar un DAG para que se ejecute en ciertos intervalos, por ejemplo, una vez por hora, día o
mes. Esto se controla en el DAG configurando el argumento intervalo_programación.

Listado 2.11 Ejecutando un DAG una vez al día

dag = dag(
dag_id="download_rocket_launches",
start_date=airflow.utils.dates.days_ago(14), schedule_interval="@daily",
Alias de flujo de aire para 0 0
)
* * * (es decir, medianoche)

Establecer el intervalo_programación en @daily le dice a Airflow que ejecute este flujo de trabajo una vez al día para que
John no tenga que activarlo manualmente una vez al día. Este comportamiento se ve mejor en la vista de árbol, como se
muestra en la figura 2.13.
La vista de árbol es similar a la vista de gráfico, pero muestra la estructura del gráfico a medida que se ejecuta a lo
largo del tiempo. En la figura 2.14 se puede ver una descripción general del estado de todas las ejecuciones de un único
flujo de trabajo.
Machine Translated by Google

36 CAPÍTULO 2 Anatomía de un DAG de flujo de aire

estructura DAG Estado de la tarea a lo largo del tiempo

Figura 2.13 Vista de árbol del flujo de aire

Figura 2.14 Relación entre la vista de gráfico y la vista de árbol

La estructura del DAG se muestra para adaptarse a un diseño de "filas y columnas", específicamente el
estado de todas las ejecuciones del DAG específico, donde cada columna representa una única
ejecución en algún momento.
Cuando configuramos el intervalo de programación en @daily, Airflow sabía que tenía que ejecutar
este DAG una vez al día. Dada la fecha de inicio proporcionada al DAG de hace 14 días, eso significa
que el tiempo desde hace 14 días hasta ahora se puede dividir en 14 intervalos iguales de un día. Dado
que tanto la fecha de inicio como la de finalización de estos 14 intervalos se encuentran en el pasado,
comenzarán a ejecutarse una vez que proporcionemos un intervalo de programación a Airflow. La
semántica del intervalo de programación y varias formas de configurarlo se tratan con más detalle en el
capítulo 3.

2.5 Manejo de tareas fallidas Hasta ahora,


solo hemos visto verde en la interfaz de usuario de Airflow. Pero, ¿qué pasa si algo falla?
No es raro que las tareas fallen, lo que podría deberse a una multitud de razones (p. ej., un servicio
externo no funciona, problemas de conectividad de red o un disco roto).
Digamos, por ejemplo, que en algún momento experimentamos un contratiempo en la red al obtener
las imágenes del cohete de John. Como consecuencia, la tarea de Airflow falla y vemos la tarea fallida
en la interfaz de usuario de Airflow. Se vería la figura 2.15.
Machine Translated by Google

Manejo de tareas fallidas 37

Figura 2.15 Fallo mostrado en vista de gráfico y vista de árbol

La tarea fallida específica se mostraría en rojo tanto en el gráfico como en la vista de árbol, como resultado de no poder
obtener las imágenes de Internet y, por lo tanto, generar un error. La tarea de notificación sucesiva no se ejecutaría en absoluto
porque depende del estado exitoso de la tarea get_pictures . Estas instancias de tareas se muestran en naranja.

De forma predeterminada, todas las tareas anteriores deben ejecutarse correctamente y cualquier tarea sucesiva de una fallida
la tarea no se ejecutará.

Resolvamos el problema inspeccionando los registros nuevamente. Abra los registros de get_
tarea de imágenes (figura 2.16).

Figura 2.16 Seguimiento de la pila de la tarea fallida get_pictures


Machine Translated by Google

38 CAPÍTULO 2 Anatomía de un DAG de flujo de aire

En el seguimiento de la pila, descubrimos la posible causa del problema:

urllib3.exceptions.NewConnectionError: <urllib3.connection.HTTPSConnection
objeto en 0x7f37963ce3a0>: No se pudo establecer una nueva conexión: [Errno
-2] Nombre o servicio desconocido

Esto indica que urllib3 (es decir, el cliente HTTP para Python) está intentando establecer una conexión pero no
puede, lo que podría insinuar que una regla de firewall bloquea la conexión o no.
conectividad a Internet. Suponiendo que solucionamos el problema (por ejemplo, enchufamos el cable de Internet),
reiniciemos la tarea.

NOTA No es necesario reiniciar todo el flujo de trabajo. Una buena característica del flujo de aire es
que puede reiniciar desde el punto de falla y en adelante, sin tener que reiniciar ninguna tarea exitosa
anterior.

Figura 2.17 Haga clic en una tarea fallida


para obtener opciones para borrarla.

Haga clic en la tarea fallida y luego haga clic en el botón Borrar en la ventana emergente (figura 2.17). Eso
le mostrará las tareas que está a punto de borrar, lo que significa que restablecerá el estado de estas
tareas y Airflow las volverá a ejecutar, como se muestra en la figura 2.18.

Figura 2.18 Limpiando el estado de get_pictures y tareas sucesivas

¡Haga clic en Aceptar! y se borrará la tarea fallida y sus sucesivas tareas, como se puede ver en
figura 2.19.

Figura 2.19 Tareas borradas que


se muestran en la vista de gráfico
Machine Translated by Google

Resumen 39

Suponiendo que se resuelvan los problemas de conectividad, las tareas ahora se ejecutarán correctamente y la
vista de árbol completa se volverá verde (figura 2.20).

Figura 2.20 Tareas completadas con


éxito después de borrar las tareas fallidas

En cualquier pieza de software, hay muchas razones para fallar. En los flujos de trabajo de Airflow, a veces se
acepta la falla, a veces no y a veces solo en ciertas condiciones. Los criterios para tratar fallas se pueden
configurar en cualquier nivel del flujo de trabajo y se tratan con más detalle en el capítulo 4.

Después de borrar las tareas fallidas, Airflow volverá a ejecutar automáticamente estas tareas. Si todo va
bien, John ahora habrá descargado las imágenes del cohete resultantes de las tareas fallidas. Tenga en cuenta
que la URL llamada en la tarea download_launches simplemente solicita los próximos lanzamientos de cohetes,
lo que significa que devolverá los próximos lanzamientos de cohetes en el momento de llamar a la API. La
incorporación del contexto de tiempo de ejecución en el que se ejecutó un DAG en su código se trata en el
capítulo 4.

Resumen
ÿ Los flujos de trabajo en Airflow se representan en DAG. ÿ Los
operadores representan una única unidad de trabajo. ÿ Airflow
contiene una serie de operadores tanto para tipos genéricos como específicos de
trabajar.

ÿ La interfaz de usuario de Airflow ofrece una vista de gráfico para ver la estructura DAG y la vista de árbol
para ver carreras DAG a lo largo del
tiempo. ÿ Las tareas fallidas se pueden reiniciar en cualquier parte del DAG.
Machine Translated by Google

Programación en Airflow

Este capítulo cubre


ÿ Ejecutar DAG a intervalos regulares ÿ
Construir DAG dinámicos para procesar datos de
manera incremental ÿ Cargar y reprocesar conjuntos
de datos anteriores mediante el relleno ÿ Aplicar las
mejores prácticas para tareas confiables

En el capítulo anterior, exploramos la interfaz de usuario de Airflow y le mostramos cómo definir un DAG
de Airflow básico y ejecutarlo todos los días mediante la definición de un intervalo programado. En este
capítulo, profundizaremos un poco más en el concepto de programación en Airflow y exploraremos cómo
esto le permite procesar datos de forma incremental a intervalos regulares. Primero, presentaremos un
pequeño caso de uso centrado en el análisis de eventos de usuarios de nuestro sitio web y exploraremos
cómo podemos crear un DAG para analizar estos eventos a intervalos regulares.
A continuación, exploraremos formas de hacer que este proceso sea más eficiente adoptando un enfoque
incremental para analizar nuestros datos y comprender cómo se relaciona esto con el concepto de fechas
de ejecución de Airflow. Finalmente, terminaremos mostrando cómo podemos llenar los vacíos anteriores
en nuestro conjunto de datos mediante el relleno y discutiendo algunas propiedades importantes de las
tareas de Airflow adecuadas.

40
Machine Translated by Google

Un ejemplo: procesamiento de eventos de usuario 41

3.1 Un ejemplo: procesamiento de eventos de usuario


Para comprender cómo funciona la programación de Airflow, primero consideraremos un pequeño ejemplo.
Imagina que tenemos un servicio que rastrea el comportamiento de los usuarios en nuestro sitio web y nos permite analizar a
qué páginas acceden los usuarios (identificados por una dirección IP). Para fines de marketing, nos gustaría saber a cuántas
páginas diferentes acceden los usuarios y cuánto tiempo pasan en cada visita. Para tener una idea de cómo cambia este
comportamiento con el tiempo, queremos calcular estas estadísticas diariamente, ya que esto nos permite comparar los
cambios en diferentes días y períodos de tiempo más largos.

Por razones prácticas, el servicio de seguimiento externo no almacena datos durante más de 30 días, por lo que debemos
almacenar y acumular estos datos nosotros mismos, ya que queremos conservar nuestro historial durante períodos de tiempo
más prolongados. Normalmente, debido a que los datos sin procesar pueden ser bastante grandes, tendría sentido almacenar
estos datos en un servicio de almacenamiento en la nube como S3 de Amazon o Cloud Storage de Google, ya que combinan
una alta durabilidad con costos relativamente bajos. Sin embargo, en aras de la simplicidad, no nos preocuparemos por estas
cosas y mantendremos nuestros datos localmente.

Para simular este ejemplo, hemos creado una API simple (local) que nos permite recuperar eventos de usuario. Por
ejemplo, podemos recuperar la lista completa de eventos disponibles de los últimos 30 días mediante la siguiente llamada a
la API:

curl -o /tmp/events.json http:/ /localhost:5000/events

Esta llamada devuelve una lista (codificada en JSON) de eventos de usuario que podemos analizar para calcular nuestras
estadísticas de usuario.

Usando esta API, podemos dividir nuestro flujo de trabajo en dos tareas separadas: una para obtener eventos de usuario
y otra para calcular las estadísticas. Los datos en sí se pueden descargar usando BashOperator, como vimos en el capítulo
anterior. Para calcular las estadísticas, podemos usar PythonOperator, que nos permite cargar los datos en un Pandas
DataFrame y calcular la cantidad de eventos usando un grupo y una agregación.

En total, esto nos da el DAG que se muestra en el listado 3.1.

Listado 3.1 Versión inicial (no programada) del evento DAG (dags/01_unscheduled.py)

importar fecha y hora como dt


desde ruta de importación pathlib

importar pandas como pd


desde airflow importar DAG desde
airflow.operators.bash importar BashOperator desde
airflow.operators.python importar PythonOperator

dag = dag(
Defina la fecha de
dag_id="01_no programado",
inicio para el DAG.
start_date=dt.datetime(2019, 1, 1),
schedule_interval=Ninguno,
Especifique que se trata
) de un DAG no programado.
Machine Translated by Google

42 CAPÍTULO 3 Programación en Airflow

fetch_events = BashOperator(
task_id="obtener_eventos",
bash_command=(
"
"mkdir -p /datos && "curl
Obtenga y almacene los
-o /datos/eventos.json "
eventos de la API.
"https:/ /localhost:5000/eventos"
),
dag=dag,
)

Cargue los eventos


def _calculate_stats(ruta_de_entrada, ruta_de_salida):
y calcule las
"""Calcula estadísticas de eventos."""
estadísticas
eventos = pd.read_json(input_path) estadísticas requeridas.
= eventos.groupby(["fecha", "usuario"]).tamaño().reset_index()
Ruta (ruta_de_salida).parent.mkdir(exist_ok=Verdadero)
Asegúrese de que
stats.to_csv(ruta_de_salida, índice=Falso)
exista el directorio
de salida y escriba
los resultados en CSV.
calcular_estadísticas = OperadorPython(
task_id="calcular_estadísticas",
python_callable=_calculate_stats,
op_kwargs={
"ruta_de_entrada": "/datos/eventos.json",
"output_path": "/datos/estadísticas.csv",
},
dag = dag,
)
Establecer
orden de ejecución.
buscar_eventos >> calcular_estadísticas

Ahora tenemos nuestro DAG básico, pero aún debemos asegurarnos de que se ejecute regularmente mediante el
flujo de aire. ¡Vamos a programarlo para que tengamos actualizaciones diarias!

3.2 Correr a intervalos regulares


Como vimos en el capítulo 2, los DAG de Airflow se pueden ejecutar a intervalos regulares definiendo un intervalo
programado para ellos mediante el argumento schedule_interval al inicializar el DAG.
De forma predeterminada, el valor de este argumento es Ninguno, lo que significa que el DAG no se programará y
se ejecutará solo cuando se active manualmente desde la interfaz de usuario o la API.

3.2.1 Definición de intervalos de programación

En nuestro ejemplo de ingesta de eventos de usuario, nos gustaría calcular estadísticas diariamente, por lo que
tendría sentido programar nuestro DAG para que se ejecute una vez al día. Como esto es común
caso de uso, Airflow proporciona la macro conveniente @daily para definir una programación diaria
intervalo, que ejecuta nuestro DAG una vez al día a la medianoche.
Machine Translated by Google

Correr a intervalos regulares 43

Listado 3.2 Definición de un intervalo de horario diario (dags/02_daily_schedule.py)

dag = dag( Programar el DAG para que se ejecute


dag_id="02_horario_diario", todos los días a medianoche.
intervalo_programación ="@diario",
fecha_inicio=dt.fechahora(2019, 1, 1), Fecha/hora para comenzar
...
a programar ejecuciones de DAG
)

Airflow también necesita saber cuándo queremos comenzar a ejecutar el DAG, especificado por su
fecha de inicio. Con base en esta fecha de inicio, Airflow programará la primera ejecución de nuestro

DAG para ejecutarse en el primer intervalo de programación después de la fecha de inicio (inicio + intervalo). Las
ejecuciones subsiguientes continuarán ejecutándose en intervalos de programación después de este primer intervalo.

NOTA Preste atención al hecho de que Airflow inicia las tareas en un intervalo en el
final del intervalo. Si desarrolla un DAG el 1 de enero de 2019 a las 13:00, con un
start_date de 01-01-2019 e @intervalo diario , esto significa que comienza a ejecutarse por primera vez a
la medianoche. Al principio, no pasará nada si ejecuta el DAG en enero
1 a las 13:00 hasta llegar a la medianoche.

Por ejemplo, supongamos que definimos nuestro DAG con una fecha de inicio el primero de enero, como se mostró
anteriormente en el listado 3.2. Combinado con un intervalo de programación diario, esto resultará en
Airflow ejecutando nuestro DAG a la medianoche todos los días posteriores al primero de enero (figura 3.1). Tenga en
cuenta que nuestra primera ejecución tiene lugar el dos de enero (el primer
intervalo siguiente a la fecha de inicio) y no el primero. Entraremos en el razonamiento detrás
este comportamiento más adelante en este capítulo (sección 3.4).

Primero Segundo Tercero


ejecución ejecución ejecución

comienzo Futuro
fecha ejecuciones
2019-01-01 2019-01-02 2019-01-03 2019-01-04
00:00 00:00 00:00 00:00

Figura 3.1 Intervalos de programación para un DAG programado diario con una fecha de
inicio especificada (2019-01-01). Las flechas indican el punto de tiempo en el que se ejecuta un DAG.
Sin una fecha de finalización especificada, el DAG seguirá ejecutándose todos los
días hasta que se apague.

Sin una fecha de finalización, Airflow (en principio) seguirá ejecutando nuestro DAG en este día.
calendario hasta el final de los tiempos. Sin embargo, si ya sabemos que nuestro proyecto tiene un
duración fija, podemos decirle a Airflow que deje de ejecutar nuestro DAG después de una fecha determinada usando
el parámetro end_date .
Machine Translated by Google

44 CAPÍTULO 3 Programación en Airflow

Listado 3.3 Definición de una fecha de finalización para el DAG (dags/03_with_end_date.py)

dag = dag(
dag_id="03_con_fecha_de_finalización",
intervalo_de_horario="@diario",
start_date=dt.datetime(año=2019, mes=1, día=1),
end_date=dt.datetime(año=2019, mes=1, día=5),
)

Esto dará como resultado el conjunto completo de intervalos de programación que se muestra en la figura 3.2.

Primero Segundo Tercero Cuatro Final


ejecución ejecución ejecución ejecución ejecución

comienzo Final
fecha fecha

2019-01-01 2019-01-02 2019-01-03 2019-01-04 2019-01-04 2019-01-05


00:00 00:00 00:00 00:00 00:00 00:00

Figura 3.2 Programar intervalos para un DAG programado diariamente con fechas de inicio
(2019-01-01) y finalización (2019-01-05) especificadas, lo que evita que el DAG se ejecute más allá de esta fecha

3.2.2 Intervalos basados en cron


Hasta ahora, todos nuestros ejemplos han mostrado DAG ejecutándose a intervalos diarios. Pero, ¿y si nosotros
¿Quieres ejecutar nuestros trabajos en intervalos de una hora o una semana? Y que tal mas complicado
intervalos en los que podemos querer ejecutar nuestro DAG a las 23:45 todos los sábados?
Para admitir intervalos de programación más complicados, Airflow nos permite definir
programar intervalos usando la misma sintaxis que usa cron, un programador de trabajos basado en el tiempo
utilizado por sistemas operativos de computadora similares a Unix, como macOS y Linux. esta sintaxis
consta de cinco componentes y se define de la siguiente manera:

# ÿÿÿÿÿÿÿÿ # ÿ minuto (0 - 59)


ÿÿÿÿÿÿÿ # ÿ # ÿ # hora (0 - 23)
ÿ#ÿ ÿ ÿÿÿÿÿÿ día del mes (1 - 31)
ÿ ÿ ÿÿÿÿÿÿ ÿ ÿ ÿ mes (1 - 12)
ÿÿÿÿÿ
semana
día
6) (domingo
de(0la- a sábado;
ÿ ÿ ÿ 7 también es domingo en algunos sistemas)
ÿ

#*****

En esta definición, un trabajo cron se ejecuta cuando los campos de especificación de hora/fecha
coincida con la hora/fecha actual del sistema. Se pueden utilizar asteriscos (*) en lugar de números para
definir campos sin restricciones, lo que significa que no nos importa el valor de ese campo.
Aunque esta representación basada en cron puede parecer un poco complicada, nos proporciona
con una gran flexibilidad para definir intervalos de tiempo. Por ejemplo, podemos definir
intervalos por hora, diarios y semanales utilizando las siguientes expresiones cron:
Machine Translated by Google

Correr a intervalos regulares 45

ÿ0**** = cada hora (corriendo en la hora)


ÿ00*** = diario (ejecutando a la medianoche)

ÿ 0 0 * * 0 = semanal (funciona a la medianoche del domingo)

Además de esto, también podemos definir expresiones más complicadas como las siguientes:

ÿ001** = la medianoche del primero de cada mes

ÿ 45 23 * * SÁB = 23:45 todos los sábados

Además, las expresiones cron le permiten definir colecciones de valores usando una coma
(,) para definir una lista de valores o un guión (-) para definir un rango de valores. Usando esta sintaxis,
podemos crear expresiones que permitan ejecutar trabajos en varios días de la semana o varios
conjuntos de horas durante un día:

ÿ 0 0 * * MON,WED,FRI = ejecutar todos los lunes, miércoles y viernes a la medianoche

ÿ 0 0 * * LUN-VIE = ejecutar todos los días de la semana a medianoche


ÿ 0 0,12 * * * = correr todos los días a las 00:00 y 12:00

Airflow también brinda soporte para varias macros que representan la forma abreviada de los intervalos de programación
de uso común. Ya hemos visto una de estas macros (@daily)
para definir los intervalos diarios. Una descripción general de las otras macros admitidas por Airflow es
se muestra en la tabla 3.1.

Tabla 3.1 Valores predeterminados de flujo de aire para intervalos de programación utilizados con frecuencia

Preestablecido Sentido

@una vez Programe una vez y solo una vez.

@cada hora Ejecutar una vez por hora al comienzo de la hora.

@diariamente Ejecutar una vez al día a la medianoche.

@semanalmente Corre una vez por semana a la medianoche del domingo por la mañana.

@monthly Ejecutar una vez al mes a la medianoche del primer día del mes.

@anual Se ejecuta una vez al año a la medianoche del 1 de enero.

Aunque las expresiones de Cron son extremadamente poderosas, puede ser difícil trabajar con ellas.
Como tal, puede ser una buena idea probar su expresión antes de probarla en Airflow.
Afortunadamente, existen muchas herramientas1 disponibles en línea que pueden ayudarlo a definir, verificar o
explique sus expresiones Cron en inglés sencillo. Tampoco está de más documentar el
razonamiento detrás de complicadas expresiones cron en su código. Esto puede ayudar a otros
(¡incluido el futuro usted!) entienda la expresión cuando revise su código.

1
https://crontab.guru traduce las expresiones cron a un lenguaje legible por humanos.
Machine Translated by Google

46 CAPÍTULO 3 Programación en Airflow

3.2.3 Intervalos basados en frecuencia


Una limitación importante de las expresiones cron es que no pueden representar ciertos horarios basados
en la frecuencia. Por ejemplo, ¿cómo definiría una expresión cron que ejecuta un DAG una vez cada tres
días? Resulta que podría escribir una expresión que se ejecute cada primer, cuarto, séptimo y así
sucesivamente día del mes, pero este enfoque tendría problemas al final del mes, ya que el DAG se
ejecutaría consecutivamente en ambos días. 31 y el primero del mes siguiente, incumpliendo el horario
deseado.

Esta limitación de cron se deriva de la naturaleza de las expresiones cron, ya que definen un patrón que
se compara continuamente con la hora actual para determinar si se debe ejecutar un trabajo. Esto tiene la
ventaja de hacer que las expresiones no tengan estado, lo que significa que no tiene que recordar cuándo
se ejecutó un trabajo anterior para calcular el siguiente intervalo. Sin embargo, como puede ver, esto tiene
el precio de cierta expresividad.
¿Qué pasa si realmente queremos ejecutar nuestro DAG una vez cada tres días? Para admitir este tipo
de programación basada en la frecuencia, Airflow también le permite definir intervalos de programación en
términos de un intervalo de tiempo relativo. Para usar una programación basada en la frecuencia, puede
pasar una instancia de timedelta (desde el módulo de fecha y hora en la biblioteca estándar) como un
intervalo de programación.

Listado 3.4 Definición de un intervalo de programación basado en la frecuencia (dags/04_time_delta.py)

dag = timedelta ofrece la posibilidad


DAG( dag_id="04_time_delta", de utilizar horarios basados
en la frecuencia.
intervalo_programación=dt.timedelta(días=3),
fecha_inicio=dt.fechahora(año=2019, mes=1, día=1),
fecha_finalización=dt.fechahora(año=2019, mes=1, día=5),
)

Esto daría como resultado que nuestro DAG se ejecute cada tres días después de la fecha de inicio (los días
4, 7, 10 y así sucesivamente de enero de 2019). Por supuesto, también puede usar este enfoque para
ejecutar su DAG cada 10 minutos (usando timedelta (minutos = 10)) o cada dos horas (usando timedelta
(horas = 2)).

3.3 Procesamiento de datos de forma incremental


Aunque ahora tenemos nuestro DAG ejecutándose a intervalos diarios (suponiendo que nos apeguemos al
programa @daily ), no hemos logrado nuestro objetivo. Por un lado, nuestro DAG está descargando y
calculando estadísticas para todo el catálogo de eventos de usuarios todos los días, lo que no es muy
eficiente. Además, este proceso solo descarga eventos de los últimos 30 días, lo que significa que no
estamos construyendo ningún historial para fechas anteriores.

3.3.1 Obtener eventos de forma incremental


Una forma de resolver estos problemas es cambiar nuestro DAG para cargar datos de forma incremental,
en el que solo cargamos eventos del día correspondiente en cada intervalo de programación y solo
calculamos estadísticas para los nuevos eventos (figura 3.3).
Machine Translated by Google

Procesamiento de datos de forma incremental 47

Eventos

Buscar Agregar
Día 1 eventos/día1.json estadísticas/día1.csv

Buscar Agregar
Dia 2 eventos/día2.json estadísticas/día2.csv

Día 3

Figura 3.3 Obtener y procesar datos de forma incremental

Este enfoque incremental es mucho más eficiente que buscar y procesar el


todo el conjunto de datos, ya que reduce significativamente la cantidad de datos que deben procesarse
en cada intervalo de programación. Además, debido a que ahora almacenamos nuestros datos en archivos
separados por día, también tenemos la oportunidad de comenzar a crear un historial de archivos
tiempo, mucho más allá del límite de 30 días de nuestra API.
Para implementar el procesamiento incremental en nuestro flujo de trabajo, necesitamos modificar nuestro
DAG para descargar datos de un día específico. Afortunadamente, podemos ajustar nuestra llamada API para
obtener eventos para la fecha actual incluyendo parámetros de fecha de inicio y finalización:

curl -O http:/ /localhost:5000/events?start_date=2019-01-01&end_date=2019-01-02

Juntos, estos parámetros de fecha indican el intervalo de tiempo para el que nos gustaría
obtener eventos. Tenga en cuenta que en este ejemplo start_date es inclusivo, mientras que end_date es
exclusivo, lo que significa que estamos recuperando eventos que ocurren entre 2019-01-01
00:00:00 y 2019-01-01 23:59:59.

Podemos implementar esta obtención de datos incremental en nuestro DAG cambiando nuestro
comando bash para incluir las dos fechas.

Listado 3.5 Obtener eventos para un intervalo de tiempo específico (dags/05_query_with_dates.py)

fetch_events = BashOperator(
task_id="obtener_eventos",
bash_command=(
"
"mkdir -p /data && "curl
-o /data/events.json " "http:/ /
localhost:5000/events?"
"start_date=2019-01-01&"
"fecha_finalización=2019-01-02"
Machine Translated by Google

48 CAPÍTULO 3 Programación en Airflow

),
dag=dag,
)

Sin embargo, para obtener datos para cualquier otra fecha que no sea 2019-01-01, debemos cambiar el
comando para usar fechas de inicio y finalización que reflejen el día en que se ejecuta el DAG.
Afortunadamente, Airflow nos brinda varios parámetros adicionales para hacerlo, que exploraremos en la
siguiente sección.

3.3.2 Referencias temporales dinámicas utilizando fechas de ejecución

Para muchos flujos de trabajo que implican procesos basados en el tiempo, es importante saber en qué
intervalo de tiempo se ejecuta una tarea determinada. Por esta razón, Airflow proporciona tareas con
parámetros adicionales que se pueden usar para determinar en qué intervalo de programación se ejecuta
una tarea (entraremos en más detalles sobre estos parámetros en el próximo capítulo).
El más importante de estos parámetros es la fecha_ejecución, que representa la fecha y la hora en
que se ejecuta nuestro DAG. Al contrario de lo que sugiere el nombre del parámetro, la fecha_ejecución no
es una fecha sino una marca de tiempo, que refleja la hora de inicio del intervalo de programación para el
que se ejecuta el DAG. La hora de finalización del intervalo de programación se indica mediante otro
parámetro denominado next_execution_date. Juntas, estas fechas definen la duración total del intervalo de
programación de una tarea (figura 3.4).

Fecha de Fecha de
ejecución anterior ejecución Próxima fecha de ejecución

Intervalo
actual
Fecha Futuras
de inicio ejecuciones

2019-01-01 2019-01-02 2019-01-03 2019-01-04


00:00 00:00 00:00 00:00

Ahora

Figura 3.4 Fechas de ejecución en Airflow

Airflow también proporciona un parámetro anterior_execution_date , que describe el inicio del intervalo de
programación anterior. Aunque no usaremos este parámetro aquí, puede ser útil para realizar análisis que
contrasten los datos del intervalo de tiempo actual con los resultados del intervalo anterior.

En Airflow, podemos usar estas fechas de ejecución al hacer referencia a ellas en nuestros operadores.
Por ejemplo, en BashOperator, podemos usar la funcionalidad de plantillas de Airflow para incluir las fechas
de ejecución de forma dinámica en nuestro comando Bash. Las plantillas se tratan en detalle en el capítulo
4.
Machine Translated by Google

Procesamiento de datos de forma incremental 49

Listado 3.6 Uso de plantillas para especificar fechas (dags/06_templated_query.py)

fetch_events = BashOperator(
formateado
task_id="obtener_eventos",
fecha de ejecución
bash_command=(
" insertado con
"mkdir -p /datos && "curl -o /
Plantillas Jinja
datos/eventos.json"
"http:/ /localhost:5000/eventos?"
"start_date={{execution_date.strftime('%Y-%m-%d')}}"
"&end_date={{next_execution_date.strftime('%Y-%m-%d')}}"
),
dag = dag, next_execution_date contiene el
) fecha de ejecución del siguiente intervalo.

En este ejemplo, la sintaxis {{variable_name}} es un ejemplo del uso de Jinja de Airflow basado (http://
jinja.pocoo.org) sintaxis de plantillas para hacer referencia a uno de los parámetros específicos de Airflow. Aquí,
usamos esta sintaxis para hacer referencia a las fechas de ejecución y el formato.
en el formato de cadena esperado utilizando el método datetime strftime (ya que ambas fechas de ejecución son
objetos datetime).
Debido a que los parámetros de fecha_ejecución a menudo se usan de esta manera para hacer referencia a
fechas como cadenas formateadas, Airflow también proporciona varios parámetros abreviados
para formatos de fecha comunes. Por ejemplo, los parámetros ds y ds_nodash son
representaciones diferentes de la fecha_ejecución , formateadas como AAAA-MM-DD y
AAAA MDD, respectivamente. Del mismo modo, next_ds, next_ds_nodash, prev_ds y prev_ds_nodash
proporcionar notaciones abreviadas para las fechas de ejecución siguiente y anterior, respectivamente.2
Usando estas notaciones abreviadas, también podemos escribir nuestro comando de búsqueda incremental
de la siguiente manera.

Listado 3.7 Usando plantillas abreviadas (dags/07_templated_query_ds.py)

fetch_events = BashOperator(
task_id="obtener_eventos",
bash_command=(
"
"mkdir -p /datos && "curl -o /
datos/eventos.json" ds proporciona la
fecha_de_ejecución
"http:/ /localhost:5000/eventos?"
con formato AAAA MM-DD.
"start_date={{ds}}&"
"end_date={{next_ds}}"
next_ds proporciona
),
lo mismo para next_
dag = dag, fecha de ejecución.
)

Esta versión más corta es un poco más fácil de leer. Sin embargo, para fechas más complicadas (o
datetime), es probable que aún necesite usar el método strftime más flexible.

2
Consulte https://airflow.readthedocs.io/en/stable/macros-ref.html para obtener una descripción general de todas las abreviaturas disponibles
opciones
Machine Translated by Google

50 CAPÍTULO 3 Programación en Airflow

3.3.3 Partición de sus datos


Aunque nuestra nueva tarea fetch_events ahora obtiene eventos de forma incremental para cada nuevo
intervalo de programación, el lector astuto puede haber notado que cada nueva tarea es simplemente
sobrescribiendo el resultado del día anterior, lo que significa que efectivamente no estamos construyendo
cualquier historia.

Una forma de resolver este problema es simplemente agregar nuevos eventos a events.json
archivo, lo que nos permitiría construir nuestro historial en un solo archivo JSON. Sin embargo, una desventaja de este
enfoque es que requiere cualquier trabajo de procesamiento posterior para cargar el
conjunto completo de datos, incluso si solo estamos interesados en calcular estadísticas para un día determinado.
Además, también hace que este archivo sea un único punto de falla, por lo que podemos correr el riesgo de perder
todo nuestro conjunto de datos en caso de que este archivo se pierda o se corrompa.
Un enfoque alternativo es dividir nuestro conjunto de datos en lotes diarios escribiendo el
salida de la tarea a un archivo que lleva el nombre de la fecha de ejecución correspondiente.

Listado 3.8 Escritura de datos de eventos en archivos separados por fecha (dags/08_templated_path.py)

fetch_events = BashOperator(
task_id="obtener_eventos",
bash_command=(
" Respuesta escrita en
"mkdir -p /data/events && "curl -o /data/
nombre de archivo con plantilla
events/{{ds}}.json " "http:/ /localhost:5000/events?"

"start_date={{ds}}&"
"end_date={{next_ds}}", dag=dag,

Esto daría como resultado que se descarguen datos para una fecha de ejecución de 2019-01-01
siendo escrito en el archivo /data/events/2019-01-01.json.
Esta práctica de dividir un conjunto de datos en partes más pequeñas y más manejables es una estrategia común
en los sistemas de almacenamiento y procesamiento de datos y se conoce comúnmente como
partición, con las piezas más pequeñas de un conjunto de datos las particiones. La ventaja de particionar nuestro
conjunto de datos por fecha de ejecución se hace evidente cuando consideramos la segunda
tarea en nuestro DAG (calculate_stats), en el que calculamos estadísticas para cada día
valor de los eventos de usuario. En nuestra implementación anterior, estábamos cargando todos los datos
establecer y calcular estadísticas para todo nuestro historial de eventos, todos los días.

Listado 3.9 Implementación previa para estadísticas de eventos (dags/01_scheduled.py)

def _calculate_stats(ruta_de_entrada, ruta_de_salida):


"""Calcula estadísticas de eventos."""
Ruta (ruta_de_salida).parent.mkdir(exist_ok=True)
eventos = pd.read_json(ruta_de_entrada)
estadísticas = eventos.groupby(["fecha", "usuario"]).tamaño().reset_index()
stats.to_csv(output_path, index=False)
Machine Translated by Google

Procesamiento de datos de forma incremental 51

calcular_estadísticas = OperadorPython(
task_id="calcular_estadísticas",
python_callable=_calculate_stats,
op_kwargs={
"ruta_de_entrada": "/datos/eventos.json",
"output_path": "/datos/estadísticas.csv",
},
dag = dag,
)

Sin embargo, al usar nuestro conjunto de datos particionados, podemos calcular estas estadísticas de
manera más eficiente para cada partición separada al cambiar las rutas de entrada y salida de esta tarea.
para señalar los datos de eventos particionados y un archivo de salida particionado.

Listado 3.10 Cálculo de estadísticas por intervalo de ejecución (dags/08_templated_path.py)

Reciba todas las


def _calculate_stats(**contexto): variables de contexto en este dict.
"""Calcula estadísticas de eventos."""
ruta_entrada = contexto["templates_dict"]["ruta_entrada"] ruta_salida = Recuperar los
contexto["templates_dict"]["ruta_salida"] valores de la
plantilla de la
Ruta (ruta_de_salida).parent.mkdir(exist_ok=True) objeto
templates_dict.
eventos = pd.read_json(ruta_de_entrada)
estadísticas = eventos.groupby(["fecha", "usuario"]).tamaño().reset_index()
stats.to_csv(output_path, index=False)

calcular_estadísticas = OperadorPython(
task_id="calcular_estadísticas",
python_callable=_calculate_stats,
Pasar los valores que nosotros
templates_dict={
quiere ser plantilla.
"ruta_de_entrada": "/datos/eventos/{{ds}}.json",
"ruta_de_salida": "/datos/estadísticas/{{ds}}.csv",
},
dag = dag,
)

Aunque estos cambios pueden parecer algo complicados, en su mayoría involucran código de placa de
caldera para garantizar que nuestras rutas de entrada y salida tengan plantillas. Lograr esto
plantilla en PythonOperator, necesitamos pasar cualquier argumento que deba ser una plantilla usando el
parámetro templates_dict del operador. Entonces podemos recuperar el
valores con plantilla dentro de nuestra función desde el objeto de contexto que se pasa a nuestro
Función _calculate_stats de Airflow.3
Si todo esto fue demasiado rápido, no se preocupe; nos sumergiremos en el contexto de la tarea en
más detalle en el siguiente capítulo. El punto importante a entender aquí es que estos

3
Para Airflow 1.10.x, deberá pasar el argumento adicional provide_context=True a PythonOperator;
de lo contrario, la función _calculate_stats no recibirá los valores de contexto.
Machine Translated by Google

52 CAPÍTULO 3 Programación en Airflow

Los cambios nos permiten calcular nuestras estadísticas de forma incremental, procesando solo un pequeño
subconjunto de nuestros datos cada día.

3.4 Comprensión de las fechas de ejecución de Airflow Debido a que las

fechas de ejecución son una parte tan importante de Airflow, tomemos un minuto para asegurarnos de que
entendemos completamente cómo se definen estas fechas.

3.4.1 Ejecutar trabajo en intervalos de longitud fija


Como hemos visto, podemos controlar cuándo Airflow ejecuta un DAG con tres parámetros: una fecha de inicio,
un intervalo de programación y una fecha de finalización (opcional). Para comenzar a programar nuestro DAG,
Airflow usa estos tres parámetros para dividir el tiempo en una serie de intervalos de programación, comenzando
desde la fecha de inicio dada y, opcionalmente, terminando en la fecha de finalización (figura 3.5).

primer Segundo Tercero Intervalos


intervalo intervalo intervalo futuros
Fecha (Opcional)
de inicio Fecha final
2019-01-01 2019-01-02 2019-01-03 2019-01-04
00:00 00:00 00:00 00:00

Figura 3.5 Tiempo representado en términos de intervalos de programación de Airflow.


Supone un intervalo diario con una fecha de inicio de 2019-01-01.

En esta representación de tiempo basada en intervalos, se ejecuta un DAG para un intervalo dado tan pronto
como ha pasado el intervalo de tiempo de ese intervalo. Por ejemplo, el primer intervalo de la figura 3.5 se
ejecutaría lo antes posible después de 2019-01-01 23:59:59, porque para entonces ya ha pasado el último punto
del intervalo. De manera similar, el DAG se ejecutaría para el segundo intervalo poco después del 2019-01-02
23:59:59, y así sucesivamente, hasta llegar a nuestra fecha de finalización opcional.

Una ventaja de usar este enfoque basado en intervalos es que es ideal para realizar el tipo de procesamiento
de datos incremental que vimos en las secciones anteriores, ya que sabemos exactamente durante qué intervalo
de tiempo se ejecuta una tarea: el inicio y el final de la tarea. intervalo de respuesta correspondiente. Esto está
en marcado contraste con, por ejemplo, un sistema de programación basado en puntos de tiempo como cron,
donde solo sabemos la hora actual en la que se ejecuta nuestra tarea. Esto significa que, por ejemplo en cron,
tenemos que calcular o adivinar dónde quedó nuestra ejecución anterior suponiendo que la tarea se está
ejecutando el día anterior (figura 3.6).

Comprender que el manejo del tiempo de Airflow se basa en intervalos de programación también ayuda a
comprender cómo se definen las fechas de ejecución dentro de Airflow. Por ejemplo, supongamos que tenemos
un DAG que sigue un intervalo de programación diario y luego consideramos el intervalo correspondiente que
debe procesar los datos para 2019-01-03. En Airflow, este intervalo se ejecutará poco después de 2019-01-04
00:00:00, porque en ese momento sabemos que ya no lo haremos.
Machine Translated by Google

Comprender las fechas de ejecución de Airflow 53

Ahora

Corre por
esto explícito
intervalo

basado en intervalos

Planificación

Adivina dónde
intervalo
empieza/termina

Basado en puntos de tiempo


Planificación
? ?

Figura 3.6 Procesamiento incremental en ventanas de programación basadas en


intervalos (p. ej., Airflow) frente a ventanas derivadas de sistemas basados en puntos
de tiempo (p. ej., cron). Para el procesamiento incremental (de datos), el tiempo
generalmente se divide en intervalos de tiempo discretos que se procesan tan pronto
como ha transcurrido el intervalo correspondiente. Los enfoques de programación
basados en intervalos (como Airflow) programan explícitamente las tareas para que
se ejecuten en cada intervalo y proporcionan información exacta a cada tarea sobre
el inicio y el final del intervalo. Por el contrario, los enfoques de programación basados
en puntos de tiempo solo ejecutan tareas en un momento dado, dejando que la tarea
en sí determine durante qué intervalo incremental se está ejecutando la tarea.

recibir nuevos datos para 2019-01-03. Pensando en nuestra explicación de usar


fechas de ejecución en nuestras tareas del apartado anterior, ¿qué os parece que la
será el valor de la fecha_ejecución para este intervalo?
Muchas personas esperan que la fecha de ejecución de esta ejecución de DAG sea el 04-01-2019, ya que
este es el momento en el que realmente se ejecuta el DAG. Sin embargo, si miramos el valor
de la variable fecha_ejecución cuando se ejecutan nuestras tareas, en realidad veremos un
fecha de ejecución de 2019-01-03. Esto se debe a que Airflow define la fecha de ejecución de un
DAG como el inicio del intervalo correspondiente. Conceptualmente, esto tiene sentido si
considere que la fecha de ejecución marca nuestro intervalo de programación en lugar del momento

nuestro DAG se ejecuta realmente. Desafortunadamente, el nombre puede ser un poco confuso.
Con las fechas de ejecución de Airflow definidas como el inicio del correspondiente
intervalos de programación, se pueden utilizar para derivar el inicio y el final de un intervalo específico
(figura 3.7). Por ejemplo, al ejecutar una tarea, el inicio y el final del intervalo correspondiente están definidos
por la fecha_ejecución (el inicio del intervalo) y el
next_execution date (el inicio del siguiente intervalo) parámetros. De manera similar, el intervalo de
programación anterior se puede derivar usando la fecha_ejecución_anterior y
parámetros de fecha_ejecución .
Sin embargo, una advertencia a tener en cuenta al usar la fecha de ejecución anterior
y next_execution_date en sus tareas es que estos solo están definidos para
Machine Translated by Google

54 CAPÍTULO 3 Programación en Airflow

Anterior Ejecución próximo

fecha de ejecución fecha fecha de ejecución

Anterior Actual próximo Futuro


intervalo intervalo intervalo intervalos
comienzo

fecha
2019-01-01 2019-01-02 2019-01-03 2019-01-04
00:00 00:00 00:00 00:00

Figura 3.7 Fechas de ejecución en el contexto de intervalos de programación. En Airflow, la


fecha de ejecución de un DAG se define como la hora de inicio del intervalo de programación
correspondiente en lugar de la hora en que se ejecuta el DAG (que suele ser el final del
intervalo). Como tal, el valor de la fecha_ejecución apunta al inicio del intervalo actual, mientras
que los parámetros fecha_ejecución_anterior y fecha_ejecución_siguiente apuntan al inicio de
los intervalos de programación anterior y siguiente, respectivamente. El intervalo actual se
puede derivar de una combinación de la fecha_ejecución y la siguiente_fecha_ejecución, lo
que significa el comienzo del siguiente intervalo y, por lo tanto, el final del actual.

DAG se ejecuta siguiendo el intervalo de programación. Como tal, los valores de estos parámetros
ser indefinido para cualquier ejecución que se active manualmente usando Airflow UI o CLI porque
Airflow no puede proporcionar información sobre los intervalos de programación anteriores o siguientes si
no están siguiendo un intervalo de programación.

3.5 Uso de relleno para llenar vacíos anteriores


Como Airflow nos permite definir intervalos de programación desde una fecha de inicio arbitraria, podemos
también defina intervalos pasados desde una fecha de inicio en el pasado. Podemos usar esta propiedad para
realizar ejecuciones históricas de nuestro DAG para cargar o analizar conjuntos de datos anteriores, un proceso
que normalmente se conoce como relleno.

3.5.1 Ejecución del trabajo en el tiempo


De forma predeterminada, Airflow programará y ejecutará cualquier intervalo de programación anterior que no haya
ha sido ejecutado Como tal, especificando una fecha de inicio pasada y activando el DAG correspondiente
dará como resultado que se ejecuten todos los intervalos que han pasado antes de la hora actual.
Este comportamiento está controlado por el parámetro catchup de DAG y se puede deshabilitar estableciendo ting
catchup en false.

Listado 3.11 Deshabilitar catchup para evitar ejecutar pasadas (dags/09_no_catchup.py)

dag = dag(
dag_id="09_no_ponerse al día",
intervalo_de_horario="@diario",
start_date=dt.datetime(año=2019, mes=1, día=1),
end_date=dt.datetime(año=2019, mes=1, día=5),
ponerse al día = falso,
)

Con esta configuración, el DAG solo se ejecutará para el intervalo de programación más reciente en lugar de
que ejecutar todos los intervalos pasados abiertos (figura 3.8). El valor predeterminado para ponerse al día puede
Machine Translated by Google

Las mejores prácticas para el diseño de tareas 55

Ponerse al día = verdadero (predeterminado)

El flujo de aire comienza a procesarse,


incluidos los intervalos pasados (= relleno).

Fecha Intervalo Ahora


de inicio actual

Ponerse al día = falso

Airflow comienza a procesar


desde el intervalo actual.

Fecha Ahora
Intervalo
de inicio
actual

Figura 3.8 Relleno en Airflow. De forma predeterminada, Airflow ejecutará


tareas para todos los intervalos anteriores hasta la hora actual. Este
comportamiento se puede deshabilitar configurando el parámetro catchup de
un DAG en falso, en cuyo caso Airflow solo comenzará a ejecutar tareas desde el intervalo actual.

controlarse desde el archivo de configuración de Airflow estableciendo un valor para la opción de configuración
catch up_by_default .
Aunque el reabastecimiento es un concepto poderoso, está limitado por la disponibilidad de datos en los sistemas
de origen. Por ejemplo, en nuestro caso de uso de ejemplo, podemos cargar eventos pasados desde nuestra API
especificando una fecha de inicio de hasta 30 días en el pasado. Sin embargo, dado que la API solo proporciona
hasta 30 días de historial, no podemos usar el relleno para cargar datos de días anteriores.

La reposición también se puede usar para reprocesar datos después de que hayamos realizado cambios en
nuestro código. Por ejemplo, digamos que hacemos un cambio en nuestra función calc_statistics para agregar una
nueva estadística. Al usar el relleno, podemos borrar ejecuciones anteriores de nuestra tarea calc_statistics para
volver a analizar nuestros datos históricos usando el nuevo código. Tenga en cuenta que en este caso no estamos
limitados por el límite de 30 días de nuestra fuente de datos, ya que ya hemos cargado estas particiones de datos
anteriores como parte de nuestras ejecuciones anteriores.

3.6 Mejores prácticas para el diseño de tareas


Si bien Airflow hace gran parte del trabajo pesado cuando se trata de rellenar y volver a ejecutar tareas, debemos
asegurarnos de que nuestras tareas cumplan con ciertas propiedades clave para obtener resultados adecuados. En
esta sección, nos sumergimos en dos de las propiedades más importantes de las tareas adecuadas de Airflow:
atomicidad e idempotencia.

3.6.1 Atomicidad
El término atomicidad se usa con frecuencia en los sistemas de bases de datos, donde una transacción atómica se
considera una serie indivisible e irreducible de operaciones de bases de datos de modo que ocurren todas o no
ocurre nada. De manera similar, en Airflow, las tareas deben definirse de modo que
Machine Translated by Google

56 CAPÍTULO 3 Programación en Airflow

o tienen éxito y producen algún resultado adecuado o fallan de una manera que no afecta el estado del
sistema (figura 3.9).

Operación no atómica operación atómica

Hay tres líneas. . .


Escribir en CSV Enviar estadísticas Escribir en CSV X Enviar estadísticas

Línea Línea
1 Línea 1 Línea
2 Línea 2 Línea
3 . . . fallar . . . 3 . . . fallar . . .

Línea 1 Ninguna salida


Línea 2
Línea 3

Figura 3.9 La atomicidad asegura que todo o nada se complete. No se produce la mitad del trabajo y, como
resultado, se evitan resultados incorrectos en el futuro.

Como ejemplo, considere una extensión simple para nuestro evento de usuario DAG, en el que nos gustaría
agregar alguna funcionalidad que envíe un correo electrónico de nuestros 10 usuarios principales al final de
cada ejecución. Una forma simple de agregar esto es extender nuestra función anterior con una llamada
adicional a alguna función que envíe un correo electrónico que contenga nuestras estadísticas.

Listado 3.12 Dos trabajos en una tarea, para romper la atomicidad (dags/10_non_atomic_send.py)

def _calculate_stats(**contexto):
"""Calcula estadísticas de eventos."""
ruta_entrada = contexto["templates_dict"]["ruta_entrada"] ruta_salida =
contexto["templates_dict"]["ruta_salida"]

events = pd.read_json(input_path) stats =


events.groupby(["date", "user"]).size().reset_index() stats.to_csv(output_path, index=False)

email_stats(estadísticas, email="usuario@ejemplo.com")

Enviar un correo electrónico después de escribir


en CSV crea dos piezas de trabajo en una sola
función, lo que rompe la atomicidad de la tarea.

Desafortunadamente, un inconveniente de este enfoque es que la tarea ya no es atómica. ¿Puedes ver por
qué? Si no, considere lo que sucede si nuestra función _send_stats falla (lo que seguramente sucederá si
nuestro servidor de correo electrónico es un poco inestable). En este caso, ya habremos escrito nuestras
estadísticas en el archivo de salida en output_path, haciendo que parezca que nuestra tarea tuvo éxito
aunque terminó en un error.
Para implementar esta funcionalidad de manera atómica, podríamos simplemente dividir el
funcionalidad de correo electrónico en una tarea separada.
Machine Translated by Google

Las mejores prácticas para el diseño de tareas 57

Listado 3.13 Dividir en múltiples tareas para mejorar la atomicidad (dags/11_atomic_send.py)

def _send_stats(correo electrónico, **contexto):


estadísticas = pd.read_csv(contexto["templates_dict"]["stats_path"])
email_stats(estadísticas, email=email)
Divida la instrucción
email_stats en una tarea
enviar_estadísticas = OperadorPython( separada para atomicidad.
task_id="enviar_estadísticas",
python_callable=_send_stats,
op_kwargs={"correo electrónico": "usuario@ejemplo.com"},
templates_dict={"stats_path": "/datos/estadísticas/{{ds}}.csv"},
dag = dag,
)

calcular_estadísticas >> enviar_estadísticas

De esta manera, el hecho de no enviar un correo electrónico ya no afecta el resultado de las estadísticas de cálculo .
task, pero solo falla send_stats, lo que hace que ambas tareas sean atómicas.
A partir de este ejemplo, podría pensar que separar todas las operaciones en operaciones individuales
tareas es suficiente para que todas nuestras tareas sean atómicas. Sin embargo, esto no es necesariamente verdad. A
comprenda por qué, piense si nuestra API de eventos nos hubiera requerido iniciar sesión antes de consultar eventos.
Por lo general, esto requeriría una llamada API adicional para obtener algún token de autenticación, después de lo
cual podemos comenzar a recuperar nuestros eventos.
Siguiendo nuestro razonamiento anterior de una operación = una tarea, tendríamos que
dividir estas operaciones en dos tareas separadas. Sin embargo, hacerlo crearía una fuerte
dependencia entre ellos, ya que la segunda tarea (obtener los eventos) fallará sin
ejecutando el primero poco antes. Esta fuerte dependencia entre los medios que probablemente
es mejor mantener ambas operaciones dentro de una sola tarea, permitiendo que la tarea forme una unidad de trabajo
única y coherente.
La mayoría de los operadores Airflow ya están diseñados para ser atómicos, por lo que muchos
los operadores incluyen opciones para realizar operaciones estrechamente acopladas, como la autenticación interna.
Sin embargo, los operadores más flexibles, como Python y Bash, pueden requerir que piense detenidamente en sus
operaciones para asegurarse de que sus tareas
permanecer atómico.

3.6.2 Idempotencia
Otra propiedad importante a tener en cuenta al escribir tareas de Airflow es la idempotencia.
Se dice que las tareas son idempotentes si se llama a la misma tarea varias veces con el mismo
entradas no tiene ningún efecto adicional. Esto significa que volver a ejecutar una tarea sin cambiar
las entradas no deben cambiar la salida general.
Por ejemplo, considere nuestra última implementación de la tarea fetch_events , que
obtiene los resultados de un solo día y los escribe en nuestro conjunto de datos particionado.
Machine Translated by Google

58 CAPÍTULO 3 Programación en Airflow

Listado 3.14 Implementación existente para obtener eventos (dags/08_templated_paths.py)

fetch_events =
BashOperator( task_id="fetch_events",
bash_command=( "mkdir -p /data/
"
events && "curl -o /data/events/
{{ds}}.json " "http:/ /localhost:5000/events? "
Particionamiento configurando
"start_date={{ds}}&" "end_date={{next_ds}}"
un nombre de archivo con plantilla

),
dag=dag,
)

Volver a ejecutar esta tarea para una fecha determinada daría como resultado que la tarea obtenga el mismo
conjunto de eventos que su ejecución anterior (suponiendo que la fecha esté dentro de nuestra ventana de 30
días) y sobrescriba el archivo JSON existente en la carpeta /data/events, produciendo el mismo resultado
Como tal, esta implementación de la tarea de obtención de eventos es claramente idempotente.
Para mostrar un ejemplo de una tarea no idempotente, considere usar un solo archivo JSON (/data/
events.json) y simplemente agregue eventos a este archivo. En este caso, volver a ejecutar una tarea daría
como resultado que los eventos simplemente se agregaran al conjunto de datos existente, duplicando así los
eventos del día (figura 3.10). Como tal, esta implementación no es indiferente, ya que las ejecuciones
adicionales de la tarea cambian el resultado general.

Tarea no idempotente tarea idempotente

Intento 1 Intento 3Intento 2 Intento 1 Intento 3Intento 2

Tarea de Tarea de Tarea de Tarea de Tarea de Tarea de


datos de proceso datos de proceso datos de proceso datos de proceso datos de proceso datos de proceso

Línea 1 Línea 1 Línea 1 Línea 1 Línea 1 Línea 1


Línea 2 Línea 2 Línea 2 Línea 2 Línea 2 Línea 2
Línea 3 Línea 3 Línea 3 Línea 3 Línea 3 Línea 3
Línea 1 Línea 1
Línea 2 Línea 2
Línea 3 Línea 3
Línea 1
Línea 2
Línea 3

Figura 3.10 Una tarea idempotente produce el mismo resultado, sin importar cuántas veces la ejecute.
La idempotencia asegura la consistencia y la capacidad de lidiar con el fracaso.

En general, las tareas que escriben datos pueden volverse idempotentes comprobando los resultados
existentes o asegurándose de que la tarea sobrescriba los resultados anteriores. En conjuntos de datos
particionados en el tiempo, esto es relativamente sencillo, ya que simplemente podemos sobrescribir la
partición correspondiente. De manera similar, para los sistemas de bases de datos, podemos usar operaciones
upsert para insertar datos, lo que nos permite sobrescribir filas existentes que fueron escritas por ejecuciones de tareas anteriores.
Machine Translated by Google

Resumen 59

Sin embargo, en aplicaciones más generales, debe considerar cuidadosamente todos los efectos
secundarios de su tarea y asegurarse de que se realicen de manera idempotente.

Resumen
ÿ Los DAG pueden ejecutarse a intervalos regulares configurando el intervalo
de programación. ÿ El trabajo de un intervalo se inicia al final del intervalo.
ÿ El intervalo de programación se puede configurar con expresiones cron y timedelta. ÿ Los
datos pueden procesarse incrementalmente configurando dinámicamente variables con
plantillas
ÿ La fecha de ejecución se refiere a la fecha y hora de inicio del intervalo, no a la hora real de
ejecución.
ÿ Un DAG puede retroceder en el tiempo con el relleno. ÿ La
idempotencia garantiza que las tareas se puedan volver a ejecutar mientras se producen los mismos resultados de salida.
Machine Translated by Google

Plantillas de tareas
usando el contexto de Airflow

Este capítulo cubre


ÿ Representación de variables en tiempo de ejecución
con plantillas ÿ Plantillas de variables con PythonOperator
frente a otros operadores
ÿ Representación de variables con plantilla para fines de
depuración
ÿ Realización de operaciones en sistemas externos

En los capítulos anteriores, tocamos la superficie de cómo los DAG y los operadores trabajan
juntos y cómo programar un flujo de trabajo en Airflow. En este capítulo, analizamos en
profundidad qué representan los operadores, qué son, cómo funcionan y cuándo y cómo se
ejecutan. También demostramos cómo se pueden usar los operadores para comunicarse con
sistemas remotos a través de ganchos, lo que le permite realizar tareas como cargar datos en
una base de datos, ejecutar un comando en un entorno remoto y realizar cargas de trabajo
fuera de Airflow.

60
Machine Translated by Google

Inspección de datos para su procesamiento con Airflow 61

4.1 Inspección de datos para su procesamiento con Airflow


A lo largo de este capítulo, trabajaremos en varios componentes de los operadores con la ayuda de
una herramienta (ficticia) de predicción del mercado de valores que aplica análisis de sentimiento, a la
que llamaremos StockSense. Wikipedia es uno de los mayores recursos de información pública en
Internet. Además de las páginas wiki, también están disponibles públicamente otros elementos, como
los recuentos de páginas vistas. A los efectos de este ejemplo, aplicaremos el axioma de que un
aumento en las páginas vistas de una empresa muestra un sentimiento positivo y es probable que
aumenten las acciones de la empresa. Por otro lado, una disminución en las páginas vistas nos indica
una pérdida de interés y es probable que el precio de las acciones disminuya.

4.1.1 Determinar cómo cargar datos incrementales


La Fundación Wikimedia (la organización detrás de Wikipedia) proporciona todas las páginas vistas
desde 2015 en formato legible por máquina.1 Las páginas vistas se pueden descargar en formato gzip
y se agregan por hora por página. Cada volcado por hora ocupa aproximadamente 50 MB en archivos
de texto comprimidos con gzip y tiene entre 200 y 250 MB de tamaño descomprimido.

Siempre que trabaje con cualquier tipo de datos, estos son detalles esenciales. Cualquier dato,
tanto pequeño como grande, puede ser complejo y es importante contar con un plan técnico de enfoque
antes de construir una canalización. La solución siempre depende de lo que usted u otros usuarios
quieran hacer con los datos, así que hágase a sí mismo y a otros preguntas como "¿Queremos volver
a procesar los datos en algún otro momento en el futuro?"; “¿Cómo recibo los datos (p. ej., frecuencia,
tamaño, formato, tipo de fuente)?”; y “¿Qué vamos a construir con los datos?” Después de conocer las
respuestas a tales preguntas, podemos abordar los detalles técnicos.

Descarguemos un solo volcado por hora e inspeccionemos los datos a mano. Para desarrollar un
pipeline de datos, debemos entender cómo cargarlo de forma incremental y cómo trabajar los datos
(figura 4.1).

1
https://dumps.wikimedia.org/other/pageviews. La estructura y los detalles técnicos de los datos de páginas vistas
de Wikipedia se documentan aquí: https://meta.wikimedia.org/wiki/Research:Page_view y https://
wikitech.wikimedia.org/wiki/Analytics/Data_Lake/Traffic/Pageviews .
Machine Translated by Google

62 CAPÍTULO 4 Plantillas de tareas usando el contexto de Airflow

El formato de URL de wikimedia sigue esta estructura:


https://dumps.wikimedia.org/other/pageviews/{year}/ {year}-{month}/pageviews-{year}
{month}{day}-{hour}0000 .gz

La fecha y la hora en el nombre del archivo se refieren al final del período,


1 , por ejemplo, 2 0000 se refiere a1 20:00:00 - 2:00:00.

$ wget https://dumps.wikimedia.org/other/pageviews/ 2019/2019-07/


pageviews-20190701-010000.gz $ gunzip pageviews-20190701-010000.gz $
head pageviews-20190701-010000

aa Main_Page 1 0 aa
Special:GlobalUsers/sysadmin 1 0 aa User_talk:Qoan
1 0 aa Wikipedia:Community_Portal 1 0 aa.d
Main_Page 2 0 aa.m Main_Page 1 0 ab 1005 1 0

ab 105 2 0 ab El archivo (g) comprimido contiene un solo archivo


1099 1 0 de texto con el mismo nombre que el archivo comprimido.

ab 1150 1 0
El contenido del archivo proporciona
los siguientes elementos, separados
por espacios en blanco: 1. Código
de dominio 2. Título de la página 3.
Número de vistas 4. Tamaño de la
respuesta en bytes Entonces, por

ejemplo, "en.m American_Bobtail 6

0" se refiere a seis páginas vistas de https: //en.m.wikipedia.org/wiki/American_Bobtail


(una especie de gato) en una hora determinada.

Los datos de páginas vistas normalmente se publican unos 45 minutos


después de finalizar el intervalo; sin embargo, a veces la liberación puede demorar entre 3 y 4 horas.

Figura 4.1 Descarga e inspección de datos de páginas vistas de Wikimedia

Vemos que las URL siguen un patrón fijo, que podemos usar al descargar los datos por
lotes (mencionado brevemente en el capítulo 3). Como experimento mental y para validar
los datos, veamos cuáles son los códigos de dominio más utilizados para el 7 de julio, de
10:00 a 11:00 (figura 4.2).
Ver los mejores resultados, 1061202 en y 995600 en.m, nos dice que los dominios
más vistos entre las 10:00 y las 11:00 del 7 de julio son "en" y "en.m" (la versión móvil
de .en), lo que hace sentido dado Inglés es el idioma más utilizado en el mundo. Además,
los resultados se devuelven tal como esperamos verlos, lo que confirma que no hay
caracteres inesperados ni desalineación de columnas, lo que significa que no tenemos
que realizar ningún procesamiento adicional para limpiar los datos. A menudo, limpiar y
transformar los datos en un estado consistente es una gran parte del trabajo.
Machine Translated by Google

Contexto de tareas y plantillas Jinja 63

$ 2wget https://dumps.wikimedia.org/other/pageviews/ 019/2019-07/pageviews-20190707-110000.gz $ gunzip pageviews-20190707-110000.gz $ |


awk -F' ' '{imprimir $1} ' vistas de página-20190707-110000
ordenar | uniq-c | ordenar -nr | cabeza

1061202 en
995600 en.m Ejemplo:
aa Main_Page 3 0 af
300753 ja.m
286381 de.m Ford_EcoSport 0 1 ab 9 1
11 1 0 ab 2009 0 1
257751 de
226334 ru 201930
ja 198182 fr.m Automóvil club británico

193331 ru.m ab
171510 it.m ab ab

Automóvil club británico

ab
ab
af

1 aa
2 ab
1 af

2 ab
1 af
1 aa

Figura 4.2 Primer análisis simple de los datos de páginas vistas de Wikimedia

4.2 Contexto de la tarea y plantilla Jinja Ahora,


juntemos todo esto y creemos la primera versión de un DAG extrayendo los recuentos de
páginas vistas de Wiki pedia. Comencemos de manera simple descargando, extrayendo y
leyendo los datos. Hemos seleccionado cinco empresas (Amazon, Apple, Facebook,
Google y Microsoft) para rastrear inicialmente y validar la hipótesis (figura 4.3).

Descargar archivo .zip Extraer archivo .zip Ext.


Extraer páginas vistas

Wikipedia
páginas Almacenamiento local Páginas vistas
vistas de Wikipedia durante una hora Datos requeridos
para una hora

Figura 4.3 Primera versión del flujo de trabajo de StockSense

El primer paso es descargar el archivo .zip para cada intervalo. La URL se construye con
varios componentes de fecha y hora:

https://dumps.wikimedia.org/other/pageviews/ {año}/{año}-{mes}/
pageviews-{año}{mes}{día}-{hora}0000.gz
Machine Translated by Google

64 CAPÍTULO 4 Plantillas de tareas usando el contexto de Airflow

Para cada intervalo, tendremos que insertar la fecha y la hora de ese intervalo específico en la
URL. En el capítulo 3, abordamos brevemente la programación y cómo usar la fecha de ejecución
en nuestro código para ejecutar un intervalo específico. Profundicemos un poco más en cómo
funciona. Hay muchas formas de descargar las páginas vistas; sin embargo, centrémonos en
BashOperator y PythonOperator. El método para insertar variables en tiempo de ejecución en esos
operadores se puede generalizar a todos los demás tipos de operadores.

4.2.1 Plantilla de argumentos del operador


Para comenzar, descarguemos las vistas de página de Wikipedia usando BashOperator, que toma
un argumento, bash_command, al que proporcionamos un comando Bash para ejecutar: todos los
componentes de la URL donde necesitamos insertar una variable al inicio del tiempo de ejecución
y terminar con llaves dobles. .

Listado 4.1 Descargando páginas vistas de Wikipedia con BashOperator

importar airflow.utils.dates desde


airflow importar DAG desde
airflow.operators.bash importar BashOperator

dag =
DAG( dag_id="chapter4_stocksense_bashoperator",
start_date=airflow.utils.dates.days_ago(3),
schedule_interval="@hourly",
)

get_data =
BashOperator( task_id="get_data",
bash_command=( "curl -o /tmp/
wikipageviews.gz " "https://dumps.wikimedia.org/ Las llaves dobles
indican una variable
other/pageviews/" "{{ejecución_fecha.año }}/"
insertada en tiempo de ejecución.
"{{ fecha_ejecución.año }}-" "{{ '{:02}'.formato(fecha_ejecución.mes) }}/"
"páginas vistas-{{ fecha_ejecución.año }}"
"{{ '{:02}' .format(fecha_ejecución.mes) }}"
"{{ '{:02}'.format(fecha_ejecución.día) }}-"
"{{ '{:02}'.format(fecha_ejecución.hora) }}0000. gz"), dag=dag, Se puede proporcionar
cualquier variable o
expresión de Python.

Como se mencionó brevemente en el capítulo 3, la fecha_ejecución es una de las variables que


está disponible "mágicamente" en el tiempo de ejecución de una tarea. Las llaves dobles denotan
una cadena con plantilla Jinja. Jinja es un motor de plantillas, que reemplaza variables y/o
expresiones en una cadena con plantilla en tiempo de ejecución. Las plantillas se utilizan cuando
usted, como programador, no sabe el valor de algo en el momento de escribir, pero sí sabe el valor
de algo en tiempo de ejecución. Un ejemplo es cuando tienes un formulario en el que puedes
insertar tu nombre, y el código imprime el nombre insertado (figura 4.4).
Machine Translated by Google

Contexto de tareas y plantillas Jinja sesenta y cinco

Inserte nombre aquí:

imprimir("Hola {{ nombre }}!") Figura 4.4 No todas las variables


se conocen por adelantado
cuando se escribe código, por
Las llaves dobles le dicen a Jinja que hay una ejemplo, cuando se usan
variable o expresión dentro para evaluar. elementos interactivos como formularios.

El valor del nombre no se conoce durante la programación porque el usuario ingresará su nombre en el
formulario en tiempo de ejecución. Lo que sí sabemos es que el valor insertado se asigna a una variable
llamada nombre, y luego podemos proporcionar una cadena con plantilla, "¡Hola {{ nombre }}!", para
representar e insertar el valor de nombre en tiempo de ejecución.
En Airflow, tiene una serie de variables disponibles en tiempo de ejecución desde el contexto de la
tarea. Una de estas variables es la fecha_ejecución. Airflow utiliza el Péndulo (https:// pendulum.eustace.io)
biblioteca para fechas y horas, y la fecha_ejecución es un objeto de fecha y hora de Pendulum. Es un
reemplazo directo para la fecha y hora nativa de Python, por lo que todos los métodos que se pueden
aplicar a Python también se pueden aplicar a Pendulum. Al igual que puede hacer datetime.now().year,
obtiene el mismo resultado con pendulum.now().year.

Listado 4.2 Comportamiento del péndulo igual a la fecha y hora nativa de Python

>>> desde fechahora importar fechahora >>> importar


péndulo >>> fechahora.ahora().año 2020

>>> péndulo.ahora().año
2020

La URL de páginas vistas de Wikipedia requiere meses, días y horas con ceros (p. ej., "07" para la hora
7). Dentro de la cadena con plantilla Jinja, por lo tanto, aplicamos ting de formato de cadena para el
relleno:

{{ '{:02}'.formato(fecha_ejecución.hora) }}

¿Qué argumentos tienen plantilla?


¡Es importante saber que no todos los argumentos de los operadores pueden ser plantillas! Cada
operador puede mantener una lista de permitidos de atributos que se pueden convertir en plantillas.
De forma predeterminada, no lo son, por lo que una cadena {{ nombre }} se interpretará literalmente
como {{ nombre }} y Jinja no la incluirá en la plantilla, a menos que se incluya en la lista de atributos
que se pueden incluir en la plantilla. Esta lista está establecida por el atributo template_fields en
cada operador. Puede verificar estos atributos en la documentación (https://airflow.apache.org/docs);
vaya al operador de su elección y vea el elemento template_fields .

Tenga en cuenta que los elementos en template_fields son nombres de atributos de clase. Por lo
general, los nombres de los argumentos proporcionados a __init__ coinciden con los nombres de
los atributos de clase, por lo que todo lo que se enumera en template_fields se asigna 1:1 a los
argumentos de __init__ . Sin embargo, técnicamente es posible que no lo hagan, y debe
documentarse qué atributo de clase asigna un argumento.
Machine Translated by Google

66 CAPÍTULO 4 Plantillas de tareas usando el contexto de Airflow

4.2.2 ¿Qué hay disponible para las plantillas?


Ahora que entendemos qué argumentos de un operador se pueden modelar, ¿qué variables tenemos a nuestra
disposición para modelar? Hemos visto el uso de la fecha_ejecución antes en varios ejemplos, pero hay más
variables disponibles. Con la ayuda de PythonOperator, podemos imprimir el contexto completo de la tarea e
inspeccionarlo.

Listado 4.3 Imprimiendo el contexto de la tarea

importar airflow.utils.dates desde


airflow importar DAG desde
airflow.operators.python importar PythonOperator

dag =
DAG( dag_id="chapter4_print_context",
start_date=airflow.utils.dates.days_ago(3),
schedule_interval="@daily",
)

def _print_context(**kwargs):
imprimir (kwargs)

print_context =
PythonOperator( task_id="print_context",
python_callable=_print_context, dag=dag,

Ejecutar esta tarea imprime un dictado de todas las variables disponibles en el contexto de la tarea.

Listado 4.4 Todas las variables de contexto para la fecha de ejecución dada

{
'dag': <DAG: print_context>, 'ds':
'2019-07-04', 'next_ds': '2019-07-04',
'next_ds_nodash': '20190704', 'prev_ds':
'2019-07 -03', 'prev_ds_nodash':
'20190703',

...
}

Todas las variables se capturan en **kwargs y se pasan a la función print() . Todas estas variables están disponibles
en tiempo de ejecución. La Tabla 4.1 proporciona una descripción de todas las variables de contexto de tareas
disponibles.
Machine Translated by Google

Contexto de tareas y plantillas Jinja 67

Tabla 4.1 Todas las variables de contexto de tareas

Llave Descripción Ejemplo

conferencia Proporciona acceso a la flujo de aire.configuración


configuración de Airflow .AirflowConfigParser
objeto

trozo de cuero El objeto DAG actual objeto DAG

dag_run El objeto DagRun actual objeto DagRun

ds fecha_ejecución formateada como “2019-01-01”


%Y-%m-%d

ds_nodash fecha_ejecución formateada como “20190101”


%Y%m%d

fecha de ejecución La fecha y hora de inicio del intervalo pendulum.datetime de la tarea


Objeto .DateTime

sumideros Abreviatura de task.inlets, una función para realizar un []


seguimiento de las fuentes de datos de entrada para el linaje
de datos

macros módulo airflow.macros módulo de macros

siguiente_ds fecha_ejecución del siguiente intervalo “2019-01-02”


(= final del intervalo actual) formateado como
%Y-%m-%d

siguiente_ds_nodash fecha_ejecución del siguiente intervalo (= final “20190102”


del intervalo actual) formateado como
%Y%m%d

próxima_ejecución_ La fecha y hora de inicio del siguiente intervalo péndulo.fechahora


fecha de la tarea (= final del intervalo actual) Objeto .DateTime

puntos de venta Abreviatura de task.outlets, una función para realizar []


un seguimiento de las fuentes de datos de salida para
el linaje de datos

parámetros Variables proporcionadas por el usuario a la tarea {}


contexto

prev_ds fecha_ejecución del intervalo anterior “2018-12-31”


formateado como %Y-%m-%d

prev_ds_nodash fecha_ejecución del intervalo anterior “20181231”


formateado como %Y%m%d

prev_ejecucion_ La fecha y hora de inicio del intervalo anterior péndulo.fechahora


fecha de la tarea Objeto .DateTime

prev_ejecucion_ Fecha y hora de inicio de la última ejecución péndulo.fechahora


fecha_éxito completada con éxito de la misma tarea (solo Objeto .DateTime
en el pasado)

anterior_fecha_de_inicio_ Fecha y hora en que se inició la última péndulo.fechahora


éxito ejecución satisfactoria de la misma tarea (solo Objeto .DateTime
en el pasado)
Machine Translated by Google

68 CAPÍTULO 4 Plantillas de tareas usando el contexto de Airflow

Tabla 4.1 Todas las variables de contexto de tareas (continuación)

Llave Descripción Ejemplo

ejecutar_id El run_id de DagRun (una clave típicamente “manual__2019-01-


compuesta por un prefijo + fecha y hora) 01T00:00:00+00:00”

tarea El operador actual objeto PythonOperator

instancia_tarea El objeto TaskInstance actual Objeto TaskInstance

instancia_tarea_ Un identificador único para el actual “dag_id__tarea_id__20190101”


cadena_clave Instancia de tarea ({dag_id}__
{task_id}__{ds_nodash})

templates_dict Variables proporcionadas por el usuario a la tarea {}


contexto

Modo de prueba Si Airflow se está ejecutando en modo de prueba Falso


(propiedad de configuración)

ti El objeto TaskInstance actual, igual que Objeto TaskInstance


task_instance

mañana_ds ds más un día “2019-01-02”

mañana_ds_nodash ds_nodash más un día “20190102”

t fecha_ejecución formateada de acuerdo “2019-01-


con el formato ISO8601 01T00:00:00+00:00”

ts_nodash fecha_ejecución formateada como “20190101T000000”


%Y%m%dT%H%M%S

ts_nodash_con_tz ts_nodash con información de zona horaria "20190101T000000+0000"

variable Objetos auxiliares para tratar con Airflow {}


Variables

ayer_ds ds menos un día “2018-12-31”

ayer_ds_nodash ds_nodash menos un día “20181231”

Impreso con PythonOperator ejecutado manualmente en un DAG con fecha de ejecución 2019-01-01T00:00:00, @intervalo diario.

4.2.3 Plantilla de PythonOperator


PythonOperator es una excepción a las plantillas que se muestran en la sección 4.2.1. Con
el BashOperator (y todos los demás operadores en Airflow), proporciona una cadena al
argumento bash_command (o cualquiera que sea el nombre del argumento en otros operadores),
que se modela automáticamente en tiempo de ejecución. El PythonOperator es una excepción a
este estándar, porque no toma argumentos que se puedan crear como plantilla con el contexto de tiempo de
ejecución, sino un argumento python_callable en el que el contexto de tiempo de ejecución
puede ser aplicado.
Inspeccionemos el código descargando las páginas vistas de Wikipedia como se muestra en el listado 4.1
con BashOperator, pero ahora implementado con PythonOperator. Funcionalmente,
esto da como resultado el mismo comportamiento.
Machine Translated by Google

Contexto de tareas y plantillas Jinja 69

Listado 4.5 Descargando páginas vistas de Wikipedia con PythonOperator

de la solicitud de importación de urllib

flujo de aire de importación


de importación de flujo de aire DAG
desde airflow.operators.python importar PythonOperator

dag = dag(
dag_id="stocksense",
start_date=airflow.utils.dates.days_ago(1),
intervalo_de_horario="@por hora",
)

def _get_data(ejecución_fecha):
año, mes, día, hora, *_ = fecha_ejecución.timetuple()
dirección URL = (

"https://dumps.wikimedia.org/other/pageviews/" el pitón
f"{año}/{año}-{mes:0>2}/" El operador toma un
f”páginas vistas-{año}{mes:0>2}{día:0>2}-{hora:0>2}0000.gz" función de Python,
) mientras que la
ruta_salida = "/tmp/wikipageviews.gz" BashOperator
request.urlretrieve(url, ruta_de_salida) toma un
comando Bash como un
cadena a ejecutar.
get_data = OperadorPython(
task_id="obtener_datos",
python_callable=_get_data, dag=dag,

Las funciones son ciudadanos de primera clase en Python, y proporcionamos un callable2 (una función es un
objeto invocable) al argumento python_callable de PythonOperator. En la ejecución, PythonOperator ejecuta
la llamada proporcionada, que podría ser cualquier función. Dado que es una función, y no una cadena
como con todos los demás operadores, el código dentro
la función no se puede crear una plantilla automáticamente. En cambio, las variables de contexto de la tarea
se pueden proporcionar y utilizar en la función dada, como se muestra en la figura 4.5.

provide_context en Airflow 1 y Airflow 2 PythonOperator


En Airflow 1, las variables de contexto de tareas deben proporcionarse explícitamente
estableciendo un argumento en PythonOperator provide_context=True, que pasa todas (!)
variables de contexto de tareas a su invocable:

OperadorPython(
task_id="contraseña_contexto",
python_callable=_contraseña_contexto,

2
En Python, cualquier objeto que implemente __call__() se considera invocable (por ejemplo, funciones/métodos).
Machine Translated by Google

70 CAPÍTULO 4 Plantillas de tareas usando el contexto de Airflow

(continuado)
provide_context=Verdadero,
dag=dag,
)

En Airflow 2, PythonOperator determina qué variables de contexto se deben pasar a su invocable al


inferirlas de los nombres de los argumentos invocables.
Por lo tanto, ya no es necesario configurar provide_context=True :

PythonOperator( task_id="pass_context",
python_callable=_pass_context, dag=dag,

Para mantener la compatibilidad con versiones anteriores, el argumento provide_context aún se admite
en Airflow 2; sin embargo, puede eliminarlo de manera segura cuando se ejecuta en Airflow 2.

Todas las variables para


esta
instancia de esta tarea

Todas las variables


pasadas a kwargs

Figura 4.5 Proporcionar contexto de tarea con PythonOperator

Python permite capturar argumentos de palabras clave en una función. Esto tiene varios casos de uso,
principalmente si no conoce los argumentos de palabras clave proporcionados por adelantado y para evitar
tener que escribir explícitamente todos los nombres de argumentos de palabras clave esperados.
Machine Translated by Google

Contexto de tareas y plantillas Jinja 71

Listado 4.6 Argumentos de palabras clave almacenados en kwargs

def _print_context(**kwargs): imprimir(kwargs) Los argumentos de palabras clave se pueden


capturar con dos asteriscos (**). Una convención
es nombrar kwargs al argumento de captura.

Para indicar a su yo futuro y a otros lectores de su código Airflow acerca de su


intenciones de capturar las variables de contexto de la tarea Airflow en los argumentos de palabras clave, una
una buena práctica es nombrar este argumento apropiadamente (por ejemplo, “contexto”).

Listado 4.7 Cambiar el nombre de kwargs a contexto para expresar la intención de almacenar el contexto de la tarea

def _imprimir_contexto(**contexto): Nombrar este contexto de


imprimir(contexto) argumento indica que esperamos
el contexto de la tarea de Airflow.

print_context = OperadorPython(
task_id="imprimir_contexto",
python_callable=_imprimir_contexto,
dag = dag,
)

La variable de contexto es un dict de todas las variables de contexto, lo que nos permite dar nuestra tarea
comportamiento diferente para el intervalo en el que se ejecuta, por ejemplo, para imprimir el inicio y el final
fecha y hora del intervalo actual:

Listado 4.8 Impresión de la fecha de inicio y finalización del intervalo

def _print_context(**contexto):
inicio = contexto["fecha_ejecución"] fin = Extraiga la fecha_ejecución del
contexto["fecha_próxima_ejecución"] contexto.
print(f"Inicio: {inicio}, fin: {fin}")

print_context = OperadorPython(
task_id="imprimir_contexto", python_callable=_imprimir_contexto, dag=dag
)

# Imprime, por ejemplo:


# Inicio: 2019-07-13T14:00:00+00:00, fin: 2019-07-13T15:00:00+00:00

Ahora que hemos visto algunos ejemplos básicos, vamos a diseccionar el PythonOperator descargando las
vistas de página de Wikipedia por hora como se ve en el listado 4.5 (figura 4.6).
La función _get_data llamada por PythonOperator toma un argumento:
**contexto. Como hemos visto antes, podríamos aceptar todos los argumentos de palabras clave en un solo
argumento denominado **kwargs (el doble asterisco indica todos los argumentos de palabras clave y
kwargs es el nombre de la variable real). Para indicar que esperamos variables de contexto de tareas,
podríamos cambiarle el nombre a **contexto. Sin embargo, hay otra forma en Python de aceptar argumentos
de palabras clave.
Machine Translated by Google

72 CAPÍTULO 4 Plantillas de tareas usando el contexto de Airflow

Variables de Extraiga los componentes de fecha y


contexto de tareas hora de la fecha_ejecución.

def _get_data(**contexto):
año, mes, día, hora, *_ = context["execution_date"].timetuple() url = ( "https://
dumps.wikimedia.org/other/pageviews/" f"{year}/{year}- {mes:0>2}/páginas vistas-{año}
{mes:0>2}{día:0>2}-{hora:0>2}0000.gz"

) ruta_salida = "/tmp/wikipageviews.gz"
solicitud.urlretrieve(url, ruta_salida)

Dar formato a URL Recuperar datos.


con componentes de fecha y hora.

Figura 4.6 PythonOperator toma una función en lugar de argumentos de cadena y, por lo tanto, no puede
tener una plantilla de Jinja. En esta función llamada, extraemos componentes de fecha y hora de la fecha_ejecución
para construir dinámicamente la URL.

Listado 4.9 Esperando explícitamente la variable fecha_ejecución

def _get_data(fecha_ejecución, **contexto): año, mes,


día, hora, *_ = fecha_ejecución.timetuple() # ...

Esto le dice a Python que esperamos recibir un argumento


llamadoejecución_fecha. No se capturará en el argumento de contexto.

Lo que sucede bajo el capó es que se llama a la función _get_data con todas las variables de contexto
como argumentos de palabras clave:

Listado 4.10 Todas las variables de contexto se pasan como argumentos de palabras clave

_get_data(conf=..., dag=..., dag_run=..., fecha_ejecución=..., ...)

Python comprobará entonces si se espera alguno de los argumentos dados en la firma de la función
(figura 4.7).
El primer argumento conf se verifica y no se encuentra en la firma (argumentos esperados) de
_get_data y, por lo tanto, se agrega a **context. Esto se repite para dag y

_get_data(conf=..., dag=..., dag_run=... , fecha_ejecución=... , ...)

conf en la firma? Si no, agregue a ** contexto.

def _get_data(fecha_ejecución, **contexto): año, mes,


día, hora, *_ = fecha_ejecución.timetuple()
# ...

Figura 4.7 Python determina si un argumento de palabra clave dado se pasa a un


argumento específico en la función, o al argumento ** si no se encontró ningún nombre coincidente.
Machine Translated by Google

Contexto de tareas y plantillas Jinja 73

dag_run ya que ambos argumentos no están en los argumentos esperados de la función.


El siguiente es la fecha_ejecución, que esperamos recibir, y por lo tanto su valor se pasa
al argumento fecha_ejecución en _get_data () (figura 4.8).

_get_data(conf=..., dag=..., dag_run=... , fecha_ejecución=... , ...)

¿fecha_ejecución en la firma?
En caso afirmativo, pase al argumento.

def _get_data(fecha_ejecución, **contexto): ple()año,


mes, día, hora, *_ = fecha_ejecución.horatu
# ...

La figura 4.8 _get_data espera un argumento llamadoejecución_fecha . No se establece ningún valor


predeterminado, por lo que fallará si no se proporciona.

El resultado final con este ejemplo es que una palabra clave con el nombre de fecha_ejecución
se pasa al argumento fecha_ejecución y todas las demás variables se pasan a **contexto ya
que no se esperan explícitamente en la firma de la función (figura 4.9).

_get_data(conf=..., dag=..., dag_run=... , fecha_ejecución=... , ...)

def _get_data(fecha_ejecución, **contexto): año, mes,


día, hora, *_ = fecha_ejecución.timetuple()
# ...

Figura 4.9 Se puede dar cualquier argumento con nombre a _get_data(). la fecha_ejecución
debe proporcionarse explícitamente porque aparece como un argumento, todos los demás
argumentos son capturados por **contexto.

Ahora, podemos usar directamente la variable de fecha_ejecución en lugar de tener que


extraerla de **contexto con contexto["fecha_ejecución"]. Además, su código será más
autoexplicativo y herramientas como linters y sugerencias de tipo se beneficiarán de la
definición de argumento explícito.

4.2.4 Proporcionar variables a PythonOperator


Ahora que hemos visto cómo funciona el contexto de la tarea en los operadores y cómo Python
trata los argumentos de las palabras clave, imagina que queremos descargar datos de más de
una fuente de datos. La función _get_data() podría duplicarse y modificarse ligeramente para
Machine Translated by Google

74 CAPÍTULO 4 Plantillas de tareas usando el contexto de Airflow

admitir una segunda fuente de datos. El PythonOperator, sin embargo, también admite el suministro
argumentos adicionales a la función invocable. Por ejemplo, supongamos que comenzamos haciendo el
output_path configurable, de forma que, dependiendo de la tarea, podamos configurar la salida
_path en lugar de tener que duplicar toda la función solo para cambiar la ruta de salida
(figura 4.10).

def _get_data(output_path, **contexto):


año, mes, día, hora, *_ = contexto["fecha_ejecución"].timetuple()
dirección URL = (

"https://dumps.wikimedia.org/other/pageviews/"
f"{año}/{año}-{mes:0>2}/páginas vistas-{año}{mes:0>2}{día:0>2}-{hora:0>2}0000.gz"
)
request.urlretrieve(url, ruta_de_salida)

output_path ahora configurable a través de un argumento

Figura 4.10 Output_path ahora se puede configurar a través de un argumento.

El valor de output_path se puede proporcionar de dos formas. La primera es a través de un argumento:


op_args.

Listado 4.11 Proporcionar variables definidas por el usuario al PythonOperator invocable

get_data = OperadorPython(
task_id="obtener_datos", Proporcione variables
adicionales al invocable con
python_callable=_get_data,
op_args=["/tmp/wikipageviews.gz"], dag=dag, op_args.

Al ejecutar el operador, cada valor en la lista proporcionada a op_args se pasa


a la función invocable (es decir, el mismo efecto que llamar a la función como tal directamente:
_get_data("/tmp/wikipageviews.gz")).
Dado que output_path en la figura 4.10 es el primer argumento en la función _get_data ,
el valor se establecerá en /tmp/ wikipageviews.gz cuando se ejecute (los llamamos
argumentos). Un segundo enfoque es usar el argumento op_kwargs , que se muestra en la siguiente
lista.

Listado 4.12 Proporcionar kwargs definidos por el usuario a llamadas

get_data = OperadorPython(
task_id="obtener_datos", Un dict dado a
python_callable=_get_data, op_kwargs se pasará
op_kwargs={"output_path": "/tmp/wikipageviews.gz"}, dag=dag, como argumentos de
palabra clave al
invocable.
)

También podría gustarte