Ansible

26 Apr 2024

Ansible is a powerful tool based on Python, where you write “playbooks” with actions that you want performed on one or more computers. You can write playbooks for anything, but I have mainly used it for setting up webservers exactly how I want them. The advantage of Ansible is that you get the same each time. You can even use it to keep software updated or for running routine tasks.

I first learned about Ansible reading Josh Mcguigan's article about running your own DNS servers. I was really amazed with his approach, where he spins up and configures servers as desired so swiftly and easily.

This is a quick guide to getting started with Ansible. I'll show you the basics, but not really go in depths with it. Let's start by setting up your server.

Server configuration

To run Ansible you need a master and one or more slaves. The master sends commands to the slaves.

SSH keys

For the master to connect to the slaves it is advisable to use SSH keys for easy access. Run the command:
ssh-keygen
to create a public and private key pair on your master. The public key can then be copied to all your slaves. There are various ways this can be done. E.g. with the ssh-copy-id command like so:
ssh-copy-id -i ~/.ssh/id_rsa.pub user@server
Replace user with your username on your server and server with the IP-address of your server.

By default the keygen command will generate a 2048-bit RSA key pair, which is secure enough for most uses. However you can also pass -b 4096 which will generate a stronger 4096-bit key pair.

Now that your master can access your slaves, it is time to setup our first playbook.

Setting up your first playbook

Let's take a look at how I build the structure of my playbook. There are a couple of different ways of doing it, but I prefer this one.

The structure of an Ansible playbook

I usually make the following structure:

Let's run through them below starting from the root:

inventory

Inventory is a file containing the list of slave servers. It may look like this:
[webservers]
web1 ansible_host=1.2.3.4 ansible_ssh_private_key_file=~/.ssh/id_rsa ansible_user=root
web2 ansible_host=1.2.3.5 ansible_ssh_private_key_file=~/.ssh/id_rsa ansible_user=root
The above file defines a group of 2 servers called webservers. The first server is named “web1” and is located at IP-address 1.2.3.4. Ansible will login with SSH using the root user.

playbook.yml

This is the main file defining what happens to which servers. In my example the file looks like this:
---
- name: Install software.
  hosts: all
  roles:
    - common
This defines a task called “Install software.”, where the role “common” is applied to all groups. You can add more tasks and/or roles if desired. Instead of using “all” I could have used my group “webservers” or any other group that I may have defined.

README.md

You don't need this file, but I like to create it anyway. I'll write a few words about what the playbook contains and how to run it.

roles/common/

This folder is the definition of my “common”-role. The folder contains the handlers, tasks, and templates for it. If you need more roles, you simply create a folder similar to this with a different name and different tasks.

roles/common/tasks/main.yml
The tasks file contains the actions I want performed for the role. E.g. install Nginx and insert a configuration file for it into the correct location as shown below.
---
- name: Install Nginx
  dnf: 
    name: nginx
    state: present

- name: Copy nginx configuration
  template: 
    src: php.conf
    dest: /etc/nginx/default.d/php.conf
  notify: restart nginx
The script above uses dnf to install nginx. The next step takes a file called php.conf from the templates-folder (we'll get to that in a bit), places it at the specified destination and calls handler (we will also get to that) with the quite telling name “restart nginx”.

roles/common/templates/
The template folder contains files that are used in the tasks. The files can even be altered through variables, so their content can be somewhat dynamic. Just add double brackets with the variable name in the files where needed like this:
{{ domain }}
roles/common/handlers/
Handlers are basically tasks that only run when notified. Usually they are used to restart a service, for example restarting Nginx after inserting a new configuration file. Each handler needs a globally unique name. Below is an example of a handler that restarts Nginx.
---
- name: restart nginx
  service: 
    name: nginx 
    state: restarted 
    enabled: yes

Running your playbook

When you are done writing your playbook, you can try running it like this:
ansible-playbook playbook.yml -i inventory
playbook.yml is the path to your playbook and inventory is the path to your inventory file.

Wrapping up

This was a very quick introduction to Ansible with a simple install script. There are tons of other possible actions that you can use — learn more by reading the Ansible documentation. You can also get the first few chapters of Mastering Ansible by Jesse Keating and Ansible for DevOps by Jeff Gerling on Ansible resources.

Ansible is a great programmatic way of ensuring you get exactly the same on your computers each time. It can be a great tool for automating tedious tasks.

You might also enjoy

RockyLinux notes

RockyLinux notes

Published 2024-05-05

DevOps

Linux

Notes

Various notes gathered after converting to RockyLinux from CentOS.

Read the post →
Docker notes

Docker notes

Published 2024-05-03

DevOps

Notes

Docker is an amazing tool for automating DevOps

Read the post →
How to easily web scrape any website with Python

How to easily web scrape any website with Python

Published 2024-05-03

Datahoarding

Notes

Python

Web development

Learn how to easily web scrape any website using Python. I go through the various techniques I use.

Read the post →