Kubernetes Homelab

Updated: 2 August, 8.44 am Central European Time

Today I’d like to introduce my little home lab.

This is just the initial post and will be updated regularly

The aim of the home lab is to deepen my knowledge and skills in Kubernetes. My goal is to get as close as possible to real-world scenarios.

Of course, I also experiment with things that have nothing to do with business or IT.


Setup

Hardware

Device Hostname Function IP-WIFI IP-LAN
Raspberry Pi 4 / 8GB winterfell Control Plane 210 211
Raspberry Pi 4 / 1GB worker01 Worker Node 212 213
Raspberry Pi 4 / 1GB worker02 Worker Node 214 215

Network
192.168.178.0/24

Storage
USB-SSD with XFS filesystem

Software

Operating System

Kubernetes distribution

Administration Tools

kubi_01

K3S Cluster

What is running on the cluster?

Monitoring

Development

Production / Private

Testing

  • many :smiley:

Apply an deployment

Here you can see how to deploy an deployment with kubernetes.
In this case it is a minimal Ubuntu Container.
Scaling an container is a bit like RAID. It depents on the settings but basicly it works similar.
Examble: Scaling the Ubuntu Container to 10 means 10 instances of the same container. If 1 or 2 (depents on the settings) are failling, the users won’t notice.
kubi_02

7 Likes

Audiobookshelf is a more complex project.

I’ve been running it with Docker for about two years. Now I’d like to migrate it to the Kubernetes cluster. Audiobookshelf itself isn’t a problem at all, but migrating the data will be interesting.

3 Likes

This is an important part of home labs. A safe place to fail, learn, fail again and learn some more.

2 Likes

Yes, absolutely!

The great thing about Kubernetes is that you can use namespaces.

When I’m testing a new app, I create a new namespace to which everything is linked.

If my test goes wrong, I delete the namespace and everything related to the app is deleted as well.

If everything went well and is working, I do the same thing. I then adjust the desired namespace in the underlying YAML file (e.g. fun) and recreate the app in the new, correct namespace.

Of course, only if I actually intend to use the app after my tests :sweat_smile:

At the same time, I then have ‘production-ready’ YAML so I can deploy the app to a cluster at any time.

4 Likes

Thanks for sharing your expertise with us @toadie !

3 Likes

Ansible Playbooks

Git Sync

To sync my Git repo over all devices, I use this Ansible Playbook.

---
- name: Synchronize Git repository on all nodes
  hosts: pi_cluster
  become: false
  vars:
    repo_url: "https://github.com/mrtoadie/lab.git"
    repo_dest: "/home/toadie/Projects/lab"
    repo_branch: "main"
    git_user: "toadie"
    
  tasks:
    # 1. Clone repository (if not present)
    - name: Check repository exists
      ansible.builtin.stat:
        path: "{{ repo_dest }}"
      register: repo_stat

    - name: Clone Repository
      ansible.builtin.git:
        repo: "{{ repo_url }}"
        dest: "{{ repo_dest }}"
        version: "{{ repo_branch }}"
        accept_hostkey: yes
      when: not repo_stat.stat.exists
      register: clone_result

    # 2. Pull repository (if already exists)
    - name: Pull Repository
      ansible.builtin.command:
        cmd: git pull origin {{ repo_branch }}
        chdir: "{{ repo_dest }}"
      when: repo_stat.stat.exists
      register: pull_result
      changed_when: "'Already up to date' not in pull_result.stdout"

    # 3. Show status
    - name: Log pull result
      ansible.builtin.debug:
        msg: |
          Host: {{ inventory_hostname }}
          Status: {{ 'Cloned' if clone_result.changed else 'Pulled' }}
          Changes: {{ pull_result.stdout | default('None') }}

Update Operating System

---
- name: Update Raspberry Pi Cluster
  hosts: pi_cluster
  become: yes
  serial: 1                       # one node at a time!
  gather_facts: yes
  
  tasks:
    - name: Update package cache
      ansible.builtin.package:
        update_cache: yes
        
    - name: Upgrade all packages
      ansible.builtin.package:
        name: "*"
        state: latest
        
    - name: Check if reboot is needed
      ansible.builtin.stat:
        path: /var/run/reboot-required
      register: reboot_required
      ignore_errors: yes
      
    - name: Reboot if necessary
      ansible.builtin.reboot:
        reboot_timeout: 300
      when: ansible_distribution == "Debian" or ansible_os_family == "RedHat"
      ignore_errors: yes
      
    - name: Wait for node to come back
      ansible.builtin.wait_for_connection:
        timeout: 300

Inventory.ini

The inventory.ini looks like this:

[pi_cluster]
winterfell ansible_host=192.168.178.210
worker01 ansible_host=192.168.178.212
worker02 ansible_host=192.168.178.214

[k3s_master]
winterfell

[k3s_workers]
worker01
worker02

7 Likes

Nice setup. Using Ansible for the Pi cluster updates is a smart move, especially the serial: 1 and reboot check that avoids taking down all nodes at once. One thing I do in similar playbooks is add a health check task after the reboot, just a simple curl or ping loop, to confirm the node actually rejoined the cluster before moving to the next one. Saves some manual checking later if a node gets stuck.

4 Likes

Thank you. Great idea with the health check!

5 Likes