A constrcution site from a birdseye view
Just like in construction, we use repeatable processes to build our infrastructure.*

Intro

Okay, okay, okay. I am late to the party. Ansible is nothing new anymore and will be able to vote in 6 years (it’s already 12 years old). Nonetheless I am here. I pushed away learning ansible for a while, because setting everything up once by hand and then just being happy with it, was fine, but everybody reaches the point where he or she is asking themselves how they set something up 2 years ago, or a disk crashed an now you have to setup everything from scratch again.

But now I am here… I want to learn ansible, I want to have executable documentation of how my homelab is setup and I want to be able to repeat the process, whenever I feel like it, without having to set aside a weekend.

Over the last 12 years, a lot of articles have been written, a lot of videos have been made. There are whole bunch of tech influencers teaching ansible, but I want to share my experience with you, because I am not a tech influencer, I am just a guy who wants to automate his homelab.

Why ansible?

… why not puppet, chef, saltstack, terraform, packer, vagrant, docker, kubernetes, helm, etc.? Well, the first reason is bare metal. You need something to setup all the services like kubernetes and docker, install packages, change OS config, configure networks. The second reason is the agentless nature of ansible. It’s just me my macbook and my servers, no master server, no extra setup to start the setup. brew install ansible and you are good to go. Drop everything into a git repository and call it a day.

The last reason is the community and ecosystem. There is a lot of learning material, experiences and documentation out there, as well as extensions to the standard library of ansible, which makes working with any server or service a breeze.

Getting started: the inventory

The first thing we need to do is tell ansible where our servers are. Ansible calls this “the inventory”.

The first frustration I encountered was that there were two formats for specifiying the inventory: INI and yaml. Without any discussion of which one to pick, which advantages the two have over each other. This is a ongoing theme with Ansible and it’s documentation, where multiple roads lead to rome and you are just expected to figure it out by yourself. I the end I went with the INI format, as didn’t have to manage a whole lot of servers.

In the inventory you define your servers and groups of servers. You can attach variable to either a server directly or a group of servers, that will be used while you execute your playbook, as well as allowing to segment your playbook for different groups of servers.

I defined my inventory in hardware classes and application classes.

openwrt ansible_host=10.10.10.1

optiplex ansible_host=10.10.10.2

microserver1 ansible_host=10.10.10.10
microserver2 ansible_host=10.10.10.20
microserver3 ansible_host=10.10.10.30

# hardware classes
[banana_pi]
openwrt
[optiplex]
optiplex
[microservers]
microserver1
microserver2
microserver3

# application classes
[router]
openwrt
[home_assistant]
optiplex
[application_servers]
microserver1
[storage_servers]
microserver2

Writing your first playbook

Ansible then introduces you to the concept of am playbook, a list of tasks to be executed on a defined group of servers.

The next frustration is again the options ansible gives you without explaining what to pick when. You have roles, tasks and playbooks.

You can use only a playbook, but then you can also use roles, but then you can just make them dependencies, but you can also import them, but you can also import other playbooks, but then you can also include them and you can do that on multiple levels. It is all very confusing and not really straighforward.

Coming from Zen of Python: “There should be one and preferably only one obvious way to do it.”. It doesn’t mean there should be only one way, but there should be a obvious way to do it if you don’t have any special requirements.

In the end I created a playbook per hardware class and a playbook per application class, that would then import the roles I defined for the hardware and application classes. This allows me to setup hardware specific tasks like drivers, network and monitoring, as well as application/role specific tasks like storage, home assistant, docker, etc.

---
- name: Basic Setup
  hosts: all
  become: true
  tasks:
    - name: Set authorized key taken from file
      ansible.posix.authorized_key:
        user: root
        state: present
        key: "{{ lookup('community.general.onepassword', 'Ed25519 SSH Key', section='Public Key') }}"

# Run tasks by hardware class
# - import_playbook: plays/banana-pi.yaml
# - import_playbook: plays/optiplex.yaml
- import_playbook: plays/microservers.yaml

# Run tasks by application class
# - import_playbook: plays/router.yaml
- import_playbook: plays/application.yaml
- import_playbook: plays/storage.yaml
- import_playbook: plays/home-assistant.yaml

As well as separating out some multi-step tasks into yaml files, that I would then include in the playbook, for better clarity and reusability.

Running the playbook

After setting up the inventory and the playbook, you can run the playbook with ansible-playbook -i inventory.ini playbook.yaml.

A good trick, if you just want to run on a subset of your servers is to use the --limit flag, which allows you to run the playbook onjust some of your servers, because you changed the playbook.

ansible-playbook -i inventory.ini playbook.yaml --limit banana_pi
# or
ansible-playbook -i inventory.ini playbook.yaml -l application_servers

Conclusion

I am still learning ansible and I am still frustrated by the lack of clear guidance in the documentation, but I am happy with the results I have achieved so far. I have a clear overview of my homelab setup and can now easily repeat the setup on new machines.

There are still a lot of features I need to have a look at and things I need to learn, like handlers and events, but all in all I am already a big step further.

I would really love to hear more guidance on how to structure a basic and a more advanced ansible setup, as well as getting some best practices for beginners using ansible. I don’t need to shoot for the moon, just the end of my backyard for now.

* Photo by Ivan Bandura on Unsplash