Zakaria Lounes

</Développeur Full-Stack>


Configurer automatiquement ses machines virtuelles avec Vagrant

Configurer automatiquement ses machines virtuelles avec Vagrant

Installation de Vagrant et Chef

Vous avez dit Vagrant ?

Avec Vagrant il n'a jamais était aussi simple de configurer automatiquement ses machines.

Nous allons le voir ensemble, mais tout d'abord nous devons concevoir le schéma de notre réseau afin d'éviter de se perdre avec l'ensemble des adresses ip, gateway et netmask.

Schéma

Notre lab sera composée de quatres machines :

  • un serveur web1 doté d'une interface reseau privé
  • un serveur web2 doté d'une interface reseau privé
  • une gateway dotée de deux interfaces reseau : une privée et une en pont
  • un proxy doté d'une interface reseau privé

Initialisation du dossier chef

À la racine de votre projet, créer le dossier chef-repo servant au provisioning de vos VM Vagrant

chef generate app chef-repo

Supprimer le cookbook généré

rm -R chef-repo/cookbooks/chef-repo

Générer le cookbook default-route

Ce cookbook vous permettra de relier la route par défaut de vos machines à la gateway, permettant ainsi la communication avec l'ensemble des machines du réseau et l'internet

cd chef-repo
chef generate cookbook default-route

Créer le dossier attributes dans le cookbook default-route et créer le fichier default.rb, afin de pouvoir passer des variables de Vagrant à votre cookbook default-route.

cd cookbooks/default-route
mkdir attributes
nano attributes/default.rb

Éditer le fichier chef-repo/cookbooks/default-route/attributes/default.rb.

default['route']['ip_gateway'] = '192.168.254.249'
default['route']['device'] = 'eth1'

Éditer le recipe chef-repo/cookbooks/default-route/recipes/default.rb.

#
# Cookbook Name:: default-route
# Recipe:: default
#
# Copyright (c) 2015 The Authors, All Rights Reserved.

route "0.0.0.0/0" do
  gateway node['route']['ip_gateway']
  device node['route']['device']
end

Installer le cookbook apache2

Depuis votre terminal, tapez :

cd chef-repo
knife cookbook site download apache2
knife cookbook site install apache2

Cela aura pour conséquence d'ajouter le cookbook apache2 dans votre dossier chef-repo/cookbooks

Installer le cookbook nginx

Depuis votre terminal, tapez :

cd chef-repo
knife cookbook site download nginx
knife cookbook site install nginx

Configurations des machines

WEB1

Créer le dossier Web1 et le fichier de configuration Vagrantfile

mkdir Web1
nano Web1/Vagrantfile

Vagrantfile

# -*- mode: ruby -*-
# vi: set ft=ruby :


app_vm_name = "devops-Web1"
app_name = "web1"
app_ip = "192.168.254.251"
app_netmask = "255.255.255.248"
app_ip_gateway = "192.168.254.249"
app_docroot = "./web1_docroot"

Vagrant.configure(2) do |conf|
  conf.vm.box = "jubianchi/debian-wheezy-chef-i386"

  conf.vm.define app_name
  conf.vm.provider :virtualbox do |vb|
    vb.name = app_vm_name
  end

  conf.vm.hostname = app_name
  conf.vm.synced_folder app_docroot, "/var/www", :mount_options => ["dmode=777", "fmode=666"]

  conf.vm.network "private_network", ip: app_ip, netmask: app_netmask

  conf.vm.provision "chef_solo" do |chef|
    chef.cookbooks_path = "../chef-repo/cookbooks"
    chef.json = {
      :route => {
        :apache => {
          :default_site_enabled => true
        },
        :ip_gateway => app_ip_gateway
      }
    }
    chef.add_recipe "apache2"
    chef.add_recipe "default-route"
  end
end

WEB2

Créer le dossier Web2 et le fichier de configuration Vagrantfile

mkdir Web2
nano Web2/Vagrantfile

Vagrantfile

# -*- mode: ruby -*-
# vi: set ft=ruby :


app_vm_name = "devops-Web2"
app_name = "web2"
app_ip = "192.168.254.252"
app_netmask = "255.255.255.248"
app_ip_gateway = "192.168.254.249"
app_docroot = "./web2_docroot"

Vagrant.configure(2) do |conf|
  conf.vm.box = "jubianchi/debian-wheezy-chef-i386"

  conf.vm.define app_name
  conf.vm.provider :virtualbox do |vb|
    vb.name = app_vm_name
  end

  conf.vm.hostname = app_name
  conf.vm.synced_folder app_docroot, "/var/www", :mount_options => ["dmode=777", "fmode=666"]

  conf.vm.network "private_network", ip: app_ip, netmask: app_netmask

  conf.vm.provision "chef_solo" do |chef|
    chef.cookbooks_path = "../chef-repo/cookbooks"
    chef.json = {
      :route => {
        :apache => {
          :default_site_enabled => true
        },
        :ip_gateway => app_ip_gateway
      }
    }
    chef.add_recipe "apache2"
    chef.add_recipe "default-route"
  end
end

Proxy

Créer le dossier Proxy et le fichier de configuration Vagrantfile

mkdir Proxy
touch Proxy/Vagrantfile

Générer le cookbook proxyconf

chef generate cookbook proxyconf

Éditer le recipe chef-repo/cookbooks/proxyconf/recipes/default.rb.

#
# Cookbook Name:: proxyconf
# Recipe:: default
#
# Copyright (c) 2015 The Authors, All Rights Reserved.

execute 'enable_devops' do
  command 'nxensite devops'
  action :nothing
end

execute 'delete_default' do
  command 'rm /etc/nginx/sites-available/default'
  action :nothing
end

service 'nginx' do
  supports :status => true, :restart => true, :reload => true
end

template '/etc/nginx/sites-available/devops' do
  source 'devops'
  owner 'root'
  group 'root'
  mode '0644'
  notifies :run, 'execute[enable_devops]', :immediately
  notifies :run, 'execute[delete_default]', :immediately
  notifies :reload, 'service[nginx]', :immediately
end

Créer le virtual host devopschef-repo/cookbooks/proxyconf/templates/debian/devops.

upstream devops {
  server 192.168.254.251;
  server 192.168.254.252;
}
server {
  listen   80;

  location / {
    proxy_pass http://devops;
  }
}

Éditer le Vagrantfile

# -*- mode: ruby -*-
# vi: set ft=ruby :


app_vm_name = "devops-Proxy"
app_name = "proxy"
app_ip = "192.168.254.250"
app_netmask = "255.255.255.248"
app_ip_gateway = "192.168.254.249"

Vagrant.configure(2) do |conf|
  conf.vm.box = "jubianchi/debian-wheezy-chef-i386"

  conf.vm.define app_name
  conf.vm.provider :virtualbox do |vb|
    vb.name = app_vm_name
  end

  conf.vm.hostname = app_name
  conf.vm.network "private_network", ip: app_ip, netmask: app_netmask
  conf.vm.network "forwarded_port", guest: 80, host: 8042

  conf.vm.provision "chef_solo" do |chef|
    chef.cookbooks_path = "../chef-repo/cookbooks"
    chef.json = {
      :route => {
	:ip_gateway => app_ip_gateway
      }
    }
    chef.add_recipe "nginx"
    chef.add_recipe "default-route"
    chef.add_recipe "proxyconf"
  end
end

Gateway

Créer le dossier Gateway et le fichier de configuration Vagrantfile

mkdir Gateway
touch Gateway/Vagrantfile

Générer le cookbook router

chef generate cookbook router

Éditer le recipe chef-repo/cookbooks/router/recipes/default.rb.

#
# Cookbook Name:: router
# Recipe:: default
#
# Copyright (c) 2015 The Authors, All Rights Reserved.

execute 'ip_forward' do
  command '/etc/network/if-up.d/iptables'
  action :nothing
end

template '/etc/network/if-up.d/iptables' do
  source 'iptables'
  owner 'root'
  group 'root'
  mode '0755'
  notifies :run, 'execute[ip_forward]', :immediately
end

Créer le script iptableschef-repo/cookbooks/default-route/templates/default/iptables.

#!/bin/sh                                                                                                                                                                                                                                     

PATH=/usr/sbin:/sbin:/bin:/usr/bin

lan="eth1"
web="eth2"

#                                                                                                                                                                                                                                             
# delete all existing rules.                                                                                                                                                                                                                  
#                                                                                                                                                                                                                                             
iptables -F
iptables -t nat -F
iptables -t mangle -F
iptables -X

# Always accept loopback traffic                                                                                                                                                                                                              
iptables -A INPUT -i lo -j ACCEPT

# Allow established connections, and those not coming from the outside                                                                                                                                                                        
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -m state --state NEW ! -i "$web" -j ACCEPT
iptables -A FORWARD -i "$web" -o "$lan" -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -i "$lan" -p tcp --dport 80 -d 192.168.254.250 -j ACCEPT

# Allow outgoing connections from the LAN side.                                                                                                                                                                                               
iptables -A FORWARD -i "$lan" -o "$web" -j ACCEPT

# Masquerade.                                                                                                                                                                                                                                 
iptables -t nat -A POSTROUTING -o "$web" -j MASQUERADE

# Don't forward from the outside to the inside.                                                                                                                                                                                               
iptables -A FORWARD -i "$web" -o "$web" -j REJECT

# Enable routing.                                                                                                                                                                                                                             
echo 1 > /proc/sys/net/ipv4/ip_forward

VagrantFile

# -*- mode: ruby -*-
# vi: set ft=ruby :

GATEWAY = exec 'netstat -rn | grep 192.168 | grep default | tr -s " " | cut -d" " -f 2'

app_vm_name = "devops-Gateway"
app_name = "gateway"
app_ip = "192.168.254.249"
app_netmask = "255.255.255.248"

Vagrant.configure(2) do |conf|
  conf.vm.box = "jubianchi/debian-wheezy-chef-i386"

  conf.vm.define app_name
  conf.vm.provider :virtualbox do |vb|
    vb.name = app_vm_name
  end

  conf.vm.hostname = app_name
  conf.vm.network "private_network", ip: app_ip, netmask: app_netmask
  conf.vm.network "public_network"

  conf.vm.provision "chef_solo" do |chef|
    chef.cookbooks_path = "../chef-repo/cookbooks"
    chef.roles_path = "../chef-repo/roles"
    chef.json = {
      :route => {
        :ip_gateway => GATEWAY,
        :device => "eth2"
      }
    }
    chef.add_recipe "default-route"
    chef.add_recipe "router"
  end
end

Utiliser des roles

Afin d'appliquer la méthodologie DRY, Chef met à disposition les roles.
Créer le dossier roles dans le dossier chef-repo

mkdir chef-repo/roles

Role web-server

Dans le dossier chef-repo/roles, créer le fichier web-server.json

nano web-server.json

Éditer le fichier web-server.json

{
    "name": "web-server",
    "description": "This is an example role defined as JSON",
    "chef_type": "role",
    "json_class": "Chef::Role",
    "default_attributes": {
      "apache": {
        "default_site_enabled": true
      }
    },
    "override_attributes": {
    },
    "run_list": [
        "recipe[apache2]",
        "recipe[default-route]"
    ]
}

Éditer les Vagrantfile de Web1 et Web2 en conséquence.

Vagrantfile de Web1

# -*- mode: ruby -*-
# vi: set ft=ruby :


app_vm_name = "devops-Web1"
app_name = "web1"
app_ip = "192.168.254.251"
app_netmask = "255.255.255.248"
app_ip_gateway = "192.168.254.249"
app_docroot = "./web1_docroot"

Vagrant.configure(2) do |conf|
  conf.vm.box = "jubianchi/debian-wheezy-chef-i386"

  conf.vm.define app_name
  conf.vm.provider :virtualbox do |vb|
    vb.name = app_vm_name
  end

  conf.vm.hostname = app_name
  conf.vm.synced_folder app_docroot, "/var/www", :mount_options => ["dmode=777", "fmode=666"]

  conf.vm.network "private_network", ip: app_ip, netmask: app_netmask

  conf.vm.provision "chef_solo" do |chef|
    chef.cookbooks_path = "../chef-repo/cookbooks"
    chef.roles_path = "../chef-repo/roles"
    chef.json = {
      :route => {
        :ip_gateway => app_ip_gateway
      }
    }
    chef.add_role("web-server")
  end
end

Vagrantfile de Web2

# -*- mode: ruby -*-
# vi: set ft=ruby :


app_vm_name = "devops-Web2"
app_name = "web2"
app_ip = "192.168.254.252"
app_netmask = "255.255.255.248"
app_ip_gateway = "192.168.254.249"
app_docroot = "./web2_docroot"

Vagrant.configure(2) do |conf|
  conf.vm.box = "jubianchi/debian-wheezy-chef-i386"

  conf.vm.define app_name
  conf.vm.provider :virtualbox do |vb|
    vb.name = app_vm_name
  end

  conf.vm.hostname = app_name
  conf.vm.synced_folder app_docroot, "/var/www", :mount_options => ["dmode=777", "fmode=666"]

  conf.vm.network "private_network", ip: app_ip, netmask: app_netmask

  conf.vm.provision "chef_solo" do |chef|
    chef.cookbooks_path = "../chef-repo/cookbooks"
    chef.roles_path = "../chef-repo/roles"
    chef.json = {
      :route => {
        :ip_gateway => app_ip_gateway
      }
    }
    chef.add_role("web-server")
  end
end

Role load-balancing

Dans le dossier chef-repo/roles, créer le fichier load-balancing.json

nano load-balancing.json

Éditer le fichier load-balancing.json

{
    "name": "load-balancing",
    "description": "This is an example role defined as JSON",
    "chef_type": "role",
    "json_class": "Chef::Role",
    "default_attributes": {
    },
    "override_attributes": {
    },
    "run_list": [
        "recipe[proxyconf]"
    ]
}

Éditer le Vagrantfile de Proxy

# -*- mode: ruby -*-
# vi: set ft=ruby :


app_vm_name = "devops-Proxy"
app_name = "proxy"
app_ip = "192.168.254.250"
app_netmask = "255.255.255.248"
app_ip_gateway = "192.168.254.249"

Vagrant.configure(2) do |conf|
  conf.vm.box = "jubianchi/debian-wheezy-chef-i386"

  conf.vm.define app_name
  conf.vm.provider :virtualbox do |vb|
    vb.name = app_vm_name
  end

  conf.vm.hostname = app_name
  conf.vm.network "private_network", ip: app_ip, netmask: app_netmask
  conf.vm.network "forwarded_port", guest: 80, host: 8042

  conf.vm.provision "chef_solo" do |chef|
    chef.cookbooks_path = "../chef-repo/cookbooks"
    chef.roles_path = "../chef-repo/roles"
    chef.json = {
      :route => {
        :ip_gateway => app_ip_gateway
      }
    }
    chef.add_recipe "nginx"
    chef.add_recipe "default-route"
    chef.add_role("load-balancing")
  end
end

Role gateway

Dans le dossier chef-repo/roles, créer le fichier gateway.json

nano gateway.json

Éditer le fichier gateway.json

{
    "name": "gateway",
    "description": "This is an example role defined as JSON",
    "chef_type": "role",
    "json_class": "Chef::Role",
    "default_attributes": {
      "router": {
        "device": "eth3"
      }
    },
    "override_attributes": {
    },
    "run_list": [
        "recipe[router]"
    ]
}

Un seul Vagrantfile ?

Il est tout à fait possible de tout réunir en un fichier Vagrantfile.
Dans un répertoire lab, créer un fichier Vagrantfile.

mkdir lab
nano lab/Vagrantfile

Vagrantfile

# -*- mode: ruby -*-
# vi: set ft=ruby :


devops = {
  :app_ip_gateway => "192.168.254.249",
  :app_netmask => "255.255.255.248",
  :web1 => {
    :app_vm_name => "devops-Web1",
    :app_name => "web1",
    :app_ip => "192.168.254.251",
    :app_docroot => "../Web1/web1_docroot"
  },
  :web2 => {
    :app_vm_name => "devops-Web2",
    :app_name => "web2",
    :app_ip => "192.168.254.252",
    :app_docroot => "../Web2/web2_docroot"
  },
  :gateway => {
    :app_vm_name => "devops-Gateway",
    :app_name => "gateway",
    :app_ip => "192.168.254.249"
  },
  :proxy => {
    :app_vm_name => "devops-Proxy",
    :app_name => "proxy",
    :app_ip => "192.168.254.250"
  }
}

Vagrant.configure(2) do |conf|

  #
  # WEB 1
  #
  conf.vm.define devops[:web1][:app_name] do |web1|  
    web1.vm.box = "jubianchi/debian-wheezy-chef-i386"

    web1.vm.provider :virtualbox do |vb|
      vb.name = devops[:web1][:app_vm_name]
    end

    web1.vm.hostname = devops[:web1][:app_name]
    web1.vm.synced_folder devops[:web1][:app_docroot], "/var/www", :mount_options => ["dmode=777", "fmode=666"]

    web1.vm.network "private_network", ip: devops[:web1][:app_ip], netmask: devops[:app_netmask]

    web1.vm.provision "chef_solo" do |chef|
      chef.cookbooks_path = "../chef-repo/cookbooks"
      chef.json = {
        :apache => {
          :default_site_enabled => true
        },
        :route => {
    	    :ip_gateway => devops[:app_ip_gateway]
        }
      }
      chef.add_recipe "apache2"
      chef.add_recipe "default-route"
    end
  end

  #
  # WEB 2
  #
  conf.vm.define devops[:web2][:app_name] do |web2|
    web2.vm.box = "jubianchi/debian-wheezy-chef-i386"

    web2.vm.provider :virtualbox do |vb|
      vb.name = devops[:web2][:app_vm_name]
    end

    web2.vm.hostname = devops[:web2][:app_name]
    web2.vm.synced_folder devops[:web2][:app_docroot], "/var/www", :mount_options => ["dmode=777", "fmode=666"]

    web2.vm.network "private_network", ip: devops[:web2][:app_ip], netmask: devops[:app_netmask]

    web2.vm.provision "chef_solo" do |chef|
      chef.cookbooks_path = "../chef-repo/cookbooks"
        chef.json = {
          :apache => {
            :default_site_enabled => true
          },
          :route => {
      	    :ip_gateway => devops[:app_ip_gateway]
        }
      }
      chef.add_recipe "apache2"
      chef.add_recipe "default-route"
    end
  end

  #
  # GATEWAY
  #
  GATEWAY = exec 'netstat -rn | grep 192.168 | grep default | tr -s " " | cut -d" " -f 2'

  conf.vm.define devops[:gateway][:app_name] do |gateway|
    gateway.vm.box = "jubianchi/debian-wheezy-chef-i386"

    gateway.vm.define devops[:gateway][:app_name]
    conf.vm.provider :virtualbox do |vb|
      vb.name = devops[:gateway][:app_vm_name]
    end

    gateway.vm.hostname = devops[:gateway][:app_name]
    gateway.vm.network "private_network", ip: devops[:gateway][:app_ip], netmask: devops[:app_netmask]
    gateway.vm.network "public_network"

    gateway.vm.provision "chef_solo" do |chef|
      chef.cookbooks_path = "../chef-repo/cookbooks"
      chef.json = {
        :route => {
          :ip_gateway => GATEWAY,
	        :device => "eth2"
        }
      }
      chef.add_recipe "default-route"
      chef.add_recipe "router"
    end
  end

  #
  # PROXY
  #
  conf.vm.define devops[:proxy][:app_name] do |proxy|
    proxy.vm.box = "jubianchi/debian-wheezy-chef-i386"

    proxy.vm.define devops[:proxy][:app_name]
    proxy.vm.provider :virtualbox do |vb|
      vb.name = devops[:proxy][:app_vm_name]
    end

    proxy.vm.hostname = devops[:gateway][:app_name]
    proxy.vm.network "private_network", ip: devops[:gateway][:app_ip], netmask: devops[:app_netmask]
    proxy.vm.network "forwarded_port", guest: 80, host: 8042

    proxy.vm.provision "chef_solo" do |chef|
      chef.cookbooks_path = "../chef-repo/cookbooks"
        chef.json = {
          :route => {
      	  :ip_gateway => devops[:app_ip_gateway]
        }
      }
      chef.add_recipe "nginx"
      chef.add_recipe "default-route"
      chef.roles_path = "../chef-repo/roles"
      chef.add_role("devops_load_balancing")
    end
  end
end
Pas de commentaire.
Soyez le premier !