top of page
Search

Create Your Own CKA Exam Lab at Home: Multipass Kubernetes Script

If you are considering the Certified Kubernetes Administrator (CKA) or any of the other Linux Foundation kubernetes certifications, you would appreciate having a reliable, exam-like lab. No cloud costs, no complex setups, just a script that spins up a full multi-node cluster (control plane + 2 workers) with Cilium CNI, containerd, and debug tools in minutes. But more important, also tear down and recreate in minutes.


Tested on macOS, but theoretically can work on Linux (Ubuntu/RHEL) and even on Windows.


CKA exam lab at home

Why This Lab?

  • Exam-accurate: kubeadm init/join, PodCIDR, etcd access, real networking: covers 100% CKA objectives (troubleshooting 30%, networking 15%, etc.).[training.linuxfoundation]​

  • Lightweight: 3 VMs (2CPU/2GB/10GB each), fast bootstrap.

  • Cilium CNI: Matches CKA and LFS258 Lab Setup.

  • Debug-ready: crictl, kubectl aliases, metrics-server ready, helm. Add more tools as needed.


Prerequisites

  • macOS: brew install --cask multipass

  • Linux (Ubuntu/Debian): sudo snap install multipass --classic

  • Linux (RHEL/CentOS/Fedora): Enable snaps (sudo dnf install snapd fuse), sudo snap install multipass --classic

  • Windows: Download/run MSI from multipass.run/install. Use PowerShell/Git Bash.



The Script - CKA Exam Lab

Save as cka-lab.sh, chmod +x cka-lab.sh.

#!/bin/bash
set -e

NODES=("cp" "worker1" "worker2")
BINARIES=("kubeadm" "kubelet" "kubectl")
POD_CIDR="10.244.0.0/16"
KUBERNETES_VERSION="1.34.0"
CRICTL_VERSION="v1.30.0"
CRICTL_FILENAME="crictl-${CRICTL_VERSION}-linux-amd64.tar.gz"

log() { echo "[$(date +'%H:%M:%S')] $1"; }


case "$1" in
  start)
    log "CKA LAB Setup"

    # Download binaries
    log "Downloading K8s"
    for binary in "${BINARIES[@]}"; do
      [ ! -f "$binary" ] && curl -LO "https://dl.k8s.io/release/v${KUBERNETES_VERSION}/bin/linux/amd64/${binary}"
    done
    chmod +x kubeadm kubelet kubectl
    curl -L -o crictl.tar.gz "https://github.com/kubernetes-sigs/cri-tools/releases/download/${CRICTL_VERSION}/${CRICTL_FILENAME}"

    # (Re)Create VMs
    log "Provisioning Cluster"
    for node in "${NODES[@]}"; do
      multipass delete "$node" --purge 2>/dev/null || true
      multipass launch --name "$node" --cpus 2 --memory 2G --disk 10G 22.04
    done

    # Setup nodes
    log "Setup (binaries + services)..."
    for node in "${NODES[@]}"; do
      log "  $node"

      # Transfer binaries
      multipass transfer kubeadm "$node":/tmp/
      multipass transfer kubelet "$node":/tmp/
      multipass transfer kubectl "$node":/tmp/
      multipass transfer crictl.tar.gz "$node":/tmp/

      # Install packages
      multipass exec "$node" -- sudo apt update -qq
      multipass exec "$node" -- sudo apt install -y containerd bash-completion apt-transport-https tree software-properties-common ca-certificates socat conntrack -qq

      # Configure stuff
      multipass exec "$node" -- /bin/bash << 'EOF'
# Overlay
sudo modprobe overlay >/dev/null 2>&1 || true
sudo modprobe br_netfilter >/dev/null 2>&1 || true

# Swap
sudo swapoff -a >/dev/null 2>&1 || true
sudo sed -i '/ swap / s/^/#/' /etc/fstab >/dev/null 2>&1 || true

# Sysctl
sudo tee /etc/sysctl.d/kubernetes.conf >/dev/null <<SYSCTL
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
SYSCTL
sudo sysctl --system >/dev/null 2>&1 || true

# Binaries
sudo mv /tmp/{kubeadm,kubelet,kubectl} /usr/local/bin/
sudo chmod +x /usr/local/bin/kube*

# Containerd config
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml >/dev/null
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml

# Kubelet service
sudo tee /etc/systemd/system/kubelet.service >/dev/null <<SERVICE
[Unit]
Description=Kubelet
Documentation=https://kubernetes.io/docs/
After=network.target containerd.service
Requires=containerd.service

[Service]
Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf"
Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml"
EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env
EnvironmentFile=-/etc/default/kubelet
ExecStart=/usr/local/bin/kubelet \$KUBELET_KUBECONFIG_ARGS \$KUBELET_CONFIG_ARGS \$KUBELET_KUBEADM_ARGS \$KUBELET_EXTRA_ARGS
Restart=always
RestartSec=10
KillMode=process

[Install]
WantedBy=multi-user.target
SERVICE

sudo systemctl daemon-reload
sudo systemctl restart containerd
sudo systemctl enable --now containerd kubelet
sudo systemctl stop kubelet

# crictl
sudo tar zxvf /tmp/crictl.tar.gz -C /usr/local/bin
rm -f /tmp/crictl.tar.gz

sudo tee /etc/crictl.yaml >/dev/null <<CONFIG
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 10
debug: false
CONFIG

sudo chmod 644 /etc/crictl.yaml

EOF

      # User aliases
      multipass exec "$node" -- bash -c "
        echo 'alias k=kubectl' >> /home/ubuntu/.bashrc
        echo 'complete -F __start_kubectl k' >> /home/ubuntu/.bashrc
        echo 'source <(kubectl completion bash)' >> /home/ubuntu/.bashrc
        "
    done

    # Bootstrap
    log "Configuring Cluster..."
    CP_IP=$(multipass info cp | grep IP | awk '{print $2}')
    for node in "${NODES[@]}"; do
      multipass exec "$node" -- /bin/bash << EOF
echo '${CP_IP} cp k8scp k8scp.lab.example.com' | sudo tee -a /etc/hosts > /dev/null
EOF
    done

    log "Init cp..."
    multipass exec cp -- sudo systemctl restart containerd kubelet
    multipass exec cp -- sudo kubeadm init --pod-network-cidr="$POD_CIDR" --cri-socket unix:///run/containerd/containerd.sock

    log "CNI..."
    multipass exec cp -- bash -c "
      mkdir -p /home/ubuntu/.kube
      sudo cp /etc/kubernetes/admin.conf /home/ubuntu/.kube/config
      sudo chown ubuntu:ubuntu /home/ubuntu/.kube/config

      curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
      helm repo add cilium https://helm.cilium.io/
      helm repo update
      helm install cilium cilium/cilium \\
        --version 1.17.4 \\
        --namespace kube-system \\
        --set ipam.operator.clusterPoolIPv4PodCIDRList={$POD_CIDR} \\
        --set kubeProxyReplacement=false
    "
    # Wait
    log "Waiting for cilium config..."
    multipass exec cp -- kubectl rollout status daemonset/cilium -n kube-system --timeout=600s

    # Join
    log "Join Workers..."
    multipass exec cp -- bash -c "
      TOKEN=\$(sudo kubeadm token create)
      HASH=\$(openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //')
      echo \"sudo kubeadm join $CP_IP:6443 --token \$TOKEN --discovery-token-ca-cert-hash sha256:\$HASH\" > /home/ubuntu/join.sh
      sudo chown ubuntu /home/ubuntu/join.sh
    "

    multipass transfer cp:/home/ubuntu/join.sh .
    for node in worker1 worker2; do
      log "Joining $node to the cluster..."
      multipass transfer join.sh "$node":/home/ubuntu/
      multipass exec "$node" -- sudo bash /home/ubuntu/join.sh
    done

    # Export config
    multipass transfer cp:/home/ubuntu/.kube/config ~/.kube/cka-final.conf
    echo "export KUBECONFIG=~/.kube/cka-final.conf" >> ~/.bashrc
    log "DONE! source ~/.bashrc && k get nodes"

    # Verify
    KUBECONFIG=~/.kube/cka-final.conf kubectl get nodes -o wide
    ;;

  stop)
    for node in "${NODES[@]}"; do multipass delete "$node" --purge 2>/dev/null || true; done
    rm -f kubeadm kubelet kubectl ~/.kube/cka-final.conf
    ;;

  cp|worker1|worker2) multipass shell "$1" ;;

  status)
    multipass list
    [ -f ~/.kube/cka-final.conf ] && KUBECONFIG=~/.kube/cka-final.conf kubectl get nodes -o wide
    ;;

  *) echo "Usage: $0 {start|stop|cp|worker1|worker2|status}"; exit 1 ;;
esac



Troubleshooting

Issue

Fix

No internet in VMs

Linux: Restart multipass daemon (sudo snap restart multipass). Windows: Check Hyper-V firewall.[github]​

ARM host

Download arm64 binaries: Edit URLs to /arm64/.[documentation.ubuntu]​

OOM

Bump --memory 4G in launch.

Cilium fails

--timeout=600s (usually starts in 2-3 mins); fallback Flannel: Replace Helm with kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml.


Next Steps


Now is the time to play around with your CKA Exam Lab, or just ask your favourite AI to create some real exam scenarios for you.



 
 
 

Comments


bottom of page