Ansible: A quick guide to quicker deployments

Getting Ansible

Ansible only needs a recent version of Python 2 installed on to run. Chances are you will find Ansible in your package manager. If your primary OS is Windows, you can use Ansible on a virtual machine, it is a command-line tool and not very resource-intensive for the client. If you don’t find it there, you can install it using Pythons package manager, pip using pip install ansible. You may need to use sudo with that if you want to install it globally. To install it for the current user you can use pip install –user ansible.

Ansible Concepts Inventory

Since the whole point of Ansible is to configure and manage remote servers, you need to provide a list of your remote servers somehow. This is accomplished by adding these to a hosts file that could be configured globally for all your projects, or locally for a single project. Ansible calls this your inventory. The inventory file is simply a text file conventionally named hosts, that has a list of all your servers. These servers can be grouped together to apply the same configuration to all servers in a group. You can even have groups of groups. This file can also include additional variables for configuring each host, such as the remote username, the kind of connection to use, the port, etc. It’s even possible to create groups of groups and group-level variables and a lot more. If you use a cloud hosting service like DigitalOcean or RackSpace or Amazon EC2, you can even use scripts that create a dynamic inventory based your hosting account. Here is an example of what this file could look like:

[databases] db1.server.com ansible_user=ubuntu db2.server.com ansible_host=192.168.1.27 [appservers] app1.server.com:27 app2.server.com

Commands

With Ansible, rather than connecting to your server using SSH and then running commands, you specify a host on which to run a command and the command to run there. Ansible will connect to the server using SSH and run the task for you. This may seem like a nuisance rather than a benefit, but remember that with Ansible you can have groups of hosts. This way you can run a command on hundreds of hosts at once. For instance if you have all your webservers defined under a group called webservers you can restart the Nginx webserver on all of them at once using: ansible webservers -m service -a “name=nginx state=restarted” In the above, webservers is the name of the group of hosts (or single host), and -m service here tells Ansible to run the service module used to restart services using init, systemd or upstart and following -a are the parameters to pass to the service module, which are the name of the service and its state. If your command needs sudo to run, you can add the parameter –become, and add –ask-become-pass to have Ansible ask you the sudo password.

Playbooks

Running a command at a time has its uses, but what Ansible really is for, is building a list of such commands as tasks, and then applying these sequence of commands to a host to get it into the state you want. Ansible does this using Playbooks; these are text files written using the YAML format that describe a servers configuration. In a playbook you specify a set of tasks to perform on each host or host group. When you run Ansible on this file, it will perform these tasks one-by-one on all the supplied hosts.

Tasks

A task is a single step in configuring a host. Such as creating a user, copying a file to the server, enabling / disabling / restarting a service, cloning / updating a code repository, installing / uninstalling / updating a package etc. Through multiple steps you can get the server set up the way you want. Ansible only performs a task if its necessary. If the package you want is already present, it wont install it again. It won’t copy over a configuration file if it hasn’t changed, etc. Tasks can also be grouped by tags. You can then provide a tag while using the Ansible command, to run only the tasks having that tag.

Notification Hooks

What if you only want to run a task based on whether another task needed to run? For instance, restart Apache only if its configuration file was updated. For this, Ansible has notifications and handlers. Notification-handler tasks are only run if they are notified to run as the result of a standard task. Such a notification task could then be to restart Apache server. The Apache config setup task can then add the Apache server restart task as its notification task. You can also use Ansible’s integrations with SMS services and mobile push notification services to send a notification message based on what happens during a run of your playbook.

Roles

To simplify reuse, you can even bundle up a couple of tasks together into a role, and then apply one or more roles to a host. For instance you would create an apache role that does all the work of installing, configuring and starting Apache, and includes a hook to restart Apache if needed. This role can then be applied to a server. You could also create a database role for your database, and have your web and database server on the same host by applying both roles to the same host, or separate them by applying the roles to different host.

Templates and Variables

It is essential for a configuration management tool to, well, manage configuration files. Often this means editing and changing bits in configuration files to fill in details like IP addresses, server ports, directory paths, email addresses, user names and other bits of information. Any such information can be stored in variables which are embedded in configuration files. This way you can change a parameter in one place, and have it automatically update all the configuration files that need that value.

To manipulate text configuration files, Ansible include a rather powerful template engine called Jinja2. Using Jinja2 you can perform advanced text manipulation, such as looping over variables to build lists, and applying filters to text content. There is a simple Ansible command that can process a configuration file template, and copy it in the right place on the remote server.

Facts

When you run an Ansible playbook it first gathers a large collection of facts about the remote server such as the server architecture, OS, kernel details, date and time, hardware details, network connection details (like the IP address), hard disk partition details, and a lot more. In addition to using the variable you supply yourself, you can also use these gathered facts in your configuration files.

For instance, to set up the load-balancer situation we described before, the load-balancing servers’ config file could automatically fill in the IPs of all your app servers. Now you could just increment a variable that controls the number of app servers and Ansible will automatically set up the new app server and include it in the load-balancer config.

Building the Linux Kernel with Ansible

Lets now create an Ansible playbook that will compile the Linux kernel for us. Before we can begin writing our script, we should understand the manual steps involved in this process. Our script should perform the following steps:

• Install a compiler and other developer tools to compile the kernel. Remember we assume it is a fresh system, Ansible will only install these packages if not already present.

• Install git to get a local copy of the Linux source code.

• Run make mrproper in the source directory to clean it.

• Run make defconfig to create a default configuration file. Better yet, we could build a nice config and copy it to the server instead.

• Run make -j4. Here 4 specifies how many compilation steps to run in parallel. Increase if your system can handle more.

• Run make tarxz-pkg make a compressed package with the kernel. Or you could run make rpm-pkg or make deb-pkg to create an rpm or deb package.

If you have an account on Amazon AWS or similar, you could create a temporary server to run all this on. Else, it is possible to test with local commands on your computer as well.

We will assume you are running Ansible commands locally in the following steps as that will work for most people. You could also create a virtual machine and manage that using Ansible.

Our Ansible Playbook

Lets start by creating our Ansible playbook script and call it build-kernel.yaml with the following contents: — – hosts: localhost vars: code_path: /home/ubuntu/linux/ tasks: – name: Update System become: yes apt: upgrade=yes update_cache=yes …

Here the first line — and the last line … are conventions of the YAML format, and not specific to Ansible. YAML is a text format for representing data like XML or JSON, but we won’t delve much more into it here. The Wikipedia page on YAML is a good introduction to the format. What we see here, is a playbook with one task. This task has a name of Update System; this is what will be shown in the terminal when this playbook is run.

The actual task here is called apt, which is the name of the package manager on Debian, and Debian-derived versions of Linux like Ubuntu and Mint.

The become: yes part of this task is to ensure that this command runs as the superuser. If youre not using a Debian-based distro, you should probably omit the above task, and replace apt with package which works on most distros. There are also support for distro/language/ framework-specific package managers lik e yum, zypper, pip, gem, npm and more. What we also see is the declaration of one variable, the code path. This is where we will keep the source code. This allows us to write this path once, and reuse it wherever else needed.

We can now quickly add two more tasks that install the build tools and git: – name: Install build tools become:

yes apt: name=build-essential state=present – name: Install git become: yes apt: name=git state=present Here, we are providing two parameters, a package name, and a state.

This will result in these packages being installed if not already present. Now that our software is installed and updated, lets get the kernel source code: – name: Updating Linux Kernel source code git:

repo=https://github.com/torvalds/linux dest={{ code_path }}

This task is simple enough. We are using the git command, and providing it a repository location and a destination to place the source code. The magic of this task is that not only will it clone the repository at the supplied destination the first time it is run, but it will also keep it up-to-date during subsequent runs.

Note how were using the code_path variable to supply the code path instead of writing that path all over again. Putting a variable name in double curly braces will tell Ansible to place its value there.

We could likewise create a variable that points to the code repository, which would allow easy switching between GitHub and Kernel.org or yet another location.

Now we can combine all the rest of the build commands into one task: –

name: Compiling kernel shell: make mrproper && make defconfig && make -j4 && tarxz-pkg chdir={{ code_path }}

This will launch the build process in a shell. Unfortunately, the above command can be quite wasteful. If we add the above command to our tasks it will run each time, and end up recompiling the kernel even if there have been no updates. It would be better if we could just run this task if the kernel has been updated.

As it turns out, this is quite simple! All you need to do is to move the compilation task out of the tasks list and into another section called handlers. Then the git command can be configured to notify the compile command, so it will only run if there is an update. Here you can see the relevant bits code: — –

hosts: localhost tasks: ### Other tasks… – name: Updating Linux Kernel source code git:

repo=https://github.com/torvalds/linux dest={{ code_path }} notify: –

Compiling kernel handlers: – name: Compiling kernel shell: make mrproper && make defconfig && make -j4 && make tarxz-pkg chdir={{ code_path }} …

Now that the playbook is complete, you can run it with Ansible as follows: ansible-playbook build-kernel.yaml -i hosts -c local Here -i hosts tells the ansible-playbook command to use the inventory of hosts in a file called hosts in the directory in which the command is being run. You may wish to change this if your inventory file is elsewhere or namesd differently. The -c local bit of the command is to be used if you want to run the commands on your own local machine rather than over SSH. Remember to omit that if you are using a remote server.

Here weve only looked at the apt, git and shell modules. Ansible has hundreds of modules for everything from creating users, to sending SMS notifications using Twilio. You can find out about all the command modules included with Ansible here http://dgit. in/AnsibleDoc.

Conclusion

Using any configuration management tool at all is a HUGE advantage over using none. Unlike some other tools it does not need to be installed on the remote server. All it needs on the remote server is Python 2, and in some cases not even that. It connects to the remote server using simple SSH and transfers scripts that run your tasks.

The initial process of learning how to create playbooks and getting everything just right can be daunting, but once you have a working playbook, setting up a server with everything up and running becomes a matter of executing a single command.

Leave a Comment

Your email address will not be published. Required fields are marked *