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