DevOps GitLab CI/CD + Ansible Molecule Pipeline
Introduzione
La semplificazione dello sviluppo, del test e della distribuzione di infrastrutture e applicazioni è una pietra miliare delle moderne pratiche DevOps. GitLab CI/CD fornisce una solida piattaforma per automatizzare le pipeline e Ansible Molecule offre un potente framework per testare i ruoli Ansible prima della distribuzione. In questo articolo, dimostrerò come sposare questi strumenti per creare una pipeline GitLab DevOps coesa, compresi i test di integrazione e il provisioning.
È possibile trovare il codice sorgente di questo progetto nel mio repository GitLab qui:
Testing Ansible with Molecule in Docker
Componenti chiave
- GitLab CI/CD: La piattaforma di orchestrazione centrale per i nostri processi di build, test e deploy automatizzati.
- Ansible: Il motore di gestione della configurazione e di automazione IT, usato per definire la nostra infrastruttura come codice.
- Ansible Roles: Blocchi di codice Ansible modulari e riutilizzabili per strutturare la configurazione dell'infrastruttura.
- Molecule: Un framework specificamente progettato per testare la logica e l'esecuzione dei ruoli di Ansible.
La pipeline
La nostra pipeline GitLab DevOps consisterà in tre fasi principali:
- Container Build:
- Un Dockerfile definisce un'immagine del container contenente Ansible, Molecule e le dipendenze necessarie.
- GitLab CI/CD automatizza la creazione dell'immagine e la invia a un registro di contenitori (GitLab Container Registry).
- Test dei ruoli Ansible con Molecule:
- Sono definiti scenari di test con Molecule, per garantire che i ruoli Ansible funzionino come previsto nelle varie architetture e nei vari sistemi operativi.
- I test vengono eseguiti all'interno del contenitore costruito, garantendo isolamento e coerenza.
- Provisioning:
- Un playbook Ansible utilizza i ruoli testati per configurare l'infrastruttura di destinazione.
- GitLab CI/CD esegue il playbook negli ambienti designati (dev, staging, production).
Esempio di codice (.gitlab-ci.yml)
Costruire immagini Docker multi-arch rootless con Kaniko
Definito in .gitlab-ci.yml
il CI le fasi:
- I Runner GitLab costruiscono i container, per 2 architetture, arm64 e amd64, in istanze separate.
- Creano i manifest di Docker per le due architetture e inviano i container al registro di GitLab
Comprendere la struttura in profondità
- Fasi: La pipeline è divisa in due fasi
build
: Qui viene costruita l'immagine Docker per le diverse architetture.multiarch-manifest
: Questo crea un manifest multi-architettura per supportare senza problemi diverse architetture.
- Jobs: Abbiamo due job di compilazione (
kaniko-amd64
,kaniko-arm64
) e un manifest job (multiarch-manifest).
1. kaniko-amd64
e kaniko-arm64
Jobs
- Scopo: Costruire immagini Docker per architetture AMD64 (kaniko-amd64) e ARM64 (kaniko-arm64).
- Immagine: Utilizzare l'immagine dell'esecutore Kaniko (gcr.io/kaniko-project/executor:debug) per costruire immagini Docker senza Docker-in-Docker.
- Variabili
- KANIKO_ARGS: Flag aggiuntivi per l'esecutore Kaniko.
- KANIKO_BUILD_CONTEXT: La directory in cui si trova il file Docker.
- Runner Tags: associa il carico di lavoro a specifici runner
kaniko-arm64
è specificamente etichettato per essere eseguito su un ARM64, in questo caso un runner privato.kaniko-amd64
è etichettato per essere eseguito su un runner AMD64.
kaniko-amd64:
variabili:
# Opzioni aggiuntive per l'esecutore Kaniko.
# Per maggiori dettagli vedere https://github.com/GoogleContainerTools/kaniko/blob/master/README.md#additional-flags
KANIKO_ARGS: ""
KANIKO_BUILD_CONTEXT: $CI_PROJECT_DIR
fase: costruire
immagine:
# Per gli ultimi rilasci vedere https://github.com/GoogleContainerTools/kaniko/releases
# Solo le versioni debug/*-debug dell'immagine di Kaniko funzionano con Gitlab CI.
nome: gcr.io/project/kaniko-project/executor:debug
entrypoint: [""]
script:
# se l'utente fornisce IMAGE_TAG allora lo usa, altrimenti costruisce il tag immagine usando la logica predefinita.
# Logica predefinita
# Comporre il nome del tag Docker
# Mappatura del ramo/tag Git al tag immagine Docker
# * Ramo predefinito: main -> latest
# * Ramo: feature/my-feature -> branch-feature-my-feature
# * Tag: v1.0.0/beta2 -> v1.0.0-beta2
se [ -z ${IMAGE_TAG+x} ]; allora
se [ "$CI_COMMIT_REF_NAME" = $CI_DEFAULT_BRANCH ]; allora
VERSIONE="ultima"
elif [ -n "$CI_COMMIT_TAG" ];then
NOSLASH=$(echo "$CI_COMMIT_TAG" | tr -s / - )
SANITIZED="${NOSLASH//[^a-zA-Z0-9.-]/}"
VERSION="$SANITIZED"
altrimenti \
NOSLASH=$(echo "$CI_COMMIT_REF_NAME" | tr -s / - )
SANITIZED="${NOSLASH//[^a-zA-Z0-9-]/}"
VERSION="branch-$SANITIZED"
fi
export IMAGE_TAG=$CI_REGISTRY_IMAGE:$VERSION-amd64
fi
- echo $IMAGE_TAG
- mkdir -p /kaniko/.docker
# Scrivere le credenziali per accedere al Gitlab Container Registry all'interno del runner/ci
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(echo -n ${CI_REGISTRY_USER}:${CI_REGISTRY_PASSWORD}} | base64 | tr -d '\n')\}}" > /kaniko/.docker/config.json
# Costruire e spingere il contenitore. Per disabilitare il push, aggiungere --no-push
- DOCKERFILE_PATH=${DOCKERFILE_PATH:-"$KANIKO_BUILD_CONTEXT/Dockerfile"}
- /kaniko/executor --contesto $KANIKO_BUILD_CONTEXT --dockerfile $DOCKERFILE_PATH -destinazione $IMAGE_TAG $KANIKO_ARGS
# Esegue questo lavoro in un ramo/tag in cui esiste un Dockerfile
regole:
- esiste:
- Dockerfile
# solo tag
- se: "$CI_COMMIT_TAG"
# percorso del file Docker personalizzato
- if: $DOCKERFILE_PATH
# contesto di compilazione personalizzato senza un percorso esplicito del file Docker
- se: $KANIKO_BUILD_CONTEXT != $CI_PROJECT_DIR
2. multiarch-manifest
Job
- Scopo: Crea un manifest multi-architettura, consentendo a Docker di estrarre l'immagine appropriata in base all'architettura del sistema dell'utente.
- Immagine: Utilizza un'immagine standard
docker:latest
, poiché questo task si basa sulla Docker CLI. - Servizi:
docker:dind
: Sfrutta Docker-in-Docker per fornire contenitori Docker all'interno del runner GitLab.
stage: multiarch-manifest
immagine:
# Per gli ultimi rilasci vedere https://github.com/GoogleContainerTools/kaniko/releases
# Solo le versioni debug/*-debug dell'immagine Kaniko sono note per funzionare all'interno di Gitlab CI.
nome: docker:latest
entrypoint: [""]
servizi:
- docker:dind
before_script:
# Connettersi a GitLab Docker in Docker DIND
- mkdir -pv ~/.docker
- cp -v $DOCKER_TLS_CERTDIR/client/* ~/.docker # Copiare i certificati docker predefiniti
- docker info # Mostra la connessione al demone docker
# Effettuare il login... se necessario
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
script:
# Lo script Docker usa un altro formato di tag `:7-8-5`, usa lo stile Kaniko per la coerenza
- |
if [ -z ${IMAGE_TAG+x} ]; then
if [ "$CI_COMMIT_REF_NAME" = $CI_DEFAULT_BRANCH ]; then
VERSIONE="ultima"
elif [ -n "$CI_COMMIT_TAG" ];then
NOSLASH=$(echo "$CI_COMMIT_TAG" | tr -s / - )
SANITIZED="${NOSLASH//[^a-zA-Z0-9.-]/}"
VERSION="$SANITIZED"
altrimenti \
NOSLASH=$(echo "$CI_COMMIT_REF_NAME" | tr -s / - )
SANITIZED="${NOSLASH//[^a-zA-Z0-9-]/}"
VERSION="branch-$SANITIZED"
fi
esportare IMAGE_TAG=$CI_REGISTRY_IMAGE:$VERSION
fi
# Esempio di tag Kaniko: registry.gitlab.com/aleliga/docker-ansible-runner:7.8.5
# Estrarre le immagini
- docker pull $IMAGE_TAG-arm64
- docker pull $IMAGE_TAG-amd64
# Riempire le variabili per il "manifesto di docker".
- export MANIFEST_LIST=$IMAGE_TAG && export MANIFEST_BASE=$MANIFEST_LIST
- echo "Debug vars $MANIFEST_LIST $MANIFEST_BASE"
# Creare il manifesto di docker
- echo "docker manifest create $MANIFEST_LIST $MANIFEST_BASE-arm64 $MANIFEST_BASE-amd64"
- docker manifest create $MANIFEST_LIST $MANIFEST_BASE-arm64 $MANIFEST_BASE-amd64
# Annotate l'arco
- echo "docker manifest annotate --os linux --arch arm64 $MANIFEST_LIST $MANIFEST_BASE-arm64"
- docker manifest annotate --os linux --arch arm64 $MANIFEST_LIST $MANIFEST_BASE-arm64
- echo "docker manifest annotate --os linux --arch amd64 $MANIFEST_LIST $MANIFEST_BASE-amd64"
- docker manifest annotate --os linux --arch amd64 $MANIFEST_LIST $MANIFEST_BASE-amd64
# Spingere il manifesto
- echo "docker manifest push $MANIFEST_LIST"
- docker manifest push $MANIFEST_LIST
regole:
- esiste:
- Dockerfile
# solo i tag
- se: "$CI_COMMIT_TAG"
# percorso del file Docker personalizzato
- if: $DOCKERFILE_PATH
# contesto di compilazione personalizzato senza un percorso esplicito del file Docker
- se: $KANIKO_BUILD_CONTEXT != $CI_PROJECT_DIR
Eseguire una shell all'interno del container
Utile per i test, possiamo eseguire una shell interattiva all'interno del container, con montato il nostro codice Ansible come volume.
docker run -it --rm \
-v $(pwd)/ansible:/ansible \
registry.gitlab.com/aleliga/docker-ansible-molecule \
bash
Aggiungi collezioni al contenitore
Il contenitore può essere usato come immagine base per aggiungere collezioni e ruoli
FROM registry.gitlab.com/aleliga/docker-ansible-molecule:latest
ESEGUIRE python3 -m venv venv
ENV PATH="/venv/bin:$PATH"
# Aggiungere una collezione
RUN ansible-galaxy collection install community.general
Eseguire Ansible Molecule in GitLab CI/CD
Una pipeline CI secondaria, è dedicata ai test di integrazione dei Playbook e dei Ruoli di Ansible con molecola, utilizzando il contenitore precedentemente costruito
Script di inizializzazione di molecola
Questo script viene eseguito all'interno del contenitore docker-ansible-molecule
durante il CI. Impostare l'ambiente (link simbolico, attivazione dell'ambiente virtuale), quindi eseguire il sottocomando Molecule fornito nella directory Ansible del progetto.
#!/usr/bin/env bash
MOLECULE_COMMAND="$1"
# Creare un collegamento per ricreare il percorso come nel contenitore
ln -s $CI_PROJECT_DIR/ansible /ansible
# Entrare nella directory di ansible
PWD="/ansible" ; cd /ansible
# Aggiungere singole variabili alla molecola
MOLECULE="true" \
molecule $1
Configurazione di GitLab CI
Il file .gitlab-ci.yaml
definisce l'immagine, esegue una pulizia forzata e molecule test
stages:
- molecola-test
variabili:
CONTAINER_RELEASE_IMAGE: "$CI_REGISTRY_IMAGE:$CI_COMMIT_TAG"
SHELL: /bin/bash
CI_REGISTRY: registry.gitlab.com
before_script:
- echo "Prima dello script"
- mkdir -p ~/.docker/
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(echo -n ${CI_REGISTRY_USER}:${CI_REGISTRY_PASSWORD} | base64)\"}}" >> ~/.docker/config.json
molecola:
immagine: registry.gitlab.com/aleliga/docker-ansible-molecule:latest
fase: molecola-test
servizi:
- docker:dind
tag:
- private-runner
- molecola
script:
- echo "Eseguire la distruzione della molecola"
- "$CI_PROJECT_DIR/bin/molecule-ci-direct.sh destroy"
- echo "Rimuovere i file temporanei"
- rm -fr ~/.cache/molecule/
- echo "Eseguire il test della molecola"
- "$CI_PROJECT_DIR/bin/molecule-ci-direct.sh test"
Deploy con Ansible in CD
L'ultima pipeline, distribuisce i playbook Ansible da GitLab Runner, utilizzando DinD (Docker in Docker)
variabili:
# Distribuzione del repository
PRIVATE_REPO: "gitlab.com/USER/PRIVATE_REPO.git"
# Parametri Docker DIND
DOCKER_HOST: "tcp://docker:2376"
DOCKER_TLS_CERTDIR: "/certs"
DOCKER_TLS_VERIFY: 1
ansible-deploy:
immagine: registry.gitlab.com/aleliga/docker-ansible-molecule:latest
stage: deploy
servizi:
- docker:dind
before_script:
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- echo -e $CI_SSH_PRIVATE_KEY
- echo -e "$CI_SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- cd ~/
- mkdir -p $PERSONAL_REPO_DIR
- apt-get update && apt-get install git -qqy
script:
- git clone https://"$ANSIBLE_USER":"$ANSIBLE_DEPLOY_TOKEN"@"$PRIVATE_REPO" $PERSONAL_REPO_DIR
# Eseguire Ansible "upgrade-hosts" per eseguire il deploy
- cd $PERSONAL_REPO_DIR
- echo "Distribuire ai server"
- bin/gitlabci-upgrade-hosts.sh
Benefici
- Maggiore affidabilità: I test automatizzati con Molecule aumentano la fiducia nei ruoli di Ansible, impedendo che gli errori scivolino negli ambienti di produzione.
- Iterazione più veloce: Una pipeline CI/CD ben definita consente uno sviluppo più rapido e modifiche con un maggior grado di sicurezza.
- Infrastruttura come codice: Ansible garantisce distribuzioni prevedibili e ripetibili in tutti gli ambienti.
Considerazioni
- Gestione sicura delle variabili: Usare le variabili segrete di GitLab CI/CD e le soluzioni Vault per memorizzare dati sensibili come le credenziali.
- Gestione degli ambienti: Considerare la possibilità di separare le configurazioni della pipeline per gli ambienti di sviluppo, staging e produzione.
Riferimenti
Aggiornamento settembre 2024
Dal rilascio di GitLab 17, l'infrastruttura DevOps cloud, fornisce Runner SaaS ARM64 condivisi, utilizzabili definendo i tag corretti in kaniko-arm64
e altri lavori, ma a partire da settembre 2024 sono solo per i clienti Premium e Ultimate.
Per utilizzare i runner ospitati da GitLab ARM64, possiamo impostare i tag appropriati nel CI stage
, ad esempio:
tags:
- saas-linux-medium-arm64
Fonte: GitLab Hosted runners on Linux
Riferimenti
Un grande ringraziamento a Jeff Geerling, per tutto il suo lavoro sulla comunità Ansible
- Testing your Ansible roles with Molecule | Jeff Geerling
- Come aggiungere test di integrazione a una raccolta di Ansible con Molecule | Jeff Geerling
Conclusione
L'integrazione di GitLab CI e Ansible Molecule favorisce un solido flusso di lavoro DevOps. Assicura modifiche affidabili all'infrastruttura, accelera i tempi di consegna e riduce il rischio di errori di distribuzione.
Sentitevi liberi di esplorare il repository, di contribuire o di usarlo come riferimento. Fatemi sapere se avete commenti o suggerimenti!
Se avete bisogno di un esperto di integrazione per le vostre pipeline Ansible, provate la nostra consulenza DevOps gratuita