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 vagrant
Vagrant Boxes
Adding vagrant box Ubuntu 12.04
$ vagrant box add precise32 http://files.vagrantup.com/precise32.box
List all the box.
$ vagrant box list
Make a dir
$ mkdir firstVM
$ cd firstVM
Initialize vagrant virtual machine with box you want to use.
$ vagrant init precise32
$ ls // to check Vagrantfile
Link: 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 up
Check 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 suspend
suspend will save the hard drive and RAM in the VM.
To restart,
$ vagrant resume
To shut down,
$ vagrant halt
This won't save RAM, but save the hard drive.
To start,
$ vagrant up
halt 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
Vagrantfile
In 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-upgrade
Link: 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: 8080
Let'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~$ exit
The Vagrantfile
First destroy the VM in firstVM dir.
vagrant destroy
The 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: 8080
Uncomment 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 destroy
Type 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 provision
Change 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 --version
Some VMs don't have puppet.
vagrant@precise32:~$ apt-get install puppet
Puppet Resources
To see what are in puppet resource, vagrant up and vagrant ssh,
vagrant@precise32:~$ puppet resource user
This will show the config for user.
vagrant@precise32:~$ puppet resource user vagrant
This 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 installed
Write a puppet script. (Puppet manifest file.)
vagrant@precise32:~$ exit
firstVM $ vim vim.pp
// in Vim
// :set ft=puppet
// to set the file type to puppet
In 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.pp
error 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"
end
not 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/www
Use 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 dir
In 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/two
Using 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.pp
In 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 up
Check 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 events
This 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.box
In 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 Vagrantfile
In 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"
end
Save and close it. In the terminal copy site-config
centosVM $ cp ../firstVM/site-config .
centosVM $ ls
Vagrantfile site-config
centosVM $ vagrant up
Open 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.box
Facter
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: ~$ exit
Creating 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"
}
end
Puppet Classes
Adding Object Oriented class to manifests/default.pp.
In firstVM,
$ vim manifests/default.pp
In 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.pp
Create a new default.pp file
$ vim manifests/default.pp
In default.pp
class{ "webserver": }
Save and close it. Run vagrant.
$ vagrant up
To see if it runs.
Shorter default.pp
# class{ "webserver": }
include webserver
Or 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 provision
Check 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 webserver
Puppet 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 Vagrantfile
In 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 =""
end
Suspend 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/modules
Here 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/apache
Now 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"
end
Save 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 well
But 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 up
And 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 provision
Check 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 puppet
Installation will create /Users/teacher/.puppet/modules and install modules in it.
$ puppet module install puppetlabs/apache
Undoing 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
...