Aller au contenu principal

Construire des images Docker avec Ansible

· 5 minutes de lecture
Stéphane ROBERT
Consultant DevOps

Ansible-bender est une application développée par Tomas Tomecek qui permet de construire des images de container à partir de playbooks ansible plutôt que des fichiers Dockerfile. Ansible-bender s'appuie sur le moteur de container Podman plutôt que docker.

Podman et Buildah

Podman est une solution de containérisation comme l'est Docker développée par RedHat. Il utilise le même interpréteur de ligne de commande que Docker et donc les principales commandes de Docker fonctionne avec Podman. Podman fonctionne sans daemon comme instance de gestion pour gérer ses pods. Cela permet d’accéder aux différentes applications virtualisées sans droits root. Podman est devenu le moteur de conteneur par défaut sous RHEL8.

Buildah est un outil complémentaire à Podman qui permet de créer des images au format Open Container Initiative (OCI). Ces images sont compatibles avec la plupart des runtimes de containers, ceux compatible avec la norme OCI: docker, containerd, runc, ... et donc kubernetes. Il utilise aussi les fichiers Dockerfile pour décrire les images.

Installation d'Ansible-bender et de ses prérequis

Provisionnement d'une VM avec vagrant

Je vais utiliser une machine provisionnée avec vagrant pour faire mes tests en prenant une box oracle8.

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  config.vm.box = "generic/oracle8"
  config.vm.provider "libvirt" do |hv|
    hv.cpus = "2"
    hv.memory = "2048"
  end
  config.vm.synced_folder '.', '/vagrant', disabled: true

  config.vm.define "bender" do |bender|
    bender.vm.network :private_network, ip: "192.168.3.11"
    bender.vm.hostname = "bender"
    bender.vm.provision "ansible" do |a|
      a.verbose = "v"
      a.playbook = "provision-playbook.yml"
    end
  end
end

Comme vous pouvez le remarquer, il fait appel à playbook ansible pour configurer la vm produite, qui permet surtout de lancer des playbooks sans à spécifier le user vagrant et d'installer podman, buildah et ansible-bender. Pour ceux qui ne connaissent pas l'écriture de playbooks ansible je vous renvoie sur mon billet.

N'oubliez pas de changer le chemin de votre clé ssh !

---
- hosts: all
  gather_facts: no
  become: true
  tasks:
    - name: Install packages
      package:
        name:
          - python3-pip
          - podman
          - buildah

    - name: Install ansible-bender and ansible
      pip:
        name:
          - ansible-bender
          - ansible

    - name: Replace a localhost entry with our own
      lineinfile:
        path: /etc/hosts
        regexp: '^127\.0\.0\.1'
        line: 127.0.0.1 localhost
        owner: root
        group: root
        mode: '0644'

    - name: Allow password authentication
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: "^PasswordAuthentication"
        line: "PasswordAuthentication yes"
        state: present
      notify: restart sshd

    - name: Set authorized key took from file
      authorized_key:
        user: vagrant
        state: present
        key: "{{ lookup('file', '/home/vagrant/.ssh/id_ed25519.pub') }}"

  handlers:
    - name: restart sshd
      service:
        name: sshd
        state: restarted

Pour se connecter à notre vm il suffit de :

ssh 192.168.3.11
[vagrant@bender ~]$

Construction d'une image docker avec ansible-bender

Le fonctionnement est assez simple. On utilise un playbook classique auquel on ajoute une variable ansible-bender pour indiquer :

  • l'image de base
  • le nom de l'image (tag) produit
  • les volumes
  • ...

La seule contrainte est d'utiliser des images sources contenant déja python3.6 au minimun (ex: python:3-alpine)

Par exemple:

---
- name: Demonstration of ansible-bender functionality
  hosts: all
  vars:
    ansible_bender:
      base_image: python:3-alpine

      working_container:
        volumes:
          - '{{ playbook_dir }}:/src'

      target_image:
        name: a-very-nice-image
        working_dir: /src
        labels:
          built-by: '{{ ansible_user }}'
        environment:
          FILE_TO_PROCESS: README.md
  tasks:
  - name: Run a sample command
    command: 'ls -lha /src'
  - name: Stat a file
    stat:
      path: "{{ lookup('env','FILE_TO_PROCESS') }}"

Construction de notre propre image

Je vais créer une image installant apache2 et affichant un simple fichier. Créer les deux fichiers suivant dans votre vm.

Le fichier file.html :

<h1>hello world!</h1>

Le fichier playbook.yml

---
- name: Serve file using apache
  hosts: all
  vars:
    ansible_bender:
      base_image: "docker.io/library/python:3-alpine"
      target_image:
        name: test:0.1
        cmd: httpd -DFOREGROUND
        ports: ["80"]
      squash: true

- name: Serve our file using httpd
  hosts: all
  tasks:
  - name: Install httpd
    package:
      name:
        - apache2
        - libxml2-dev
        - apache2-utils
      state: installed
  - name: Copy
    copy:
      src: file.html
      dest: /var/www/html/

Vous remarquez que j'ai ajouté quelques variables à ansible-bender permettant d'exposer le port 80 (un tableau de string), la commande du container et squash qui permet d'aplatir l'image produite à un seul layer. La liste des toutes ces options se trouve ici

Lançons la construction de l'image:

ansible-bender build playbook.yml

Qui retourne :

PLAY [Serve file using apache] *************************************************

TASK [Gathering Facts] *********************************************************
ok: [test-0-1-20210415-073450224792-cont]

PLAY [Serve our file using httpd] **********************************************

TASK [Gathering Facts] *********************************************************
ok: [test-0-1-20210415-073450224792-cont]

TASK [Install httpd] ***********************************************************
loaded from cache: 'c4211dd45723cd1caafaa3037aed2325666d8159dbbfc207a8bbf806c62a4530'
skipping: [test-0-1-20210415-073450224792-cont]

TASK [Copy] ********************************************************************
changed: [test-0-1-20210415-073450224792-cont]

PLAY RECAP *********************************************************************
test-0-1-20210415-073450224792-cont : ok=3    changed=1    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

Getting image source signatures
Copying blob sha256:5d0c4fd59dd60a3b05d6fd9c099f6791ca4358e2c1685929da8f4ab23774da61
Copying config sha256:fca2397b32294275d3c86dde9e1fbf7873febd63b9a541e1cb78d641516669ff
Writing manifest to image destination
Storing signatures
fca2397b32294275d3c86dde9e1fbf7873febd63b9a541e1cb78d641516669ff
Image 'test:0.1' was built successfully \o/
[vagrant@bender ~]$ podman images
REPOSITORY                TAG                               IMAGE ID      CREATED         SIZE
localhost/test            0.1                               fca2397b3229  11 seconds ago  62.9 MB

Lançons l'image construite :

podman run -p 80:80 -d test:0.1

Vérifions qu'elle fonctionne :

curl http://localhost:8080
<html><body><h1>It works!</h1></body></html>

Mais combien a t'elle de layers ?

podman image inspect test:0.1

...

        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:5d0c4fd59dd60a3b05d6fd9c099f6791ca4358e2c1685929da8f4ab23774da61"
            ]
        },
...

Un seul comme demandé.

Conclusion

Je trouve ansible-bender plutôt sexy et je pense même l'intégrer dans un runner gitlab pour construire quelques-unes de mes images. Un beau couple ansible docker !