Vagrant: a closer look
Previously we have talked about the benefits of using virtual machines for local development and how Vagrant and Ansible help you manage your VMs. In this post we will talk about the Vagrant part of our setup.
Here's the repository that we use https://github.com/Webscope/vagrant-ansible-drupal7. We recommend you download it to better follow the description. So let's begin.
Vagrant allows you to spin up and take down your VMs with a single command. All the instructions are stored in a text file called Vagrantfile. You will need to have a Vagrantfile for each of your VMs. You can generate it by running the following command:
vagrant init
Our Vagrantfile starts by checking if the vagrant-triggers plugin has been installed. This plugin comes in handy if we need to perform various actions when booting up or shutting down the VM.
if !Vagrant.has_plugin?("vagrant-triggers")
puts "'vagrant-triggers' plugin is required"
puts "This can be installed by running:"
puts
puts " vagrant plugin install vagrant-triggers"
puts
exit
endThe next step is defining a few variables that will be used throughout the file:
# Find the current vagrant directory. vagrant_dir = File.expand_path(File.dirname(__FILE__)) provision_hosts_file = vagrant_dir + '/provision/host.ini' # Include config from provision/settings.yml require 'yaml' vconfig = YAML::load_file(vagrant_dir + "/provision/settings.yml") # Configuration boxipaddress = vconfig['boxipaddress'] boxname = vconfig['boxname']
The file settings.yml is an important one, as it contains all the settings for your VM. This file doesn't exist in a repo as it is specific to each VM, so it needs to be copied from example.settings.yml, and the settings should be adjusted according to your needs before you start the VM. Vagrant will need the following variables from settings.yml:
--- boxipaddress: "192.168.100.100" boxname: "vagrant" memory: 1024 cpus: 2 webserver_hostname: 'vagrant-multi.local'
Other variables are for Ansible provisioning.
Here are some of the most basic VM settings, which are quite self-explanatory:
# Configure virtual machine options. config.vm.box = "hashicorp/precise64" config.vm.hostname = boxname config.vm.network :private_network, ip: boxipaddress # Set *Vagrant* VM name config.vm.define boxname do |boxname| end # Configure virtual machine setup. config.vm.provider :virtualbox do |v| v.customize ["modifyvm", :id, "--memory", vconfig["memory"]] v.customize ["modifyvm", :id, "--cpus", vconfig["cpus"]] end
In order for the VM to use the DNS settings of your network adapter:
v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
Another handy setting allows you to use your local SSH keys when connecting to remote servers from within the VM:
config.ssh.forward_agent = true
With Vagrant you can sync folders between your local computer and your VM. It's super cool, because you can edit code in your editor of choice, and the changes will be instantly synced into the VM, where your project actually lives. We are using NFS which is natively supported by Vagrant, it gives us far greater speeds compared to the default shared folders implementation. There's still one downside to NFS - it slows down when the number of files in the project becomes too big. It is the case for all Drupal projects, and we see the speed downgrade when running git commands from within the box, but there's no noticeable impact on syncing when files are being updated during development. NFS support is enabled as follows:
config.vm.synced_folder "./webroot", "/home/vagrant/sites/" + vconfig["webserver_hostname"], type: "nfs", create: true
Windows users might want to check out this plugin https://github.com/GM-Alex/vagrant-winnfsd
Now back to vagrant triggers. Every time when we run vagrant up, Vagrant launches a small helper Ansible playbook, which performs some pre-provisioning tasks. File host.ini is being created at this point and is populated with some helper variables, this file also serves as an indication that the VM has been booted.
# Run an Ansible playbook on setting the box up
if !File.exist?(provision_hosts_file)
config.trigger.before :up, :stdout => true, :force => true do
run 'ansible-playbook -i ' + boxipaddress + ', --ask-sudo-pass ' + vagrant_dir + '/provision/playbooks/local_up.yml --extra-vars "local_ip_address=' + boxipaddress + '"'
end
endWhen the VM is shut down or destroyed, another helper playbook executes a few cleanup tasks and it removes the host.ini file:
# Run the halt/destroy playbook upon halting or destroying the box
if File.exist?(provision_hosts_file)
config.trigger.before [:halt, :destroy], :stdout => true, :force => true do
run 'ansible-playbook -i ' + boxipaddress + ', --ask-sudo-pass ' + vagrant_dir + '/provision/playbooks/local_halt_destroy.yml'
end
endAfter the VM has been booted Vagrant launches the provisioning script:
config.vm.provision "ansible" do |ansible|
ansible.playbook = vagrant_dir + "/provision/playbooks/site.yml"
ansible.host_key_checking = false
ansible.extra_vars = {user:"vagrant"}
if vconfig['ansible_verbosity'] != ''
ansible.verbose = vconfig['ansible_verbosity']
end
endOur VM is set up to host multiple sites, so our Vagrantfile also sets up synced folders and virtual hosts for every project. That happens after the box has been booted up and provisioned.
vconfig['vhosts'].each do |vhost|
site_alias = vhost['alias']
folder_path = vhost['path']
config.vm.synced_folder folder_path,
"/home/vagrant/sites/" + site_alias,
create: true,
type: "nfs",
mount_options: ['rw', 'fsc'], # the fsc is for cachedfilesd
:nfs => nfs_setting
end
# Create all virtual hosts
config.trigger.after [:up, :reload], :stdout => true, :force => true do
run 'ansible-playbook -i ' + boxipaddress + ', -K --user=vagrant --private-key=~/.vagrant.d/insecure_private_key ' + vagrant_dir + '/provision/playbooks/virtual_hosts.yml --extra-vars "local_ip_address=' + boxipaddress + '"'
endVagrant box can be accessed via SSH by running:
vagrant ssh
In order to shut down the VM, just run:
vagrant halt
To destroy the VM completely:
vagrant destroy
And that's basically it. For more information, please check the official documentation at https://docs.vagrantup.com/v2/.
We'll take a closer look at Ansible in the next post. Stay tuned.
You can also check out: