Manage Linux patching with Ansible and Netbox!

Nick Schmidt
4 min readApr 6, 2024

Patching all of my random experiments took too much of my free time, so I automated it.

This is a pretty cheesy thing to do, but over the years it became more and more time-consuming to maintain all the different deployed workloads and infrastructure.

Requirements

With all system design, it’s best to consider all relevant needs ahead of time. Given that this is a home lab, I decided to adopt an intentionally aggressive, but theoretically viable in production approach:

  • Nightly patching
  • Nightly reboots
  • No exempt packages
  • Distribution-agnostic, it should patch multiple distributions at once
  • This workflow should execute consistently from-code

Iteration 1: Ansible with Jenkins

The earliest implementation I built here had the least refinement by far. Here I tied Jenkins to an internal repository:

To leverage this, I started out with an INI inventory, but it quickly became problematic. I wanted a hierarchy, with each distribution potentially fitting multiple categories. This became pretty messy pretty quickly, so I moved to a YAML Inventory:

This allowed me to simplify my playbooks and inventory by making “groups of groups”, and avoid crazy stuff like taking down all nodes for an application at once. We’ll use nameservers: as an example here:

The serial: 1 key instructs the Ansible controller to only execute this playbook on one machine at a time, so DNS continuity is preserved.

Retrospective

I had several issues with this approach, but to my surprise, Linux patching and actual Ansible issues haven’t cropped up at all. With most mainstream distributions, the QC must be good enough to patch nightly like this.

I did have issues with inventory management, however. To update the Ansible inventory, I could deploy as-code, which was nice, but it was still clunky. If I deployed 5 Alpine images in a day, I want them to automatically be added to my inventory for maximum laziness.

I also quickly discovered that maintaining Jenkins was labor-intensive. It’s a truly powerful engine, and great if you need all the extra features, but there aren’t many low-friction ways to automate all the required maintenance, particularly around plugins. I was able to update Jenkins itself with a package manager, but it seems like every few days I had to patch plugins (manually).

Iteration 2: Ansible, Netbox, GitHub Actions

I’ll be up-front — for parameterized builds, GitHub Actions is less capable.

It has some pretty big upsides, however:

  • You don’t have to maintain the GUI at all
  • Logging is excellent
  • Itegration with GitHub is excellent
  • Pipelines are YAML defined in their own repository
  • Status badges in Markdown (we don’t need some stinkin’ badges!)

This workflow has been much smoother to operate. Since the deployment workflow already updates Netbox, all machines are added to the “maintenance loop after first boot.

I was really surprised at how little work was required to convert these CI pipelines. This was naive of me — ease of conversion is the entire point of CI pipelines, but it’s still mind-boggling to realize how effective it is at times.

To make this work, I first needed to create a CI process in .github/workflows on my Lab repository:

This executes on a GitHub Self-Hosted runner in my lab with a Python Virtual Environment. The workflow will run a clean build, every time — by wiping out the workspace prior to each execution. No configuration artifacts are left behind.

With GitHub Actions, all processes are listed alphabetically, you can’t do folders and trees to keep it more organized. I developed a naming convention:

To keep things sane.

From there, we need a way to point to Netbox as an inventory source. This requires a few files:

requirements.txt is the Python 3 Pip inventory — since things are running in a virtual environment, it will only use python packages in this list.

The next step is to build an inventory file. This has to be named specifically for the plugin to work — local.netbox.netbox.nb_inventory.yml:

This file is pretty straightforward. It indicates that we should use Netbox tags to develop our inventory, and we can assign multiple tags in the netbox application to each Virtual Machine. I also added the has_primary_ip directive - if a machine doesn't get an IP address for some reason, it won't try to reach that VM and patch it, causing late night failures.

Here’s a preview of the Netbox application with these tags:

Refactoring the Ansible playbooks was hilariously easy. The Netbox inventory plugin prepends the group_by field onto the group, so all I had to do in each playbook was prepend tags_ to each name. Here's an example:

After that, the CI tooling just takes care of it all for me!

Retrospective

I’m going to stick with this method for a while. Netbox tagging makes inventory management much more intuitive, and I can develop tag “pre-sets” in my deployment pipeline to correctly categorize all the stuff I deploy. Since it’s effectively documentation, I have an easy place to put data I’ll need to find later for those “what was I thinking?” moments.

I’ll be honest — behind that, I haven’t really given it much thought. This approach requires zero attention to continue, and it happens while I sleep. I haven’t gotten any problems from it, and it allows me to focus my free time on things that are more important.

10/10 would recommend.

Originally published at https://blog.engyak.co on April 6, 2024.

--

--

Nick Schmidt

I am a network engineer based out of Alaska, pursuing various methods of achieving SRE/NRE