Skip to content

Instantly share code, notes, and snippets.

@awilliams
Last active December 12, 2015 05:39
Show Gist options
  • Save awilliams/4723391 to your computer and use it in GitHub Desktop.
Save awilliams/4723391 to your computer and use it in GitHub Desktop.
Vagrant

Development box installation guide

This guide will show how to setup a VirtualBox similiar to an Ubuntu 12.04.1 server for use with Amazon EC2.

Covered in this guide:

  • VeeWee: Tool for building base boxes which will be used by vagrant
  • Vagrant: Tool for managing virtual machines with an easy to use CLI
  • Librarian: Bundler for chef cookbooks
  • Chef-solo & Knife solo: Tool for automating installing and management of servers

Prerequisites not covered in this guide:

Initial Setup

We'll create a base directory and make it a RVM and bundler project

% mkdir systems; cd systems
% rvm --rvmrc --create 1.9.3@systems
% cd ..; cd systems # accept the RVM notice
% ruby -v # ensure that the correct ruby is being used
% bundle init

Ruby 1.9.3 is used, but for no other reason than it's the latest. Everything should work with other versions as well.

Edit your Gemfile to include the following gems, or use our Gemfile

  • vagrant
  • veewee
  • knife-solo
  • librarian
  • multi_json
  • ruby-shadow

Install the gems

% bundle install

If bundler fails on ffi library, try commenting out all gems in gemfile except first, and doing bundle install one-by-one.

VeeWee

https://github.com/jedi4ever/veewee Tool to easily build vagrant base boxes or kvm, virtualbox and fusion images.

We'll use VeeWee to create our Vagrant basebox. The base box is the base virtual machine that can be copied for each project and modified further using chef or puppet per project needs. More info here. There are pre built baseboxes available. You could download one and skip this VeeWee section.

First we'll clone the git project into ours. Instead of using submodules, we'll create a subrepository. More info

% git clone https://github.com/jedi4ever/veewee.git

Before cd'ing into this directory, which has it's own .rvmrc file, we'll delete it to use the same rvm project throughout.

% rm veewee/.rvmrc
% cd veewee

See list of available Ubuntu templates

% veewee vbox templates | grep -i ubuntu

Next we follow the instructions from the veewee repo. In our example, we'll create an ubuntu-12.04.1-server-amd64.iso machine

Name should not contain underscores!

% veewee vbox define 'ubuntu-12.04.1-server-x64' 'ubuntu-12.04.1-server-amd64'

This will create a directory with 3 config files for your box in definitions/ubuntu-12.04.1-server-x64/. We'll edit these file to make a few changes:

  • cpu_count => 2
  • memory => 1024mb
  • hostname => devbox
  • Removal of various post install commands. We'll instead accomplish the same using chef-solo. This is to create a basebox which more closely resembles a default EC2 instance.

cd into definition directory, then into the directory of your template (name is the same as you defined previously).

$ cd definitions/ubuntu-12.04.1-server-x64/

Edit the definition files accordingly. Our definition files:

Update The new version of VeeWee has new (better) format for the 'definition.rb' file and breaks apart the post install script (postinstall.sh) into multiple files. For this install, I left out the "ruby.sh", "chef.sh", and "puppet.sh" postinstall files, which can just be commented out of the defintions.rb file.

No we are ready to build the VM box. Good time to get a coffee while executing the following

% veewee vbox build ubuntu-12.04.1-server-x64

The validate command will fail as we've removed things from the postinstall. You can run it anyways

% veewee vbox validate 'ubuntu-12.04.1-server-x64'

Export the box into Vagrant .box format

% vagrant basebox export 'ubuntu-12.04.1-server-x64'

Add box to vagrant

% vagrant box add 'ubuntu-12.04.1-server-x64' 'ubuntu-12.04.1-server-x64.box'
% vagrant box list # should list box

This completes our use of VeeWee to create the base Vagrant box. We'll now work in the base directory

% cd ..

Vagrant

http://www.vagrantup.com/ Create and configure lightweight, reproducible, and portable development environments.

Install NFS support on host

% sudo apt-get install nfs-common nfs-kernel-server

Init directory as vagrant project

% vagrant init 'ubuntu-12.04.1-server-x64'

This will create a Vagrantfile. Edit the file to define the ports which will be open on the VM, and any post installation instructions. Vagrant integrates with Chef or Puppet, running the provisioner automatically. In our case, we'll do all this manually with knife solo to better replicate an EC2 instance.

See our Vagrantfile for example config, which creates a NFS share on the VM. The host directory must exist before doing next step.

Startup VM box. By default, the VM is headless so you won't see the typical VM window.

% vagrant up

SSH into box

% vagrant ssh
vagrant@devbox:~$ uname -m # 64bit
x86_64
vagrant@devbox:~$ cat /proc/cpuinfo # 2 cpus
...
vagrant@devbox:~$ logout

Librarian

Librian will manage our chef cookbooks and their dependencies https://github.com/applicationsonline/librarian

Since Librarian takes over control of the cookbooks directory, we'll make sure it's not added to version control.

% git rm -r cookbooks
% echo /cookbooks >> .gitignore
% echo /tmp >> .gitignore 

Init directory with knife solo, will create default chef directory structure

% knife solo init .

Init librarian

% librarian-chef init

Edit the Chefile with the cookbooks you wish to use. See our Cheffile for an example. Opscode maintains a repository of cookbooks, similiar to rubygems for bundler. http://community.opscode.com/cookbooks/

To create a new cookbook

% knife cookbook create mylocalcookbook -o site-cookbooks

Place any cookbooks which are not in accesible by git in the site-cookbooks directory. Use them in your Cheffile with the :path parameter.

cookbook 'mylocalcookbook'
  :path => 'site-cookbooks/mylocalcookbook'

Run librian update which downloads (or copies in the case of local cookbooks) into your cookbooks directory.

% librarian-chef install

Knife-solo + Chef-solo

Chef-solo executes the cookbooks without the need for a Chef-server. Knife-solo runs Chef-solo remotely, in this case, on our VM http://wiki.opscode.com/display/chef/Chef+Solo https://github.com/matschaffer/knife-solo

Bootstrap virtual machine for use with Chef solo. This connects via ssh to the VM and installs the chef package.

% knife solo prepare [email protected] -P vagrant -V

Note: When using Ubuntu 13.04, the prepare command fails. See this ticket. The easy workaround is manually installing Chef on the guest machine.

Creating Chef roles and nodes

Roles: "A role provides a means of grouping similar features of similar nodes, providing a mechanism for easily composing sets of functionality." - Chef website Nodes: "A node is a host that runs the Chef client. The primary features of a node, from Chef's point of view, are its attributes and its run list. Nodes are the thing that Recipes and Roles are applied to. In practice, there is usually a one-to-one mapping between a node and a physical device (a computer, a switch, a router, etc.), but in special > cases a system may execute the recipes for multiple nodes." - Chef website

To create a role, create a file with the name of your role in the roles directory. See example role. The specifics of this file are outside the scope of this guide.

Next, create a node definition for our VM, ie nodes/devbox.json. In our case, this will just be a reference to the previously created role. devbox.json

To copy over our cookbooks and definitions to the VM, but not actually run chef-solo, use the following command. This is useful for debugging cookbooks.

% knife solo cook [email protected] -P vagrant -V --sync-only

Run chef-solo on virtual machine. First SSH into virutal machine

% vagrant ssh

Librarian transfers everything in the VM to ~/chef-solo

% vagrant@devbox:~$ cd ~/chef-solo/

While testing, it can be useful to run an individual cookbook or role

% vagrant@devbox:/tmp/chef-solo$ sudo chef-solo -l debug -o "recipe[myrecipe]" -c solo.rb # runs 1 recipe
% vagrant@devbox:/tmp/chef-solo$ sudo chef-solo -l debug -o "role[dev]" -c solo.rb # runs 1 role

To run the node definition

% vagrant@devbox:/tmp/chef-solo$ sudo chef-solo -l debug -o "role[dev]" -c solo.rb

To sync the cookbook and run chef-solo from the VM host

% knife solo cook [email protected] -P vagrant -V -c solo.rb -N devbox
#!/usr/bin/env ruby
#^syntax detection
site 'http://community.opscode.com/api/v1'
cookbook 'user',
:git => 'https://github.com/fnichol/chef-user.git'
cookbook 'hostsfile',
:git => 'https://github.com/customink-webops/hostsfile.git'
cookbook 'vim'
cookbook 'htop',
:git => 'https://github.com/phlipper/chef-htop.git'
cookbook 'java',
:git => 'https://github.com/opscode-cookbooks/java.git'
cookbook 'git',
:git => 'https://github.com/awilliams/chef-cookbook-git.git'
cookbook 'runit'
cookbook 'rvm',
:git => 'https://github.com/fnichol/chef-rvm'
cookbook 'mysql',
:git => 'https://github.com/opscode-cookbooks/mysql'
cookbook 'database',
:git => 'https://github.com/opscode-cookbooks/database.git'
cookbook 'nginx',
:git => 'https://github.com/opscode-cookbooks/nginx.git'
cookbook 'rvm_passenger',
:git => 'https://github.com/awilliams/chef-rvm_passenger.git'
## Example local cookbook
# cookbook 'mycookbook',
# :path => "site-cookbooks/mycookbook"
Veewee::Session.declare({
:cpu_count => '2',
:memory_size => '1024',
:disk_size => '65536',
:disk_format => 'VDI',
:hostiocache => 'off',
:os_type_id => 'Ubuntu_64',
:iso_file => "ubuntu-13.04-server-amd64.iso",
:iso_src => "http://releases.ubuntu.com/13.04/ubuntu-13.04-server-amd64.iso",
:iso_md5 => "7d335ca541fc4945b674459cde7bffb9",
:iso_download_timeout => "1000",
:boot_wait => "4",
:boot_cmd_sequence => [
'<Esc><Esc><Enter>',
'/install/vmlinuz noapic preseed/url=http://%IP%:%PORT%/preseed.cfg ',
'debian-installer=en_US auto locale=en_US kbd-chooser/method=us ',
'hostname=devbox ',
'fb=false debconf/frontend=noninteractive ',
'keyboard-configuration/modelcode=SKIP keyboard-configuration/layout=us keyboard-configuration/variant=us console-setup/ask_detect=false ',
'initrd=/install/initrd.gz -- <Enter>'
],
:kickstart_port => "7122",
:kickstart_timeout => "10000",
:kickstart_file => "preseed.cfg",
:ssh_login_timeout => "10000",
:ssh_user => "vagrant",
:ssh_password => "vagrant",
:ssh_key => "",
:ssh_host_port => "7222",
:ssh_guest_port => "22",
:sudo_cmd => "echo '%p'|sudo -S sh '%f'",
:shutdown_cmd => "shutdown -P now",
:postinstall_files => [
"build_time.sh",
"apt.sh",
"vbox.sh",
"sudo.sh",
#"ruby.sh",
#"chef.sh",
#"puppet.sh",
"vagrant.sh",
"cleanup.sh"
],
:postinstall_timeout => "10000"
})
name 'dev'
description 'Development environment'
default_attributes({
"java" => {
"install_flavor" => "oracle",
"oracle" => {"accept_oracle_download_terms" => true}
},
"mysql" => {
"server_root_password" => "root",
"server_repl_password" => "root",
"server_debian_password" => "root"
},
"rvm" => {
"rubies" => ["ree-1.8.7-2011.12"],
"rvmrc" => {
'rvm_project_rvmrc' => 1,
'rvm_gemset_create_on_use_flag' => 1,
'rvm_trust_rvmrcs_flag' => 1
}
}
})
run_list "role[utils]",
"recipe[java]",
"recipe[git::default]",
"recipe[rvm::system]",
"recipe[mysql::server]"
{
"run_list":[
"role[dev]"
]
}
source "https://rubygems.org"
gem 'veewee',
:git => 'https://github.com/jedi4ever/veewee'
gem 'knife-solo',
:git => 'https://github.com/matschaffer/knife-solo.git'
gem 'librarian-chef'
gem 'multi_json'
gem 'ruby-shadow'
# postinstall.sh created from Mitchell's official lucid32/64 baseboxes
date > /etc/vagrant_box_build_time
# Apt-install various things necessary for Ruby, guest additions,
# etc., and remove optional things to trim down the machine.
apt-get -y update
apt-get -y upgrade
apt-get -y install linux-headers-$(uname -r) build-essential
#apt-get -y install zlib1g-dev libssl-dev libreadline-gplv2-dev
#apt-get -y install vim
apt-get clean
# Installing the virtualbox guest additions
apt-get -y install dkms
VBOX_VERSION=$(cat /home/vagrant/.vbox_version)
cd /tmp
wget http://download.virtualbox.org/virtualbox/$VBOX_VERSION/VBoxGuestAdditions_$VBOX_VERSION.iso
mount -o loop VBoxGuestAdditions_$VBOX_VERSION.iso /mnt
sh /mnt/VBoxLinuxAdditions.run
umount /mnt
rm VBoxGuestAdditions_$VBOX_VERSION.iso
rm /home/vagrant/VBoxGuestAdditions_$VBOX_VERSION.iso
# Setup sudo to allow no-password sudo for "sudo"
usermod -a -G sudo vagrant
cp /etc/sudoers /etc/sudoers.orig
sed -i -e 's/%sudo\tALL=(ALL:ALL) ALL/%sudo\tALL=(ALL:ALL) NOPASSWD:ALL/' /etc/sudoers
# Add puppet user and group
#adduser --system --group --home /var/lib/puppet puppet
# Install NFS client
apt-get -y install nfs-common
# Install Ruby from source in /opt so that users of Vagrant
# can install their own Rubies using packages or however.
#wget http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.2-p290.tar.gz
#tar xvzf ruby-1.9.2-p290.tar.gz
#cd ruby-1.9.2-p290
#./configure --prefix=/opt/ruby
#make
#make install
#cd ..
#rm -rf ruby-1.9.2-p290
# Install RubyGems 1.7.2
#wget http://production.cf.rubygems.org/rubygems/rubygems-1.8.11.tgz
#tar xzf rubygems-1.8.11.tgz
#cd rubygems-1.8.11
#/opt/ruby/bin/ruby setup.rb
#cd ..
#rm -rf rubygems-1.8.11
# Installing chef & Puppet
#/opt/ruby/bin/gem install chef --no-ri --no-rdoc
#/opt/ruby/bin/gem install puppet --no-ri --no-rdoc
# Add /opt/ruby/bin to the global path as the last resort so
# Ruby, RubyGems, and Chef/Puppet are visible
#echo 'PATH=$PATH:/opt/ruby/bin/'> /etc/profile.d/vagrantruby.sh
# Installing vagrant keys
mkdir /home/vagrant/.ssh
chmod 700 /home/vagrant/.ssh
cd /home/vagrant/.ssh
wget --no-check-certificate 'https://raw.github.com/mitchellh/vagrant/master/keys/vagrant.pub' -O authorized_keys
chmod 600 /home/vagrant/.ssh/authorized_keys
chown -R vagrant /home/vagrant/.ssh
# Remove items used for building, since they aren't needed anymore
apt-get -y remove linux-headers-$(uname -r) build-essential
apt-get -y autoremove
# Zero out the free space to save space in the final image:
dd if=/dev/zero of=/EMPTY bs=1M
rm -f /EMPTY
# Removing leftover leases and persistent rules
echo "cleaning up dhcp leases"
rm /var/lib/dhcp/*
# Make sure Udev doesn't block our network
# http://6.ptmc.org/?p=164
echo "cleaning up udev rules"
rm /etc/udev/rules.d/70-persistent-net.rules
mkdir /etc/udev/rules.d/70-persistent-net.rules
rm -rf /dev/.udev/
rm /lib/udev/rules.d/75-persistent-net-generator.rules
echo "Adding a 2 sec delay to the interface up, to make the dhclient happy"
echo "pre-up sleep 2" >> /etc/network/interfaces
exit
name 'utils'
description 'Basic utilities needed on all installs'
run_list "recipe[vim]",
"recipe[htop]"
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant::Config.run do |config|
config.vm.box = "ubuntu-12.04.1-server-x64"
# Assign this VM to a host-only network IP, allowing you to access it
# via the IP. Host-only networks can talk to the host machine as well as
# any other machines on the same network, but cannot be accessed (through this
# network interface) by any external networks.
config.vm.network :hostonly, "33.33.33.10"
config.vm.forward_port 3000, 3000
# Share an additional folder to the guest VM. The first argument is
# an identifier, the second is the path on the guest to mount the
# folder, and the third is the path on the host to the actual folder.
config.vm.share_folder "hostbox", "/hostbox", "../", :nfs => true, :extra => 'uid=1001,gid=1003,dmode=770,fmode=770'
# Set VM RAM size
config.vm.customize ["modifyvm", :id, "--memory", "1024"]
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment