Ansible: a closer look
Ansible is a great tool for automating tasks executed either locally or remotely. Today we will talk about using Ansible in conjunction with Vagrant for setting up a local development environment.
All our Ansible playbooks can be split into 4 groups:
- Pre-provision - executed on every VM bootup
- Provision - executed when VM is being created, or when executing vagrant provision in the terminal
- Virtual hosts - executed after VM was booted up, or when executing vagrant reload in the terminal
- Cleanup - executed when VM is being shut down or destroyed
All of these playbooks are launched by Vagrant. We will use Provision scrips to walk you through the basics of what Ansible has to offer.
It all starts with provision/playbooks/site.yml.
You need to specify which hosts that playbook will run on (defined in an inventory file). We have specified the option "all", which is defined in provision/host.ini. More info on inventory is available here.
--- - hosts: all
You can define your own variables for later use in playbooks, and you can import a YAML file that already has all the variables defined. Variables are used like this - {{ sites_dir }}.
--- vars: sites_dir: /home/vagrant/sites vars_files: - ../settings.yml
Roles is a great way of keeping your playbooks organised.
roles: - base - nginx - php - mysql - adminer - codeception - front
When Ansible is going through site.yml, the first role that is being executed is base. Ansible will automatically look for the file playbooks/roles/base/tasks/main.yml, which in its turn includes a bunch of other playbooks.
--- - include: aptget.yml - include: bashrc.yml - include: gitconfig.yml - include: nfs.yml - include: vimrc.yml
Out of the box Ansible supports a ton of modules, which significantly simplify writing playbooks. You can find a great example of installing packages in aptget.yml. Ansible loops through the list and makes sure that the packages are installed. Running commands as sudo is a breeze. Please note that if that package is already installed, Ansible skips it and goes to the next item. That saves heaps of time when you're re-provisioning for whatever reason.
---
- name: apt-get | apt-get update
apt: update_cache=yes cache_valid_time=3600
tags: aptget
sudo: true
- name: apt-get | ensure core packages are installed
apt: pkg={{ item }} state=installed
tags: aptget
sudo: true
with_items:
- atool
- atop
- curl
- git
- imagemagick
- make
- mc
- python-software-properties
- tig
- tree
- vim
- unzipYou will be able to write most of the tasks using modules, however sometimes the actual terminal command works best.
- name: Git Config | Install git completion raw: curl https://raw.githubusercontent.com/git/git/master/contrib/completion/git-completion.bash -o ~/.git-completion.bash tags: git
By using the template module, you can provide your own content for files.
- name: Git Config | add .gitconfig file
template: src=gitconfig.j2 dest=/home/{{ user }}/.gitconfig
tags: git
sudo: trueTemplates are processed by the Jinja2 templating language, and the great thing is that you can use your variables in templates, which makes templates dynamic.
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
port={{ mysql_port }}
max_allowed_packet={{ mysql_max_allowed_packet }}
{{ innodb_file_per_table }}
character_set_server={{ mysql_character_set_server }}
collation_server={{ mysql_collation_server }}
bind-address=0.0.0.0
[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pidOnce in a while you will face situations when the next action depends on the result of the previous one. In our case, we check if drush has been installed, and depending on the output we either proceed with the installation or skip it. In the first task we capture the output of the shell command into a variable, and install drush when that output contains "command not found".
---
- name: Drush | Check if Drush is installed (ignore if fails)
raw: drush --version
register: drush_installed
ignore_errors: true
tags: drush
- name: Drush | install drush via composer
command: composer global require drush/drush:6.*
when: drush_installed.stdout.find('command not found') != -1
tags: drush
- name: Drush | set drush to executable
command: chmod u+x drush chdir=/home/{{ user }}/.composer/vendor/drush/drush
when: drush_installed.stdout.find('command not found') != -1
tags: drush
- name: Drush | link up the drush command so everyone can run it
file: src=/home/{{ user }}/.composer/vendor/drush/drush/drush dest=/usr/bin/drush state=link
when: drush_installed.stdout.find('command not found') != -1
tags: drush
sudo: trueAs you can see, Ansible makes scripting a walk in the park, which is why we love it.
For more information and other modules please check http://docs.ansible.com/.
You can also check out: