Les configurations doivent être testées et éprouvées. Surtout quand c’est aussi aisé avec des outils de configuration management tels que Puppet, Ansible, ou SaltStack. Mais il est parfois délicat d’obtenir une infrastructure disponible. Personnellement, pour mes développements, mes essais, et mes premières validations, je provisionne des VMs Virtualbox sur mon laptop à l’aide de Vagrant.
Le cas d’usage du lab’ nécessitant plusieurs VMs d’usage différent et donc de ressources variées a été présenté dans mon précédent article pour la partie provision. Voyons donc maintenant comment se faciliter la vie côté réseau en un exposé étape par étape.
$ vagrant status IP: Ports: Host: 192.168.10.10 22=>2210 kiwi.oloc 192.168.10.11 22=>2211 koala-01.oloc 192.168.10.12 22=>2212 koala-02.oloc Current machine states: kiwi running (virtualbox) koala-01 running (virtualbox) koala-02 running (virtualbox)
Voyons comment en arriver à ce vagrant status
répondant à notre besoin d’un lab’ complet aux VMs variées dans un réseau ad hoc…
Repartons de la situation à la fin du précédent article. La première nécessité est de constituer un réseau ad hoc. L’idée est simple, nous allons alimenter les /etc/hosts
des différentes VMs avec les adresses des autres. Et pour cela nous allons profiter de l’avantage du fichier provision.json
contenant le dernier digit des adresses IP. Plaçons-nous dans le cas concret suivant :
$ jq '.' provision.json [ { "name": "kiwi", "iplastdigit": "10" }, { "name": "koala-01", "iplastdigit": "11" }, { "name": "koala-02", "iplastdigit": "12" } ]
Procédons par étape et prenons le temps d’un petit atelier ruby. Constituons un premier petit script oloc.rb pour valider notre apport :
require 'json' SubNet="192.168.10" Domain="oloc" fileContent = JSON.parse(File.read("./provision.json"), symbolize_names: true) puts fileContent
Pour les moins aguerris au ruby, quelques petites explications.
Nous ajoutons le module ‘json’ afin de pouvoir lire aisément ce format, puis nous valorisons le SubNet
et le Domain
.
Le JSON.parse
permet de remplir la variable fileContent
du contenu du fichier. Puis le puts
affiche cette variable.
Si on lance ce script on obtient ceci :
$ ruby oloc.rb {:name=>"kiwi", :iplastdigit=>"10"} {:name=>"koala-01", :iplastdigit=>"11"} {:name=>"koala-02", :iplastdigit=>"12"}
Notez au passage que chaque ligne est ce qu’on a définit comme étant un node dans notre Vagrantfile.
Maintenant au lieu d’afficher le fileContent
, nous allons le parcourir pour constituer le nécessaire au format /etc/hosts
:
fileContent = JSON.parse(File.read("./provision.json"), symbolize_names: true) # puts fileContent fileContent.each do |node| @hostsList = "#{@hostsList}#{SubNet}.#{node[:iplastdigit]}\t#{node[:name]}.#{Domain} #{node[:name]}\n" end puts @hostsList
Nous réalisons une boucle sur le contenu du fichier, le fameux fileContent
et nous remplissons récursivement la variable hostsList
.
Si on le lance notre script avec cet aménagement, on obtient bien de quoi alimenter nos /etc/hosts
:
$ ruby oloc.rb 192.168.10.10 kiwi.oloc kiwi 192.168.10.11 koala-01.oloc koala-01 192.168.10.12 koala-02.oloc koala-02
Afin de parfaire notre alimentation au sein du Vagrantfile, il faudra y ajouter en bonne place les lignes suivantes :
hostsListUpdate="echo -e \"#{@hostsList}\\n\" >> /etc/hosts" machine.vm.provision :shell, :inline => hostsListUpdate
Reprenons donc le code de l’article précédent cette fois avec une box en Debian Jessie 64 (ça change) :
# -*- mode: ruby -*- # vi: set ft=ruby : SubNet="192.168.10" Domain="oloc" fileContent = JSON.parse(File.read("./provision.json"), symbolize_names: true) fileContent.each do |node| @hostsList = "#{@hostsList}#{SubNet}.#{node[:iplastdigit]}\t#{node[:name]}.#{Domain} #{node[:name]}\n" end # To update the /etc/hosts with all the nodes hostsListUpdate="echo -e \"#{@hostsList}\\n\" >> /etc/hosts" # Vagrantfile API/syntax version. # Don't touch unless you know what you're doing! VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "debian/jessie64" fileContent.each do |node| config.vm.define node[:name] do |machine| machine.vm.hostname = "#{node[:name]}.#{Domain}" machine.vm.network :private_network, ip: "#{SubNet}.#{node[:iplastdigit]}" machine.vm.provision :shell, :inline => hostsListUpdate machine.vm.provider "virtualbox" do |vb| vb.customize ["modifyvm", :id, "--name", node[:name]] end end end end
Petite vérification sur le kiwi tout autant que sur les deux koalas :
$ vagrant up $ vagrant ssh kiwi vagrant@kiwi:~$ cat /etc/hosts 127.0.0.1 kiwi.oloc kiwi 127.0.0.1 localhost 127.0.1.1 jessie.raw jessie 192.168.10.10 kiwi.oloc kiwi 192.168.10.11 koala-01.oloc koala-01 192.168.10.12 koala-02.oloc koala-02
Un premier aménagement bien satisfaisant ! Continuons donc.
Lorsqu’on sollicite la commande vagrant ssh
, en fait Vagrant utilise un port forwarding du port 22 vers un port 2222 dans le cas d’une machine unique vers un port 2200 incrémenté dans le cas multi-machine.
Pour différentes raisons, il peut être intéressant de fixer ces ports. Dans le cas de notre lab’, il semble plus simple (même si en terme de réseau ça n’a rien à voir) d’ajouter le dernier digit de l’IP à 2200.
Attention, ça va aller très vite. Il suffit d’ajouter :
vb.customize ["modifyvm", :id, "--natpf1", ",tcp,127.0.0.1,22#{node[:iplastdigit]},,22"]
Pour aider à la compréhension :
$ vboxmanage modifyvm --help
VBoxManage modifyvm
(...)
[--natpf<1-N> [<rulename>],tcp|udp,[<hostip>],
<hostport>,[<guestip>],<guestport>]
Ca va si vite à implémenter que l’on reste sur sa faim, et qu’on aimerait bien ajouter un petit quelque chose. Et si nous affichions ce port forwarding
? Et pourquoi pas l’ajouter lorsqu’on affiche le vagrant status
? Allez c’est parti !
A l’instar de la liste des hosts
, nous allons profiter de la boucle de lecture du fileContent
pour créer une petite topology
, initialisée et alimentée ainsi :
topology = "%-15s %-10s %s" % ["IP:","Ports:","Host:"] topology = "#{topology}\n%-15s 22=>22%-4s %s" % \ ["#{SubNet}.#{node[:iplastdigit]}","#{node[:iplastdigit]}","#{node[:name]}.#{Domain}"]
Enfin pour le voir s’afficher lorsque l’on fait un vagrant status
il faut ajouter cet affichage conditionné :
if ARGV[0] == 'status' puts "#{topology}\n" end
Ce qui nous donne le résultat de la toute première illustration.
Pour finir et avoir une vue globale, l’ensemble du code est :
# -*- mode: ruby -*- # vi: set ft=ruby : SubNet = "192.168.10" Domain = "oloc" topology = "%-15s %-10s %s" % ["IP:","Ports:","Host:"] fileContent = JSON.parse(File.read("./provision.json"), symbolize_names: true) fileContent.each do |node| @hostsList = "#{@hostsList}#{SubNet}.#{node[:iplastdigit]}\t#{node[:name]}.#{Domain} #{node[:name]}\n" topology = "#{topology}\n%-15s 22=>22%-4s %s" % \ ["#{SubNet}.#{node[:iplastdigit]}","#{node[:iplastdigit]}","#{node[:name]}.#{Domain}"] end # To update the /etc/hosts with all the nodes hostsListUpdate="echo -e \"#{@hostsList}\\n\" >> /etc/hosts" # Status displays topology if ARGV[0] == 'status' puts "#{topology}\n\n" end # Vagrantfile API/syntax version. # Don't touch unless you know what you're doing! VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "debian/jessie64" fileContent.each do |node| config.vm.define node[:name] do |machine| machine.vm.hostname = "#{node[:name]}.#{Domain}" machine.vm.network :private_network, ip: "#{SubNet}.#{node[:iplastdigit]}" machine.vm.provision :shell, :inline => hostsListUpdate machine.vm.provider 'virtualbox' do |vb| vb.customize ["modifyvm", :id, "--name", node[:name]] vb.customize ["modifyvm", :id, "--natpf1", ",tcp,127.0.0.1,22#{node[:iplastdigit]},,22"] end end end end
https://github.com/oloc/vagrant-ansible/releases/tag/v1.2
—