Sunday, March 29, 2015

More Vagrant Learnings

Last time I wrote about how we use Vagrant to manage our local development environment at work. Since then I've dug into Vagrant a little more deeply and found a few nuggets I wanted to share.

Package your own Vagrant Box

There are some great base boxes (images) available for vagrant. It will even install them for you. If you want to tweak the box, you can use one or more of the many available provisioning providers. However, sometimes you might want to just bake the tweak into a new base image, for example, if the tweak is something you want to apply to all your instances, or if it is time consuming. Vagrant makes it very easy to do this.

Initialize, start VM

$ vagrant init hashicorp/precise32
$ vagrant up

Install new stuff in VM

$ vagrant ssh
vagrant@precise32:~$ echo 'set editing-mode vi' > ~/.inputrc
vagrant@precise32:~$ sudo apt-get update
vagrant@precise32:~$ sudo apt-get install vim
vagrant@precise32:~$ exit

Package up VM image as a new Vagrant box

$ vagrant package
==> default: Attempting graceful shutdown of VM...
==> default: Clearing any previously set forwarded ports...
==> default: Exporting VM...
==> default: Compressing package to: /Users/ryanmckay/projects/vagrant/package.box

$ ls -l
total 801784
-rw-r--r--  1 ryanmckay  staff       3001 Mar 14 22:13 Vagrantfile
-rw-r--r--  1 ryanmckay  staff  410509096 Mar 15 20:32 package.box

$ vagrant box list
hashicorp/precise32 (virtualbox, 1.0.0)

$ vagrant box add --name 'ryanmckay/withvim' package.box 
==> box: Adding box 'ryanmckay/withvim' (v0) for provider: 
    box: Downloading: file:///Users/ryanmckay/projects/vagrant/package.box
==> box: Successfully added box 'ryanmckay/withvim' (v0) for 'virtualbox'!

$ vagrant box list
hashicorp/precise32 (virtualbox, 1.0.0)
ryanmckay/withvim   (virtualbox, 0)

Provisioning

Vagrant provides a boatload of provisioning providers, e.g. puppet, chef, ansible, and even shell script. Normally configured provisioners are run when bringing up the VM for the first time. However, you can also run the provisioners with vagrant provision. For example, if you have the following:

provision_counter.sh shell script

echo "provisioning"
if [ -f /etc/provision_count ]; then
 counter=`cat /etc/provision_count`
else
 counter=0
fi
counter=$((counter+1))
echo -n $counter > /etc/provision_count
echo "provisioned: $counter"

Configured in your Vagrantfile

config.vm.provision "shell", path: "provision_counter.sh"

When you vagrant up, provisioners are run

$ vagrant up
...
==> default: Running provisioner: shell...
    default: Running: /var/folders/k5/6mc1_m_n1675572ts12qr7588mtjh5/T/vagrant-shell20150329-86419-qkjn7x.sh
==> default: stdin: is not a tty
==> default: provisioning
==> default: provisioned: 1

And you can run provisioners explicitly

$ vagrant provision
==> default: Running provisioner: shell...
    default: Running: /var/folders/k5/6mc1_m_n1675572ts12qr7588mtjh5/T/vagrant-shell20150316-31070-d35qp6.sh
==> default: stdin: is not a tty
==> default: provisioning
==> default: provisioned: 2

You can even add/modify provisioning lines in the Vagrantfile of a running VM, and when you run 'vagrant provision', the new lines will be used.

Reloading Vagrantfile

The Vagrantfile is the main config file for your vagrant VM. It specifies all the stuff that needs to be set up before the OS is running, as well as the hooks for provisioning once it has booted. The Vagrantfile can be reloaded by 'vagrant reload', which is basically 'halt' and then 'up'. It does not re-run provisioners unless you tell it to. Some examples of why you would want to do this are to modify your network setup or your shared folders. For example, if you want to:

Add a network port forward and a shared folder

config.vm.network "forwarded_port", guest: 80, host: 8080
config.vm.synced_folder "src/", "/srv/website"

Then 'vagrant reload' would restart your VM with the new configuration without having to reprovision.

Vagrant SSH

'vagrant ssh' is how you ssh into an interactive session on your VM. You can also use 'vagrant ssh -c command' to run a command in the VM instead of an interactive shell, similar to 'ssh host command'.
$ vagrant ssh -c 'hostname'
precise32

In order to connect to your VM with regular ssh (or anything that depends on ssh, e.g. scp or rsync over ssh), you need to

Export vagrant ssh-config

$ vagrant ssh-config
Host default
  HostName 127.0.0.1
  User vagrant
  Port 2222
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile /Users/ryan.mckay/.vagrant.d/insecure_private_key
  IdentitiesOnly yes
  LogLevel FATAL

$ vagrant ssh-config > ssh-config

Then provide ssh-config to ssh and friends

$ ssh -F ssh-config default 'hostname'
precise32

$ touch scp_me
$ scp -F ssh-config scp_me default:/tmp
scp_me                              100%    0     0.0KB/s   00:00    
$ ssh -F ssh-config default 'ls /tmp'
scp_me

$ mkdir rsync_test
$ touch rsync_test/rsync_me
$ rsync -a -e 'ssh -F ssh-config' rsync_test default:/tmp/
$ ssh -F ssh-config default 'find /tmp'
/tmp
/tmp/rsync_test
/tmp/rsync_test/rsync_me
/tmp/scp_me