1.     Requirements

Ruby

https://www.ruby-lang.org/en/

VirtualBox

https://www.virtualbox.org/

EC2 tools (Optional)

https://juanvicenteherrera.eu/2012/02/21/first-steps-with-ec2-api-tools/

Knife

https://learnchef.opscode.com/quickstart/workstation-setup/

Vagrant

$ gem install vagrant

Vagrant plugins

https://github.com/mitchellh/vagrant-aws

https://github.com/schisamo/vagrant-omnibus

https://github.com/cassianoleal/vagrant-butcher

$ vagrant plugin install vagrant-aws
$ vagrant plugin install vagrant-omnibus
$ vagrant plugin install vagrant-butcher

2.     Tests

Vagrant first steps in workstation with VirtualBox

$ vagrant box add base http://files.vagrantup.com/lucid32.box
$ vagrant init
$ vagrant up

Vagrant  test  with manual chef bootstrap

$  vagrant box add CentOS-6.4 http://developer.nrel.gov/downloads/vagrant-boxes/CentOS-6.4-x86_64-v20130309.box
$  vagrant init opscode-ubuntu-1204 https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04-i386_chef-11.4.4.box --no-color
$  vagrant up --no-color
$  knife bootstrap localhost  --ssh-user ec2-user   --ssh-password vagrant   --ssh-port 2222   --run-list "recipe[apache2]"   --sudo
$  vagrant ssh

  Vagrant ec2 test 2 without chef management

$ vagrant plugin install vagrant-aws
$ vagrant box add dummy https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box
$ vagrant up --provider=aws

3.    POC Vagrant/EC2/Chef

Briefing

Base Box: The base box is simply a saved hard-disk of a Virtual Machine created with VirtualBox. It can contain anything but it needs at least :

  • Ruby
  • VirtualBox guest additions
  • Puppet or Chef

In  the case that use AWS provider the box format is basically just the required metadata.json file along with a Vagrantfile that does default settings for the provider-specific configuration for this provider.

Box used in the example

Vagrant AWS Example Box

Vagrant providers each require a custom provider-specific box format. These files compose the contents of a box for the aws provider.

  • README.md
  • Vagrantfile
  • metadata.json

To turn this into a box:

$ tar cvzf aws.box ./metadata.json ./Vagrantfile

This box works by using Vagrant’s built-in Vagrantfile merging to setup defaults for AWS. These defaults can easily be overwritten by higher-level Vagrantfiles (such as project root Vagrantfiles).

Vagrant basic commands

vagrant init :

creates a file called ‘Vagrantfile’ in your current directory

when you look at the file, it will contain the directive …

config.vm.box = "base"

this is what makes the link to the box we called ‘base’

you can further edit the Vagrantfile before you start it

vagrant up:

up until now, no virtual machine was created

therefore vagrant will import the disks from the box ‘base’ into Virtualbox(not applied for AWS, the instance that is going to be created has EBS disks already configured)

map via NAT the port 22 from your VM to a free local port

it will create a .vagrant file : a file that contains a mapping between your description ‘base’ and the UUID of the virtual machine

vagrant ssh:

this will lookup the mapping of the ssh inside and will execute the SSH process to log into the machine use a privatekey of use vagrant to login to a box that has the user vagrant with it’s public setup in the virtual machine

Vagrant commands

$ vagrant
Tasks:
vagrant box                        # Commands to manage system boxes
vagrant destroy                    # Destroy the environment, deleting the created virtual machines
vagrant halt                       # Halt the running VMs in the environment
vagrant help [TASK]                # Describe available tasks or one specific task
vagrant init [box_name] [box_url]  # Initializes the current folder for Vagrant usage
vagrant package                    # Package a Vagrant environment for distribution
vagrant provision                  # Rerun the provisioning scripts on a running VM
vagrant reload                     # Reload the environment, halting it then restarting it.
vagrant resume                     # Resume a suspended Vagrant environment.
vagrant ssh                        # SSH into the currently running Vagrant environment.
vagrant ssh_config                 # outputs .ssh/config valid syntax for connecting to this environment via ssh
vagrant status                     # Shows the status of the current Vagrant environment.
vagrant suspend                    # Suspend a running Vagrant environment.
vagrant up                         # Creates the Vagrant environment
vagrant version                    # Prints the Vagrant version information

POC

Add dummy AWS box

$ vagrant box add dummy https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box

Vagrant example with 1 ec2 instance and configuration managed by Chef

Vagrantfile


Vagrant.configure("2") do |config|

config.vm.box = "dummy"

config.omnibus.chef_version = "11.6.0"

config.vm.provider :aws do |aws, override|

aws.keypair_name = "bq"

aws.access_key_id     = " xxxxxx "

aws.secret_access_key = " xxxxxxxxxxxxxxxxxx "

aws.ami = "ami-3ad1af53"

override.ssh.username = "ec2-user"

override.ssh.private_key_path = "/Users/juanvi/keypairs/bq.pem"

end

config.vm.provision :chef_client do |chef|

chef.chef_server_url = "https://api.opscode.com/organizations/juanvi"

chef.validation_key_path = "/Users/juanvi/chef-repo/.chef/juanvi-validator-new.pem"

chef.validation_client_name = "juanvi-validator"

# Provision with the database role

chef.add_role("webserver")

# Set the environment for the chef server

chef.environment = "dev"

end

end

Notes: The ami used as template is a standard AMI 64 bits EBS backed http://aws.amazon.com/amazon-linux-ami/ with sudo permissions for vagrant user.

 

Vagrant example with 2 instances in ec2 and configuration managed by Chef


Vagrant.configure("2") do |config|

config.vm.define :web do |web|

web.vm.box = "dummy"

web.vm.provider :aws do |aws, override|

aws.keypair_name = "bq"

config.omnibus.chef_version = "11.6.0"

aws.access_key_id     = " xxxxxx "

aws.secret_access_key = " xxxxxxxxxxxxxxxxxx "

aws.ami = "ami-3ad1af53"

override.ssh.username = "ec2-user"

override.ssh.private_key_path = "/Users/juanvi/keypairs/bq.pem"

end

web.vm.provision :chef_client do |chef|

chef.chef_server_url = "https://api.opscode.com/organizations/juanvi"

chef.validation_key_path = "/Users/juanvi/chef-repo/.chef/juanvi-validator-new.pem"

chef.validation_client_name = "juanvi-validator"

#  Provision with the database role

chef.add_role("webserver")

# Set the environment for the chef server

chef.environment = "dev"

end

end

config.vm.define :db do |db|

db.vm.box = "dummy"

db.vm.provider :aws do |aws, override|

aws.keypair_name = "bq"

config.omnibus.chef_version = "11.6.0"

aws.access_key_id     = " xxxxxx "

aws.secret_access_key = " xxxxxx "

aws.ami = "ami-3ad1af53"

override.ssh.username = "ec2-user"

override.ssh.private_key_path = "/Users/juanvi/keypairs/bq.pem"

end

db.vm.provision :chef_client do |chef|

chef.chef_server_url = "https://api.opscode.com/organizations/juanvi"

chef.validation_key_path = "/Users/juanvi/chef-repo/.chef/juanvi-validator-new.pem"

chef.validation_client_name = "juanvi-validator"

#  Provision with the database role

chef.add_role("db")

# Set the environment for the chef server

chef.environment = "dev"

end

end

end

Provisioning only one kind of server executing chef_client

$ vagrant provision web --provision-with chef_client

Provisioning the whole platform executing chef_client

$ vagrant provision --provision-with chef_client

After editing the Vagrantfile you need to ‘reboot’ the machine to take this settings

$ vagrant reload

Version control

Now is a good time to version control your vagrant project

$ cd
$ git init
$ git add Vagrantfile
$ git commit -m "First version of Vagranfile"

Cleanup

Install the following plugin for vagrant:

$ vagrant plugin install vagrant-butcher

add this line to your Vagrantfile:


config.butcher.knife_config_file = '.chef/knife.rb'

then you can terminate an instance and deregister in the Chef server executing:

$ vagrant destroy -f
[Butcher] knife.rb location set to '/path/to/knife.rb'
[Butcher] Chef node 'node_name' successfully butchered from the server...
[Butcher] Chef client 'node_name' successfully butchered from the server...
[default] Forcing shutdown of VM...
[default] Destroying VM and associated drives...

4.    Tips

  • Enabling different settings based on environment

Setting these variables is as easy as prepending them to the vagrant command

$ vagrant_env=development vagrant up
  • Create a config file in ruby with the aws credentials and include in all of the vagrant files to have only one configuration file:

require File.expand_path('~/.chef/aws_credentials.rb')

inside Vagrantfile you can use these variables with:


access_key_id=<strong>@access_key_id</strong>

  • I recommend you have all the chef certificates in your ~/.chef folder  and a knife.rb file per project with specific configuration(path to chef certificates and ec2 keypairs in your ~/.chef folder)
  • If you include .vagrant folder in your git repositor all the team can manage the vagrant environment
  • Create a base chef role with all of the common tools needed in all of the servers (basic tools)
{
"name": "web-base",
"description": "Base role applied to all nodes.",
"run_list":[
 "recipe[git]",
 "recipe[build-essential]",
 "recipe[vim]",
 "recipe[aws_hostname]",
 "recipe[aws_hostname::register-dns]"
],
"override_attributes":{},
"chef_type":"role"

}

5.     Some  observations

  • It clearly helps everybody to have a consistent environment to develop against, the latest version is just one git pull away.
  • The central approach drives people to a) do frequent commits and b) do stable commits.
  • The task of writing recipes is not picked up by all team members, and seem to stay the main job of the system oriented people on the team.
  • Reading coobooks help people understand what is needed and makes it easy to point out what needs to be changed. But learning the skills to write recipes/manifest is a blocking factor just as having a backend developer writing frontend code.
  • The test the admins have to do before committing their cookbooks, is that they destroy their own ‘development’ box and re-provision a new box to see if this works.
  • The longer the provision takes, the less frequent people do it. It’s important to keep that process as fast as possible. It’s all about feedback and we want it fast.
  • Installation problems would get noticed far sooner in the process.

People would only do a full rebuild in the morning when getting their coffee.

6.     Conclusions

  • Vagrant is an essential part of the DevOps  process: it is the solution to developing, testing and deploying in the same environment. It thus ensures a smoother transition of your project from the dev team to the ops team.
  • Vagrant is EASY. And it is compatible with Chef.

7.     References and resources

You can check the cookbook used in these examples and all of the chef resources ( https://github.com/juanviz/chef-jv/blob/master/cookbooks/wiki_app/README.md and https://github.com/juanviz/chef-jv/) and more Vagrant examples (https://github.com/juanviz/VagrantProjects)

http://docs.vagrantup.com/v2/cli/index.html

http://red-badger.com/blog/2013/02/21/automating-your-infrastructure-with-vagrant-chef-from-development-to-the-cloud/

http://docs-v1.vagrantup.com/v1/docs/provisioners/chef_server.html

http://www.jasongrimes.org/2012/06/managing-lamp-environments-with-chef-vagrant-and-ec2-1-of-3/

http://docs-v1.vagrantup.com/v1/docs/getting-started/provisioning.html

http://beacon.wharton.upenn.edu/404/2013/04/starting-the-server-build/

Advertisements