Créer son environnement de développement avec Vagrant et VirtualBox

Lorsque vous travaillez sur un projet, un jour vous devrez déployer votre application pour la tester dans des conditions proches de l’environnement de production. Au lieu de configurer tout l’environnement localement ou de louer des serveurs supplémentaires, il est plus intéressant d’utiliser des machines virtuelles.

Deux outils vont nous faciliter la tâche : VirtualBox va nous permettre de créer des machines virtuelles, et Vagrant de les gérer et distribuer facilement, grâce à un fichier de configuration. Une commande permet de lancer la machine virtuelle, en la téléchargement au préalable depuis un serveur web. Vous n’aurez qu’à distribuer ce fichier de configuration à vos collègues pour qu’ils puissent travailler sur un environnement totalement identique.

Dans un premier temps, installez les outils gratuits VirtualBox et Vagrant sur votre système. Cette procédure a été testée sur Mac mais elle devrait être quasiment identique sur GNU/Linux.

On peut trouver des machines virtuelles toutes prêtes pour Vagrant, mais je préfère créer la mienne de A à Z car je n’ai pas forcément confiance en leur contenu. De plus, on ne sait pas exactement comment elles ont été configurées.

Créer sa propre machine virtuelle Vagrant

La première étape consiste à créer une machine virtuelle compatible avec Vagrant. Concernant VirtualBox, je préfère utiliser les outils en ligne de commande VBoxManage. Tout se fera depuis un terminal. Pour simplifier la procédure, j’ai utilisé une variable VM_NAME pour définir le nom de la machine virtuelle à créer.

# définition du nom de la VM sous Mac ou GNU/Linux
VM_NAME='Debian7-64'
# utilisation de la variable
echo "$VM_NAME"

C’est parti !

L’installation se concentre sur une Debian 7 en 64 bit, c’est donc la raison pour laquelle j’ai nommé ma machine virtuelle : Debian7-64. Vous pouvez utiliser une autre distribution, il faudra juste adapter.

On crée donc la variable, de sorte de ne pas avoir à retaper le nom de la machine virtuelle systématiquement, et si vous souhaitez l’appeler autrement, ça n’aura pas d’impact sur les commandes à taper.

Créer la nouvelle machine virtuelle

VBoxManage createvm --name "$VM_NAME" --register

Ceci crée le fichier XML de définition de la machine virtuelle et la déclare dans VirtualBox de sorte qu’elle apparaisse dans la liste des machines existantes.

Définir les propriétés de la machine

La première étape consiste à indiquer à VirtualBox quel est le type de système d’exploitation qui va être installé.

VBoxManage modifyvm "$VM_NAME" --ostype Debian_64

Il s’agit d’une Debian 64 bit, mais si vous souhaitez installer un autre système, il faudra l’indiquer. Vous pourrez obtenir la liste des types disponibles grâce à la commande VBoxManage list ostype.

Ensuite, on indique la mémoire souhaitée. Dans mon cas, j’ai besoin de 512 Mo.

VBoxManage modifyvm "$VM_NAME" --memory 512

Ensuite, je choisis l’ordre d’amorçage des périphériques, DVD puis disque dur et rien pour les deux autres périphériques.

VBoxManage modifyvm "$VM_NAME" --boot1 dvd
VBoxManage modifyvm "$VM_NAME" --boot2 disk
VBoxManage modifyvm "$VM_NAME" --boot3 none
VBoxManage modifyvm "$VM_NAME" --boot4 none

Ensuite, je configure le réseau. Pour cela, j’indique que la première carte sera utilisée en tant que pont réseau depuis l’interface en0. Enfin, je précise que le câble est connecté.

VBoxManage modifyvm "$VM_NAME" --nic1 bridged
VBoxManage modifyvm "$VM_NAME" --bridgeadapter1 en0
VBoxManage modifyvm "$VM_NAME" --cableconnected1 on

Enfin, j’active l’ACPI, j’indique que le système est à l’heure universelle (UTC), je désactive USB car je n’en ai pas besoin et je change le temps d’affichage du logo VirtualBox afin de démarrer le système un poil plus vite.

VBoxManage modifyvm "$VM_NAME" --acpi on --ioapic on
VBoxManage modifyvm "$VM_NAME" --rtcuseutc on
VBoxManage modifyvm "$VM_NAME" --usb off
# minimise le temps d'apparition du logo vbox
VBoxManage modifyvm "$VM_NAME" --bioslogodisplaytime 1

Créer le disque dur

La machine est créée, mais pour le moment, elle n’a pas de disque dur. Les commandes suivantes permettent de créer le disque virtuel, de 15000 Mo (taille dynamique) avec un contrôleur SATA.

VBoxManage createhd --filename "$HOME/VirtualBox VMs/$VM_NAME/$VM_NAME.vdi" --size 15000
VBoxManage storagectl "$VM_NAME" --name "SATA" --add sata
VBoxManage storageattach "$VM_NAME" --storagectl "SATA" --port 0 --device 0 --type hdd --medium "$HOME/VirtualBox VMs/$VM_NAME/$VM_NAME.vdi"

Monter l’image ISO d’installation

Au premier démarrage de la machine virtuelle, il faudra installer le système de base. J’ai téléchargé la dernière version en date de Debian 7 version netinst, en 64 bit (prenez la version qui correspond à votre processeur). Je monte donc ce média pour pouvoir commencer l’installation.

VBoxManage storageattach "$VM_NAME" --storagectl "SATA" --port 1 --device 0 --type dvddrive --medium $HOME/Downloads/debian-7.4.0-amd64-netinst.iso

Lancer l’installation du système

À ce stade, la machine est prête à être installée.

VBoxManage startvm "$VM_NAME"

La machine démarre et l’installation peut commencer.

Pour information, il est possible de démarrer la machine en mode headless, c’est-à-dire qu’aucune fenêtre VirtualBox ne s’ouvre, mais ce n’est pas l’objet de cette page car l’installation doit alors se faire via le protocole RDP…

# pour information, seulement (installation via RDP)
VBoxHeadless --startvm "$VM_NAME"

Durant l’installation, si vous suivez les captures, vous allez créer un utilisateur nommé vagrant ayant pour mot de passe vagrant. Vous pouvez l’appeler autrement, ce n’est pas un problème, on en reparle plus tard. Lorsque l’installeur vous proposera d’installer les outils VirtualBox, ne le faites pas !

Configurer le système installé

Lorsque l’installation est terminée, le système redémarre, et c’est à ce moment-là que l’on configure le système invité pour qu’il puisse fonctionner avec Vagrant. Premièrement, on se connecte via l’identifiant vagrant ou celui que vous avez choisi, si tel est le cas.

En ce qui me concerne, j’aime bien utiliser Vim, donc c’est le premier paquet que j’installe et je le mets en éditeur par défaut.

sudo apt-get update
sudo apt-get install -y vim
sudo update-alternatives --config editor

Vagrant utilise le compte vagrant pour gérer la machine virtuelle. On va modifier la configuration de sudo pour que ce compte soit en mesure d’exécuter des commandes en tant qu’administrateur, sans mot de passe. Si vous avez nommé le compte autrement que vagrant, adaptez la configuration.

On édite le fichier de configuration de sudo.

sudo visudo

Et on ajoute à la fin du fichier, la ligne suivante.

vagrant ALL=(ALL) NOPASSWD: ALL

Ensuite, pour se connecter en SSH sans devoir saisir de mot de passe, Vagrant a besoin d’une clé sans phrase secrète. Je n’aime pas utiliser la clé par défaut car elle est disponible publiquement, et n’importe qui pourrait s’en servir pour se connecter à ma machine, dans la mesure où celle-ci serait accessible publiquement. C’est la raison pour laquelle je crée ici une nouvelle clé, depuis ma machine hôte.

ssh-keygen -b 2048 -t rsa -f ~/.ssh/my_vagrant_key

Une clé publique my_vagrant_key.pub ainsi que sa clé privée correspondante my_vagrant_key sont créées. Ne saisissez pas de phrase secrète.

Toujours depuis la machine hôte, on installe la clé publique sur la machine virtuelle (login vagrant sauf si vous l’avez changé et adresse IP de la machine virtuelle que vous pouvez obtenir en faisant sudo ifconfig). On transfère la clé publique via SSH et on l’ajoute au fichier ~/.ssh/authorized_keys.

cat ~/.ssh/my_vagrant_key.pub | ssh vagrant@ip "mkdir -p -m 700 ~/.ssh; cat >> ~/.ssh/authorized_keys; chmod 600 ~/.ssh/authorized_keys"

L’étape suivante consiste à installer les Guest Additions Tools de VirtualBox. Depuis la machine hôte, on charge le média d’installation.

VBoxManage storageattach "$VM_NAME" --storagectl "SATA" --port 1 --device 0 --type dvddrive --medium additions

Ensuite, sur la machine virtuelle, on installe les outils nécessaires pour effectuer l’installation, puis on monte le DVD contenant l’installeur et on installe.

sudo apt-get install -y dkms linux-headers-$(uname -r) build-essential
sudo mount -t iso9660 -o ro /dev/dvd /mnt
sudo /mnt/VBoxLinuxAdditions.run
sudo umount /mnt
sudo ln -s /opt/VBoxGuestAdditions-4.3.10/lib/VBoxGuestAdditions/ /usr/lib/

La machine virtuelle est maintenant prête. Avant de l’empaqueter, on va faire un peu de ménage : vider le cache d’APT, effacer l’historique de Bash ainsi que de Vim et stopper la machine virtuelle, le tout via SSH depuis la machine hôte pour éviter de laisser de nouveau des traces.

ssh -i ~/.ssh/my_vagrant_key vagrant@ip "rm -f .viminfo .bash_history; sudo rm -rf /root/.viminfo /root/.aptitude; sudo apt-get clean; sudo halt"

Ensuite, on démonte le média des Guest Additions Tools de VirtualBox.

VBoxManage storageattach "$VM_NAME" --storagectl "SATA" --port 1 --device 0 --type dvddrive --medium emptydrive

Empaqueter la machine virtuelle pour Vagrant

Voilà, il ne reste plus qu’à empaqueter la machine virtuelle fraichement créée.

vagrant package --base "$VM_NAME" --output "$VM_NAME".box

Un fichier portant l’extension box est créé. La machine virtuelle est prête à être utilisée avec Vagrant. Pour cela, on déposera le fichier généré sur un serveur web (ça marche avec un serveur web local).

Utiliser les machines virtuelles de Vagrant

Maintenant que la machine virtuelle est prête, voici comment l’utiliser.

Initialiser l’environnement

Le lancement d’une machine virtuelle se fait grâce à un fichier de configuration, nommé Vagrantfile. Il contient la définition d’une ou plusieurs machines virtuelles à lancer. Tout d’abord, on crée un dossier dans lequel on va mettre ce fameux fichier que l’on va créer grâce à la commande vagrant.

mkdir ma_config_vagrant
cd ma_config_vagrant
vagrant init

La quasi-totalité de ce fichier (syntaxe ruby) est en commentaire mais cela permet de bien voir ce qu’il est possible d’en faire. Voici le Vagrantfile de base que j’utilise pour lancer la machine virtuelle Debian7-64.box depuis mon serveur web local.

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|

	# url de la box à télécharger si non présente en local
	config.vm.box_url = "http://192.168.0.46:8080/Debian7-64.box"

	# nom de la box vagrant à démarrer (le modèle)
	config.vm.box = "d7"

	# les login / mot de passe / clé privée pour la connexion
	# à décommenter si vous n'utilisez pas les valeurs par défaut
	# c'est le cas pour la clé privée, ici
	# config.ssh.username = "vagrant"
	# config.ssh.password = "vagrant"
	config.ssh.private_key_path = "~/.ssh/my_vagrant_key"

end

Ensuite, toujours dans le dossier contenant le fichier Vagrantfile, on démarre la machine virtuelle.

vagrant up

Le fichier Debian7-64.box téléchargé depuis le serveur web est stocké dans le dossier local ~/.vagrant.d/boxes/d7, où d7 représente le nom que vous avez choisi pour stocker la machine virtuelle. Ainsi, si vous souhaitez réutiliser cette machine virtuelle, il ne sera pas nécessaire de la télécharger de nouveau. Les login, mot de passe et clé sont à spécifier seulement si vous n’avez pas utilisé les valeurs par défaut, ce qui est mon cas concernant la clé.

On peut stopper la machine virtuelle sans perdre son état actuel et il est toujours possible de la redémarrer via vagrant up.

vagrant halt

On peut détruire la machine virtuelle, elle disparait de VirtualBox seulement, son état actuel est perdu mais il est toujours possible de la recréer ensuite sans la télécharger de nouveau via vagrant up.

vagrant destroy

On peut se connecter à la machine virtuelle en SSH sans connaître son adresse IP.

vagrant ssh

Configurer la machine virtuelle

Pouvoir lancer une machine virtuelle, c’est bien, mais si on ne peut rien en faire depuis l’extérieur, ça ne sert à rien. Ici, on ajoute une redirection de port pour pouvoir accéder à un serveur web (port 80) depuis le port 8080 de la machine hôte. Ainsi, si vous ouvrez une page web à l’adresse http://localhost:8080, vous visualiserez le contenu du serveur web installé sur la machine virtuelle, à condition d’avoir installé ce serveur, évidemment. Il faut une ligne par port à rediriger. Un peu plus bas, on définit un nom d’hôte, ce nom est affiché dans le prompt, ce qui permet d’identifier facilement la machine sur laquelle on est connecté. Ensuite, on choisit de démarrer en mode graphique si besoin, on définit la mémoire à utiliser et on indique le nom à afficher dans VirtualBox.

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|

	# nom de la box vagrant à démarrer (le modèle)
	config.vm.box = "d7"

	# url de la box à télécharger si non présente en local
	config.vm.box_url = "http://192.168.0.46:8080/Debian7-64.box"

	# les login / mot de passe / clé privée pour la connexion
	# à décommenter si vous n'utilisez pas ces valeurs par défaut
	# c'est le cas pour la clé privée, ici
	# config.ssh.username = "vagrant"
	# config.ssh.password = "vagrant"
	config.ssh.private_key_path = "~/.ssh/my_vagrant_key"

	# redirection du port local 8080 vers le port 80 de la box. si ce port
	# est déjà pris, un autre sera utilisé grâce à auto_correct.
	# par défaut, le port 2222 est également redirigé vers le port 22 pour ssh
	config.vm.network "forwarded_port", guest: 80, host: 8080, auto_correct: true

	# nom d'hôte affiché dans le prompt
	config.vm.host_name = "debian7"

	config.vm.provider "virtualbox" do |vb|
		# booter en mode graphique
		vb.gui = true
		# changer la mémoire disponible
		vb.customize ["modifyvm", :id, "--memory", "512"]
		# définit le nom affiché dans virtualbox
		vb.customize ["modifyvm", :id, "--name", "d7"]
	end
end

Rempaqueter une machine virtuelle

Parfois, il peut arriver que l’on ait besoin de recréer une machine virtuelle à partir d’une autre. On utilise pour cela la commande package de Vagrant. Ici, je lance la machine virtuelle, j’installe Apache, j’éteins le système et je rempaquette la machine virtuelle.

vagrant up
vagrant ssh
sudo apt-get -y install apache2
sudo halt
vagrant package --output debian-apache.box

Vous pouvez ensuite utiliser la machine virtuelle fraichement empaquetée comme précédemment, en la mettant sur un serveur web.

Configurer plusieurs machines virtuelles

Utiliser une machine virtuelle, c’est bien, mais si vous voulez simuler un environnement comprenant plusieurs serveurs, c’est possible avec Vagrant. La déclaration des machines virtuelles se fait différemment dans le fichier Vagrantfile. Pour chaque machine, il faut utiliser une instruction define. L’argument de define permet d’identifier la machine virtuelle à démarrer, arrêter, etc. Ensuite, dans le bloc define, on configure chaque machine virtuelle individuellement comme on l’aurait fait s’il n’y en avait eu qu’une.

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|

	config.vm.define "serveur1" do |conf|
		conf.vm.box = "d7"
		conf.vm.box_url = "http://192.168.0.46:8080/Debian7-64.box"
		conf.ssh.private_key_path = "~/.ssh/my_vagrant_key"
		conf.vm.host_name = "serveur1"

		conf.vm.provider "virtualbox" do |vb|
			vb.customize ["modifyvm", :id, "--memory", "512"]
			vb.customize ["modifyvm", :id, "--name", "serveur1"]
		end
	end

	config.vm.define "serveur2" do |conf|
		conf.vm.box = "d7"
		conf.vm.box_url = "http://192.168.0.46:8080/Debian7-64.box"
		conf.ssh.private_key_path = "~/.ssh/my_vagrant_key"
		conf.vm.host_name = "serveur2"

		conf.vm.provider "virtualbox" do |vb|
			vb.customize ["modifyvm", :id, "--memory", "512"]
			vb.customize ["modifyvm", :id, "--name", "serveur2"]
		end
	end

	config.vm.define "serveur3" do |conf|
		conf.vm.box = "d7apache"
		conf.vm.box_url = "http://192.168.0.46:8080/debian-apache.box"
		conf.ssh.private_key_path = "~/.ssh/my_vagrant_key"
		conf.vm.network "forwarded_port", guest: 80, host: 8080, auto_correct: true
		conf.vm.host_name = "serveur3"

		conf.vm.provider "virtualbox" do |vb|
			vb.customize ["modifyvm", :id, "--memory", "512"]
			vb.customize ["modifyvm", :id, "--name", "serveur3"]
		end
	end
end

Les deux machines serveur1 et serveur2 utilisent la même machine virtuelle de base d7. La troisième, serveur3, utilise celle que l’on vient de rempaqueter.

On peut lancer ces trois machines virtuelles en même temps en utilisant la commande vagrant up.
Cependant, si on souhaite ne lancer que serveur1, on utilise la commande vagrant up serveur1. C’est le même principe avec halt ou destroy. Pour se connecter en SSH ou rempaqueter une machine virtuelle, on utilisera les commandes déjà vues précédemment, mais en passant le nom de la machine virtuelle concernée comme serveur1.

Vous pouvez créer tout type de machine virtuelle, et utiliser une ou plusieurs fois chaque modèle… Les possibilités sont infinies.

Permettre la communication entre les machines virtuelles

Avec Vagrant, par défaut, les machines virtuelles utilisent un réseau NAT ce qui ne facilite pas la communication entre machines. La solution consiste à définir un réseau privé de machines afin qu’elles puissent communiquer, tout en permettant également à la machine hôte de communiquer avec elles, mais aucune autre machine du réseau ne pourra y accéder.

Dans l’exemple suivant, je définis deux adresses IP 10.0.0.2 et 10.0.0.3. La machine hôte se voit attribuer automatiquement l’adresse IP 10.0.0.1. On peut même créer une redirection de port NAT pour permettre à une machine extérieure d’accéder au point d’entrée de notre application (un reverse proxy ou un load balancer, par exemple).

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|

	config.vm.define "serveur1" do |conf|
		conf.vm.box = "d7"
		conf.vm.box_url = "http://192.168.0.46:8080/Debian7-64.box"
		conf.ssh.private_key_path = "~/.ssh/my_vagrant_key"

		conf.vm.network "forwarded_port", guest: 80, host: 8080, auto_correct: true
		conf.vm.network "private_network", ip: "10.0.0.2"

		conf.vm.host_name = "serveur1"

		conf.vm.provider "virtualbox" do |vb|
			vb.customize ["modifyvm", :id, "--memory", "512"]
			vb.customize ["modifyvm", :id, "--name", "serveur1"]
		end
	end

	config.vm.define "serveur2" do |conf|
		conf.vm.box = "d7"
		conf.vm.box_url = "http://192.168.0.46:8080/Debian7-64.box"
		conf.ssh.private_key_path = "~/.ssh/my_vagrant_key"

		conf.vm.network "private_network", ip: "10.0.0.3"

		conf.vm.host_name = "serveur2"

		conf.vm.provider "virtualbox" do |vb|
			vb.customize ["modifyvm", :id, "--memory", "512"]
			vb.customize ["modifyvm", :id, "--name", "serveur2"]
		end
	end
end

Si on souhaite faire tester les collègues non développeurs, ou si l’application doit fonctionner avec des serveurs redondants devant être accessibles depuis l’extérieur, la configuration en réseau public est possible. Pour cela, on utilise public_network au lieu de private_network.

config.vm.network "public_network", :bridge => 'en0: Wi-Fi (AirPort)'

On précise l’interface à utiliser pour créer le pont réseau via l’argument :bridge. Si vous ne connaissez pas le nom exact de l’interface à utiliser, omettez l’argument :bridge et Vagrant vous proposera alors la liste des interfaces disponibles, que vous pourrez alors copier-coller.

Commandes diverses

Si une machine virtuelle a été modifiée sur votre serveur web (nouvelle version, par exemple), elle ne sera pas téléchargée de nouveau si vous aviez déjà l’ancienne en local. Pour obtenir la version à jour, il faudra effacer ladite machine virtuelle.

Pour effacer une machine virtuelle locale afin de la retélécharger :

vagrant box remove nomdemabox

Et pour lister toutes les machines virtuelles présentes en local :

vagrant box list

Aller plus loin

Enfin, vous aurez sans doute besoin de configurer des répertoires à synchroniser entre le système hôte et le système invité ou encore provisionner une machine virtuelle au moment de son premier lancement, via des scripts ou outils divers. Tout ceci est consultable dans la documentation de Vagrant.

Voilà, vous avez tout les cartes en main pour pouvoir effectuer vos tests.