Vagrant : ad hoc

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

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *