Intenté programar mi canalización ETL. Esto es lo que no esperaba.

mencioné que la programación es el próximo muro hacia el que caminaré.

Así que supongo que aquí estoy, caminando hacia allí.

Pero antes de entrar en lo que pasó, permítanme dar algo de contexto para cualquiera que se encuentre con esto por primera vez.

Soy un analista de sistemas que decidió hacer la transición a la ingeniería de datos. En lugar de simplemente tomar cursos y obtener certificados, decidí aprender construyendo y escribiendo sobre ello públicamente. Cada artículo de esta serie documenta algo que realmente construí, las decisiones que tomé, las cosas que fallaron y lo que aprendí de ello.

El primer artículo fue mi hoja de ruta de autoaprendizaje de 12 meses, donde expuse el plan sobre cómo iba a abordar esta transición. El segundo fue construir mi primer canal ETL desde cero usando la API de GitHub, como un completo principiante. En el tercero, tomé ese mismo proceso y lo preparé más para producción agregando almacenamiento SQLite, manejo de idempotencia y persistencia de Google Drive, todo dentro de Google Colab.

Este artículo es el cuarto. Y continúa exactamente donde terminó el último.

Esperaba pasar la mayor parte de mi tiempo eligiendo una herramienta de programación y configurándola. Lo que no esperaba era que antes de poder siquiera pensar en la programación, tuviera que lidiar con algo más fundamental. Mi canalización no se pudo ejecutar fuera de Google Colab. Y hasta que eso cambiara, ningún planificador del mundo podría ayudarme.

Esta es la historia de lo que realmente sucedió.

El primer muro: mi tubería vivía en Colab

Antes incluso de empezar a programar, quería entender qué se necesitaría realmente para ejecutar mi canalización automáticamente. Entonces miré mi código correctamente por primera vez con esa pregunta en mente.

Así es como se veía la sección de carga:

conexión = sqlite3.connect(‘/content/drive/MyDrive/github_repos.db’)

Esa ruta, /content/drive/MyDrive/, solo existe dentro de Google Colab. Es la ruta montada de Google Drive que Colab le proporciona cuando conecta su Drive a una computadora portátil. Fuera de Colab, ese camino no existe. Si algún programador intentara ejecutar este script, fallaría allí mismo.

Lo interesante es que mi código no tenía importaciones de google.colab. No hay bibliotecas específicas de Colab. Sólo una ruta codificada que había estado escribiendo sin pensar realmente en ello. Ese camino era la dependencia, no el código.

Esto fue lo primero que no esperaba. Pensé que el desafío sería aprender una herramienta de programación. En cambio, la primera lección fue que mi entorno era parte de mi canalización y no me había dado cuenta.

La solución fue sencilla. En lugar de codificar la ruta de Colab, hice que la ruta de la base de datos fuera configurable a través de una variable de entorno:

importar sistema operativo DB_PATH = os.environ.get(‘DB_PATH’, ‘github_repos.db’) conexión = sqlite3.connect(DB_PATH)

Ahora el script utiliza cualquier ruta establecida en el entorno. Si no se configura nada, se vuelve a crear un archivo github_repos.db local en la misma carpeta. Un cambio y el oleoducto ya no estaría vinculado a Colab.

Ejecutándolo fuera de Colab por primera vez

Antes de configurar cualquier programador, quería confirmar que el script realmente funcionaba por sí solo. Así que lo guardé como pipeline.py y creé un require.txt con las dos bibliotecas que necesita:

solicita pandas

Y lo ejecuté desde mi terminal:

Imprimió: Tubería completa. Se manejan duplicados.

Y apareció un archivo llamado github_repos.db en mi carpeta. La misma canalización que había estado ejecutando en Colab ahora se ejecutaba como un script de Python simple, en cualquier lugar.

Eso me pareció algo más importante de lo que esperaba. No porque el cambio fuera complejo, no lo fue. Sino porque me di cuenta de que había estado pensando en mi canal como un cuaderno, cuando lo que en realidad tenía era un guión que vivía dentro de uno.

Elegir una herramienta de programación

En este punto tenía un guión independiente. Ahora necesitaba algo para ejecutarlo según un cronograma.

Miré algunas opciones. APScheduler le permite definir horarios dentro de su código Python, que funciona mientras se ejecuta una sesión pero se detiene en el momento en que cierra su terminal. Eso no es realmente una programación, es solo un bucle. Airflow es el estándar de la industria para orquestar canalizaciones, pero requiere ejecutar un servidor, una base de datos de metadatos y una interfaz web. Esa es mucha infraestructura para donde estoy ahora.

GitHub Actions se encontraba en el medio. Es gratis, se ejecuta en los servidores de GitHub, el cronograma se define en código y no requiere que mantenga ninguna infraestructura. La desventaja es que está diseñado para flujos de trabajo de CI/CD, no para la orquestación de canalizaciones, por lo que tiene límites en cuanto a dependencias y monitoreo complejos. Pero para un oleoducto en mi etapa, es una opción práctica.

También quiero ser honesto: herramientas como Airflow existen por una razón. Cuando una canalización crece, cuando hay dependencias entre tareas, cuando necesita visibilidad de lo que se ejecutó y lo que falló, necesita una orquestación adecuada. GitHub Actions no es eso. Pero es un buen primer paso, y comprender por qué es limitado es parte del aprendizaje de lo que realmente están resolviendo esas herramientas más serias.

Configurar acciones de GitHub

GitHub Actions funciona a través de archivos de flujo de trabajo, que son archivos YAML que colocas en una carpeta específica de tu repositorio. La estructura de carpetas se ve así:

github-etl/ ├── .github/ │ └── flujos de trabajo/ │ └── Schedule.yml ├── pipeline.py └── requisitos.txt

Aquí está el archivo de flujo de trabajo completo que creé:

nombre: Ejecute ETL Pipeline en: programación: – cron: ‘0 9 * * *’ flujo de trabajo_dispatch: trabajos: ejecutar-pipeline: ejecuciones en: ubuntu-últimos pasos: – nombre: Usos del código de pago: acciones/checkout@v4 – nombre: Configurar usos de Python: acciones/setup-python@v5 con: versión-python: ‘3.11’ – nombre: Instalar dependencias ejecutar: pip install -r requisitos.txt – nombre: Ejecutar canalización ejecutar: python pipeline.py

Permítanme explicar lo que hace cada parte.

cron: ‘0 9 * * *’ es el programa real. Cron es un formato de programación de trabajos basado en el tiempo que existe en los sistemas Unix desde hace décadas. Los cinco valores representan minuto, hora, día del mes, mes y día de la semana. Entonces 0 9 * * * significa: en el minuto 0 de la hora 9, todos los días, todos los meses, todos los días de la semana. En otras palabras, a las 9 a. m. UTC todos los días. flujo de trabajo_dispatch agrega un activador manual. Esto significa que también puede ejecutar el flujo de trabajo haciendo clic en un botón en GitHub, sin esperar el tiempo programado. Esto es útil para realizar pruebas. run-on: ubuntu-latest le dice a GitHub que active una nueva máquina Linux para cada ejecución. Cada vez que se activa el flujo de trabajo, GitHub crea un entorno limpio, instala sus dependencias, ejecuta su script y luego cierra todo. No hay ninguna máquina persistente en algún lugar ejecutando su código. Es efímero.

Los pasos son sencillos. Checkout extrae su código del repositorio al corredor. El programa de instalación Python instala la versión que especifique. Instalar dependencias ejecuta pip install -r requisitos.txt. Y luego Ejecutar canalización ejecuta su script.

¿Qué pasó cuando lo ejecuté?

Después de enviar el archivo de flujo de trabajo a GitHub, fui a la pestaña Acciones en mi repositorio y lo activé manualmente usando el botón flow_dispatch.

Corrió. Veintisiete segundos de principio a fin. La canalización extrajo datos de la API de GitHub, los transformó y los cargó en SQLite, todo en un servidor de GitHub, sin que yo hiciera nada después de hacer clic en el botón.

Recibí una advertencia en la primera ejecución:

Las acciones de Node.js 20 están en desuso…

Esto se debió a que había usado versiones anteriores de las acciones de pago y configuración de Python. La solución fue actualizar acciones/checkout@v3 a acciones/checkout@v4 y acciones/setup-python@v4 a acciones/setup-python@v5. Después de eso, el flujo de trabajo funcionó sin problemas.

Lo que realmente aprendí

Al entrar en esto, pensé que la programación consistía en elegir la herramienta adecuada. Lo que descubrí fue que la programación me obligó a pensar en algo en lo que no había pensado detenidamente antes: la portabilidad.

Una canalización que solo se ejecuta en un entorno específico no es realmente una canalización. Es un guión ligado a una plataforma. Hacerlo programable significaba primero hacerlo portátil, y hacerlo portátil significaba comprender de qué dependía realmente.

La ruta codificada era algo pequeño. Pero captarlo cambió mi forma de pensar acerca de escribir código de canalización en el futuro. Cada vez que escribo una ruta, una credencial o un valor específico del entorno, ahora pregunto si eso existirá fuera del contexto en el que estoy construyendo.

La otra cosa que aprendí es que la programación y la orquestación son problemas diferentes. GitHub Actions maneja bien la programación. No maneja cosas como reintentar ejecuciones fallidas con retroceso, alertar cuando algo sale mal, visualizar dependencias de canalizaciones o administrar múltiples canalizaciones que dependen unas de otras. Esos son problemas de orquestación y para eso están diseñadas herramientas como Airflow.

Todavía no he llegado. Pero ahora entiendo por qué esas herramientas existen como antes.

¿Qué sigue?

El oleoducto ahora funciona todos los días a las 9 a. m. UTC. Se están recopilando datos. Y estoy empezando a notar algo: cuando tienes un pipeline funcionando a diario, empiezas a preocuparte por los datos que produce de una manera diferente.

¿Están todos los registros limpios? ¿Hay repositorios que faltan campos? ¿La bandera viral es realmente significativa o la definí de una manera que hace que casi todo sea “No”?

Esas son preguntas sobre la calidad de los datos. Y son la siguiente pared hacia la que camino.

Esto es parte de mi serie continua que documenta mi transición de analista de sistemas a ingeniero de datos. Si me has seguido, gracias. Si este es su primer artículo de la serie, los anteriores están vinculados a continuación.

De analista de datos a ingeniero de datos: mi hoja de ruta de autoaprendizaje de 12 meses

Construí mi primer canal ETL como un completo principiante. He aquí cómo.

Pensé que la ingeniería de datos era solo escribir guiones. Me equivoqué.

Conéctese conmigo en LinkedIn, YouTube y Twitter.