cargando...
cargando...
lección 6
Una alerta de Airflow a las 9:15 AM. Tu primer incidente de producción: diagnosticar un DAG que falló, comunicar al equipo y aplicar un fix rápido mientras planeas el definitivo.
⏱ 55 min
Jueves por la mañana. Te sientas con el café, abres Slack y lo primero que ves es un mensaje en #data-alerts con un emoji rojo que no habías visto antes. Tu corazón se acelera un poco:
1#data-alerts 9:15 AM2🚨 AIRFLOW ALERT — DAG: raw_orders_daily — FAILED3Task: extract_orders failed at 2024-09-19 03:00 UTC4Error: ConnectionRefusedError: [Errno 111] Connection refused5Retries exhausted (3/3)6Next retry: NONE (max retries reached)78Dag run: scheduled__2024-09-19T03:00:00+00:009Duration before failure: 2m 34s
Tu primera alerta de producción — el DAG de pedidos no se ejecutó anoche
Esto significa que el pipeline que extrae los pedidos diarios de la base de datos de producción y los deja en el data lake... no se ejecutó anoche. Los datos de pedidos de ayer NO están en el lake. Cualquier dashboard que dependa de datos frescos está mostrando información de anteayer. No es tu DAG ni tu responsabilidad directa, pero estás a punto de aprender cómo se gestiona un incidente de verdad.
1Elena Torres 9:31 AM2¿Alguien ha visto la alerta de raw_orders? Lleva fallando desde las 3AM.3Raúl, ¿es tuyo ese DAG?45Raúl Vega 9:34 AM6sí, es mío... pero estoy en medio de lo del inventario7la verdad no sé por qué falla, la semana pasada iba bien8puede ser que RDS esté saturado?910Elena Torres 9:36 AM11tú, ¿puedes echarle un ojo? No te pido que lo arregles, solo12que diagnostiques qué pasa y me digas. Si es algo de RDS escala a13infra. Si es un bug en el DAG, dime qué ves.1415Tú 9:37 AM16Dale, me pongo con ello ahora mismo.
Elena confía en ti para diagnosticar — no para arreglar (aún)
Nota lo que acaba de pasar: Elena te pidió DIAGNOSTICAR, no arreglar. Esto es importante. Como junior, tu trabajo en un incidente es investigar, documentar hallazgos y proponer opciones. No es aplicar un fix a ciegas que pueda empeorar las cosas. Esa distinción entre "investigar" y "actuar" es una de las cosas más importantes que aprenderás en tus primeros meses.
Consejo de senior: en un incidente de producción, la PEOR respuesta es entrar en pánico y hacer cambios sin entender el problema. He visto juniors que ante una alerta hacen un "fix rápido" que rompe 3 cosas más. La secuencia correcta es siempre: (1) entender qué hace el sistema, (2) leer los logs, (3) formular una hipótesis, (4) verificarla, (5) proponer solución. Nunca saltes al paso 5 directamente.
Antes de mirar los logs, necesitas entender QUÉ se supone que hace este DAG. Abres el repositorio en GitHub y buscas el archivo del DAG:
1# dags/raw_orders_daily.py (simplificado)2"""3DAG: raw_orders_daily4Schedule: Cada día a las 03:00 UTC5Qué hace:6 1. Conecta a PostgreSQL de producción (RDS)7 2. Extrae todos los pedidos del día anterior8 3. Los escribe como CSV en S3: raw/orders/{year}/{month}/{day}/9 4. Envía un mensaje a #data-alerts si falla10"""11from airflow import DAG12from airflow.operators.python import PythonOperator13from datetime import datetime, timedelta14import psycopg215import pandas as pd16import boto31718default_args = {19 "owner": "raul.vega",20 "retries": 3,21 "retry_delay": timedelta(minutes=5),22}2324def extract_orders(**context):25 """Extrae pedidos del día anterior de PostgreSQL."""26 execution_date = context["ds"]2728 # ⚠️ CONEXIÓN HARDCODEADA (esto lo verás después)29 conn = psycopg2.connect(30 host="freshmart-prod.xxxxx.eu-west-1.rds.amazonaws.com",31 port=5432,32 dbname="orders",33 user="data_readonly",34 password="FrM4rt_R0_2024!", # 🚩 CREDENCIAL EN EL CÓDIGO35 )3637 query = f"""38 SELECT * FROM orders39 WHERE order_date = '{execution_date}'40 """41 df = pd.read_sql(query, conn)42 conn.close()4344 # Escribir a S345 output_path = f"s3://freshmart-lake/raw/orders/{execution_date}/"46 df.to_csv(f"{output_path}orders.csv", index=False)4748with DAG(49 "raw_orders_daily",50 default_args=default_args,51 schedule_interval="0 3 * * *",52 start_date=datetime(2024, 1, 1),53 catchup=False,54) as dag:55 extract = PythonOperator(56 task_id="extract_orders",57 python_callable=extract_orders,58 )
El DAG de Raúl: funcional pero con MUCHOS problemas de buenas prácticas
Al leer el código ya ves varios problemas (que no son la causa del fallo, pero que anotas mentalmente): la credencial está hardcodeada en el código (gravísimo), la query es vulnerable a inyección SQL, no hay validación del DataFrame antes de escribir. Pero ahora toca centrarse en el ERROR, no en refactorizar.
NUNCA pongas credenciales en el código. Ni en un script "temporal", ni en un notebook "de prueba", ni en un commit que "después borro". Las credenciales hardcodeadas son la causa número 1 de brechas de seguridad en empresas. Usa variables de entorno, AWS Secrets Manager, Airflow Connections o cualquier sistema de secrets — pero NUNCA el código fuente.
Accedes a la interfaz web de Airflow (http://airflow.internal:8080 en la red de FreshMart) y navegas al DAG raw_orders_daily. Ves que el último run exitoso fue el martes 17 de septiembre. El del miércoles 18 falló. El del jueves 19 (hoy) también:
1# Logs del task extract_orders — run del 2024-09-1923[2024-09-19 03:00:05] INFO - Starting task extract_orders4[2024-09-19 03:00:05] INFO - Execution date: 2024-09-185[2024-09-19 03:00:06] INFO - Connecting to PostgreSQL at freshmart-prod.xxxxx.eu-west-1.rds.amazonaws.com:54326[2024-09-19 03:00:06] ERROR - Connection failed:7 psycopg2.OperationalError: could not connect to server: Connection refused8 Is the server running on host "freshmart-prod.xxxxx.eu-west-1.rds.amazonaws.com"9 and accepting TCP/IP connections on port 5432?10[2024-09-19 03:00:06] INFO - Retrying in 300 seconds (attempt 1/3)11[2024-09-19 03:05:06] ERROR - Connection failed (retry 2/3): Connection refused12[2024-09-19 03:10:06] ERROR - Connection failed (retry 3/3): Connection refused13[2024-09-19 03:10:06] ERROR - Max retries reached. Task FAILED.1415# Logs del run anterior (2024-09-18, que también falló)16[2024-09-18 03:00:06] ERROR - Connection failed:17 psycopg2.OperationalError: FATAL: password authentication failed18 for user "data_readonly"19[2024-09-18 03:05:06] ERROR - password authentication failed (retry 2/3)20[2024-09-18 03:10:06] ERROR - password authentication failed (retry 3/3)
Los logs revelan DOS errores distintos: primero auth failed, luego connection refused
Ahora piensas. Los logs te dicen dos cosas diferentes en dos días:
Hipótesis: alguien cambió las credenciales de la base de datos entre el martes y el miércoles. El miércoles el DAG intentó conectar con la contraseña vieja → "auth failed". Y el jueves, quizás RDS rechaza conexiones del usuario data_readonly porque está bloqueado por demasiados intentos fallidos → "connection refused".
Preguntas en el canal #infra (infraestructura) si alguien rotó credenciales esta semana:
1Tú 10:15 AM en #infra2Hola equipo, estoy investigando un fallo en el DAG raw_orders_daily.3Los logs muestran "password authentication failed" desde el miércoles.4¿Se rotaron credenciales de RDS esta semana?56Carlos (Infra) 10:22 AM7Sí, rotamos las credenciales de todos los usuarios de solo lectura8el martes por la noche. Es parte del protocolo trimestral de seguridad.9Mandamos un email al canal de engineering el lunes avisando.10Las nuevas credenciales están en AWS Secrets Manager:11secret: freshmart/prod/rds/data_readonly1213Carlos (Infra) 10:23 AM14¿El equipo de data no usa Secrets Manager? 🤔1516Tú 10:25 AM17Aparentemente este DAG tiene las credenciales hardcodeadas en el código...18Vamos a arreglarlo. Gracias Carlos!
Verificación: infra rotó credenciales y el DAG usa las antiguas hardcodeadas
Hipótesis confirmada. El problema es claro: las credenciales se rotaron el martes por la noche, el DAG tiene la contraseña vieja escrita directamente en el código, y como nadie del equipo de data leyó el email de aviso, nadie se enteró hasta que la alerta saltó el miércoles.
No reportas solo el problema — reportas el problema Y propones soluciones. Esto es lo que diferencia a un junior que investiga de un ingeniero que resuelve:
1Tú 11:22 AM2Elena, encontré el problema del DAG raw_orders.3No es que RDS esté caído — las credenciales se rotaron el martes4por la noche (protocolo trimestral de seguridad de infra) y nadie5avisó al equipo de data. El DAG usa las credenciales antiguas6hardcodeadas en el script.78Opciones:91. Fix rápido (5 min): actualizar la contraseña en el código con la10 nueva. Mismo problema la próxima vez que roten (en 3 meses).112. Fix definitivo (2-3h): migrar la conexión a Airflow Connections12 o leer de AWS Secrets Manager. Nunca más se rompe por rotación.1314¿Cómo prefieres que proceda?1516Elena Torres 11:25 AM17Buena investigación. 👏18Haz el fix rápido AHORA (que llevamos sin datos de pedidos desde19ayer) y abre un ticket para el fix definitivo. Lo hacemos la semana20que viene con calma.21Y por favor, manda un mensaje a #data-team diciendo qué pasó y que22ya está arreglado. El equipo necesita saber.
Reporta: problema + causa raíz + opciones con trade-offs. Esto es profesional.
Consejo de senior: cuando reportes un incidente a tu lead, SIEMPRE presenta opciones con trade-offs. No digas "se rompió, ¿qué hago?". Di "se rompió por X, puedo hacer A (rápido pero temporal) o B (más trabajo pero definitivo). ¿Cuál prefieres?". Esto demuestra que no solo investigaste sino que PENSASTE en la solución. Y le das a tu lead la información para tomar una decisión informada.
Elena dijo "fix rápido ahora". Así que actualizas la credencial en el código del DAG. Sí, es feo. Sí, es temporal. Pero la prioridad ahora es RESTAURAR EL SERVICIO — los datos llevan 2 días sin actualizarse.
1# Fix rápido: actualizar la credencial (temporal)2# Obtenemos la nueva credencial de AWS Secrets Manager3# (en la terminal, con aws cli)45# 🟦 Windows (PowerShell):6# aws secretsmanager get-secret-value --secret-id freshmart/prod/rds/data_readonly --query SecretString --output text78# 🍎 Mac (Terminal):9# aws secretsmanager get-secret-value --secret-id freshmart/prod/rds/data_readonly --query SecretString --output text1011# Luego actualizamos el DAG con la nueva password:12# (esto es el fix TEMPORAL — la semana que viene lo migramos a Connections)13conn = psycopg2.connect(14 host="freshmart-prod.xxxxx.eu-west-1.rds.amazonaws.com",15 port=5432,16 dbname="orders",17 user="data_readonly",18 password="NEW_FrM4rt_R0_Q4_2024!", # ← Nueva credencial (FIX TEMPORAL)19)
Fix temporal: actualizar la credencial. La semana que viene se hace bien.
Haces el cambio, pusheas a una branch, abres PR (que Elena aprueba inmediatamente sin review detallada porque es urgente), mergeas y triggeas un re-run del DAG manualmente desde la UI de Airflow. A los 3 minutos el DAG está en verde. Los datos de ayer y hoy empiezan a fluir.
Elena te pidió que comunicaras al equipo. No es opcional — es parte del protocolo de incidentes. El equipo necesita saber: (1) que hubo un problema, (2) cuál fue la causa, (3) que ya está resuelto, y (4) si hay impacto residual.
1Tú 12:05 PM en #data-team2🔧 Resumen del incidente raw_orders_daily:34**Qué pasó:** El DAG raw_orders_daily falló miércoles y jueves (2 días sin datos de pedidos nuevos).56**Causa raíz:** Infra rotó las credenciales de RDS el martes noche (protocolo trimestral). El DAG tenía las credenciales hardcodeadas en el código → usaba las antiguas.78**Fix aplicado:** Actualizada la credencial en el código. DAG re-ejecutado. Los datos de miércoles y jueves ya están en el lake.910**Impacto:** Dashboards de pedidos mostraron datos de hace 2 días entre miércoles 3AM y hoy 12PM. Ya están actualizados.1112**Próximos pasos:** Ticket DATA-162 abierto para migrar la conexión a Airflow Connections / Secrets Manager (fix definitivo, sprint que viene).1314cc @elena.torres @raul.vega1516Elena Torres 12:08 PM17Perfecto. Gracias por el resumen claro.1819Jorge Ruiz 12:10 PM20Gracias! Entonces los números de pedidos de ayer en Metabase ya son correctos?2122Tú 12:11 PM23Sí, el backfill ya corrió. Los datos de miércoles y jueves están en el lake.2425Raúl Vega 12:15 PM26uf perdona, ese DAG lleva meses con las creds hardcodeadas 😅27lo de Secrets Manager lo tenía en mi TODO... gracias por el ticket
Comunicar el incidente: claro, estructurado y con next steps
Observa la reacción del equipo: Elena agradece la claridad, Jorge verifica el impacto en SU trabajo (normal), y Raúl reconoce la deuda técnica. Nadie te culpa — investigaste, resolviste y comunicaste. Eso es lo que se espera.
Antes de seguir con tu trabajo del día, abres el ticket en Linear para que el fix definitivo no se pierda:
1╔══════════════════════════════════════════════════════════════╗2║ TICKET: DATA-162 ║3║ Título: Migrar credenciales de raw_orders_daily a ║4║ Airflow Connections / AWS Secrets Manager ║5╠══════════════════════════════════════════════════════════════╣6║ Contexto: ║7║ El DAG raw_orders_daily tiene credenciales de RDS ║8║ hardcodeadas en el código. Cuando infra rotó las creds ║9║ (protocolo trimestral), el DAG falló 2 días hasta que ║10║ se detectó y se actualizó manualmente. ║11║ ║12║ Solución propuesta: ║13║ 1. Crear una Airflow Connection tipo 'postgres' con las ║14║ credenciales correctas ║15║ 2. O: leer la credential de AWS Secrets Manager en runtime ║16║ 3. Eliminar la password del código fuente ║17║ 4. Verificar que no haya más DAGs con creds hardcodeadas ║18║ ║19║ Prioridad: Media (fix rápido ya aplicado) ║20║ Asignado a: Por definir (¿Raúl? ¿Yo?) ║21║ Estimación: 2-3 horas ║22╚══════════════════════════════════════════════════════════════╝
El ticket documenta el problema para que el fix definitivo no se olvide
Son las 12:30 y tu primer incidente de producción está resuelto. No era tu DAG, no era tu código, ni siquiera era un error de código — era un problema de configuración causado por falta de comunicación entre equipos. Y sin embargo, TÚ fuiste quien lo diagnosticó y resolvió. Eso es lo que hace un ingeniero de datos en la vida real.
Un error que veo en juniors: resolver el incidente y NO comunicarlo. Piensas "ya está arreglado, no hace falta molestar". ERROR. El equipo necesita saber que hubo un problema, por cuánto tiempo, y si les afecta. Jorge revisó sus números de ayer pensando que eran correctos — si tú no avisas, él puede tomar decisiones basadas en datos obsoletos sin saberlo.
Se te dan los logs de un DAG de Airflow que falló. Analízalos y responde: (1) ¿Cuál es el error exacto?, (2) ¿Es un problema de código, de red, o de credenciales?, (3) ¿Qué preguntarías para confirmar tu hipótesis? Escribe un script que parsee los logs y extraiga las líneas de ERROR.
Reescribe la función de conexión del DAG para que NO tenga credenciales hardcodeadas. Implementa dos opciones: (1) leer de variables de entorno, (2) leer de un diccionario que simula AWS Secrets Manager. La función debe intentar Secrets Manager primero y caer a variables de entorno como fallback.
Escribe el mensaje que enviarías al canal #data-team después de resolver el incidente. Debe incluir: (1) Qué pasó (en una frase), (2) Causa raíz, (3) Fix aplicado, (4) Impacto (qué estuvo mal y por cuánto tiempo), (5) Próximos pasos. Escríbelo como texto plano tipo Slack — no como código.
Regístrate para guardar tu progreso.
Reporta erratas, ayuda a otros o comparte tu opinión. Sé constructivo.
Inicia sesión para comentar y responder.
cargando comentarios...