Desde hace algo más de un par de años, llevo usando Runners de Gitlab para desplegar y realizar test de algunos proyectos que Desarrollo. Todo comenzó como una prueba con proyectos simples, como Music DL y socializer. La idea de los runners es utilizarlos para completar el Ciclo de CI/CD (esto es, integración y entrega continua). En términos generales, esto hace posible que cada que se cumpla una condición específica dentro del repositorio de código fuente (por ejemplo, crear una etiqueta para cada nueva versión, enviar un commit en un repositorio), un Runner se encargue de ejecutar una serie de comandos o scripts para ejecutar las unidades de prueba del proyecto, o incluso pueda compilar, empaquetar y distribuir directamente versiones listas para los usuarios. Esto es lo que hemos venido usando en TWBlue para las actualizaciones Snapshot desde hace un par de años, lo que al final fue un factor importante al decidir acortar el ciclo de desarrollo de las versiones de la aplicación. Personalmente, además, me gusta que un runner ejecute todas las tareas, ya que eso nos ayuda a nosotros a mantener nuestros equipos personales o de trabajo fuera de la distribución de TWBlue. Como se trata de proyectos Python, en ocasiones los módulos que nos ayudan a generar versiones distribuibles (como por ejemplo, cx_freeze) incluyen paquetes incorrectamente que no se usan en la aplicación. Esto no siempre puede controlarse apropiadamente y en ocasiones, una versión de TWBlue generada en el pasado incluía más o menos módulos extras e innecesarios según en el equipo de quién se habían construido las versiones distribuibles. Con un runner creando cada versión de nuestro software, nos aseguramos de tener una máquina que solo lleve instaladas las herramientas necesarias para lo que se va a construir, y también proporcionamos a todo quien quiera revisarlo, una lista de pasos reproducibles que necesitan ser ejecutados para crear una copia idéntica de nuestras versiones. Finalmente, el hecho que todo esto ocurra de manera completamente automática nos hace tener qué preocuparnos menos en prepararlo todo y ejecutar cada paso manualmente en nuestras máquinas: Un desarrollador solo tiene qué enviar un commit o crear la etiqueta adecuada, y el runner probará, construirá y subirá los ejecutables a nuestro sitio ftp en cuestión de minutos.
Cuando empecé con la idea de MCV Software, decidí intentar ir un paso más allá y comenzar con la creación de algo de infraestructura para las necesidades de los proyectos tanto públicos como privados. Una de las cosas que tenía en mente es el paso de Socializer de CX Freeze a Nuitka, lo que reduciría considerablemente el tiempo de respuesta de la aplicación y un poco el peso de los ficheros distribuibles, además de evitar incluir dll innecesarias que CX Freeze en ocasiones tiende a incluir. Desafortunadamente, no me fue posible reducir los tiempos de compilación del proyecto completo dentro de los runners Windows compartidos disponibles en Gitlab.com (el límite máximo por paso es de una hora y no hay manera de cambiarlo), así que decidí desplegar un runner propio basado en Windows para construir algunos de los proyectos que suelen tener nuevas versiones regularmente.
Estrategia
Hay muchas formas de crear runners para ejecutar trabajos en Gitlab. Mi idea perfecta sería desplegar runners automáticamente. Dichos runners serían creados durante el tiempo que fueran necesarios. Incluirían una instalación mínima de Windows y Chocolatey instalado, y el desarrollador, mediante el fichero .gitlab-ci.yml, se encargaría de instalar todo lo que el proyecto requiera, construir lo que haya qué construir, subir los artefactos y finalmente una vez que el proyecto termine el ciclo de integración y entrega continua, destruir las instancias de máquina virtual creadas para así regresar a un estado inicial.
Desafortunadamente, por ahora no me resulta posible realizar esta configuración sin gastar una cantidad considerable de dinero. Mantener un runner autoescalable suele ser un proceso algo costoso, y que se lleva a cabo dentro de las nubes de Azure, Google Cloud Platform o Amazon Web Services. Todos estos servicios son complejos y en este momento he optado por una solución mucho más sencilla. He adquirido un VPS con Windows Server 2019 a un precio bastante razonable al que le he instalado los paquetes indispensables para poder trabajar:
- Git for Windows.
- PowerShell.
- Python 3.7.9 (he instalado ambas versiones, para 32 y 64 bits, ya que hay algunos proyectos que se publican con soporte para ambos. Las rutas son c:\python37-32 y C:\python37).
- Gitlab Runner para Windows.
- Chocolatey, y gracias a este gestor de paquetes, también fue sencillo instalar las siguientes aplicaciones:
Además, lógicamente, incluye ya las últimas actualizaciones del sistema operativo, para mantener todo siempre a sus últimas versiones.
Esta estrategia de despliegue tiene una Ventaja y a su vez una desventaja. La ventaja es que el Runner siempre está listo y creado, esperando para ejecutar órdenes y entregar o probar el proyecto que se le solicite. No es necesario esperar a la creación de ningún recurso. Sin embargo, la posible desventaja es que el sistema no inicia limpio del todo. Como es el mismo sistema operativo utilizado para todos los proyectos, pudiera causar algún problema el hecho de reutilizar el runner. Aunque, luego de evaluar las cosas concienzudamente, y realizar algunas pruebas internas, he considerado que utilizar un entorno virtual en los paquetes Python es aislamiento suficiente para no mezclar paquetes entre diferentes proyectos. En cada proyecto construido en la infraestructura de MCV Software, se crea un entorno virtual de Python al inicio de las tareas de construcción de la versión distribuible, y el mismo entorno se usa para instalar y generar todo lo que se requiere por el proyecto. Finalmente, al terminar el proyecto, el entorno virtual es destruido, eliminando todos los paquetes y dejando el Python del sistema igual de limpio que cuando fue instalado por primera vez.
Otra cosa que personalmente me gustaría explorar son los Runners en Docker. Para Windows no, ya que el soporte con este tipo de sistemas en Docker en Gitlab no se encuentra muy desarrollado, pero creo que sería buena idea desplegar un segundo runner que se ejecute en un sistema Linux y pueda utilizar contenedores Docker. Esto haría más fácil mucho del trabajo de pruebas, subir los artefactos hacia servidores ftp y demás, donde solamente es necesario el entorno Python y Windows puede ser dejado algo de lado. Pero todo esto será tal vez para una próxima ocasión.