
Learn how to set up a complete Kubernetes cluster with 3 nodes using Vagrant. Includes master node setup, worker nodes, networking, and cluster validation.
Setting up a Kubernetes cluster for learning or development can be challenging. This comprehensive guide walks you through creating a 3-node Kubernetes cluster using Vagrant, providing you with a complete local development environment that mirrors production setups.
Table of Contents
Prerequisites and Setup
Required Software
Before starting, ensure you have the following installed:
- VirtualBox: Version 6.1 or later
- Vagrant: Version 2.2 or later
- At least 8GB RAM and 50GB free disk space
- Host OS: Windows, macOS, or Linux
Installation Commands
# Install VirtualBox (Ubuntu/Debian)
sudo apt update
sudo apt install virtualbox virtualbox-ext-pack
# Install Vagrant (Ubuntu/Debian)
wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor | sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com hashicorp main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install vagrant
# Verify installations
vagrant --version
vboxmanage --versionVagrant Configuration
Create Project Directory
mkdir k8s-vagrant-cluster
cd k8s-vagrant-clusterVagrantfile Configuration
Create a comprehensive Vagrantfile for our 3-node cluster:
# -*- mode: ruby -*-
# vi: set ft=ruby :
# Vagrantfile for 3-node Kubernetes cluster
Vagrant.configure("2") do |config|
# Define cluster configuration
MASTER_NODE = "k8s-master"
WORKER_NODES = ["k8s-worker1", "k8s-worker2"]
# Network configuration
NETWORK_BASE = "192.168.56"
MASTER_IP = "#{NETWORK_BASE}.10"
WORKER_IPS = ["#{NETWORK_BASE}.11", "#{NETWORK_BASE}.12"]
# Resource allocation
MASTER_MEMORY = 2048
MASTER_CPUS = 2
WORKER_MEMORY = 1536
WORKER_CPUS = 2
# Base box configuration
config.vm.box = "ubuntu/focal64"
config.vm.box_check_update = false
# Disable default sync folder
config.vm.synced_folder ".", "/vagrant", disabled: true
# Master node configuration
config.vm.define MASTER_NODE do |master|
master.vm.hostname = MASTER_NODE
master.vm.network "private_network", ip: MASTER_IP
# Port forwarding for Kubernetes API
master.vm.network "forwarded_port", guest: 6443, host: 6443, auto_correct: true
master.vm.network "forwarded_port", guest: 8080, host: 8080, auto_correct: true
master.vm.provider "virtualbox" do |vb|
vb.name = MASTER_NODE
vb.memory = MASTER_MEMORY
vb.cpus = MASTER_CPUS
vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
vb.customize ["modifyvm", :id, "--natdnsproxy1", "on"]
end
# Provisioning script for master
master.vm.provision "shell", path: "scripts/common.sh"
master.vm.provision "shell", path: "scripts/master.sh", args: [MASTER_IP]
end
# Worker nodes configuration
WORKER_NODES.each_with_index do |worker_name, index|
config.vm.define worker_name do |worker|
worker.vm.hostname = worker_name
worker.vm.network "private_network", ip: WORKER_IPS[index]
worker.vm.provider "virtualbox" do |vb|
vb.name = worker_name
vb.memory = WORKER_MEMORY
vb.cpus = WORKER_CPUS
vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
vb.customize ["modifyvm", :id, "--natdnsproxy1", "on"]
end
# Provisioning script for workers
worker.vm.provision "shell", path: "scripts/common.sh"
worker.vm.provision "shell", path: "scripts/worker.sh", args: [MASTER_IP]
end
end
endCreate Scripts Directory
mkdir scriptsBase System Configuration
Common Configuration Script
Create scripts/common.sh for all nodes:
#!/bin/bash
set -e
echo "=== Starting Common Configuration ==="
# Update system
echo "Updating system packages..."
export DEBIAN_FRONTEND=noninteractive
apt-get update -y
apt-get upgrade -y
# Install required packages
echo "Installing required packages..."
apt-get install -y \
apt-transport-https \
ca-certificates \
curl \
gnupg \
lsb-release \
software-properties-common \
net-tools \
htop \
vim
# Disable swap (required for Kubernetes)
echo "Disabling swap..."
swapoff -a
sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
# Load required kernel modules
echo "Loading kernel modules..."
cat <<EOF | tee /etc/modules-load.d/k8s.conf
br_netfilter
overlay
EOF
modprobe br_netfilter
modprobe overlay
# Set sysctl parameters for Kubernetes
echo "Setting sysctl parameters..."
cat <<EOF | tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sysctl --system
# Install containerd
echo "Installing containerd..."
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update -y
apt-get install -y containerd.io
# Configure containerd
echo "Configuring containerd..."
mkdir -p /etc/containerd
containerd config default | tee /etc/containerd/config.toml
# Enable SystemdCgroup
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
systemctl restart containerd
systemctl enable containerd
# Install kubeadm, kubelet, and kubectl
echo "Installing Kubernetes components..."
curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | tee /etc/apt/sources.list.d/kubernetes.list
apt-get update -y
apt-get install -y kubelet=1.28.2-00 kubeadm=1.28.2-00 kubectl=1.28.2-00
apt-mark hold kubelet kubeadm kubectl
# Configure kubelet
echo "Configuring kubelet..."
systemctl enable kubelet
# Set up bash completion
echo "Setting up bash completion..."
echo 'source <(kubectl completion bash)' >> /home/vagrant/.bashrc
echo 'alias k=kubectl' >> /home/vagrant/.bashrc
echo 'complete -F __start_kubectl k' >> /home/vagrant/.bashrc
echo "=== Common Configuration Complete ==="Kubernetes Installation
Master Node Setup Script
Create scripts/master.sh:
#!/bin/bash
set -e
MASTER_IP=$1
echo "=== Starting Master Node Configuration ==="
echo "Master IP: $MASTER_IP"
# Pull required images
echo "Pulling Kubernetes images..."
kubeadm config images pull
# Initialize Kubernetes cluster
echo "Initializing Kubernetes cluster..."
kubeadm init \
--apiserver-advertise-address=$MASTER_IP \
--apiserver-cert-extra-sans=$MASTER_IP \
--node-name=k8s-master \
--pod-network-cidr=10.244.0.0/16 \
--service-cidr=10.96.0.0/12 \
--kubernetes-version=1.28.2
# Set up kubectl for vagrant user
echo "Setting up kubectl for vagrant user..."
mkdir -p /home/vagrant/.kube
cp -i /etc/kubernetes/admin.conf /home/vagrant/.kube/config
chown vagrant:vagrant /home/vagrant/.kube/config
# Set up kubectl for root user
mkdir -p /root/.kube
cp -i /etc/kubernetes/admin.conf /root/.kube/config
# Install Flannel CNI
echo "Installing Flannel CNI..."
export KUBECONFIG=/etc/kubernetes/admin.conf
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
# Generate join command for worker nodes
echo "Generating join command..."
kubeadm token create --print-join-command > /vagrant/join-command.sh
chmod +x /vagrant/join-command.sh
# Wait for master node to be ready
echo "Waiting for master node to be ready..."
kubectl wait --for=condition=Ready node/k8s-master --timeout=300s
# Install Kubernetes Dashboard (optional)
echo "Installing Kubernetes Dashboard..."
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml
# Create admin user for dashboard
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin-user
namespace: kubernetes-dashboard
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin-user
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: admin-user
namespace: kubernetes-dashboard
EOF
# Get dashboard token
echo "Creating dashboard access token..."
kubectl -n kubernetes-dashboard create token admin-user > /vagrant/dashboard-token.txt
echo "=== Master Node Configuration Complete ==="
# Display cluster information
echo "=== Cluster Information ==="
kubectl get nodes -o wide
kubectl get pods -A
echo "Dashboard token saved to: /vagrant/dashboard-token.txt"
echo "Access dashboard at: https://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/"Worker Node Setup Script
Create scripts/worker.sh:
#!/bin/bash
set -e
MASTER_IP=$1
echo "=== Starting Worker Node Configuration ==="
echo "Master IP: $MASTER_IP"
# Wait for master node to be ready and join command to be available
echo "Waiting for join command from master..."
while [ ! -f /vagrant/join-command.sh ]; do
echo "Waiting for join command..."
sleep 10
done
# Execute join command
echo "Joining cluster..."
bash /vagrant/join-command.sh
# Verify node joined successfully
echo "Verifying node status..."
sleep 30
echo "=== Worker Node Configuration Complete ==="Master Node Setup
Start the Cluster
# Start all VMs
vagrant up
# Check status
vagrant status
# SSH into master node
vagrant ssh k8s-masterVerify Master Node
# Check node status
kubectl get nodes
# Check system pods
kubectl get pods -A
# Check cluster info
kubectl cluster-infoWorker Nodes Setup
Monitor Worker Node Join
# SSH into worker nodes to check status
vagrant ssh k8s-worker1
vagrant ssh k8s-worker2
# From master node, verify all nodes
kubectl get nodes -o wideExpected Output
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
k8s-master Ready control-plane 10m v1.28.2 192.168.56.10 <none> Ubuntu 20.04.6 LTS 5.4.0-174-generic containerd://1.6.21
k8s-worker1 Ready <none> 8m v1.28.2 192.168.56.11 <none> Ubuntu 20.04.6 LTS 5.4.0-174-generic containerd://1.6.21
k8s-worker2 Ready <none> 6m v1.28.2 192.168.56.12 <none> Ubuntu 20.04.6 LTS 5.4.0-174-generic containerd://1.6.21Network Configuration
Flannel CNI Setup
Flannel is automatically installed during master setup. Verify it's working:
# Check Flannel pods
kubectl get pods -n kube-flannel
# Verify network connectivity
kubectl get nodes -o wideTest Pod-to-Pod Communication
# Create test deployment
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-deployment
spec:
replicas: 3
selector:
matchLabels:
app: test-app
template:
metadata:
labels:
app: test-app
spec:
containers:
- name: nginx
image: nginx:1.20
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: test-service
spec:
selector:
app: test-app
ports:
- port: 80
targetPort: 80
type: ClusterIP
EOF# Verify pods are distributed across nodes
kubectl get pods -o wide
# Test service connectivity
kubectl run debug --image=busybox -it --rm -- wget -qO- test-serviceCluster Validation
Comprehensive Cluster Tests
# Check all system components
kubectl get componentstatuses
# Verify all nodes are ready
kubectl get nodes
# Check all system pods
kubectl get pods -A
# Test DNS resolution
kubectl run dns-test --image=busybox -it --rm -- nslookup kubernetes.default
# Deploy a test application
kubectl create deployment nginx --image=nginx:1.20
kubectl expose deployment nginx --port=80 --type=NodePort
# Get service details
kubectl get svc nginxPerformance Testing
# Create a resource-intensive pod
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: resource-test
spec:
containers:
- name: stress
image: polinux/stress
command: ["stress"]
args: ["--cpu", "1", "--timeout", "60s"]
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
EOF
# Monitor resource usage
kubectl top nodes
kubectl top podsLoad Balancing Verification
# Scale deployment across nodes
kubectl scale deployment nginx --replicas=6
# Verify distribution
kubectl get pods -o wide | grep nginx
# Test load balancing
for i in {1..10}; do
kubectl exec -it $(kubectl get pod -l app=nginx -o jsonpath='{.items[0].metadata.name}') -- hostname
doneTroubleshooting
Common Issues and Solutions
1. Node Not Ready
# Check node status
kubectl describe node <node-name>
# Check kubelet logs
vagrant ssh <node-name>
sudo journalctl -u kubelet -f
# Restart kubelet if needed
sudo systemctl restart kubelet2. Pod Network Issues
# Check Flannel pods
kubectl get pods -n kube-flannel
# Restart Flannel if needed
kubectl delete pods -n kube-flannel -l app=flannel
# Check iptables rules
sudo iptables -L -n | grep FORWARD3. DNS Resolution Problems
# Check CoreDNS pods
kubectl get pods -n kube-system | grep coredns
# Test DNS resolution
kubectl run dns-debug --image=busybox -it --rm -- nslookup google.com4. Resource Constraints
# Check resource usage
kubectl top nodes
kubectl describe nodes
# Check for resource pressure
kubectl get events --sort-by='.lastTimestamp'Useful Commands for Debugging
# Get cluster information
kubectl cluster-info dump
# Check all events
kubectl get events -A --sort-by='.lastTimestamp'
# Describe problematic resources
kubectl describe pod <pod-name>
kubectl describe node <node-name>
# Access logs
kubectl logs <pod-name> -c <container-name>Vagrant-Specific Troubleshooting
# Restart VMs
vagrant reload
# Destroy and recreate
vagrant destroy -f
vagrant up
# Check VM status
vagrant status
vagrant global-status
# SSH debugging
vagrant ssh-configAdvanced Configuration
Installing Additional Tools
# Install Helm
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
# Install ingress controller
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.1/deploy/static/provider/baremetal/deploy.yaml
# Install metrics server
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yamlPersistent Storage Setup
# Create local storage class
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: local-pv
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: /tmp/data
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- k8s-worker1Monitoring Setup
# Install kube-prometheus-stack with Helm
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install monitoring prometheus-community/kube-prometheus-stackCleanup and Management
Cluster Maintenance
# Update node labels
kubectl label nodes k8s-worker1 role=worker
kubectl label nodes k8s-worker2 role=worker
# Drain node for maintenance
kubectl drain k8s-worker1 --ignore-daemonsets --delete-emptydir-data
# Uncordon node
kubectl uncordon k8s-worker1Complete Cleanup
# Destroy Vagrant environment
vagrant destroy -f
# Clean up VirtualBox
VBoxManage list vms
VBoxManage unregistervm <vm-name> --deleteConclusion
You now have a fully functional 3-node Kubernetes cluster running on Vagrant! This setup provides:
Key Features:
- Multi-node cluster with 1 master and 2 worker nodes
- Container networking with Flannel CNI
- Persistent storage capabilities
- Dashboard access for web-based management
- Production-like environment for learning and testing
Next Steps:
- Deploy sample applications
- Experiment with scaling and load balancing
- Set up CI/CD pipelines
- Practice disaster recovery scenarios
- Explore advanced Kubernetes features
Use Cases:
- Development environment for Kubernetes applications
- Learning platform for container orchestration
- Testing ground for deployment strategies
- Prototype environment for production workloads
For expert Kubernetes consulting and enterprise cluster setup, our team provides comprehensive Kubernetes services and container orchestration solutions.
Need help with production Kubernetes deployments? Our certified Kubernetes administrators can help you design and implement scalable container orchestration solutions. Contact us for a free consultation.
Written by
Abhishek
KEEP LEARNING WITH THEDBADMIN
Turn this guide into a skill you can prove
Keep reading
Related articles
NextGen DBA + AI Engineer Bootcamp
Instructor-led, lab-first training for production DBAs: PostgreSQL, MongoDB, Docker, Kubernetes and AI.
- Live labs on real execution plans
- Backup, PITR & HA in production
- pgvector, RAG & AI for DBAs
🛒 FROM THE SHOP
200 PostgreSQL DBA Interview Questions & Answers DBA · DevOps · SRE / DBRE Cloud DBA · Vector DBA · AI / ML Ops | A practitioner's preparation
PDF · 64 pages
₹199₹5,00096% off