Ansible is an open-source software that automates software provisioning, configuration management and application deployment, similar to Chef, Puppet or Salt. Ansible lets us control and configure nodes from a single machine. It is different from other management software in a way where Ansible uses SSH infrastructure and uses push mode and the configuration is pushed from a master machine to nodes. Ansible achieves all of this through the playbooks.
What is a Playbook?
As on the documentation of Ansible, playbooks are the basis for a really simple configuration management and multi-machine deployment system, unlike any that already exist, and one that is very well suited to deploying complex applications. Playbooks can declare configurations, but they can also coordinate steps of any manual ordered process, even when different steps must bounce back and forth between sets of machines in particular orders. They can launch tasks synchronously or asynchronously.
We can draw an analogy between a Playbook and instructions to assemble an appliance. The manufacturer includes instructions so we can put the parts together in the correct order. When followed in order, the appliance looks like what was purchased. That’s basically how a playbook works.
Playbooks are are simple YAML files and have a minimum of syntax, which intentionally tries to not be a programming language or script, but rather a model of a configuration or a process. Each playbook is composed of one or more ‘plays’ in a list. The goal of a play is to map a group of hosts to some well defined roles called tasks. At a basic level, a task is nothing more than a call to an Ansible module.These modules can control system resources, like services, packages, or files or handle executing system commands.
A Play is just like a strategy in sports. For example, while playing football, we can have different instructions for defense, midfield and attack but they will play the game as one team similarly, there may be different Plays for different resources in a multi-machine deployment system, but eventually they’ll be running as a single application.
YAML relies on indentation to serialize data structures. For that reason, when writing playbooks and especially when copying examples, we need to be extra careful to maintain the correct indentation.
Basic Directives Used
Here’s a list the commonly playbook objects and their directives. There are many more and can be explored in the Ansible documentation.
- hosts : It can be all or the group names specified in the inventory file.
- name : It’s a name, works mostly for documentation, in the case of tasks/handlers it can be an identifier.
- vars : Defines a variable that is later used in a task. We can define multiple variables in a playbook.
- tasks : The section where the actual tasks are defined.
- become : This portion tells Ansible to use privilege escalation (sudo) for executing all the tasks in this playbook or not.
- handlers : We have the handlers section, where the services are declared.
- gather_facts : A boolean that controls if the play will automatically run the ‘setup’ task to gather facts for the hosts.
- port : Used to override the default port used in a connection.
- roles : List of roles to be imported into the play.
- environment : A dictionary that gets converted into environment variables to be provided for the task upon execution.
- always : List of tasks, in a block, that execute no matter if there is an error in the block or not.
- ignore_errors : Boolean that allows us to ignore task failures and continue with play. It does not affect connection errors.
Roles are a further level of abstraction that are used for organizing playbooks. As we add more and more functionality to our playbooks, they can become difficult to maintain as a single file. Roles allows us to create very minimal playbooks that look to a directory structure to determine the actual configuration steps they need to perform.
Roles expect files to be in certain directory names. Roles must include at least one of these directories, however we can exclude any directory which is not being used. When in use, each directory must contain a main.yml file, which contains the relevant content. The directory structure of a role is :
- Tasks : Contains the main list of tasks to be executed by the role.
- Handlers : Contains handlers, which may be used by this role or even anywhere outside this role.
- Defaults : Default variables and configurations for the role.
- Files : Contains files which can be deployed via this role. This may also include script files to run.
- Templates : Contains templates which can be deployed via this role.
- Meta : We can list roles that must be applied before the current role can work correctly in this directory.
Running A Playbook
There are two ways to run a playbook. First one is a dry run, which does not make any changes to the remote machines. It will report what changes they would have been made on the remote machines rather than making them. Check mode is just a simulation and it is great for one-node-at-time basic configuration management use cases. The command for this is :
ansible-playbook yourplaybookname.yml –check
The second one is for the actual executing the playbook on the remote machines. The command for this is :
Writing A Playbook
Let’s see a simple example for writing a playbook. In this example, we connect to a remote host, updates it’s apt package cache and create a file called ‘hello’ in the root directory. For this example, I have created a AWS-EC2 server instance to work as my host and this instance of the server must be accessible through SSH via the local machine.
- hosts: 13.xxx.xx.xxx
- name: Update Package Cache
- name: Print A Message
debug: message="My First Playbook"
- name: Create File On Host
Here, in the first line, ‘ — -’ is specifying that the following file is in the YAML format. The next line specifies the host/hosts for the specific task and the various modifiers associated with it. We should name our tasks as the playbook becomes human-readable when we have many plays defined in our playbook, so that even a non technical person can understand what is going on in this particular task.
Best Practices For Writing Playbook
Try to keep the complexity of the playbook to the minimal level. If the task is very large, then break it into roles.
- Optimize your Ansible content for readability. If done properly, it can be the documentation of your work-flow automation.
- Always mention the state. The ‘state’ parameter is optional to a lot of modules. Whether ‘state=present’ or ‘state=absent’, it’s always best to leave that parameter in your playbooks to make it clear, especially as some modules support additional states.
- Generous use of whitespace to break things up, and use of comments (which start with ‘#’), is encouraged.
- Use prefixes and human meaningful names with variables.