Steve mitchell in Raspberry 10 minutes

Ansible for your Home Lab

Ansible logo

Disaster struck in my home lab, again. This time I knocked a power cord loose on my USB power hub, taking down four of my Kubernetes nodes. That usually would not have been a “disaster,” but it broke the “sudo” command, and you can’t maintain Ubuntu without the sudo command. Long story.

There are nine Raspberry Pis in my home lab that first ran Raspbian, then Ubuntu, and I rebuilt for other reasons along the way. I decided that rather than do yet another manual re-install, now is a good time to add some automation.

Home Lab Raspberry Pis
Home Lab Raspberry Pis

Ansible

Ansible is an open-source tool written in python, power shell, and ruby to provision infrastructure, networks, containers, security, cloud, and more. Terraform and Ansible are two of the tools used for creating infrastructure-as-code.

It is easy to get started with Ansible because there is nothing to install on the target systems. Simply install it on your computer and start automating. Redhat sells Ansible Tower for the enterprise, but it is not required to use Ansible. Check out How Ansible Works to learn more about Ansible.

I first used Ansible in 2016 when the startup where I worked migrated our DC/OS (Data Center Operating System) cluster from vSphere to the cloud. Our three-person team did a cloud bake-off to decide which cloud to use. I took GCP (Google Cloud Platform), which happened to have a DC/OS Ansible playbook. It made deploying DC/OS to GCP easy, so the team chose GCP.

Home Lab Ansible Playbook

Despite using Ansible off and on since 2016, I never needed to write my own playbooks. This home lab outage is my excuse to automate some tasks using Ansible. My previous post, Running Ubuntu on a Raspberry Pi Cluster, details the eight steps to set-up Ubuntu on a Raspberry Pi:

  1. Flash the drive with the Ubuntu server image.
  2. Add the ssh file.
  3. Change the password at the first login.
  4. Add the ssh key on the host as an authorized key.
  5. Update and upgrade the Ubuntu packages.
  6. Change the hostname.
  7. Add the hostname to the /etc/hosts file.
  8. Set the static IP address.

My Ansible playbook only handles the last four steps because I ran into a problem with step three, the first login’s mandatory password change. I found playbooks using the sshpass command to handle the initial password change, but I could not install sshpass on my Mac. Another long story. So, I still do step three manually, as well as step four, adding the SSH certificate to the host.

Inventory

Ansible needs an inventory of the target hosts for a playbook. I find the DHCP address of each new host as it boots using my network scanner software, then add the IP address to the list.

Ansible Hosts Inventory with Variables
Ansible Hosts Inventory with Variables

Each host in the inventory group gets a “new_ip” and “new_host” variable, that are passed into my Ansible playbook. In the image above, you see that the static IP address, 192.168.1.50, is given to the first host, and its hostname is pi5.

Ansible Playbooks

My playbook picks up after step four, automating setup steps five through eight. It does this using seven separate Ansible tasks. There are hundreds of pre-built Ansible modules available to use in your tasks, whether you need to copy a file, or deploy an application to the cloud. Checkout the Ansible Module index to see the complete list. These are the seven Ansible tasks in my playbook.

  1. Update Ubuntu.
  2. Change the hostname.
  3. Update the /etc/hosts file.
  4. Disable Netplan automatic configuration.
  5. Update the Netplan configuration with the static IP address.
  6. Apply the static IP Netplan configuration.
  7. Reboot the host.

1) Update Ubuntu

First, make sure that Ubuntu is up-to-date.

Apt Update and Upgrade
Apt Update and Upgrade

2) Change the Hostname

Change the hostname using the “new_host” variable specified in the inventory file.

Change the Hostname
Change the Hostname

3) Replace the /etc/hosts file

Add the hostname specified in the “new_host” variable to the /etc/hosts file. The playbook uses Jinga2 to replace a variable in the template file created from the default /etc/hosts file.

Template Task to Replace /etc/hosts
Template Task to Replace /etc/hosts

4) Disable Automatic Netplan Configuration

This task places a file in the /etc/netplan directory to disable the automatic generation of the Netplan configuration.

Disable Automatic Netplan Configuration Generation
Disable Automatic Netplan Configuration Generation

5) Add the Static IP Address to the Netplan Configuration

This task uses Jinja2 to replace the host IP in a Netplan configuration template with the “new_ip” variable from the Ansible host inventory.

Replace the Netplan Configuration using the Static IP Template
Replace the Netplan Configuration using the Static IP Template

6) Apply the Netplan Configuration

The purpose of this task is to apply the network configuration. I should also add a step to generate and debug the format, but I didn’t find an example.

Apply the Netplan Configuration
Apply the Netplan Configuration

7) Reboot the Host

Finally, the Ansible playbook reboots the host.

Reboot the Host
Reboot the Host

Ansible didn’t save me time today since I was learning Ansible as I went, but now I have an automated playbook to save me time and avoid future mistakes. Here is the whole playbook:

Ansible Playbook
Ansible Playbook

Putting it all together

I modeled this project after Jakub Skałecki’s project in GitHub: github.com/Valian/ansible-multienv-base. With this layout.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|- hosts
|  |- shared-secrets.yml  # encrypted vars, used in all environments
|  |- shared-vars.yml     # not encrypted vars, used in all environments
|  |- prod                # directory for prod environment
|  |  |- inventory        # inventory file with definitions of all required hosts
|  |  |- secrets.yml      # encrpted vars for this environment
|  |  |- groups_vars
|  |     |- all.yml       # normal group_vars, like in typical ansible project
|  |- test                # directory for test environment
|     |- inventory
|     |- secrets.yml
|     |- groups_vars
|        |- all.yml
|- roles                  # all ansible roles used in playbooks
|  |- role1
|     |- ...
|- playbook1.yml          # here we put our playbooks

My new servers are setup in the /hosts/test/inventory file, and my playbooks are at the root of the project. From the root of the project I run this command to setup the new hosts:

1
ansible-playbook -i hosts/test/inventory setup_new_hosts.yml

References