Automate homelab microK8s cluster provisioning with Vagrant and Ansible
Published: 2024-02-05 11:47:54

Getting quick feedback is essential while developing. When it comes to setting up infrastructure, it often involves tons of scripting and is good to have a simple workflow you can iterate on. This tutorial is all about giving you a solid head start if you're itching to dive into DevOps and build your own homelab. We'll be installing microk8s, a fully compliant and up-to-date Kubernetes distribution that boasts minimal machine requirements.

To manage our VMs we will use Vagrant, its a complete cli tool to manage virtual machines and provides a simple but complete workflow. Vagrant is very good at giving us a context to our project, instead of configuring each VM individually and running provisioning scripts against them we have one declarative file that describe the project. 

### Vagrant installation

```bash
$ brew install virtualbox

$ brew cask install vagrant
```

### Vagrantfile

Vagrant uses a Vagrantfile to describe the machines the project is going to need. Here we will have our provisioning script that will install update repositories, install Ansible, and setup host records. For each machine we describe we will run the provisioning script when we call "vagrant up <vm-name>".

```script
# -*- mode: ruby -*-
# vi: set ft=ruby :
$script = <<-SCRIPT
apt-get update
apt-get install -y ansible sshpass
echo "192.168.56.11 controller" >> /etc/hosts
echo "192.168.56.12 node-1" >> /etc/hosts
SCRIPT
Vagrant.configure("2") do |config|
  config.vm.define "controller" do |controller|
  controller.vm.box = "ubuntu/focal64"
  controller.vm.network "private_network", ip: "192.168.56.11"
  controller.vm.hostname = "controller"
  controller.vm.provider "virtualbox" do |vb|
    vb.memory = "2048"
  end
 controller.vm.provision "shell", inline: $script
end
config.vm.define "node1" do |node1|
  node1.vm.box = "ubuntu/focal64"
  node1.vm.network "private_network", ip: "192.168.56.12"
  node1.vm.hostname = "node1"
  node1.vm.provider "virtualbox" do |vb|
   vb.memory = "1024"
  end
  node1.vm.provision "shell", inline: $script
end
end
``` 

> **_NOTE:_**  if you need a ip range out of vagrant default '192.168.56.0/21', create a file '/etc/vbox/networks.conf' and add the line with the range you need. Ex: * 192.168.32.0/24.


Next we call vagrant up: 

```bash
$ vagrant up controller
```

If you already run and need to make changes to any VM described in the Vagrantfile, run the command again with the flag --provision, that will run the provisioning script again for the targeted VM.

### Ansible playbook

For k8s provisioning we will be using a Ansible. Ansible is a powerful automation to provision and manage resources, we can automate rolling updates and state management of our deployments in a declarative file. Here we will use in its simplest form to install microk8s and add alias to kubectl and completion using a playbook.


```bash
---
- name: Install Microk8s
  hosts: localhost
  gather_facts: false
  become: true
  tasks:
    - name: Install microk8s
      snap:
        name: microk8s
        state: present
        classic: yes
    - name: Add alias to kubectl
      become: false
      lineinfile:
        path: '{{ lookup("env", "HOME") }}/.bashrc'
        regexp: '^alias kubectl='
        line: 'alias kubectl="microk8s kubectl"'
        state: present
    - name: Add bash completion for kubectl
      become: false
      lineinfile:
        path: '{{ lookup("env", "HOME") }}/.bashrc'
        regexp: '^source \<\(kubectl'
        line: 'source <(kubectl completion bash)'
        state: present
```

Now we just need to access the vm using ssh, Vagrant already setup a user with ssh access to the machines, so we only need to run: 

```bash 
vagrant ssh controller

```

Ansible is already installed, we did that in the provisioning script so now we only need to run the playbook:

``` bash
$ ansible-playbook /vagrant/homelab-microk8s.yml

```

After ansible finish to run the playbook, we need to source the .bashrc, you can just log out wait 2s and log back in with vagrant ssh: 

```bash 
$ exit

$ vagrant ssh controller
```

Now you can use kubectl with completion and get info of the services running:


```
$ kubectl get nodes
NAME         STATUS   ROLES    AGE   VERSION
controller   Ready    <none>   60m   v1.28.3
```

```
$ kubectl get pods -A

NAMESPACE     NAME                                     READY   STATUS    RESTARTS   AGE
kube-system   calico-node-sfngb                        1/1     Running   0          61m
kube-system   coredns-864597b5fd-drm79                 1/1     Running   0          60m
kube-system   calico-kube-controllers-77bd7c5b-vnx5j   1/1     Running   0          60m


```

references:
https://developer.hashicorp.com/vagrant/docs/vagrantfile
https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_intro.html

[Here](https://github.com/caiocampoos/homelab-k8s/tree/main) you will find a repo with the files used in this guide.

Photo by <a href="https://unsplash.com/@jordanharrison?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Jordan Harrison</a> on <a href="https://unsplash.com/photos/blue-utp-cord-40XgDxBfYXM?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Unsplash</a>