Before we start to describe roles, playbooks, etc, we need to decide which one we use for what. This section is meant for topics which span across multiple structures and don’t fit nicely within one.
Details
As an overall guiding principle for designing good automation, inspired by the Zen of Python, by Tim Peters, the Zen of Ansible was created to serve as a guidepost to follow. The principles defined in this are very applicable and can give guidance when the specific practice is unclear.
The Zen of Ansible, by Tim Appnel Ansible is not Python. YAML sucks for coding. Playbooks are not for programming. Ansible users are (most probably) not programmers. Clear is better than cluttered. Concise is better than verbose. Simple is better than complex. Readability counts. Helping users get things done matters most. User experience beats ideological purity. “Magic” conquers the manual. When giving users options, always use convention over configuration. Declarative is always better than imperative - most of the time. Focus avoids complexity. Complexity kills productivity. If the implementation is hard to explain, it's a bad idea. Every shell command and UI interaction is an opportunity to automate. Just because something works, doesn't mean it can't be improved. Friction should be eliminated whenever possible. Automation is a continuous journey that never ends.
Details
- Explanations
-
define for which use case to use roles, playbooks, potentially workflows (in Ansible Controller/Tower/AWX), and how to split the code you write.
- Rationale
-
especially when writing automation in a team, it is important to have a certain level of consistency and make sure everybody has the same understanding. By lack of doing so, your automation becomes unreadable and difficult to grasp for new members or even for existing members.
Following a consistent structure will increase re-usability. If one team member uses roles where another one uses playbooks, they will both struggle to reuse the code of each other. Metaphorically speaking, only if stones have been cut at roughly the same size, can they be properly used to build a house.
- Examples
-
The following is only one example of how to structure your content but has proven robust enough on multiple occasions.
-
a landscape is anything you want to deploy at once, the underlay of your cloud, a three tiers application, a complete application cluster… This level is represented at best by a Controller/Tower/AWX workflow, potentially by a "playbook of playbooks", i.e. a playbook made of imported type playbooks, as introduced next.
-
a type must be defined such that each managed host has one and only one type, applicable using a unique playbook.
-
each type is then made of multiple functions, represented by roles, so that the same function used by the same type can be re-used, written only once.
-
and finally components are used to split a function in maintainable bits. By default a component is a task file within the function-role, if the role becomes too big, there is a case for splitting the function role into multiple component roles.
Noteif functions are defined mostly for re-usability purposes, components are more used for maintainability / readability purposes. A re-usable component might be a candidate for promotion to a function. Let’s have a more concrete example to clarify:
-
as already written, a landscape could be a three tier application with web-front-end, middleware and database. We would probably create a workflow to deploy this landscape at once.
-
our types would be relatively obvious here as we would have "web-front-end server", "middleware server" and "database server". Each type can be fully deployed by one and only one playbook (avoid having numbered playbooks and instructions on how to call them one after the other).
-
each server type is then made up of one or more functions, each implemented as a role. For example, the middleware server type could be made of a "virtual machine" (to create the virtual machine hosting the middleware server), a "base Linux OS" and a "JBoss application server" function.
-
and then the base OS role could be made of multiple components (DNS, NTP, SSH, etc), each represented by a separate
tasks/{component}.yml
file, included or imported from thetasks/main.yml
file of the function-role. If a component becomes too big to fit within one task file, it might make sense that it gets its own component-role, included from the function-role.Notein terms of re-usability, see how you could simply create a new "integrated three tiers server" type (e.g. for test purposes), by just re-combining the "virtual machine", "base Linux OS", "JBoss application server", "PostgreSQL database" and "Apache web-server" function-roles into one new playbook.
-
Beware that those rules, once defined, shouldn’t be applied too strictly. There can always be reasons for breaking the rules, and sometimes requires discussion with your team to decide what is more important. For example if a "hardened Linux OS" and a "normal Linux OS" are two different functions, or the same function with different parameters. You could consider SSH to be a function on its own and not a component of the base OS. Also, external re-usable roles and collections, obviously not respecting your rules, might force you to bend them. Important is to break the rules not by ignorance of those but because of good and practical reasons. Respecting the rules is to know and acknowledge them, not to follow them blindly even if they don’t make sense. As long as exceptions are discussed openly in the team, they won’t hurt.