Introduction
Like you need a concept of designing your software code base, you also need to have concept to design your ansible code.
A concept for your ansible code enables you to adjust or fix faster your code, as you know the places to look and what is dependet from it.
The development process itself will be faster, because the concept tells you where to place roles and how they are structured.
The onboarding of new members to your code base will be easier with a clear and documented structure.
A lot of these advantages will only come as your code base grows, but then you will be happy that you had a concept in place.
In this blog post I will give you an overview of our concept for ansible code.
Overall structure
The main parts of a ansible repository structure is defined by ansible, but how to structure our playbooks and your roles is up to you.
We structure by platform.
For example, we put our playbooks and roles in folders that represent the name of the platform like linux
kubernetes
or even elasticstack
if we need to to do a lot for elasticstack
.
Our folder structure for playbooks looks like this:
# inside playbook/ folder
├── k8s
│ ├── playbook.yml
│ └── SaaS
│ ├── playbook.yml
├── linux
│ ├── playbook.yml
│ ├── security
│ │ └── playbook.yml
└── utils
├── playbook.yml
The folder structure inside our roles folder has the same structure.
Roles
Roles are the centerpieces of ansible, here happends the dirty stuff, that’s why it is most important to have this decoupled and clearly written.
-
Roles should be idempotent, executing the roles multiple times should not lead to different results
- In case it would be best if there would be no
changed
tasks after running the task once.
- In case it would be best if there would be no
-
Roles should be do one thing and they should do it good
- Don’t create roles, like
common
orsecurity
, one role for each logical step incommon
would be the better. These “common” roles can then be called from a playbook calledcommon
- Don’t create roles, like
-
Defining a naming schema for your roles will help you a lot to navigate through your code base
- e.g.
install_gitlab
,configure_gitlab
,set_hostname
…
- e.g.
-
Roles should not represent workflows
- It might be okay if a role has to prepare data for other roles,
-
Don’t split the installation of the same tool on different platforms in separate roles, check inside the role and call the correct tasks
-
Don’t copy code!
- If you need the same logic on more then one role, think of creating a
utility
role or apre task
in a shared playbook.
- If you need the same logic on more then one role, think of creating a
-
Be cautious if you’re using inventory groups, it can lead to a thight coupling of your inventories and the roles.
-
Think of giving the state from outside, then you can not only ensure that for example a tool is installed (present), you can also ensure that a tool gets uninstalled (absent)
Testing
Test your roles isolated from other roles.
If you need first execute other roles to test your role, you might need to rethink the design of your role.
Playbooks
Not every role needs a playbook. Playbooks are the orchestration of roles, they should contain workflows to set up an application and your system.
-
Avoid using to many tasks in playbooks
-
Aim to have one workflow per playbook
- You can have orchestration playbooks that call other playbooks
-
Playbooks should be able to be executed from the host that they configure after they are executed once
- You need this if you plan to use an automation server like AWX, which you also want to configure via ansible
-
Avoid adding variables to playbooks, use
group_vars
orinventories
instead -
“Utility playbooks” can be a nice replacement for a bunch of small scripts, but don’t rely too heavily on these
Testing
Testing a playbook is more like integration testing of the infrastructure, you will need more preparation and you need to make sure that everything is set up accordingly.
It is also possible that playbooks are not able to be tested on their own and that you will need more like end 2 end tests that execute multiple playbooks.
Inventories
Designing inventories can be very challenging and is very individual.
The main thing to keep in mind is to properly handle variables.
- Prefer putting variables in to
group_vars
instead of your inventory - Try to avoid
host_vars
, as too many of them can get overwhelming, usegroup_vars
wherenever possible
Client handling
I prefer to create an own project for each client and share the roles and if necessary the playbooks.
Handling multiple clients in one repository can get very junky after some time.
Last but not least
If you find a exception to your current concept, find a general way to tackle the exception and extend your concept.
If you add exception after exception to your code, it will get flaky and the advantages of a concrete concept will be gone.
Sometimes changing rules of the concept is nessesary. But if you change something in the concept, you need to adjust all affected places of the change. That doesn’t mean you need to do this directly, but you need to keep track of these changes and adjust everything as quickly as you can.