Vagrant and Puppet notes
Personal notes on Vagrant and Puppet.
Resources
Automating Development Environments with Vagrant and Puppet
Installing Virtual box and vagrant
Install Virtual box and also VM VirtualBox Extension Pack.
$ install vagrantVagrant Boxes
Adding vagrant box Ubuntu 12.04
$ vagrant box add precise32 http://files.vagrantup.com/precise32.boxList all the box.
$ vagrant box listMake a dir
$ mkdir firstVM
$ cd firstVMInitialize vagrant virtual machine with box you want to use.
$ vagrant init precise32
$ ls // to check VagrantfileLink: Vagrantbox.es. You can find the url of boxes.
How to remove a box.
$ vagrant box remove lucid32 virtualbox // virtualbox is a provider
// because you can have a differnt box with a different provider.Starting a virtual machine
($ ls firstVM // change the dir where you have your Vagrantfile)
$ vagrant upCheck your virtual box that firstVM is running. It is headless, so it does not have window to interact. In stead we use the command line.
How to stop the VM, Teardown
$ vagrant suspendsuspend will save the hard drive and RAM in the VM.
To restart,
$ vagrant resumeTo shut down,
$ vagrant haltThis won't save RAM, but save the hard drive.
To start,
$ vagrant uphalt will take up some space in the main hard drive since it will save the VM hard drive.
To destroy the VM when you finished your project. However you can save all the config and files in Vagrantfile. If you setup it properly you don't lose anything.
$ vagrant destroy // y for yes.SSHing into the VM
$ vagrant up
$ vagrant ssh // this will take you to the inside of VM.Once you are in ssh, ctrl+D is how to exit ssh.
Now in the VM, the command line has vagrant@precise32:~$. (The vagrant@precise32:~$ in the followings are in the VM.)
vagrant@precise32:~$ cd /vagrant
vagrant@precise32:/vagrant ls
VagrantfileIn the /vagrant file, there is the same Vagrantfile as my firstVM dir and they are synced. Also /vagrant dir is a shared dir.
Don't forget to update apt-get and upgrade
vagrant@precise32:~$ sudo apt-get update
vagrant@precise32:~$ sudo apt-get upgrade
// or
vagrant@precise32:~$ sudo apt-get dist-upgrade
// dist do more than upgrade so all in one with
vagrant@precise32:~$ sudo apt-get update&&sudo apt-get dist-upgradeLink: What does “sudo apt-get update” do?
Note
You can add the following to the Vagrantfile to update as well.
# Update the server
config.vm.provision :shell, :inline => "apt-get update --fix-missing"This line in Vagrantfile should be uncommented.
config.vm.network :forwarded_port, guest: 80, host: 8080Let's create a index.html in /vagrant and start a server.
# vagrant@precise32:/vagrant~$ vim index.html // this VM doesn't have vim yet.
# write it without vim
vagrant@precise32:/vagrant~$ echo "<h1> Hi! </h1>" > index.html
vagrant@precise32:/vagrant~$ ls
vagrant@precise32:/vagrant~$ cat index.html
vagrant@precise32:/vagrant~$ sudo python -m SimpleHTTPServer 80
// sudo is needed if you want to choose port 80.You have to 'sudo python -m SimpleHTTPServer 80' in /vagrant dir.
This port 80 is redirected to the localhost:8080 in the normal browser. Open your browser and go to localhost:8080.
ctrl+C to quit the server.
And to leave the VM, type exit.
vagrant@precise32:/vagrant~$ exitThe Vagrantfile
First destroy the VM in firstVM dir.
vagrant destroyThe Vagrantfile must be in the root folder of the project. /firstVM/Vagrantfile and it is ruby file. Change the file type to ruby.
Guest in the file means the VM and host means the hosting computer, my computer.
You can change box to others if you need to. And this line should be uncommented.
config.vm.box = "precise32"config.vm.network can be changed to guest: 3000, host:3000 or something else. But don't forget to exit the host. Otherwise you can't use that port from other VM.
And this line should be uncommented.
config.vm.network :forwarded_port, guest: 80, host: 8080Uncomment the following line to use the synced folder as specified. But not at the moment.
config.vm.synced_folder "../data", "/vagrant_data"Provisioning with Shell Scripts
Destroy the VM if it is still running and open the Vagrantfile.
$ vagrant destroyType the following in the Vagrantfile.
config.vm.provision :shell, inline: "echo Hello World!"This will output the followings.
...
[default] Running: inline script
stdin: is not a tty
Hello World!Don't worry about the line, stdin: is not a tty.
You can rerun the provisioning without restarting the VM. Change the Vagrantfile.
config.vm.provision :shell, inline: "echo Hello World Aha!"And in the terminal,
$ vagrant provisionChange the provision script, (this gives erros for me.)
config.vm.provision :shell, path: './provision.sh'Create provision.sh file in the current dir.
echo "This is our provisioning script"
apt-get install vim -y-y does not ask yes or no.
Use puppet for more heavy duty tools.
Getting Started with Puppet
$ vagrant up
$ vagrant ssh
vagrant@precise32:~$ which puppet
/opt/vagrant_ruby/bin/puppet
vagrant@precise32:~$ puppet --versionSome VMs don't have puppet.
vagrant@precise32:~$ apt-get install puppetPuppet Resources
To see what are in puppet resource, vagrant up and vagrant ssh,
vagrant@precise32:~$ puppet resource userThis will show the config for user.
vagrant@precise32:~$ puppet resource user vagrantThis will show the vagrant user which we are logged in as.
user { 'vagrant':
ensure => 'present',
comment => 'vagrant,,,',
gid => '1000',
groups => ['adm', 'cdrom', 'sudo', 'dip', 'plugdev', 'lpadmin', 'sambashare', 'admin'],
home => '/home/vagrant',
shell => '/bin/bash',
uid => '1000',
}Don't forget to add , at the end as well.
Try another.
vagrant@precise32:~$ puppet resource package vim
package { 'vim':
ensure => 'purged',
}
// we don't have vim
vagrant@precise32:~$ which vim
// no output means not installedWrite a puppet script. (Puppet manifest file.)
vagrant@precise32:~$ exit
firstVM $ vim vim.pp
// in Vim
// :set ft=puppet
// to set the file type to puppetIn vim.pp
package{ 'vim':
name => 'vim',
ensure => 'installed',
}In stead of 'installed', you can use 'present' as well.
firstVM $ vagrant ssh
vagrant@precise32:~$ cd /vagrant
vagrant@precise32:/vagrant~$ ls // to check if we have vim.pp file
index.html provision.sh Vagrantfile vim.pp
// run the puppet manifest to install vim as a root so use sudo
vagrant@precise32:/vagrant~$ sudo puppet apply vim.pperror of fqdn
// if you an error telling warning: Could not retrieve fact fqdn
// then add the following to Vagrantfile and destroy and up again
// it should be working with vagrant provision, not so far
config.vm.hostname = "vagrant.example.com"
// from pyrocms
# Enable Puppet
config.vm.provision :puppet do |puppet|
puppet.facter = { "fqdn" => "local.pyrocms", "hostname" => "www" }
puppet.manifests_path = "puppet/manifests"
puppet.manifest_file = "ubuntu-apache2-pgsql-php5.pp"
puppet.module_path = "puppet/modules"
endnot able to install vim
Add the following to Vagrantfile and $ vagrant reload in the terminal
# Update the server
config.vm.provision :shell, :inline => "apt-get update --fix-missing"This can be done in this way as well.
config.vm.provision :shell, :path => "bootstrap.sh"And write in bootstrap.sh to apt-get update
// /vagrant/bootstrap.sh
#!/usr/bin/env bash
apt-get update
apt-get install -y apache2
rm -rf /var/www
ln -fs /vagrant /var/wwwUse vagrant reload to quick-restart.
Since we installed Apache server and setup the default DocumentRoot of Apache to point to our /vagrant in the bootstrap.sh, we can run this as well.
$ vagrant ssh
vagrant@vagrant:~$ wget -qO- 127.0.01
// this will display index.html which we created before.
<h1> Hi! </h1>Check if vim is in.
vagrant@vagrant:/vagrant$ which vim
/usr/bin/vim
vagrant@vagrant:/vagrant$ puppet resource package vim
package { 'vim':
ensure => '2:7.3.429-2ubuntu2.1',
}Putting Resources in Order
Ordering puppet resource decorations.
$ vagrant ssh
vagrant@vagrant:~$ vim files.pp // create it in the home dirIn order to check that resources are not run in order,
In files.pp
file{ 'one':
path => '/vagrant/one',
content => 'one',
}
file{ 'two':
path => '/vagrant/two',
content => 'two',
}
// save and close it with :wq
vagrant@vagrant:~$ puppet apply files.pp
notice: /Stage[main]//File[two]/ensure: defined content as '{md5}b8a9f715dbb64fd5c56e7783c6820a61'
notice: /Stage[main]//File[one]/ensure: defined content as '{md5}f97c5d29941bfb1b2fdab0874906ab82'It started from two as you see above.
Remove one and two
vagrant@vagrant:~$ rm /vagrant/one /vagrant/twoUsing meta parameter to order of resources.
In files.pp
file{ 'one':
path => '/vagrant/one',
content => 'one',
before => File['two'],
}
file{ 'two':
path => '/vagrant/two',
content => 'two',
}Save and close it with :wq.
vagrant@vagrant:~$ puppet apply files.
notice: /Stage[main]//File[one]/ensure: defined content as '{md5}f97c5d29941bfb1b2fdab0874906ab82'
notice: /Stage[main]//File[two]/ensure: defined content as '{md5}b8a9f715dbb64fd5c56e7783c6820a61'Notice file one is created before two.
In the following, the file two's source is the file one. But the file one is not created before the file two, therefore it will give an error.
In files.pp
file{ 'one':
path => '/vagrant/one',
content => 'one',
# before => File['two'], # comment this out
}
file{ 'two':
path => '/vagrant/two',
source => '/vagrant/one',
}In the terminal
vagrant@vagrant:~$ puppet apply files.pp
err: /Stage[main]//File[two]: Could not evaluate: Could not retrieve information from environment production source(s) file:/vagrant/one at /home/vagrant/files.pp:10
notice: /Stage[main]//File[one]/ensure:
...In the following file two require one. And give no error.
file{ 'one':
path => '/vagrant/one',
content => 'one',
# before => File['two'], # comment this out
}
file{ 'two':
path => '/vagrant/two',
source => '/vagrant/one',
require => File['one'],
}Another way to create a hirachy with ->.
file{ 'one':
path => '/vagrant/one',
content => 'one',
}
file{ 'two':
path => '/vagrant/two',
source => '/vagrant/one',
}
File['one'] -> File['two']Or you can use the opposite direction.
File['two'] <- File['one']Writing a Complete Puppet Manifest
Open the Vagrantfile and change the followings.
# config.vm.provision :shell, path: './provision.sh' //comment
config.vm.provision :puppet // change to this.Create dir called manifests under the root dir which is the same as Vagrantfile.
$ mkdir manifests
$ vim manifests/default.ppIn Vim
package { 'apache2':
ensure => 'installed',
}
file { 'site-config':
path => "/etc/apache2/sites-enabled/000-default",
source => "/vagrant/site-config",
require => Package["apache2"],
}
file { '/vagrant/index.html':
content => "<h1> Vagrant + Puppet </h1>",
}The source "/vagrant/site-config" is in the VM.
Open the other tab in the vim.
:tabe site-config
//Copy and pasted the following to site-config
<VirtualHost *:80>
DocumentRoot /vagrant
<Directory /vagrant>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>
</VirtualHost>Save and close the both.
firstVM $ vagrant upCheck it in the browser at localhost:8080. But it did not render it correctly with "Vagrant + Puppet". Because it install the apache and then site-config. We need to restart the Apache after configure it.
Add the following to manifests/default.pp. This will restart apache2 after reading the site-config.
$ vim manifests/default.pp
service { 'apache2' :
ensure => 'running',
hasrestart => true,
subscribe => File["site-config"]
}You need to remove the first /etc/apache2/sites-enabled/000-default
$ vagrant ssh
vagrant@vagrant:~$ sudo rm /etc/apache2/sites-enabled/000-default
vagrant@vagrant:~$ exit
firstVM $ vagrant provision
notice: /Stage[main]//File[site-config]/ensure: defined content as '{md5}469af2b4aee15edd883273c985b41eeb'
notice: /Stage[main]//Service[apache2]: Triggered 'refresh' from 1 eventsThis tells that it read the site-config and refreshed the apache.
Now go to localhost:8080 to see the effect.
You don't need to ssh. You can open index.html and edit it from firstVM
Variables and Conditionals in manifest file
$ vagrant box list
# this didn't work
# $ vagrant box add https://dl.dropbox.com/sh/9rldlpj3cmdtntc/chqwU6EYaZ/centos-63-32bit-puppet.box
// adding centos6.3
# in stead I installed this and it worked
$ vagrant box add centos64 http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210.boxIn manifests/default.pp, we want to install httpd(web server) when we are operating the centos OS. We need a config path for each OS. In manifests/default.pp
case $operatingsystem {
centos: {
$apache = "httpd"
$configfile = "/etc/httpd/conf.d/vhost.conf"
}
ubuntu: {
$apache = "apache2"
$configfile = "/etc/apache2/sites-enabled/000-default"
}
}
package { 'apache':
name => $apache,
ensure => 'installed',
}
file { 'site-config':
path => $configfile,
source => "/vagrant/site-config",
require => Package["apache"],
}
service { 'apache' :
name => $apache,
ensure => 'running',
hasrestart => true,
subscribe => File["site-config"]
}
file { '/vagrant/index.html':
content => "<h1> Vagrant + Puppet + ${apache} (${operatingsystem}) </h1>",
}
if $apache == "httpd" {
service { 'iptables':
ensure => 'stopped',
}
}Starting Precise32
// in firstVM dir, start VM
firstVM $ vagrant up
// after started, check a browser at localhost:8080
firstVM $ cd ..
mkdir centosVM
cd centosVM
centosVM $ vagrant init centos64
centosVM $ vim VagrantfileIn Vagrantfile with vim, change :set ft=ruby and uncomment the following and change 8080 to 8081
config.vm.box = "centos64"
config.vm.network :forwarded_port, guest: 80, host:8081
# add the folloings
config.vm.provision :puppet do |puppet|
puppet.manifests_path = "../firstVM/manifests"
endSave and close it. In the terminal copy site-config
centosVM $ cp ../firstVM/site-config .
centosVM $ ls
Vagrantfile site-config
centosVM $ vagrant upOpen localhost:8081 in a browser to check Vagrant + Puppet + httpd(CentOS)
note for my case
CentOS6.3 takes a lot of time, so I downloaded centos64 from http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210.box.
I also added precise64, http://files.vagrantup.com/precise64.box.
$ vagrant box add precise64 http://files.vagrantup.com/precise64.boxFacter
cd firstVM
firstVM $ vagrant ssh
vagrant@precise32: ~$ which facter
/opt/vagrant_ruby/bin/facter
vagrant@precise32: ~$ facter // to get different properties
vagrant@precise32: ~$ facter operatingsystem
Ubuntu
vagrant@precise32: ~$ facter is_virtual
true
vagrant@precise32: ~$ exitCreating your own facts with facter file. Open the Vagrantfile. Change the provision statement.
config.vm.provision : puppet do |puppet|
puppet.facter = {
"key" => "value",
"database_server" => "included"
}
endPuppet Classes
Adding Object Oriented class to manifests/default.pp.
In firstVM,
$ vim manifests/default.ppIn default.pp, create a class and tell the code to run.
class webserver{
case $operatingsystem {
centos: {
$apache = "httpd"
$configfile = "/etc/httpd/conf.d/vhost.conf"
}
ubuntu: {
$apache = "apache2"
$configfile = "/etc/apache2/sites-enabled/000-default"
}
}
package { 'apache':
name => $apache,
ensure => 'installed',
}
file { 'site-config':
path => $configfile,
source => "/vagrant/site-config",
require => Package["apache"],
}
service { 'apache' :
name => $apache,
ensure => 'running',
hasrestart => true,
subscribe => File["site-config"]
}
file { '/vagrant/index.html':
content => "<h1> Vagrant + Puppet + ${apache} (${operatingsystem}) </h1>",
}
if $apache == "httpd" {
service { 'iptables':
ensure => 'stopped',
}
}
}Move this file to manifests/webserver.pp
$ mv manifests/default.pp manifests/webserver.ppCreate a new default.pp file
$ vim manifests/default.ppIn default.pp
class{ "webserver": }Save and close it. Run vagrant.
$ vagrant upTo see if it runs.
Shorter default.pp
# class{ "webserver": }
include webserverOr in webserver.pp at the end of codes.
class webserver{
...
...
}
class { "webserver" : }
//or
include webserver
class take parameters in webserver.pp.
class webserver($message = ""){
}
In default.pp
class { "webserver":
message => "Hi there!",
}
In webserver.pp
class (){
...
file { '/vagrant/index.html':
content => "<h1> Vagrant + Puppet + ${apache} (${operatingsystem}) </h1><p>${message}"</p>,
}
}Save and close it.
firstVM $ vagrant provisionCheck the browser if it has new message.
My case
It didn't work. But I made the default.pp as followings and it worked. I deleted webserver.pp.
class webserver{
case $operatingsystem {
centos: {
$apache = "httpd"
$configfile = "/etc/httpd/conf.d/vhost.conf"
}
ubuntu: {
$apache = "apache2"
$configfile = "/etc/apache2/sites-enabled/000-default"
}
}
package { 'apache':
name => $apache,
ensure => 'installed',
}
file { 'site-config':
path => $configfile,
source => "/vagrant/site-config",
require => Package["apache"],
}
service { 'apache' :
name => $apache,
ensure => 'running',
hasrestart => true,
subscribe => File["site-config"]
}
file { '/vagrant/index.html':
content => "<h1> Vagrant + Puppet + ${apache} (${operatingsystem}) </h1>",
}
if $apache == "httpd" {
service { 'iptables':
ensure => 'stopped',
}
}
}
include webserverPuppet Modules
To list installed module
$ puppet module list
# for help
$ puppet help module$ mkdir module-test
$ cd moduel-test
module-test $ vagrant init precise32
module-test $ vim VagrantfileIn Vagrantfile, add the local path for module_path
config.vm.network :forwarded_port, guest: 80, host: 8080
config.vm.provision :puppet do |puppet|
puppet.module_path =""
endSuspend vim by ctrl+z, to resume fg
To check path
module-test $ puppet module install pupeetlabs/apache
# this give an error to tell about a dir. In precise64
...
Directory /home/vagrant/.puppet/modules does not exist
# to find puppet path
module-test $ puppet apply --configprint modulepath
/Users/teacher/.puppet/modules:/usr/share/puppet/modulesHere you can see the path in the first part If you have warning like above then you need to make a dir. But my case I installed puppet through gem, I did not need to make it.
$ mkdir -p ~/.puppet/modules
module-test $ puppet module install pupeetlabs/apacheNow we know the path, /Users/teacher/.puppet/modules. Put it in Vagrantfile.
# going back to vim from suspend is fg.
config.vm.provision :puppet do |puppet|
puppet.module_path ="/Users/teacher/.puppet/modules"
endSave and close it.
$ mkdir manifests
$ vim manifests/default.pp
// in default.pp
// execute the code in apache class
include apache
// also include nested class in apache class
include apache::mod::php
// this will install php as wellBut this won't install php until apt-get installed first. Set
include apache
Exec {
path =>["/usr/bin", "usr/local/bin"],
}
exec { "update":
# you could give path here, path =>. But use Exec {} by setting parameters that passed on all child resources.
command => "apt-get update",
}
class { "apache::mod::php":
require => Exec["update"]
}
# create an apache virtual host
apache::vhost{ "personal-site":
port => 80,
docroot => "/vagrant",
}
# something to show
file{ "/vagrant/index.php":
content => "<?php $title = 'From the PHP' ; ?> <h1><?php echo $title; ?> </h1>",
}Save it but it is not finished yet.
$ vagrant upAnd open it in a browser at localhost:8080. But it gives no page. Because php variables need to be escaped. Puppet thinks $ as puppet's variable $ sign.
file{ "/vagrant/index.php":
content => "<?php \$title = 'From the PHP' ; ?> <h1><?php echo \$title; ?> </h1>",
}Save and close it and run vagrant provision.
$ vagrant provisionCheck it in a browser.
My case on Mac
How to install puppet on Mac
Don't use dmg from puppetlab. Use rubygem by
$ gem install puppetInstallation will create /Users/teacher/.puppet/modules and install modules in it.
$ puppet module install puppetlabs/apacheUndoing Puppet Manifests
No offical way to undo puppet manifests. But there are tricks.
$ mkdir undo
$ cd undo
$ undo $ vagrant init precise32
# create manifests dir and site-config as before
$ ls
Vagrantfile manifests site-config
$ vim Vagrantfile
config.vm.box = "precise32"
config.vm.provision : puppet
config.vm.network :forwarded_port, guest:80, host:8080
...