Aller au contenu principal

Des boxes Vagrant Docker avec WSL 2

· 8 minutes de lecture
Stéphane ROBERT
Consultant DevOps

Pour ceux qui veulent tout de même utiliser WSL plutôt qu'une machine hyper-v, il est compliqué de faire fonctionner Vagrant. Pourtant, on peut y arriver en utilisant des boxes au format Docker. Le fonctionnement sera le même qu'avec des machines provisionnées avec libvirt sauf une seule chose : Les services ne seront pas utilisables, Docker oblige, à moins peut être de créer ses propres boxes ou systemd est opérationnel. Je l'ai déjà fait dans un job précédent, je vais fouiller dans mon historique et je vous les ajouterai au bout de ce billet.

Installation de Docker sur WSL

Ici je ne vais pas utiliser Docker Desktop, qui consomme des ressources mémoires qui parfois sont limitées sur un poste d'entreprise. Je vais donc installer Docker au sein de WSL et ajouter à mon profil le lancement de celui-ci.

# Installation des pre-requis
sudo apt update
sudo apt install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

# Ajout du repo Docker
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Ajout de la clé du repo Docker
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

# Installation de Docker
sudo apt update && sudo apt install docker-ce docker-ce-cli containerd.io

# Ajout du user courant au group Docker
sudo usermod -aG docker $USER

Maintenant l'astuce pour le démarrer.

  • Sous windows 11, il faut ajouter ces lignes au fichier /etc/wsl.conf :
vi /etc/wsl.conf
[boot]
command = "service docker start"
wsl.exe --shutdown

Une fois la machine WSL redémarré vérifier que Docker est bien opérationnel :

docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
  • Sous Windows 10, il faut ajouter ces lignes à votre .profile :
vi ~/.profile
if service docker status 2>&1 | grep -q "is not running"; then
    wsl.exe -d "${WSL_DISTRO_NAME}" -u root -e /usr/sbin/service docker start >/dev/null 2>&1
fi
wsl.exe --shutdown

Une fois la machine WSL redemarrer vérifier que Docker est bien opérationnel

docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

Installation de Vagrant

Il ne reste plus qu'à installer Vagrant :

curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
sudo apt update && sudo apt install vagrant

On redémarre pour récupérer le groupe vagrant :

wsl.exe --shutdown

Utilisation d'une box Vagrant utilisant le provider Docker

Nous allons créer notre image Docker à base d'Ubuntu :

FROM ubuntu:jammy

ENV DEBIAN_FRONTEND noninteractive

# Install packages needed for SSH and interactive OS
RUN apt update && \
    yes | unminimize && \
    apt -y install \
        openssh-server \
        passwd \
        sudo \
        man-db \
        curl \
        wget && \
    apt -qq clean && \
    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

# Enable systemd (from Matthew Warman's mcwarman/vagrant-provider)
RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); \
    rm -f /lib/systemd/system/multi-user.target.wants/*; \
    rm -f /etc/systemd/system/*.wants/*; \
    rm -f /lib/systemd/system/local-fs.target.wants/*; \
    rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
    rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
    rm -f /lib/systemd/system/basic.target.wants/*; \
    rm -f /lib/systemd/system/anaconda.target.wants/*;


# Create the vagrant user
RUN useradd -m -G sudo -s /bin/bash vagrant && \
    echo "vagrant:vagrant" | chpasswd && \
    echo 'vagrant ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/vagrant && \
    chmod 440 /etc/sudoers.d/vagrant

# Establish ssh keys for vagrant
RUN mkdir -p /home/vagrant/.ssh; \
    chmod 700 /home/vagrant/.ssh
ADD https://raw.githubusercontent.com/hashicorp/vagrant/master/keys/vagrant.pub /home/vagrant/.ssh/authorized_keys
RUN chmod 600 /home/vagrant/.ssh/authorized_keys; \
    chown -R vagrant:vagrant /home/vagrant/.ssh

# Enable ssh for vagrant

RUN systemctl enable ssh.service;
EXPOSE 22

# Run the init daemon
VOLUME [ "/sys/fs/cgroup" ]
CMD ["/usr/sbin/init"]

Maintenant notre Vagrantfile :

ENV['VAGRANT_DEFAULT_PROVIDER'] = 'docker'

Vagrant.configure("2") do |config|
  config.vm.provider "docker" do |docker|
    docker.name = "test"
    docker.build_dir = "vagrant_docker"
    docker.remains_running = true
    docker.has_ssh = true
    docker.privileged = true
    docker.create_args     = ['--tmpfs', '/tmp', '--tmpfs', '/run', '--tmpfs', '/run/lock', '-v', '/sys/fs/cgroup:/sys/fs/cgroup:ro', '-t']
    # docker.run_args     = ['--tmpfs', '/tmp', '--tmpfs', '/run', '--tmpfs', '/run/lock', '-v', '/sys/fs/cgroup:/sys/fs/cgroup:ro', '-t']
  end
  config.vm.synced_folder ".", "/vagrant", disabled: true
end

On lance la construction de la box :

vagrant up
==> default: Deleting the container...
==> default: Creating and configuring docker networks...
==> default: Building the container from a Dockerfile...
    default: Sending build context to Docker daemon   5.12kB
    default: Step 1/12 : FROM ubuntu:jammy
    default:  ---> f0b07b45d05b
    default: Step 2/12 : ENV DEBIAN_FRONTEND noninteractive
    default:  ---> Using cache
    default:  ---> 8719e8bebd18
    default: Step 3/12 : RUN apt update &&     yes | unminimize &&     apt -y install         openssh-server         passwd         sudo         man-db         curl         wget &&     apt -qq clean &&     rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
    default:  ---> Using cache
    default:  ---> b98aead5545e
    default: Step 4/12 : RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done);     rm -f /lib/systemd/system/multi-user.target.wants/*;     rm -f /etc/systemd/system/*.wants/*;     rm -f /lib/systemd/system/local-fs.target.wants/*;     rm -f /lib/systemd/system/sockets.target.wants/*udev*;     rm -f /lib/systemd/system/sockets.target.wants/*initctl*;     rm -f /lib/systemd/system/basic.target.wants/*;     rm -f /lib/systemd/system/anaconda.target.wants/*;
    default:  ---> Using cache
    default:  ---> 6446c02572c6
    default: Step 5/12 : RUN useradd -m -G sudo -s /bin/bash vagrant &&     echo "vagrant:vagrant" | chpasswd &&     echo 'vagrant ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/vagrant &&     chmod 440 /etc/sudoers.d/vagrant
    default:  ---> Using cache
    default:  ---> 85245e51458d
    default: Step 6/12 : RUN mkdir -p /home/vagrant/.ssh;     chmod 700 /home/vagrant/.ssh
    default:  ---> Using cache
    default:  ---> 98f6d137e908
    default: Step 7/12 : ADD https://raw.githubusercontent.com/hashicorp/vagrant/master/keys/vagrant.pub /home/vagrant/.ssh/authorized_keys
    default:
    default:  ---> Using cache
    default:  ---> 99f3d4a28c38
    default: Step 8/12 : RUN chmod 600 /home/vagrant/.ssh/authorized_keys;     chown -R vagrant:vagrant /home/vagrant/.ssh
    default:  ---> Using cache
    default:  ---> 911ca2434ea8
    default: Step 9/12 : RUN systemctl enable ssh.service;
    default:  ---> Using cache
    default:  ---> 53a988bb68c2
    default: Step 10/12 : EXPOSE 22
    default:  ---> Using cache
    default:  ---> 37d6136d6e1b
    default: Step 11/12 : VOLUME [ "/sys/fs/cgroup" ]
    default:  ---> Using cache
    default:  ---> 4b5c40d8c1ea
    default: Step 12/12 : CMD ["/usr/sbin/init"]
    default:  ---> Using cache
    default:  ---> 3c5a7f9c664a
    default: Successfully built 3c5a7f9c664a
    default:
    default: Image: 3c5a7f9c664a
==> default: Creating the container...
    default:   Name: test
    default:  Image: 3c5a7f9c664a
    default:   Port: 127.0.0.1:2222:22
    default:
    default: Container created: b46c9ae50f3f3cac
==> default: Enabling network interfaces...
==> default: Starting container...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
    default:
    default: Vagrant insecure key detected. Vagrant will automatically replace
    default: this with a newly generated keypair for better security.
    default:
    default: Inserting generated public key within guest...
    default: Removing insecure key from the guest if it's present...
    default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!

On vérifie que notre container tourne :

docker ps -a
CONTAINER ID   IMAGE                   COMMAND            CREATED         STATUS                    PORTS                                       NAMES
b46c9ae50f3f   3c5a7f9c664a            "/usr/sbin/init"   8 seconds ago   Up 7 seconds              127.0.0.1:2222->22/tcp                      test

On se connecte :

vagrant ssh

Welcome to Ubuntu 22.04 LTS (GNU/Linux 5.10.102.1-microsoft-standard-WSL2 x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage
vagrant@b46c9ae50f3f:~$systemctl status sshd.service

● ssh.service - OpenBSD Secure Shell server
     Loaded: loaded (/lib/systemd/system/ssh.service; enabled; vendor preset: enabled)
     Active: active (running) since Thu 2022-04-21 07:36:51 UTC; 6min ago
       Docs: man:sshd(8)
             man:sshd_config(5)
    Process: 22 ExecStartPre=/usr/sbin/sshd -t (code=exited, status=0/SUCCESS)
   Main PID: 23 (sshd)
      Tasks: 6 (limit: 1157)
     Memory: 23.6M
     CGroup: /system.slice/ssh.service
             ├─ 23 "sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups"
             ├─261 "sshd: vagrant [priv]"
             ├─271 "sshd: vagrant@pts/1" ""
             ├─272 -bash
             ├─275 systemctl status sshd

On se croirait sur un VM, non ? En plus ça se lance très vite !

Il ne reste plus qu'à créer notre provisioner ansible. On crée le fichier provision.yml avec ce contenu :

---
- hosts: all
  tasks:
  - debug:
      msg: "Hello from Docker"

On met à jour notre Vagrantfile :

ENV['VAGRANT_DEFAULT_PROVIDER'] = 'docker'

Vagrant.configure("2") do |config|
  config.vm.provider "docker" do |docker|
    docker.name = "test"
    docker.build_dir = "vagrant_docker"
    docker.remains_running = true
    docker.has_ssh = true
    docker.privileged = true
    docker.create_args     = ['--tmpfs', '/tmp', '--tmpfs', '/run', '--tmpfs', '/run/lock', '-v', '/sys/fs/cgroup:/sys/fs/cgroup:ro', '-t']
  end
  config.vm.synced_folder ".", "/vagrant", disabled: true
  config.vm.provision "ansible" do |ansible|
    ansible.playbook = "provision.yml"
  end
end

On relance la box :

vagrant reload
==> default: Stopping container...
==> default: Deleting the container...
==> default: Creating and configuring docker networks...
==> default: Building the container from a Dockerfile...
...

==> default: Machine booted and ready!
==> default: Running provisioner: ansible...
    default: Running ansible-playbook...

    default: Running ansible-playbook...
 ____________
< PLAY [all] >
 ------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

 ________________________
< TASK [Gathering Facts] >
 ------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

ok: [default]
 ______________
< TASK [debug] >
 --------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

ok: [default] => {
    "msg": "Hello from Docker"
}
 ____________
< PLAY RECAP >
 ------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

default                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Et voilà le top du top !!!!