My Ansible installation contains 8 servers, 18 roles, and approximately 200 tasks. It includes servers running NGINX, Passenger, Ruby on Rails, Sidekiq, Redis, PostgreSQL, and more. The following is how I use roles, tags, and dependencies to keep my sanity and stay DRY.
Let's use Redis as an example. There are three servers, one master, two slaves, all running Redis Sentinal.
The relevant portion of my inventory file:
[redis-master]
prestwick
[redis-slave]
carnoustie
birkdale
The relevant portion of my main playbook:
- hosts: redis-master
roles:
- {role: 'redis-master', tags: 'redis-master'}
- {role: 'redis-sentinel', tags: 'redis-sentinel'}
- hosts: redis-slave
roles:
- {role: 'redis-slave', tags: 'redis-slave'}
- {role: 'redis-sentinel', tags: 'redis-sentinel'}
By adding the tags
in this playbook all dependent tasks will automatically
pick up that tag. This includes any dependencies! It also means no more
needing to sprinkle tags throughout the other playbooks.
Naming the same as the role helps my sanity as I can call ansible-playbook
--tags=redis-master
which will run all tasks necessary for the redis-master
role. Exactly as if ansible had a --role
option.
Each of these roles has some common tasks, namely ensuring a particular APT repository is present and some common handlers (restart redis, restart sentinel). This is where dependencies come into play.
Each of the redis-master
, redis-slave
, and redis-sentenel
roles has a meta/main.yml
file with the following contents:
dependencies:
- { role: 'redis-common' }
It is in this redis-common
role that I place the tasks, handlers, files, and
templates common to all of the other redis related roles.
The great thing about this setup is that you never have to specify
redis-common
directly. It will be picked up as necessary while running the
other roles.
You can see how it works by comparing the task list for redis-master
and redis-slave
below:
$ ansible-playbook -i inventory/staging -t redis-master --list-tasks site.yml
playbook: site.yml
...
play #6 (redis-master): TAGS: []
apt_key url=http://www.dotdeb.org/dotdeb.gpg state=present TAGS: [redis-master]
apt_repository repo='deb http://packages.dotdeb.org wheezy all' state=present TAGS: [redis-master]
apt_repository repo='deb-src http://packages.dotdeb.org wheezy all' state=present TAGS: [redis-master]
apt pkg=redis-server state=present install_recommends=yes TAGS: [redis-master]
service name=redis-server enabled=yes TAGS: [redis-master]
template src=redis.conf dest=/etc/redis/redis.conf owner=redis group=redis mode=0644 TAGS: [redis-master]
play #7 (redis-slave): TAGS: []
$ ansible-playbook -i inventory/staging -t redis-slave --list-tasks site.yml
playbook: site.yml
...
play #6 (redis-master): TAGS: []
play #7 (redis-slave): TAGS: []
apt_key url=http://www.dotdeb.org/dotdeb.gpg state=present TAGS: [redis-slave]
apt_repository repo='deb http://packages.dotdeb.org wheezy all' state=present TAGS: [redis-slave]
apt_repository repo='deb-src http://packages.dotdeb.org wheezy all' state=present TAGS: [redis-slave]
apt pkg=redis-server state=present install_recommends=yes TAGS: [redis-slave]
service name=redis-server enabled=yes TAGS: [redis-slave]
template src=redis.conf dest=/etc/redis/redis.conf owner=redis group=redis mode=0644 TAGS: [redis-slave]
Here is how the files are organized:
roles/redis-client/meta/main.yml
roles/redis-client/tasks/main.yml
roles/redis-common/handlers/main.yml
roles/redis-common/tasks/main.yml
roles/redis-master/meta/main.yml
roles/redis-master/tasks/main.yml
roles/redis-master/templates/redis.conf
roles/redis-sentinel/files/redis-sentinel
roles/redis-sentinel/meta/main.yml
roles/redis-sentinel/tasks/main.yml
roles/redis-sentinel/templates/sentinel.conf
roles/redis-slave/meta/main.yml
roles/redis-slave/tasks/main.yml
roles/redis-slave/templates/redis.conf
This has helped me tremendously. The above is fairly simple, but this setup works great when your roles include: postgresql-client, postgresql-common, postgresql-master, postgresql-pgpool2, postgresql-server, and postgresql-slave.