Ir al contenido
  1. Posts/

Publicando Colecciones de Ansible en Galaxy con GitLab

·2492 palabras·12 mins· loading · loading ·
Ansible DevOps GitLab ansible gitlab devops automatizacion galaxy
Autor
Enmanuel Moreira
Ingeniero DevOps de día y aprendiz de Barman en mis tiempos libres, con experiencia en Kubernetes, Cloud, y DevOps. También disfruta de hacer stream de juegos, hablar de CI/CD, desplegar en producción un viernes con Terraform y automatizar tareas aburridas con Ansible.

Ansible es una de mis herramientas favoritas para configurar servidores en masa, y a diferencia de Chef o Puppet, Ansible no necesita un agente para poder ejecutar las tareas, solo se necesita tener ssh instalado en los servidores remotos para poder hacer su trabajo.

Una de las maneras más fáciles para poder compartir y descargar Roles y Collections es la plataforma Ansible Galaxy, sin embargo, por alguna razón solo nos permite autenticarnos a través de GitHub. En este articulo te voy a mostrar como publicar tus collections en GitLab de manera automática con GitLab CI.

PROMO DigitalOcean
#

Antes de comenzar, quería contarles que hay una promoción en DigitalOcean donde te dan un crédito de USD 200.00 durante 60 días para que puedas probar los servicios que este Proveedor Cloud ofrece. Lo único que tienes que hacer es suscribirte a DigitalOcean con el siguiente botón:

DigitalOcean Referral Badge

O a través del siguiente enlace: https://bit.ly/digitalocean-itsm

Preparando el Terreno
#

En este artículo estoy asumiendo que sabes Ansible y entiendes el concepto de Integración Continua. De todas maneras voy a ser lo más específico posible para que el usuario mas novel pueda hacerlo.

Asumo también que tienes cuentas creadas tanto en GitHub como en GitLab para poder avanzar.

Instalando Ansible en Nuestro Equipo
#

Dependiendo de tu distribución, la instalación será un poco diferente. En este caso vamos a instalar Ansible directamente desde pip:

sudo pip3 install ansible

Creando la Cuenta en Ansible Galaxy
#

Podemos crear nuestra cuenta en Ansible Galaxy yendo al sitio: https://galaxy.ansible.com, vamos a la esquina superior derecha en la opción Login:

Login
Ansible Galaxy

Iniciamos sesión con nuestra cuenta GitHub:

GitHub
Inicio de Sesión

GitHub
Ingresamos credenciales

Una vez iniciamos sesión, nos redirigirá de nuevo a la página de Ansible Galaxy, vamos de nuevo a la esquina superior derecha y hacemos click en nuestro nombre de usuario, seguido en Preferences:

Galaxy
Preferencias

Y vamos a ver nuestra API Key, vamos a copiarla y reservarla para configurarla en GitLab.

Preferencias
Copiamos API Key

Recuerda que la API Key es una clave privada por lo que debes tratarla como si fuera tu clave personal.

Creando la Colección
#

  • Creamos una colección de Ansible en nuestro equipo:
ansible-galaxy collection init usuario.micoleccion

Lo cual creará una colección con la siguiente estructura:

.
├── docs
├── galaxy.yml
├── plugins
│   └── README.md
├── README.md
└── roles

docs: en este directorio almacenamos la documentación de la colección.
plugins: en este directorio se almacenan plugins de Ansible.
roles: en este directorio almacenamos los roles que se ejecutarán en la colección.
galaxy.yml: es el manifiesto de la colección. El cual tiene la siguiente estructura:

### REQUERIDO
# Es el namespace de la colección. Puede ser el nombre de la empresa, persona u organización (normalmente colocamos el nombre de usuario del repositorio donde se almacenará la colección
# Solo puede contener letras en minusculas y guines bajos (_) El Namespace no puede comenzar con
# guiones bajos o números y no puede tener guiones bajos consecutivos
namespace: enmanuelmoreira

# El nombre de la colección. Se debe cumplir la misma restricción de caracteres como la del Namespace
name: micoleccion

# La versión de de la colección. Debe ser compatibile con versionado semántico
# https://es.wikipedia.org/wiki/Versionado_de_software
version: 1.0.0

# Archivo README.md. El path debe ser relativo al directorio raíz de la colección
readme: README.md

# Lista de Autores. Puede ser solo el nombre o con el formato 'Nombre Completo <email> (url)
# @nicks:irc/im.site#channel'
authors:
- your name <example@domain.com>


### OPCIONAL pero altamente recomendado
# Una breve descripción de la colección
description: descripción de la colección

# Lista de Licencias por el contenido de la colección. Ansible Galaxy actualmente acepta solo
# Licencias L(SPDX,https://spdx.org/licenses/) . 
license:
- GPL-2.0-or-later

# La ruta de la Licencia de la colección. El path debe ser relativo al directorio raíz de la colección. Esta clave es 
# mutuamente excluyente con la clave 'license'
license_file: ''

# Una lista de etiquetas para que asocies el contenido de la colección para facilitar la búsqueda / indexación. Una etiqueta debe tener la misma restricción de caracteres que 
# 'namespace' y 'name'
tags: []

# Las Collecciones como esta necesitan ser instaladas para que se puedan utilizar. La forma en que se representa es 
# 'namespace.name'. 
dependencies: {}

# La URL del repositorio original
repository: http://example.com/repository

# La URL con la documentación de la colección
documentation: http://docs.example.com

# La URL a la pagina principal de la colección o proyecto
homepage: http://example.com

# La URL del issue tracker de la colección
issues: http://example.com/issue/tracker

build_ignore: []

Ajustamos el archivo galaxy.yml a nuestras necesidades:

---
namespace: enmanuelmoreira
name: micoleccion
version: "1.0.0"
readme: README.md

authors:
  - enmanuelmoreira

description: Ejemplo de despliegue de una colección en Ansible Galaxy con GitLab.

license:
  - MIT

tags:
  - software
  - download

dependencies:
  community.general: ">=3.0.0"

repository: https://gitlab.com/enmanuelmoreira/ansible-collection-micoleccion
documentation: https://gitlab.com/enmanuelmoreira/ansible-collection-micoleccion
homepage: https://www.enmanuelmoreira.com
issues: https://gitlab.com/enmanuelmoreira/ansible-collection-micoleccion/-/issues

build_ignore: []

Creamos también una carpeta en la raiz de la colección llamada meta con el archivo runtime.yml:

mkdir meta
touch meta/runtime.yml

Y pegamos el siguiente contenido:

---
requires_ansible: '>=2.9.10'

Creando los Roles
#

Entramos en la carpeta roles y vamos a crear un par de roles:

cd roles
ansible-galaxy role init software
ansible-galaxy role init download

Tendremos entonces siguiente estructura:

.
├── download
│   ├── defaults
│   │   └── main.yml
│   ├── files
│   ├── handlers
│   │   └── main.yml
│   ├── meta
│   │   └── main.yml
│   ├── README.md
│   ├── tasks
│   │   └── main.yml
│   ├── templates
│   ├── tests
│   │   ├── inventory
│   │   └── test.yml
│   └── vars
│       └── main.yml
└── software
    ├── defaults
    │   └── main.yml
    ├── files
    ├── handlers
    │   └── main.yml
    ├── meta
    │   └── main.yml
    ├── README.md
    ├── tasks
    │   └── main.yml
    ├── templates
    ├── tests
    │   ├── inventory
    │   └── test.yml
    └── vars
        └── main.yml

Para ahorrarnos tiempo, vamos a editar el archivo tasks/main.yml en cada rol, con una tarea sencilla:

En el Rol download vamos realizar una tarea que descargue una imagen de Debian en el directorio /tmp:

- name: Descargando Imagen minima de debian
  get_url:
    url: https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-11.2.0-amd64-netinst.iso
    dest: /tmp
    mode: 0755

En el rol software vamos a instalar MariaDB y Apache:

---
- name: Instalando MariaDB y apache.
  pkg:
    name:
      - httpd
      - mariadb-server
    state: latest

Subiendo Nuestra Colección a GitLab
#

En esta etapa vamos a crear el proyecto en GitLab y luego subir los cambios realizados.

Creando Proyecto
#

Vamos a la página de GitLab (donde ya devemos haber iniciando sesión) Esquina superior derecha, Nuevo Proyecto:

Nuevo Proyecto
Nuevo Proyecto

Creamos un proyecto en blanco:

Proyecto en blanco
Crear un proyecto en blanco

Le damos un nombre, una descripción y destildamos las opciones Crear README y SAST.

Crear proyecto
Crear Proyecto

Una vez creado, nos redireccionará a la página principal del proyecto, Vamos a la parte Izquierda en Configuración -> CI / CD

Configuracion
Configuración -> CI / CD

Bajamos un poco y nos encontraremos con la opción variables. Damos click a Expandir y click en la opción Añadir Variable, aquí vamos a añadir la API Key que sacamos de Ansible Galaxy para que GitLab construya la colección y se autentique con Galaxy para publicarla.

Configuracion
Variables

En Clave colocamos el nombre con que queremos guardar la variable, en este caso GALAXY_TOKEN.
En Valor colocamos la API Key de Ansible Galaxy.

Tildamos ambas opciones de: Proteger Variable y Enmascarar Variable. La primera nos va a permitir que esa variable solo pueda ser accedida por este repositorio y no por otros. y al segunda opción va a evitar que la API Key se muestre por el output del Pipeline, así nos cercioramos de no exponer la API Key a otros ojos.

Configuracion
Añadir Variable

Listo, quedaría asi:

Configuracion

Creando el Pipeline de Gitlab
#

Ahora viene la parte interesante. Vamos a crear el pipeline con las instrucciones que debe seguir GitLab CI para que nos construya el paquete de la colección y lo publique en Ansible Galaxy todo de manera automática.

Creamos el archivo .gitlab-ci.yml en la raíz de la colección. Veremos uno por uno las instrucciones al detalle:

variables:
  GALAXY_NAME: micoleccion

image: enmanuelmoreira/docker-ansible-alpine:latest

stages:
  - build
  - publish

build:
  stage: build
  rules:
    - if: $CI_COMMIT_TAG
  script:
    - ansible-galaxy collection build --force
  artifacts:
    paths:
      - $CI_PROJECT_NAMESPACE-$GALAXY_NAME-$CI_COMMIT_TAG.tar.gz
    expire_in: 30 days

publish:
  stage: publish
  rules:
    - if: $CI_COMMIT_TAG
  dependencies:
    - build
  script:
    - ansible-galaxy collection publish ./$CI_PROJECT_NAMESPACE-$GALAXY_NAME-$CI_COMMIT_TAG.tar.gz --token $GALAXY_TOKEN
variables:
  GALAXY_NAME: micoleccion

La clave variables nos permite predefinir variables que se van a ejecutar a lo largo del pipeline. De verdad que nos ahorra un montón de trabajo si es que una variable es utilizada por varios stages.

En este caso particular, para efectos prácticos, estamos definiendo una variable GALAXY_NAME con el nombre de nuestra colección que nos va a servir en la etapa de build para nombrar el paquete que contiene la colección. Podríamos usar una variable definida de GitLab llamada CI_PROJECT_NAME y a menos que el repositorio se llame tal cual como Galaxy pide que sean los nombres de las colecciones, la puedes utilizar. En caso contrario, nos toca definir esta variable que cumple con lo que Galaxy pide en términos de nombrar los archivos.

ìmage: enmanuelmoreira/docker-ansible-alpine:latest

Es una imagen Docker que contiene Ansible instalado. Estoy utilizando una que ya construí y que puede ver el código fuente en https://gitlab.com/enmanuelmoreira/docker-ansible-alpine

stages:
  - build
  - publish

Los stages son las etapas en la que el pipeline ejecutará los pasos contenidos. En este caso tenemos dos etapas: build y publish.

En la etapa build vamos a empaquetar la colección utilizando una serie de instrucciones:

build:
  stage: build
  rules:
    - if: $CI_COMMIT_TAG
  script:
    - ansible-galaxy collection build --force
  artifacts:
    paths:
      - $CI_PROJECT_NAMESPACE-$GALAXY_NAME-$CI_COMMIT_TAG.tar.gz
    expire_in: 30 days

rules: se ejecutará la etapa build solo si cumple con las reglas establecidas. En este caso vamos a restringirla al tag del commit (en un momento explico el porqué)
script: aquí vamos a ejecutar los comandos para construir el paquete que se enviará a Galaxy.
artifacts: es el archivo resultante de la construcción. Para que el paquete cumpla con los requerimientos de nombres de Galaxy, vamos a utilizar una variable que nos provee GitLab: $CI_PROJECT_NAMESPACE que nos proveerá el nombre de usuario del repo (el dueño del repo), seguidor de un guión y luego la variable que definimos previamente en el encabezado del manifiesto $GALAXY_NAME, seguido de otro guión con la variable del commit tag $CI_COMMIT_TAG y al extensión del archivo .tag.gz

Este “artefacto” o archivo resultante se va a almacenar en GitLab por 30 días con la clave expire_in

En la etapa publish vamos a autenticarnos con Ansible Galaxy y publicar nuestra colección empaquetada:

publish:
  stage: publish
  rules:
    - if: $CI_COMMIT_TAG
  dependencies:
    - build
  script:
    - ansible-galaxy collection publish ./$CI_PROJECT_NAMESPACE-$GALAXY_NAME-$CI_COMMIT_TAG.tar.gz --token $GALAXY_TOKEN

Bueno, aquí tenemos algo nuevo dependencies. Esta clave le dice a GitLab que esta etapa solo se va a ejecutar si la etapa build tuvo éxito.

En script podemos ver el comando con el cual vamos a publicar nuestra colección, con el nombre del artefacto tal cual como lo hicimos en la etapa de build y el argumento final sería proporcionar el token de Galaxy y que previamente lo guardamos como una variable dentro de GitLab.

Subiendo los Cambios
#

Perfecto. Solo nos queda subir los cambios que hemos realizado a GitLab:

Inicializamos el repo con la rama main por defecto:

git init --initial-branch=main

Añadimos el repositorio de GitLab con nuestras credenciales (sustituir usuario por tu usuario)

git remote add origin git@gitlab.com:usuario/ansible-collection-micoleccion.git

Añadimos los archivos de la colección

git add .

Hacemos commit de los cambios:

git commit -m "Initial commit"

Etiquetamos la version 1.0.0 de nuestra colección:

git tag -a 1.0.0 -m "Version 1.0.0"

Subimos los cambios:

git push -u origin main

Por ultimo subimos los cambios del tag 1.0.0:

git push -u origin 1.0.0

¿Recuerdas que te mencioné que restringimos la ejecución del pipeline al tag o etiquetas? La razón es que Galaxy necesita saber que versión de la colección se está enviando, por lo que, además de colocarla en el manifiesto galaxy.yml esta debe coincidir con el nombre del paquete y etiquetando o colocándole tag a la versión del paquete podemos tener ese dato e incluirlo. Por consiguiente, cada vez que realicemos un cambio a nuestra colección y esta pase a ser la versión 1.1.0 por ejemplo, debemos hacer git tag 1.1.0 y ejecutar git push -u origin 1.1.0 y asi GitLab nos creará el tag de version en nuestro repo y ejecutará el pipeline.

Monitoreando el Pipeline
#

Vamos a GitLab en la parte izquierda en el Menu CI/CD -> Pipelines:

Pipelines

Una vez dentro, veremos todos los pipelines que se han ejecutado hasta el momento:

Pipelines
Pipelines Ejecutados

Vamos a entrar en el primero y aprovechar de hacer un poco de troubleshooting:

Pipelines
Pipelines Ejecutados

Entremos al stage build que si se ejecutó correctamente:

build
Stage build

Si observamos bien, está todo correcto, y si te fijas en la parte derecha, hay tres botones: Mantener, Descargar y Explorar, esto nos permite ver nuestro artefacto recien creado (que no es mas que la coleccion empaquetada), descargarla, etc. Durante 30 dias va a permanecer almacenado en GitLab porque asi se lo hemos dicho en el archivo .gitlab-ci.yml

Vamos ahora a ver que pasó con el stage publish:

publish
Stage publish

Pues por alguna razón el comando falló al no encontrar el token, pero si ya lo habiamos configurado en GitLab con su respectiva variable llamada $GALAXY_TOKEN, ¿Qué ha podido suceder entonces?

¿Recuerdas cuando al momento de configurar el token como variable activamos la opción Proteger Variable? Nos limita a utilizar las ramas del repositorio que estén protegidas. Por defecto los tags no vienen protegidos y por ende, si configuramos un tag la 1.0.1 por ejemplo, al actualizar a la 1.0.2 no se va a ejecutar el pipeline porque el tag 1.0.2 no estaría protegido. Una solución a esto seria agregar de una vez todos los tags que se suban a continuación.

Para esto tenemos que ir a Configuración -> Repositorio, en la opción Protected Tags, donde dice etiqueta le colocamos un asterisco * escogemos Create wildcard, Autorizado a crear: Mantainers (puedes colocarte a ti mismo o un miembro del equipo) y click en Proteger:

publish
Crear wilddard

publish
Proteger tags

Ahora nos vamos de nuevo a CI/CD -> Pipelines, vamos a el stage que falló (publish) y hacemos click en Reintentar:

publish
Reintentamos el stage

Y voilá, se ha ejecutado el stage publish:

publish
Stage Terminado

Revisando en Ansible Galaxy
#

Para terminar, nos aseguramos que nuestra colección se encuentra publicada, nos vamos a la página de Ansible Galaxy, en el menú de la izquierda a My Contents:

My Content

Ahí lo tenemos:

My Content

Si queremos ver más detalles, hagamos click en el nombre de la colección micoleccion

My Content

Y con eso estaríamos.

Conclusión
#

GitLab nos permite poder publicar nuestras colecciones a Ansible Galaxy de manera fácil y automatizada a través de GitLab CI. Si quisieramos extender más nuestro pipeline y colocarle pruebas con molecule, por ejemplo, lo podríamos hacer, pero eso sería tema para otro artículo 😄

El código del pipeline como de la coleccion de prueba, quedará en el repositorio:
https://gitlab.com/enmanuelmoreira/ansible-collection-micoleccion

Espero les haya gustado y nos vemos en la próxima!