Automating your development and production environments with Puppet, Vagrant and Packer
Who I am
A brief history of why/Case study
A brief what is
[puppet|vagrant|packer]
[puppet|vagrant|packer]
Puppet
Vagrant
Packer
Who I am
Head of caffeine consumption and
automating-all-the-crap-that-pisses-me-off-when-noones-looking
@fusions
Real title:
Systems Developer
Systems Developer
@gabriel403
http://autonomicpilot.co.uk
A Brief History of Why
- monolithic application
- emails, admin, sales and
main site in one - just developers
- just ubuntu
- monolithic set-up guide
- rewrite decided to split
- multiple applications
- use more ready made tech
- use more diverse tech
- less DIY code
- queued and centralised log
- queueing for email
- started off small, just the main site
- I came up with the centralised log,
convinced others and started work - initially just on my machine
- email and queue, just on mine & 1 dev
- even larger set up
- what to do when it needed spreading
- UX on windows
- Sys Admins on Mac
- Me on arch linux
- Another dev on pure debian
Brief What Is
puppet
provisioning software
run a set of commands in an automated way
vagrant
VM config/control abstraction
packer
virtual image builder
What is Puppet
simply a way of executing commands
sudo apt-get install apache2
sudo nano /etc/apache2/sites-enable/somesite
sudo service apache2 restart
apache2 is ubuntu specific
httpd on debian/arch/bsd
apt-get is ubuntu/debian specific
pacman/yum/emerge on arch/redhat/gentoo
service is ubuntu specific
systemd/init.d/rc.d on arch/redhat/bsd
Puppet magic
package {'apache2':
ensure => installed
}
service {'apache2':
ensure => running
}
file {'/etc/apache2/sites-enable/somesite':
content => 'somecontent',
notify => Service['apache2']
}
file {'/etc/apache2/sites-enable/someothersite':
source => template('module/templatename'),
notify => Service['apache2']
}
(templates are good mkay)
There are still some problems
Facter
Ruby library for system info
architecture => amd64
hostname => gabriel-dev
lsbdistcodename => saucy
lsbdistid => Ubuntu
lsbdistrelease => 13.10
osfamily => Debian
is_virtual => true
virtual => virtualbox
More magic!
if $::osfamily == 'RedHat' or $::operatingsystem == 'amazon' {
$package = 'httpd'
} elsif $::osfamily == 'Debian' {
$package = 'apache2'
} elsif $::osfamily == 'Archlinux' {
$package = 'apache'
} else {
fail("Unsupported osfamily: ${::osfamily}")
}
if $::osfamily == 'RedHat' or $::operatingsystem == 'amazon' { $package = 'httpd' } elsif $::osfamily == 'Debian' { $package = 'apache2' } elsif $::osfamily == 'Archlinux' { $package = 'apache' } else { fail("Unsupported osfamily: ${::osfamily}") }package {$package: ensure => installed }service {$package: ensure => running, require => Package[$package] } file {'/etc/apache2/sites-enable/somesite': content => 'somecontent', notify => Service[$package] } file {'/etc/apache2/sites-enable/someothersite': source => template('module/templatename'), notify => Service[$package] }
parameters class
class apache::params {
if $::osfamily == 'RedHat' or $::operatingsystem == 'amazon' {
$package = 'httpd'
$httpd_dir = '/etc/httpd'
$vhost_dir = "${httpd_dir}/conf.d"
} elsif $::osfamily == 'Debian' {
$package = 'apache2'
$httpd_dir = '/etc/apache2'
$vhost_dir = "${httpd_dir}/sites-available"
} elsif $::osfamily == 'Archlinux' {
$package = 'apache'
$httpd_dir = '/etc/apache'
$vhost_dir = "${httpd_dir}/conf.d"
} else {
fail("Unsupported osfamily: ${::osfamily}")
}
}
The Final Puppet magic
include apache::paramspackage {$apache::params::package: ensure => installed }service {$apache::params::package:ensure => running, require => Package[$apache::params::package]} file {"${apache::params::vhost_dir}/somesite": content => 'somecontent', notify => Service[$apache::params::package]} file {"${apache::params::vhost_dir}/someothersite":source => template('module/templatename'), notify => Service[$apache::params::package]}
Puppet Forge
https://forge.puppetlabs.com
Case Study
-
Puppet allows us to automate
our installation and setup
-
No setup guide documentation, just code!
-
When updating our setup guide
we're really just writing code
What is Vagrant
Interface to personal Virtual Machines
Abstraction layer for VM configuration
Virtual Box for example
Imports a base box and runs provisioners
Easy plugin system
Huge range of existing plugins
Can be extended to work with cloud providers
AWS
Digital Ocean
Proxmox
Simple Config
Vagrant.configure("2") do |config|
config.vm.box = "lamp base 1"
config.vm.box_url = "https://some.host.com/lampbase.box"
config.vm.network :private_network, ip: "5.5.5.5"
config.vm.hostname = "lampy.dev"
config.vm.synced_folder "~/Dropbox/code", "/code", type: "nfs"
config.vm.provision :puppet, :module_path => "~/puppet/modules" do |puppet|
puppet.manifests_path = "manifests"
puppet.manifest_file = "init.pp"
end
end
Multi Box Configuration
Vagrant.configure("2") do |config| # bring them up in these order # db cdn services web pound config.vm.define :db do |db| db.vm.box = "Ubuntu 12.10 Quantal x86_64 (Guest Additions 4.2.2)" db.vm.box_url = "https://github.com/downloads/roderik/VagrantQuantal64Box/quantal64.box" db.vm.network :private_network, ip: "10.11.12.43" db.vm.hostname = "stagingmysql.icheev.com" db.vm.synced_folder "~/Dropbox/GoMADTech/code", "/code", :nfs => true db.vm.provision :puppet, :module_path => "~/Dropbox/GoMADTech/boxen/puppet/modules" do |puppet| puppet.manifests_path = "manifests" puppet.manifest_file = "db.pp" end end config.vm.define :cdn do |cdn| cdn.vm.box = "Ubuntu 12.10 Quantal x86_64 (Guest Additions 4.2.2)" cdn.vm.box_url = "https://github.com/downloads/roderik/VagrantQuantal64Box/quantal64.box" cdn.vm.network :private_network, ip: "10.11.12.42" cdn.vm.hostname = "stagingcdn.icheev.com" cdn.vm.synced_folder "~/Dropbox/GoMADTech/code", "/code", :nfs => true cdn.vm.provision :puppet, :module_path => "~/Dropbox/GoMADTech/boxen/puppet/modules" do |puppet| puppet.manifests_path = "manifests" puppet.manifest_file = "cdn.pp" end end config.vm.define :services do |services| services.vm.box = "Ubuntu 12.10 Quantal x86_64 (Guest Additions 4.2.2)" services.vm.box_url = "https://github.com/downloads/roderik/VagrantQuantal64Box/quantal64.box" services.vm.network :private_network, ip: "10.11.12.44" services.vm.hostname = "stagingservices.icheev.com" services.vm.synced_folder "~/Dropbox/GoMADTech/code", "/code", :nfs => true services.vm.provision :puppet, :module_path => "~/Dropbox/GoMADTech/boxen/puppet/modules" do |puppet| puppet.manifests_path = "manifests" puppet.manifest_file = "services.pp" end end config.vm.define :web do |web| web.vm.box = "Ubuntu 12.10 Quantal x86_64 (Guest Additions 4.2.2)" web.vm.box_url = "https://github.com/downloads/roderik/VagrantQuantal64Box/quantal64.box" web.vm.network :private_network, ip: "10.11.12.41" web.vm.hostname = "staging.icheev.com" web.vm.synced_folder "~/Dropbox/GoMADTech/code", "/code", :nfs => true web.vm.provision :puppet, :module_path => "~/Dropbox/GoMADTech/boxen/puppet/modules" do |puppet| puppet.manifests_path = "manifests" puppet.manifest_file = "web.pp" end end config.vm.define :pound do |pound| pound.vm.box = "Ubuntu 12.10 Quantal x86_64 (Guest Additions 4.2.2)" pound.vm.box_url = "https://github.com/downloads/roderik/VagrantQuantal64Box/quantal64.box" pound.vm.network :private_network, ip: "10.11.12.40" pound.vm.hostname = "stagingpound.icheev.com" pound.vm.synced_folder "~/Dropbox/GoMADTech/code", "/code", :nfs => true pound.vm.provision :puppet, :module_path => "~/Dropbox/GoMADTech/boxen/puppet/modules" do |puppet| puppet.manifests_path = "manifests" puppet.manifest_file = "pound.pp" end end end
Case study
- despite being on different host OS
we now have a unified dev env - Works the same on windows/mac/linux
- Any changes to provisioners is reflected
in everyone's dev env
What is Packer
- AWS,Digital Ocean,Proxmox,
Rackspace all use base boxes - Some are as simple as base OS installs
- Some are slightly complex, user set ups
- Some are very complex, full LAMP + deployment
- Can use the same provisioning as vagrant
- Packer build base boxes
- From ISOs
- From other base boxes
- Immutable servers
{
"builders": [{
"type" : "digitalocean",
"client_id" : "clientid",
"api_key" : "apikey",
"image_id" : 1505699,
"region_id" : 5,
"size_id" : 64,
"snapshot_name" : "test-snap",
"droplet_name" : "test.jeepers.com"
}],
"provisioners": [{
"type": "shell",
"execute_command": "echo 'vagrant' | sudo -S sh '{{ .Path }}'",
"inline": [
"apt-get update -y",
"apt-get install -y linux-headers-$(uname -r) build-essential dkms puppet-common subversion ruby-hiera",
"apt-get clean"
]
},{
"type" : "puppet-masterless",
"manifest_file" : "manifests/site.pp",
"module_paths" : ["../../puppet/modules"],
"facter" : { "fqdn" : "test.jeepers.com" }
}]
}
{
"builders": [{
"type" : "virtualbox-iso",
"iso_url" : "http://releases.ubuntu.com/13.10/ubuntu-13.10-server-amd64.iso",
"iso_checksum" : "4d1a8b720cdd14b76ed9410c63a00d0e",
"iso_checksum_type" : "md5",
"disk_size" : 80000,
"guest_os_type" : "Ubuntu_64",
"http_directory" : "preseed",
"ssh_username" : "vagrant",
"ssh_password" : "vagrant",
"output_directory" : "output/13.10_5.5_64_virtualbox",
"boot_command" : [
"<esc><esc><enter><wait>",
"/install/vmlinuz noapic ",
"preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg ",
"debian-installer=en_GB auto locale=en_GB kbd-chooser/method=uk ",
"hostname={{ .Name }} ",
"fb=false debconf/frontend=noninteractive ",
"leopard-configuration/modelcode=SKIP leopard-configuration/layout=GB ",
"leopard-configuration/variant=GB console-setup/ask_detect=false ",
"initrd=/install/initrd.gz -- <enter>"
],
"vboxmanage" : [
["modifyvm", "{{.Name}}", "--memory", "1024"]
],
"shutdown_command" : "echo 'shutdown -P now' > shutdown.sh; echo 'vagrant'|sudo -S sh 'shutdown.sh'"
}],
"provisioners" : [{
"type": "shell",
"execute_command": "echo 'vagrant' | sudo -S sh '{{ .Path }}'",
"inline": [
"apt-get update -y",
"apt-get install -y linux-headers-$(uname -r) build-essential dkms puppet-common nfs-kernel-server nfs-common ruby-hiera",
"apt-get clean",
"mount -o loop VBoxGuestAdditions.iso /media/cdrom",
"sh /media/cdrom/VBoxLinuxAdditions.run",
"umount /media/cdrom",
"mkdir ~/.ssh",
"wget -qO- https://raw.github.com/mitchellh/vagrant/master/keys/vagrant.pub >> ~/.ssh/authorized_keys",
"echo 'vagrant ALL=NOPASSWD:ALL' > /tmp/vagrant",
"chmod 0440 /tmp/vagrant",
"mv /tmp/vagrant /etc/sudoers.d/"
]
},{
"type" : "puppet-masterless",
"manifest_file" : "manifests/site.pp",
"module_paths" : ["../../puppet/modules","../../../puppet"],
"facter" : {
"fqdn" : "serverbase.dev"
}
}],
"post-processors" : [
{
"type" : "vagrant",
"only" : ["virtualbox-iso"],
"output" : "output/ubuntu_13.10_5.5_base_v2.box"
}
]
}
Case Study
- Using the same provisioning on
production and dev leads to a nicer life - Updating OS images (dev/prod) is simpler
by just editing a config file
In Conclusion
Puppet is used for installing and configuring packages/services/files
Vagrant abstracts configuration and control for multiple VM types into a simple config file and command line.
It can also use puppet and other provisioning s/w.
It can also use puppet and other provisioning s/w.
Packer builds and provisions base boxes and servers, using other base boxes or isos as a starting point and can use puppet or other provisiong s/w.
Automating your development and production environments with Packer, puppet and vagrant
By Gabriel Baker
Automating your development and production environments with Packer, puppet and vagrant
- 5,099