Open post

Ansible Best Practice

Overview

Ansible is an open source configuration management tool. It’s simple use makes it very popular, but being a simple tool does not limit its usage. Ansible can be used for simple automation tasks, but also for complex provisioning and orchestration.In this post we are going to show you some best practice steps you should in consideration when using Ansible. We are assuming you have some basic concepts of Ansible, if not, you can check our previous post here with instruction on how to get started with Ansible.

Project Structure

One of the first things you have to start with, is the directory structure and layout.
The default layout should be something similar to below:

– production and staging are our inventory files. It’s always a good practice to keep the production and staging host in separate files…having them in one file can get messy.
– group_vars and host_vars are used for assigning variables to specified group of hosts or single host.
– library and module_utils are used for any custom module or module_utils
– filter_plugin is used for custom filter plugins.
– site.yml is the master playbook
– webserver.yml and dbserver.yml are playbooks for the web and db servers tiers taken for example in this demo. You can also have other tiers, depending on what you are provisioning
– roles is the directory where all the roles are stored.
– roles/common the directories inside roles directory, like common, represent the roles. This means that everything inside of the common directory, is part of the common role.
Structure inside a role:
tasks/main.yml : this is where the task of the role are specified
handlers/main.yml : handlers file
templates/ : files to be used with the template resource
files/ : files to be used with the copy and script resource
vars/main.yml : variables associated with this role
defaults/main.yml : default lower priority variables for this role
meta/main.yml : role dependencies
library/ : custom modules for this role
module_utils/ : custom module_utils for this role
lookup_plugins/ : custom plugins for this role

Naming

If you plan on maintaining your playbooks, you need to keep the name consistency between your inventories,roles,plays, variables, etc. For example, use the name of the role to separate variables in each group. Let’s say you are installing Apache and PHP on your web servers. You can separate the variables used for each of them, group_vars/webservers/apache.yml and group_vars/webservers/php.yml.

Staging

As mentioned above, you should keep your staging or testing environment separate from you production environment by using different inventory files. Using the -i flag, you can choose on which inventory to run the tasks. Testing your playbooks before running them in production is always a good idea.

Encrypt passwords

There is a high chance that you might end up using passwords or certificates in your playbooks. Having to store them in plain text files, is always not a good idea. Ansibles offers ansible-vault which can be used to encrypt sensitive data. Instead of using plain text files, you store your sensitive data in encrypted files. When running the playbooks, you need to use a flag, –ask-vault-pass or –vault-password-file, which will then decrypt the files(in memory only). The only drawback of this is that at the moment you can only use one password for encrypting your files.

Version Control

Use version control on all you playbooks. Keep your files in a git server, so you will commit when you have changes. This will ensure you have an audit trail describing why and when changes were done to your playbooks.

Modules before run commands

Run commands like command and shell modules enable the user to execute shell command on the hosts. At first this may seem convenient but on the long run it’s always suggested to use the modules when possible. Running you tasks with shell command may work the first time, but there high chances that it will fail a second time, when something exist, like an error. Also having the built in modules can make it easier and help you with getting the idempotency of the playbooks.

Idem-potency
Idem-potency means running the same playbook different times on the same host, and expecting the same results. These means, that after you run your playbook the first time on a host and all changes are done, the second time, the playbook should return everything green and make no changes. There some ideas to start when trying to archive idem-potency.
Always use built modules instead of run commands when is possible as explained above.
Always use the same inputs
The argument force:no can be used on some modules to make sure the task is run only once. This means that if you are using a template, and want it copied only once if not existence, this will ensure that the second time it will not be copied.
When forced to use the command or shell tasks, always use the argument creates. Otherwise, the tasks will be considered as “changed” .

Open post

Getting Started with Ansible

Overview

When working on devops or system administration, one of the main tools you need to keep your eyes on is the configure management. Either be it a set of complex orchestrations or simply automated task, a configuration management tool can make your day easier.
Let’s be honest, we are living in the times of automation. If you have to do a task twice, it’s time to consider automating it. For this post we have decided to go with Ansible.

Ansible is an open source configuration management tool. What makes ansible so famous is its agent-less and push approach. In a nutshell, other solution like Chef or Puppet, work by installing an agent on the hosts that you manage. The agent is responsible for pulling changes from a master host. Usually they use their own channels, not ssh.
The advantage of Ansible is the simplicity on which it operates. Pushes are applied through SSH service, basically is the same as you would connect to your machines and run a set of commands, but instead you use a script to do the whole thing automatically, and with the bonus of having a lot of modules to make it even easier to write the script.

In this post we will show you how to install ansible, run some simple starting scripts and get started using it.

Installing Ansible

Installing ansible is also very simple. You can follow the instruction here depending on you machine. If you use pip, you can install it running this command:

$ sudo pip install ansible

Make sure to check that everything went ok by checking the ansible version

$ ansible --version

Adding hosts to inventory

Once you have Ansible installed, the first thing to do before running your tasks, is to specify the hosts that will be managed by Ansible.
Ansible uses a file called hosts, inside of ansible configuration files( for linux should be /etc/ansible/hosts) were you define instances of your hosts. You can add single hosts, or group of hosts like web servers or database servers, were you can run the same task on multiple hosts.
Let’s start by adding one host in a group of webservers. To do so, modify the file and add two lines like this:

[webservers]
app1Server.example.com

[webservers] is the then name of the group or the group header. app1Server.example.com is the name of the host. You can use also IP’s.

Save the file and close it.
Now that we have at least one host configured, lets run some simple tasks with ansible.

$ ansible webservers -m ping -u root

This should return:
app1Server.example.com | SUCCESS => {
"changed": false,
"ping": "pong"
}

As you can see, the ping command worked.

Running Ansible playbooks

In this part, we will start using Ansible Playbooks.
Playbooks combines task to be runned against a hosts when executed. For this demo, we will provision a LAMP stack. The playbook will install the LAMP components, Apache, PHP, MySQL. It will start Apache and then show a “Hello World” page.

First that start by writing simple web server with only php and apache.

The playbook.yml should look like this:

---
- hosts: all
tasks:
- name: Install Apache
apt: name=apache2 state=present

- name: Install PHP module for Apache
apt: name=libapache2-mod-php5 state=present

- name: Start Apache
service: name=apache2 state=running enabled=yes

- name: Install Hello World PHP script
copy: src=index.php dest=/var/www/index.php mode=0664

The playbooks use YAML language. First is the hosts entry. This tell the playbook were the tasks will be runned. Then it’s the task session. Here you define the tasks that will the script will run. For each of them we set a name, so it’s more easy to understand what is going on during the playbook execution. For this playbooks we will use this modules: apt, service, copy. You can find more detailed information for each of them and more on the ansible docs pages.
Since in the last task, we are using the copy modolu, we need to have the index.php file that will be copied at the host.

<?php
echo “Hello World”;
?>

A simple Hello World will do for the purpose of this post.

To run the playbook, you need to run the following:

$ ansible-playbook playbook.yml -u root

(for this demo am using root user, you can use also non root users)

After the playbook has been executed we will have a simple web server running on our hosts.

Ansible Roles

What did in the previous step, is that we wrote a simple ansible playbook which installs a web server using apache,php and runs a Hello World page.Now, what if our application needs also a database. Normally, we would include another task on the playbook which installs the database. But, in our production environments, databases are located in different servers. We don’t want to install apache on the database server, as we dont want a database installed in the web servers. So including everything in one playbook wont work in this case. Here is where we need Ansible Roles.

A role is a set of tasks and configuration grouped by a common functionality. For instance, a web server could be a role, a database server could be another role.
Before starting with writing the roles we need to understand the project directory structure

At the end, this the directory structure we will have for the project. First is the hosts file, were we will define the web servers and the database servers.Then, is the playbook.yml which will route how the roles will be runned.
The roles directory is were the tasks and the files will be stored. All the task will be writen within the main.yml file in their corresponding folder.
We already have the webserver main.yml file ready, all we need to do is remove the hosts entry and copy the rest.
Now we need to write the main playbook.yml. It should look like this:

---
- name: Apply web server configuration
hosts: webserver
roles:
- webserver

- name: Apply database server configuration
hosts: database
roles:
- database

By doing so, we are sure that the webserver tasks will be runned only on the webservers and the database tasks only on the database servers.

Now we need to write the last one, the database main.yml.
It should look like this:

---
- name: Add APT GPG signing key
apt_key: url=http://keyserver.ubuntu.com/pks/lookup?op=get&search=0xCBCB082A1BB943DB state=present

- name: Add APT repository
apt_repository: repo='deb http://ftp.osuosl.org/pub/mariadb/repo/10.0/ubuntu $ansible_distribution_release main' state=present update_cache=yes

- name: Install MariaDB
apt: name=mariadb-server state=present

- name: Start Mysql Service
service: name=mysql state=started enabled=true

- name: Install python Mysql package #required for mysql_db tasks
apt: name=python-mysqldb state=present

- name: Create a new database
mysql_db: name=demo state=present collation=utf8_general_ci

- name: Create a database user
mysql_user: name=demo password=demo priv=*.*:ALL host=localhost state=present

- name: Copy sample data
copy: src=dump.sql dest=/tmp/dump.sql

- name: Insert sample data
shell: cat /tmp/dump.sql | mysql -u demo -p demo

- name: Install MySQL extension for PHP
apt: name=php5-mysql state=present

The above task will install and configure our database.
We add a simple db.php file with the connection string and a simple query to get the database data:

<?php
$connection = new PDO('mysql:host=localhost;dbname=demo', 'demo', 'demo');
$statement  = $connection->query('SELECT message FROM demo');
echo $statement->fetchColumn();
php>

Also, to have the data stored in the first place in the database, we can set up a simple dump.sql file:

CREATE TABLE IF NOT EXISTS demo (
  message varchar(255) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO demo (message) VALUES('Hello World!');

After we have all the files ready, you run again the playbook:

$ ansible-playbook -i hosts playbook.yml -u root

This again will start up a web server, showing a Hello World page, but this time, the web server is using a database to fetch the data.

Conclusion

Ansible is really easy and simple to set up and use. The YAML syntax is simple and easy to understand with a wide documentation and examples. The more structured your projects will be, the more easier to understand and less complex they will be. Ansible can be used to handle small task, or large automation processes, even orchestration.

Scroll to top