Zakaria Lounes

</Développeur Full-Stack>

Drapeau FrançaisDrapeau Anglais

Blog

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
Commentaires Dimanche 28 Juin 2015

Comment installer Chef DK ?

Comment installer Chef DK ?

Pour installer Chef DK, rien de plus simple !

Rendez-vous sur https://downloads.chef.io/chef-dk/, télécharger la version correspondant à votre environnement, installer Chef DK et c'est tout !

Depuis unix, ouvrez votre terminal et tapez :

chef

Vous devriez avoir le résultat suivant :

Usage:
    chef -h/--help
    chef -v/--version
    chef command [arguments...] [options...]


Available Commands:
    exec        Runs the command in context of the embedded ruby
    gem         Runs the `gem` command in context of the embedded ruby
    generate    Generate a new app, cookbook, or component
    shell-init  Initialize your shell to use ChefDK as your primary ruby
    install     Install cookbooks from a Policyfile and generate a locked cookbook set
    update      Updates a Policyfile.lock.json with latest run_list and cookbooks
    push        Push a local policy lock to a policy group on the server
    diff        Generate an itemized diff of two Policyfile lock documents
    provision   Provision VMs and clusters via cookbook
    export      Export a policy lock as a Chef Zero code repo
    verify      Test the embedded ChefDK applications

C'est bon, Chef DK est bien installé !

Commentaires Dimanche 28 Juin 2015

Comment installer Vagrant ?

Comment installer Vagrant ?

Pour installer Vagrant, rien de plus simple !

Rendez-vous sur https://www.vagrantup.com/downloads.html, télécharger la version correspondant à votre environnement, installer Vagrant et c'est tout !

Depuis unix, ouvrez votre terminal et tapez :

vagrant

Vous devriez avoir le résultat suivant :

Usage: vagrant [options] []

    -v, --version                    Print the version and exit.
    -h, --help                       Print this help.

Common commands:
     box             manages boxes: installation, removal, etc.
     connect         connect to a remotely shared Vagrant environment
     destroy         stops and deletes all traces of the vagrant machine
     global-status   outputs status Vagrant environments for this user
     halt            stops the vagrant machine
     help            shows the help for a subcommand
     init            initializes a new Vagrant environment by creating a Vagrantfile
     login           log in to HashiCorp's Atlas
     package         packages a running vagrant environment into a box
     plugin          manages plugins: install, uninstall, update, etc.
     provision       provisions the vagrant machine
     push            deploys code in this environment to a configured destination
     rdp             connects to machine via RDP
     reload          restarts vagrant machine, loads new Vagrantfile configuration
     resume          resume a suspended vagrant machine
     share           share your Vagrant environment with anyone in the world
     ssh             connects to machine via SSH
     ssh-config      outputs OpenSSH valid configuration to connect to the machine
     status          outputs status of the vagrant machine
     suspend         suspends the machine
     up              starts and provisions the vagrant environment
     version         prints current and latest Vagrant version

For help on any individual command run `vagrant COMMAND -h`

Additional subcommands are available, but are either more advanced
or not commonly used. To see all subcommands, run the command
`vagrant list-commands`.

C'est bon, Vagrant est bien installé !

Commentaires Dimanche 28 Juin 2015