I use Ansible to automate the deployments of my websites (LinuxJobs.fr, Journal du hacker) and my applications (Feed2toot, Feed2tweet). I’ll describe in this blog post my setup in order to test my Ansible Playbooks locally on my laptop.
Why testing the Ansible Playbooks
I need a simple and a fast way to test the deployments of my Ansible Playbooks locally on my laptop, especially at the beginning of writing a new Playbook, because deploying directly on the production server is both reeeeally slow… and risky for my services in production.
Instead of deploying on a remote server, I’ll deploy my Playbooks on a VirtualBox using Vagrant. This allows getting quickly the result of a new modification, iterating and fixing as fast as possible.
Disclaimer: I am not a profesionnal programmer. There might exist better solutions and I’m only describing one solution of testing Ansible Playbooks I find both easy and efficient for my own use cases.
- Begin writing the new Ansible Playbook
- Launch a fresh virtual machine (VM) and deploy the playbook on this VM using Vagrant
- Fix the issues either from the playbook either from the application deployed by Ansible itself
- Relaunch the deployment on the VM
- If more errors, go back to step 3. Otherwise destroy the VM, recreate it and deploy to test a last time with a fresh install
- If no error remains, tag the version of your Ansible Playbook and you’re ready to deploy in production
What you need
First, you need Virtualbox. If you use the Debian distribution, this link describes how to install it, either from the Debian repositories either from the upstream.
Second, you need Vagrant. Why Vagrant? Because it’s a kind of middleware between your development environment and your virtual machine, allowing programmatically reproducible operations and easy linking your deployments and the virtual machine. Install it with the following command:
# apt install vagrant
Setting up Vagrant
Everything about Vagrant lies in the file Vagrantfile. Here is mine:
Vagrant.require_version ">= 2.0.0" Vagrant.configure(1) do |config| config.vm.box = "debian/stretch64" config.vm.provision "shell", inline: "apt install --yes git python3-pip" config.vm.provision "ansible" do |ansible| ansible.verbose = "v" ansible.playbook = "site.yml" ansible.vault_password_file = "vault_password_file" end end
- The 1st line defines what versions of Vagrant should execute your Vagrantfile.
- The first loop of the file, defining what kind of Vagrant configuration file you’re using (here #1).
- The 3rd line defines the official Vagrant image we’ll use for the virtual machine.
- The 4th line is really important: those are the missing apps we miss on the VM. Here we install git and python3-pip with apt.
- The next line indicates the start of the Ansible configuration.
- On the 6th line, we want a verbose output of Ansible.
- On the 7th line, we define the entry point of your Ansible Playbook.
- On the 8th line, if you use Ansible Vault to encrypt some files, just define here the file with your Ansible Vault passphrase.
When Vagrant launches Ansible, it’s going to launch something like:
$ ansible-playbook --inventory-file=/home/me/ansible/test-ansible-playbook/.vagrant/provisioners/ansible/inventory -v --vault-password-file=vault_password_file site.yml
After writing your Vagrantfile, you need to launch your VM. It’s as simple as using the following command:
$ vagrant up
That’s a slow operation, because the VM will be launched, the additionnal apps you defined in the Vagrantfile will be installed and finally your Playbook will be deployed on it. You should sparsely use it.
Ok, now we’re really ready to iterate fast. Between your different modifications, in order to test your deployments fast and on a regular basis, just use the following command:
$ vagrant provision
Once your Ansible Playbook is finally ready, usually after lots of iterations (at least that’s my case), you should test it on a fresh install, because your different iterations may have modified your virtual machine and could trigger unexpected results.
In order to test it from a fresh install, use the following command:
$ vagrant destroy && vagrant up
That’s again a slow operation. You should use it when you’re pretty sure your Ansible Playbook is almost finished. After testing your deployment on a fresh VM, you’re now ready to deploy in production.Or at least better prepared :p
Possible improvements? Let me know
I find the setup described in this blog post quite useful for my use cases. I can iterate quite fast especially when I begin writing a new playbook, not only on the playbook but sometimes on my own latest apps, not yet ready to be deployed in production. Deploying on a remote server would be both slow and dangerous for my services in production.
I could use a continous integration (CI) server, but that’s not the topic of this blog post. As said before, the goal is to iterate as fast as possible in the beginning of writing a new Ansible Playbook.
Commiting, pushing to your Git repository and waiting for the execution of your CI tests is overkill at the beginning of your Ansible Playbook, when it’s full of errors waiting to be debugged one by one. I think CI is more useful later in the life of the Ansible Playbooks, especially when different people work on it and you have a set or code quality rules to enforce. That’s only my opinion and it’s open to discussion, one more time I’m not a professionnal programmer.
If you have better solutions to test Ansible Playbooks or to improve the one describe here, let me know by writing a comment or by contacting me through my accounts on social networks below, I’ll be delighted to listen to your improvements.
Carl Chenet, Free Software Indie Hacker, Founder of LinuxJobs.fr, a job board for Free and Open Source Jobs in France.
Follow Me On Social Networks
- My Mastodon account : @carlchenet
- My Diaspora* account : @carlchenet
- My Twitter account : @carl_chenet
7 thoughts on “Testing Ansible Playbooks With Vagrant”
En anglais nous disons:
“to automate” au lieu de “to automatize”
“This allows quickly getting the result” a.l.d. “This allows to get quickly the result”
“There might exist” a.l.d. “It might exist”
“we’ll use for the virtual machine.” a.l.d. “we’ll use to for the virtual machine.”
“those are the missing apps” a.l.d. “that’s the missing apps”
“On the 6th line” a.l.d “At the 6th line”
“When Vagrant launches Ansible” a.l.d. “When Vagrant will launch Ansible”
Step #5 in your process says that you destroy the VM before starting again. Rather than destroying it, it would be faster to just make a snapshot before running the test. That way if the test fails, just restore from the snapshot.
Thanks a lot for the English lesson 🙂 Fixed all the errors you reported.
The goal of the step #5 is to get a new fresh installation and deploy on this fresh installation as the last test.
Each time you use the “vagrant provision” command, you execute Ansible on the same VM. And you usually use it a lot when you start writing your new playbooks. So at the end, your last successful deployment could be the result of some of your deployments and not only the result of the last one.
For example, let’s say you need special rights on a file. One of your iterations changed the rights of this file with the ones you need. But during the next iterations, you removed this order from your playbook. You won’t realize your mistake before deploying the playbook on a fresh install. That’s why step #5 is needed.
Have you considered using some proper testing tool like https://molecule.readthedocs.io/ ?
I didn’t know molecule and I’ll give it a try, thanks.
fabfile still be more easy than ansible
Your article claims that the `1` in `Vagrant.configure(1)` means the number of configured machines, but the number is actually the Vagrant configuration version.
You’re right, thanks! I just fixed it.