Docker Machine et Vagrant

Docker Machine est un outil de la famille Docker qui se charge de créer et configurer des machines pour exécuter le daemon Docker. Avec Kitematic, l’interface graphique construite pour OS X et Windows, Docker Machine prend la suite de Boot2Docker qui permettait aux utilisateurs de ces deux OS de facilement créer leurs conteneurs. Les machines sont créées avec une distribution linux boot2docker ou Ubuntu, ce qui peut convenir à la plupart des utilisateurs. Dans mon cas, j’avais besoin d’utiliser une distribution CentOS 7 pour faire tourner mon daemon Docker. Je vous propose donc dans cet article de découvrir comment utiliser Vagrant pour instancier les machines et Docker Machine pour les configurer

Voyons tout d’abord ce que fait Docker Machine. A l’aide d’un driver, Docker Machine va créer une machine virtuelle dans le cloud (Amazon Web Services, DigitalOcean, Google Cloud Platform, IBM Softlayer, OpenStack ou Microsoft Azure) ou bien sur un hyperviseur on premise (VMWare, Virtualbox, Hyper V). Selon le driver utilisé, Docker Machine va utiliser une image boot2docker ou Ubuntu 14.04 LTS. Une fois créée, la machine va être configurée via des commandes ssh pour installer la version souhaitée de Docker, générer des certificats TLS et activer le service Docker daemon. Toutes ces étapes étant réalisées, Docker Machine permet d’initialiser un environnement en ligne de commande pour utiliser la commande Docker local de votre ordinateur interfacée avec le daemon que vous venez de créer.

Prenons un exemple avec Virtualbox.

Pour créer une machine b2d :

$ docker-machine create -d virtualbox b2d

Pour configurer le shell :

$ eval "$(docker-machine env b2d)"

Docker peut maintenant être utilisé en local pour contrôler le serveur docker lancé dans notre machine.

$ docker ps -a 

A ce stade, vous pouvez déjà profiter de Docker pour réaliser vos développements. Nous ne nous attarderons pas sur le détail des commandes fournies par Docker Machine car la documentation est assez claire.

Pour pouvoir utiliser l’OS de votre choix sur la machine virtuelle nous allons maintenant préparer un fichier Vagrantfile. Nous utiliserons ensuite le driver générique de Docker Machine pour réaliser l’installation et la configuration de Docker en ssh.

Vagrant

Par défaut, Vagrant définit les machines avec des interfaces privées en NAT, en d’autres termes les machines virtuelles tournent sur des réseaux entièrement isolé de la machine hôte. Pour permettre à Docker Machine de communiquer avec le daemon Docker sans entrer dans une logique de forwarding de port, nous allons utiliser une interface private_network en dhcp.

Avec cette configuration, Virtualbox va déclarer une nouvelle interface réseau sur la machine hôte qui permettra de communiquer avec le réseau privé sur lequel sera créé notre machine virtuelle. Le but étant de pouvoir créer et tester des images Docker à partir d’un ordinateur de bureau, je monte un partage de fichier sur la home de mon utilisateur sur la machine Vagrant.

Dans le répertoire projet Vagrant créé pour l’occasion, il faut créer un fichier Vagrantfile qui décrira notre machine virtuelle.

# -*- mode: ruby -*-
# vi: set ft=ruby :
# Plugin vagrant-vbguest
class MyInstaller < VagrantVbguest::Installers::Linux

  # use /temp instead of /tmp
  def tmp_path
    '/temp/VBoxGuestAdditions.iso'
  end

  # use /media instead of /mnt
  def mount_point
    '/media'
  end

  def install(opts=nil, &block)
    communicate.sudo('yum -y install epel-release dkms gcc kernel-devel bzip2', opts, &block)
    super
    communicate.sudo('modprobe -a vboxguest vboxfs vboxvideo', opts, &block)
  end
end

# Vagrantfile
Vagrant.configure(2) do |config|  
   # Image Vagrant à utiliser comme source
   config.vm.box = "centos/7"
  # Déclaration du réseau privé en dhcp
  config.vm.network "private_network", type: "dhcp"
  # Montage du répertoire home sur la machine virtuelle
  # ATTENTION : remplacer ici par vos propres chemins
  #                                |                 |
  #                                |                 |
  #                                V                 V
  config.vm.synced_folder "/Users/slemesle", "/Users/slemesle", create: true

  config.ssh.insert_key       = false
  config.vm.provider "virtualbox" do |vb|
  #   Utilisation d’1Go de RAM
     vb.memory = "1024"
  #  Nom de la machine virtuelle dans l’hyperviseur
     vb.name = "docker-machine"
   end
  # Script de bootstrap de la machine
  config.ssh.pty = true
  config.vm.provision "shell", inline: <<-SHELL
    # installation des net-tools pour obtenir netstat
    sudo yum -y install net-tools
    # chargement des modules Virtualbox au démarrage de la machine
    cat <<EOT > /tmp/vbox.conf
# vboxguest vboxsf vboxvideo
install vboxguest  
install vboxfs  
install vboxvideo  
EOT  
    sudo mv /tmp/vbox.conf /etc/modprobe.d/
  SHELL
  config.vbguest.auto_update = true
  # Plugin vagrant-vbguest => https://github.com/dotless-de/vagrant-vbguest
  config.vbguest.no_remote = true
end  

Voyons dans le détail ce fichier, la classe MyInstaller est utilisée pour préparer la machine à l’ajout des Virtualbox Guest Additions qui permettent notamment de monter des répertoires partagé dans la VM. En l’occurence, nous installons les packages nécessaires à la compilation des modules noyau Linux pour VirtualBox. Après installation, la méthode ‘install’ s’assure que les modules sont bien chargés dans le noyau.

Pour le script de bootstrap, nous installons le paquet ‘net-tools’ pour obtenir la commande netstat utilisée par Docker Machine lors de la configuration.  La création du fichier vbox.conf correspond au chargement au démarrage des modules VirutalBox. Les dernières lignes configurent le plugin vagrant-vbguest pour gérer la mise à jour des modules Virtualbox de façon automatique.  Si votre image Vagrant bénéficie déjà de ces modules, vous pouvez sauter ces étapes.

Notez bien que pour utiliser ce Vagrantfile tel quel, vous devrez installer le plugin vagrant-vbguest qui se charge de l’installation des Virtual Box Guest Additions:

vagrant plugin install vagrant-vbguest

Nous pouvons maintenant lancer notre machine virtuelle, à l’aide de la commande ‘vagrant up’ depuis le répertoire projet.

Docker Machine

Passons maintenant à la configuration de Docker Machine pour utiliser notre nouvelle machine virtuelle. La première étape sera de récupérer l’adresse IP de la machine virtuelle. Attention à bien récupérer l’IP associée au réseau privé hôte et non celle associée au réseau NAT. Dans notre cas il suffira de se connecter en ssh sur la machine ‘vagrant ssh’ puis de copier l’IP de l’interface eth1.

echo $(ip addr show eth1 | grep -o 'inet [0-9]\+.[0-9]\+.[0-9]\+.[0-9]\+' | cut -d' ' -f2)  

Cette commande vous affichera l’IP que nous recherchons.

Nous pouvons maintenant créer la machine Docker :

docker-machine create -d generic \  
 --generic-ssh-user vagrant \
 --generic-ssh-key ~/.vagrant.d/insecure_private_key \
 --generic-ip-address addresse.ip.de.vm \
 docker-machine

Cette commande va installer le Docker sur la machine et générer les certificats TLS pour le serveur et l’authentification du client. Pour pouvoir commencer à travailler avec Docker il vous suffit maintenant de configurer votre terminal en lançant la commande :

eval "$(docker-machine env docker-machine)"  

A partir de là, vous pouvez utiliser le client Docker comme si le daemon tournait sur la machine hôte. Pour vérifier le bon fonctionnement de l’ensemble, lancez un simple :

docker ps -a  

Qui devrait vous afficher une liste de conteneurs vides et sans erreurs.

Conclusion

Avec cette méthode, nous autres utilisateurs d’OS non Linux, nous sommes capable d’utiliser Docker facilement pour nos développements en utilisant l’OS des machines cible de la production. L’un des gros avantages de Docker Machine est de configurer les certificats TLS pour assurer la protection des communications entre le client et le serveur ainsi que l’authentification du client par certificat. Cette fonctionnalité manque tout de même de documentation et souffre de quelques bugs.

Dans l’ensemble Docker Machine donne une bonne impression tant que l’on reste sur une image boot2docker dans Virtualbox. Mais dès que l’on sort de ce sentier battu, l’absence de documentation implique des difficultés. Par exemple, l’utilisation de netstat pour vérifier les ports en écoute TCP n’est spécifiée nulle part, j’ai dû aller chercher dans le code pour identifier le problème lors du check final de l’installation.

Ainsi, Docker Machine est à considérer comme un bon outil pour faciliter les développements Docker (création d’images et tests de conteneurs) depuis une machine hôte. Pour ce qui est de son utilisation sur des instances Cloud, je ne vois aucune plus value. Les outils de provisionning comme Salt ou Ansible répondent parfaitement au besoin et je ne vois que peu d’intérêt à pouvoir interfacer un client Docker local avec des machines déployées sur AWS par exemple.