Aller au contenu principal

Build d'images en // avec Ansible

· 4 minutes de lecture
Stéphane ROBERT
Consultant DevOps

Un pattern devops très souvent utilisé et qui complète la formation Ansible: l'utilisation de Docker.

Lors des builds d'applications, il m'arrive parfois d'utiliser docker pour lancer plusieurs compilations en parallèle. Mais à chaque fois, je cherche comment bien paramétrer le module docker_container pour qu'ils lancent bien les containers et qu'Ansible attendent la fin du traitement pour passer à la suite.

Quel est le besoin ?

J'ai besoin de pouvoir lancer la compilation d'une même application pour différents clients et différents environnements. Chacun de ses cas possèdent son propre paramétrage et sa propre version à compiler.

Dans mon explication je n'utiliserai pas de cas concret, mais je vous expose simplement la solution que j'ai retenue. Donc tout est fictif, mais je l'ai déjà mis en oeuvre à plusieurs reprises.

Solution retenue

Je vais utiliser Ansible pour embarquer les paramétrages client et des environnements dans les inventaires. L'image du container doit prendre en entrée des arguments et partager un volume pour y déposer les artefacts.

Construction du container de build Docker

Il faut que ce container accepte en entrée plusieurs qui seront utilisé pour la compilation du programme. Il suffit d'ajouter des ARG comme ceci :

FROM oraclelinux:8
ARG VERSION
ARG CLONE_TOKEN
ENV VOTRE_PARAMETRE=tot
RUN dnf install -y tar git python3 && mkdir /artefact
RUN git clone --branch ${VERSION} https://gitlab+deploy-token-n:${CLONE_TOKEN}@https://gitlab.com/Bob74/rundeck.git
WORKDIR /src

Vous avez remarqué :

  1. je n'indique pas de CMD car je vais le définir dans le playbook Ansible.
  2. derrière le git clone j'utilise en paramètre --branch la version à compiler.
  3. la clé de déploiement est passé en argument car elle sera stocké dans une variable masquée du ci-cd gitlab.
  4. je crée le répertoire /artefact pour y déposer le résultat de la compilation. Ce répertoire sera monté via un volume

Construction de l'inventaire Ansible

Je reprends la structure que je vous ai expliquée dans un précédent billet.

inventories
└── dev
    ├── group_vars
    │   ├── all
    │   │   └── vars.yml
    │   ├── client1
    │   │   └── vars.yml
    │   └── client2
    │       └── vars.yaml
    └── hosts
  • Dans le groupe de variables all, je stocke les paramètres communs comme CLONE_TOKEN.
  • Dans les autres groupes les valeurs propres à chaque client par exemple :
    • CLIENT: client1
    • VERSION: v0.0.1
    • PARAM1: toto
  • Dans hosts j'indique les machines cibles d'installation. Pour le développement j'utilise Vagrant pour simuler mon environnement décrit ci-dessous.
[all:children]
client1
client2

[client1]
host1
[client2]
host2

Ecriture du playbook

Le playbook :

---
- name: Build une application
  gather_facts: yes
  # serial: 1

    - name: Build
      docker_image:
        build:
          dockerfile: ./Dockerfile
          path: ./
          nocache: true
          args:
            VERSION: "\{\{ VERSION \}\}"
            CLONE_TOKEN: "\{\{ CLONE_TOKEN \}\}"
        name: build
        tag: "\{\{ VERSION \}\}"
        force_source: true
        source: build
      delegate_to: 127.0.0.1

    - name: Launch compilation
      remote_user: root
      docker_container:
        name: "container\{\{ ansible_hostname \}\}"
        image: build:\{\{ VERSION \}\}
        command: "/bin/sh -c 'python3 build.py && tar cvfz /ext/${CLIENT}-\$\{VERSION\}.tar.gz result'"
        privileged: yes
        recreate: yes
        state: started
        detach: no
        volumes:
          - ./ext:/ext
        env:
          VERSION: "\{\{ VERSION \}\}"
          CLIENT: "\{\{ CLIENT \}\}"
          PARAM1: "\{\{ PARAM1 \}\}"
      delegate_to: 127.0.0.1
      register: docker_container_output
  • J'utilise les modules Ansible docker_image et docker_container mis à disposition par la communauté Ansible
  • Pour lancer les commandes en local, j'utilise delegate_to. Docker étant installé sur mon node Ansible.
  • L'étape Build utilise args pour injecter les paramètres pour récupérer le bon repository avec la bonne version.
  • L'étape Launch utilise :
    • image: build:{{ VERSION }} pour indiquer l'image avec la bonne version
    • volumes: le point de montage pour y déposer les artefacts
    • command: la commande de compilation avec la création de l'artefact dans le volume
    • env: les paramètres de compilation
    • register: pour des besoins de debug
    • Les paramètres super important :
      • recreate: yes on détruit une précédente exécution
      • state: started on indique bien started et pas juste present sinon la commande n'est pas lancée.
      • detach: no on indique à Ansible d'attendre la fin de l'exécution.
      • privileged: yes on peut en avoir besoin dans certains cas.

Lancement du tout

Très simple une simple commande ansible-playbook et on compile n versions en peu de temps. Attention cela peut être très consommateur de ressources et c'est pour cela que j'ai serial en paramètre du playbook pour limiter le nombre de compilations en parallèle.

Remarquez l'utilisation de l'indication du user vagrant pour lancer la compilation de votre environnement virtualisé avec vagrant. Dans votre Ci Gitlab, vous pourrez l'enlever ou le mettre en paramètre.

ansible-playbook -i inventories/dev build.yml -u vagrant

Voila, il s'agit ici d'un cas simple, mais avec Ansible, on peut vite trouver une solution pour gagner du temps sur le déploiement d'une application.

La suite de la formation Ansible dans de prochains billets.