TL;DR: Learn how to build a lightweight Kubernetes cluster on your local machine using Vagrant, Ansible, and VMware Fusion. Perfect for ARM-based Mac users looking to experiment with Kubernetes in a reproducible environment.
Why This Guide?
Setting up a Kubernetes cluster manually can be complex and time-consuming. But with the power of Vagrant for managing virtual machines and Ansible for automating setup tasks, you can spin up a local cluster with minimal effort and maximum reproducibility. This tutorial walks you through building a two-node cluster on macOS with ARM chips (e.g., M1/M2), but it's adaptable to other setups.
Table of Contents
- Prerequisites
- Project Structure
- Step 1: Configure Vagrant
- Step 2: Configure Ansible
- Step 3: Ansible Playbook for Kubernetes Setup
- Step 4: Test and Deploy the Cluster
- What's Next?
- Useful links
Prerequisites
Before we start, ensure you have the following installed:
For macOS ARM users, refer to this special setup guide: Run Vagrant VMs in a M1/M2/M3 Apple Sillicon Chip
Project Structure
Create a project directory and replicate the following structure:
.
├── ansible
│ ├── ansible.cfg
│ ├── inventory.ini
│ └── k8s-cluster-setup.yml
└── Vagrantfile
Step 1: Configure Vagrant
Install the VMware Desktop plugin:
vagrant plugin install vagrant-vmware-desktop
Next, reserve two static private IPs from your LAN. Scan your local network using:
nmap -sn 192.168.1.0/24
Replace
192.168.1.0
with you LAN network IP
Update your Vagrantfile
with something like this:
Vagrant.configure("2") do |config|
config.vm.define "kubmaster" do |kub|
kub.vm.box = "spox/ubuntu-arm"
kub.vm.box_version = "1.0.0"
kub.vm.hostname = 'kubmaster'
kub.vm.provision "docker"
kub.vm.network "public_network", ip: "192.168.1.101", bridge: "en0: Wifi"
kub.vm.provider "vmware_desktop" do |v|
v.allowlist_verified = true
v.gui = false
v.vmx["memsize"] = "4096"
v.vmx["numvcpus"] = "2"
end
end
config.vm.define "kubnode1" do |kubnode|
kubnode.vm.box = "spox/ubuntu-arm"
kubnode.vm.box_version = "1.0.0"
kubnode.vm.hostname = 'kubnode1'
kubnode.vm.provision "docker"
kubnode.vm.network "public_network", ip: "192.168.1.102", bridge: "en0: Wifi"
kubnode.vm.provider "vmware_desktop" do |v|
v.allowlist_verified = true
v.gui = false
v.vmx["memsize"] = "4096"
v.vmx["numvcpus"] = "2"
end
end
end
Replace the IPs with ones that match your LAN.
Now bring up the VMs:
vagrant up
Step 2: Configure Ansible
ansible/inventory.ini
[master]
kubmaster ansible_host=192.168.1.101 ansible_ssh_private_key_file=.vagrant/machines/kubmaster/vmware_desktop/private_key
[workers]
kubnode1 ansible_host=192.168.1.102 ansible_ssh_private_key_file=.vagrant/machines/kubnode1/vmware_desktop/private_key
[all:vars]
ansible_user=vagrant
Make sure to replace the IP addresses
ansible/ansible.cfg
[defaults]
inventory = inventory.ini
host_key_checking = False
Step 3: Ansible Playbook for Kubernetes Setup
ansible/k8s-cluster-setup.yml
This playbook performs the following:
- Prepares all nodes: disables swap, installs required packages, configures kernel modules, adds K8s repositories, installs
kubeadm
,kubelet
, andkubectl
. - Initializes the master node and stores the cluster join command.
- Sets up Flannel CNI for networking.
- Joins worker nodes using the generated join command.
---
- name: Prepare Kubernetes Nodes
hosts: all
become: yes
tasks:
- name: Disable swap (runtime)
command: swapoff -a
when: ansible_swaptotal_mb > 0
- name: Comment out swap line in /etc/fstab
lineinfile:
path: /etc/fstab
regexp: '^\s*([^#]\S*\s+\S+\s+swap\s+\S+)\s*$'
line: '# \1'
backrefs: yes
- name: Apply mount changes
command: mount -a
- name: Stop AppArmor
systemd:
name: apparmor
state: stopped
enabled: no
- name: Restart containerd
systemd:
name: containerd
state: restarted
- name: Configure sysctl for Kubernetes
copy:
dest: /etc/sysctl.d/kubernetes.conf
content: |
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
- name: Apply sysctl settings
command: sysctl --system
- name: Install transport and curl
apt:
name:
- apt-transport-https
- curl
update_cache: yes
state: present
- name: Add Kubernetes APT key
shell: |
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.33/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
args:
creates: /etc/apt/keyrings/kubernetes-apt-keyring.gpg
- name: Add Kubernetes APT repository
apt_repository:
repo: "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.33/deb/ /"
filename: "kubernetes"
state: present
- name: Install Kubernetes components
apt:
name:
- kubelet
- kubeadm
- kubectl
- kubernetes-cni
update_cache: yes
state: present
- name: Enable kubelet service
systemd:
name: kubelet
enabled: yes
- name: Initialize Kubernetes Master
hosts: master
become: yes
vars:
pod_cidr: "10.244.0.0/16"
tasks:
- name: Remove default containerd config
file:
path: /etc/containerd/config.toml
state: absent
- name: Restart containerd
systemd:
name: containerd
state: restarted
enabled: yes
- name: Wait for containerd socket to be available
wait_for:
path: /run/containerd/containerd.sock
state: present
timeout: 20
- name: Initialize Kubernetes control plane
command: kubeadm init --apiserver-advertise-address={{ ansible_host }} --node-name {{ inventory_hostname }} --pod-network-cidr={{ pod_cidr }}
register: kubeadm_output
args:
creates: /etc/kubernetes/admin.conf
- name: Extract join command
shell: |
kubeadm token create --print-join-command
register: join_command
changed_when: false
- name: Set join command fact
set_fact:
kube_join_command: "{{ join_command.stdout }}"
- name: Create .kube directory for vagrant user
become_user: vagrant
file:
path: /home/vagrant/.kube
state: directory
mode: 0755
- name: Copy Kubernetes admin config to vagrant user
copy:
src: /etc/kubernetes/admin.conf
dest: /home/vagrant/.kube/config
remote_src: yes
owner: vagrant
group: vagrant
mode: 0644
- name: Configure networking
hosts: all
become: yes
tasks:
- name: Ensure br_netfilter loads at boot
copy:
dest: /etc/modules-load.d/k8s.conf
content: |
br_netfilter
- name: Load br_netfilter kernel module now
command: modprobe br_netfilter
- name: Configure sysctl for Kubernetes networking
copy:
dest: /etc/sysctl.d/k8s.conf
content: |
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
- name: Apply sysctl settings
command: sysctl --system
- name: Configure flannel
hosts: master
become: yes
tasks:
- name: Apply Flannel CNI plugin
become_user: vagrant
command: kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml
environment:
KUBECONFIG: /home/vagrant/.kube/config
- name: Join worker nodes to cluster
hosts: workers
become: yes
vars:
kube_join_command: "{{ hostvars['kubmaster']['kube_join_command'] }}"
tasks:
- name: Remove default containerd config
file:
path: /etc/containerd/config.toml
state: absent
- name: Restart containerd
systemd:
name: containerd
state: restarted
enabled: yes
- name: Wait until the Kubernetes API server is reachable
wait_for:
host: "{{ hostvars['kubmaster']['ansible_host'] }}"
port: 6443
delay: 10
timeout: 120
state: started
- name: Join the node to Kubernetes cluster
command: "{{ kube_join_command }}"
args:
creates: /etc/kubernetes/kubelet.conf
Step 4: Test and Deploy the Cluster
Test Ansible SSH connectivity:
ansible all -m ping -i ansible/inventory.ini
Run the full cluster setup:
ansible-playbook -i ansible/inventory.ini ansible/k8s-cluster-setup.yml
Retrieve the kubeconfig file locally:
vagrant ssh kubmaster -c "sudo cat /etc/kubernetes/admin.conf" > ~/kubeconfig-vagrant.yaml
Test your cluster:
KUBECONFIG=~/kubeconfig-vagrant.yaml kubectl get nodes
You should see both the master and worker nodes in Ready
status.
What's Next?
You now have a fully functioning local Kubernetes cluster on ARM-based hardware, with everything automated and reproducible. You can now:
- Experiment with Helm charts
- Try GitOps with ArgoCD
- Deploy sample apps
Stay tuned for the next part of this series!
Useful Links
- Vagrant: https://developer.hashicorp.com/vagrant/install
- Ansible: https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html
- VMware Fusion Tech Preview: https://knowledge.broadcom.com/external/article/315638/download-and-install-vmware-fusion.html
- ARM-specific setup guide: https://dev.to/houssambourkane/run-vagrant-vms-in-a-m1m2m3-macos-chip-2p3l
- Vagrant VMware utility: https://developer.hashicorp.com/vagrant/docs/providers/vmware/vagrant-vmware-utility
Top comments (2)
Thanks for this tutorial!! I have been struggling doing it manually for so long, can't wait for upcoming sections
I mean, I've always wanted to automate my K8s cluster deployment, and I looked it up on Google I found this. How lucky am i ???