— 4 min de lectura
Nos encanta Jenkins, tenemos que decirlo. Creo que hará más de diez años que, hartos de las pesadillas de los despliegues manuales, de los errores por omisión (cuando algo ha fallado porque te has olvidado de hacer alguno de los pasos del proceso), conocimos Jenkins y montamos nuestra primera instancia de Jenkins para resolver los despliegues.
Bueno, igual tendría que haber empezado por explicar qué es Jenkins, por si has caído por aquí sin saberlo, Jenkins es un software de gestión de automatizaciones. Básicamente, puedes definir trabajos (jobs), como una serie de tareas que se realizan una tras otra, y describir el resultado esperado. Se pueden enlazar unos trabajos con otros, de tal modo que solo si una cosa se ha hecho bien, se haga la siguiente, etc. Hay una serie de tareas que vienen de base, y un sinfín de plugins que le añaden otro mundo entero de posibilidades. Un tipo de trabajo común es hacer que jenkins descargue una versión concreta de un repositorio de código (una tag), lo empaquete como artefacto y suba a algún almacenamiento común privado, como Google Storage, por ejemplo. Otra tarea sería coger ese artefacto y subirlo a un entorno concreto, con los parámetros propios del entorno (bases de datos, etc). De este modo, podemos hacer integración continua (CI) y desarrollo continuo (CD) apoyándonos en Jenkins. Como decía al principio, nos encanta Jenkins y llevamos muchos años usándolo, pero últimamente hemos topado con una serie de inconvenientes que nos están haciendo migrar la mayoría de nuestros procesos de entrega continua y automatizada a Cloud Build.
Ejecuciones en paralelo
Con Jenkins pueden ejecutarse muchas tareas en paralelo, pero hay que tener cuidado si estas tareas utilizan variables de entorno o librerías comunes y las variables de entorno o las librerías cambian de una tarea a otra. Por poner un ejemplo, si la ejecución utiliza los comandos de gcloud para configurar Google Cloud, y otra tarea que se ejecuta en paralelo hace lo mismo, si ambas tareas utilizan proyectos distintos de gcloud, es probable que una de las dos falle. Si las dos las está ejecutando la misma persona, o si forman parte del mismo despliegue (aunque sería raro que dos tareas del mismo despliegue fueran en proyectos diferentes), basta con encadenarlas o fijarse, pero la gracia de los sistemas como Jenkins es poder automatizar algunas de estas tareas enlazándolas a cambios en el repositorio de código, por ejemplo, que cuando haya una nueva versión en el código se lancen las tareas solas.
Una forma de controlar este problema, con Jenkins, sería tener varias instancias del mismo, en un funcionamiento principal/secundario y que estas ejecuciones que no se llevan bien en paralelo, se ejecuten en instancias diferentes, pero esto ya es más complicado de gestionar e implica tener varias instancias de Jenkins. Para nuestro día a día y para una empresa pequeña, es poco eficiente.
Reducir la infraestructura
Otra de las motivaciones para migrar a Cloud Build es que ya tenemos toda nuestra infraestructura en Google Cloud. Montar Jenkins en Google Cloud es sencillo, pero tendríamos que crear una máquina para ello y gestionarlo nosotros de forma manual, mientras que Cloud Build está gestionado por Google directamente.
Cómo funciona Cloud Build
De forma general, Cloud Build funciona primero conectando repositorios de código de Github o Bitbucket (en BETA) y luego definiendo en un fichero .yaml del repositorio qué acciones tendrá que realizar Cloud Build cuando sea lanzado.
Cloud Build será lanzado en función de algún evento desencadenante (Trigger). El Trigger desencadena la ejecución de Cloud Build concreta, con su definición (normalmente en un fichero cloudbuild.yaml).
Qué eventos podemos usar para lanzar Cloud Build
Como Triggers o eventos que desencadenan una ejecución de Cloud Build, podemos utilizar subidas de código a una rama o etiqueta concreta del repositorio (push a branch o tag), un Pull Request, una petición manual, un mensaje de Pub/Sub (un sistema de gestión de colas de mensajes de Google: https://cloud.google.com/pubsub ), o un mensaje enviado por un webhook.
Qué ocurre cuando lanzamos Cloud Build
Como he explicado antes, en el fichero de configuración (normalmente cloudbuild.yaml) vamos a configurar cómo queremos que funcione Cloud Build.
Lo primero que podemos escoger, es el tipo de máquina en la que queremos realizar el trabajo. Al final, Google nos cobrará por el tiempo de compilación y nos cobrará más cuánto más potente sea la máquina: https://cloud.google.com/build/pricing?hl=es Aparte de la máquina, podemos definir artefactos que vayamos a utilizar y sus localizaciones (por ejemplo, si tenemos componentes paquetizados alojados en Google Cloud Storage).
Una vez definida la máquina, podemos listar todas las tareas a realizar, y para cada una hay que elegir qué builder queremos que se ejecute. Dicho de otro modo, hay distintos constructores/compiladores que podemos utilizar para realizar las tareas que hayamos definido, como por ejemplo Ubuntu, docker, git, gcloud, Maven, dotnet, etc. Cuál escojamos depente mucho de lo que vayamos a hacer, por ejemplo, si queremos interactuar con unas instancias que tenemos en Google Cloud, es posible que usemos gcloud, si queremos compilar unos assets de javascript usaremos npm, etc. En el mismo fichero podemos describir una serie de diferentes "pasos" que se pueden ejecutar con diferentes constructores, de tal modo que podemos bajarnos un código, preparar los assets, crear la imagen de docker correspondiente y subirla a un entorno, todo desde el mismo fichero de configuración.
Una vez hecho, solo nos queda visualizar las ejecuciones en el Dashboard y programar alertas, si es necesario, para enterarnos de cuando algo no esté yendo bien.
Cloud Build Dashboard