network: version: 2 wifis: wlan0: dhcp4: true access-points: "YOUR_WIFI_SSID": password: "your-wifi-password" ethernets: eth0: addresses: - 10.0.0.1/24
network: version: 2 wifis: wlan0: dhcp4: true access-points: "YOUR_WIFI_SSID": password: "your-wifi-password" ethernets: eth0: addresses: - 10.0.0.1/24
network: version: 2 wifis: wlan0: dhcp4: true access-points: "YOUR_WIFI_SSID": password: "your-wifi-password" ethernets: eth0: addresses: - 10.0.0.1/24
sudo netplan apply
sudo netplan apply
sudo netplan apply
# Enable IP forwarding
# This tells the Linux kernel to route packets
# between interfaces instead of dropping them
echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/ip-forward.conf
sudo sysctl --system # Set up NAT masquerade
# Rewrite source IPs from 10.0.0.x to Pi 1's
# Wi-Fi address so the internet sees them as coming from Pi 1
sudo apt-get install -y iptables-persistent
sudo iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
sudo netfilter-persistent save
# Enable IP forwarding
# This tells the Linux kernel to route packets
# between interfaces instead of dropping them
echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/ip-forward.conf
sudo sysctl --system # Set up NAT masquerade
# Rewrite source IPs from 10.0.0.x to Pi 1's
# Wi-Fi address so the internet sees them as coming from Pi 1
sudo apt-get install -y iptables-persistent
sudo iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
sudo netfilter-persistent save
# Enable IP forwarding
# This tells the Linux kernel to route packets
# between interfaces instead of dropping them
echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/ip-forward.conf
sudo sysctl --system # Set up NAT masquerade
# Rewrite source IPs from 10.0.0.x to Pi 1's
# Wi-Fi address so the internet sees them as coming from Pi 1
sudo apt-get install -y iptables-persistent
sudo iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
sudo netfilter-persistent save
sudo apt-get install -y isc-dhcp-server
sudo apt-get install -y isc-dhcp-server
sudo apt-get install -y isc-dhcp-server
# The subnet the DHCP server manages
subnet 10.0.0.0 netmask 255.255.255.0 { range 10.0.0.10 10.0.0.50; # dynamic range for any new devices option routers 10.0.0.1; # point clients to Pi 1 as gateway option domain-name-servers 8.8.8.8, 1.1.1.1; # public DNS servers
} # Static leases. Each worker always gets the same IP
# Find your Pi's MAC with: ip link show eth0
host worker-1 { hardware ethernet XX:XX:XX:XX:XX:XX; # replace with Pi 2's eth0 MAC fixed-address 10.0.0.11;
}
host worker-2 { hardware ethernet XX:XX:XX:XX:XX:XX; # replace with Pi 3's eth0 MAC fixed-address 10.0.0.12;
}
host worker-3 { hardware ethernet XX:XX:XX:XX:XX:XX; # replace with Pi 4's eth0 MAC fixed-address 10.0.0.13;
}
# The subnet the DHCP server manages
subnet 10.0.0.0 netmask 255.255.255.0 { range 10.0.0.10 10.0.0.50; # dynamic range for any new devices option routers 10.0.0.1; # point clients to Pi 1 as gateway option domain-name-servers 8.8.8.8, 1.1.1.1; # public DNS servers
} # Static leases. Each worker always gets the same IP
# Find your Pi's MAC with: ip link show eth0
host worker-1 { hardware ethernet XX:XX:XX:XX:XX:XX; # replace with Pi 2's eth0 MAC fixed-address 10.0.0.11;
}
host worker-2 { hardware ethernet XX:XX:XX:XX:XX:XX; # replace with Pi 3's eth0 MAC fixed-address 10.0.0.12;
}
host worker-3 { hardware ethernet XX:XX:XX:XX:XX:XX; # replace with Pi 4's eth0 MAC fixed-address 10.0.0.13;
}
# The subnet the DHCP server manages
subnet 10.0.0.0 netmask 255.255.255.0 { range 10.0.0.10 10.0.0.50; # dynamic range for any new devices option routers 10.0.0.1; # point clients to Pi 1 as gateway option domain-name-servers 8.8.8.8, 1.1.1.1; # public DNS servers
} # Static leases. Each worker always gets the same IP
# Find your Pi's MAC with: ip link show eth0
host worker-1 { hardware ethernet XX:XX:XX:XX:XX:XX; # replace with Pi 2's eth0 MAC fixed-address 10.0.0.11;
}
host worker-2 { hardware ethernet XX:XX:XX:XX:XX:XX; # replace with Pi 3's eth0 MAC fixed-address 10.0.0.12;
}
host worker-3 { hardware ethernet XX:XX:XX:XX:XX:XX; # replace with Pi 4's eth0 MAC fixed-address 10.0.0.13;
}
INTERFACESv4="eth0"
INTERFACESv4="eth0"
INTERFACESv4="eth0"
sudo mkdir -p /etc/systemd/system/isc-dhcp-server.service.d
cat <<'EOF' | sudo tee /etc/systemd/system/isc-dhcp-server.service.d/wait-for-eth0.conf
[Unit]
After=sys-subsystem-net-devices-eth0.device
Wants=sys-subsystem-net-devices-eth0.device [Service]
ExecStartPre=/bin/sh -c 'i=0; while [ $i -lt 30 ]; do ip -4 addr show eth0 | grep -q inet && exit 0; sleep 1; i=$((i+1)); done; exit 1'
Restart=on-failure
RestartSec=5
EOF sudo systemctl daemon-reload
sudo systemctl enable --now isc-dhcp-server
sudo mkdir -p /etc/systemd/system/isc-dhcp-server.service.d
cat <<'EOF' | sudo tee /etc/systemd/system/isc-dhcp-server.service.d/wait-for-eth0.conf
[Unit]
After=sys-subsystem-net-devices-eth0.device
Wants=sys-subsystem-net-devices-eth0.device [Service]
ExecStartPre=/bin/sh -c 'i=0; while [ $i -lt 30 ]; do ip -4 addr show eth0 | grep -q inet && exit 0; sleep 1; i=$((i+1)); done; exit 1'
Restart=on-failure
RestartSec=5
EOF sudo systemctl daemon-reload
sudo systemctl enable --now isc-dhcp-server
sudo mkdir -p /etc/systemd/system/isc-dhcp-server.service.d
cat <<'EOF' | sudo tee /etc/systemd/system/isc-dhcp-server.service.d/wait-for-eth0.conf
[Unit]
After=sys-subsystem-net-devices-eth0.device
Wants=sys-subsystem-net-devices-eth0.device [Service]
ExecStartPre=/bin/sh -c 'i=0; while [ $i -lt 30 ]; do ip -4 addr show eth0 | grep -q inet && exit 0; sleep 1; i=$((i+1)); done; exit 1'
Restart=on-failure
RestartSec=5
EOF sudo systemctl daemon-reload
sudo systemctl enable --now isc-dhcp-server
# Check DHCP leases were assigned
cat /var/lib/dhcp/dhcpd.leases # Ping each worker
ping -c 2 10.0.0.11
ping -c 2 10.0.0.12
ping -c 2 10.0.0.13 # Verify workers can reach the internet through Pi 1's NAT
ssh [email protected] 'ping -c 2 8.8.8.8'
# Check DHCP leases were assigned
cat /var/lib/dhcp/dhcpd.leases # Ping each worker
ping -c 2 10.0.0.11
ping -c 2 10.0.0.12
ping -c 2 10.0.0.13 # Verify workers can reach the internet through Pi 1's NAT
ssh [email protected] 'ping -c 2 8.8.8.8'
# Check DHCP leases were assigned
cat /var/lib/dhcp/dhcpd.leases # Ping each worker
ping -c 2 10.0.0.11
ping -c 2 10.0.0.12
ping -c 2 10.0.0.13 # Verify workers can reach the internet through Pi 1's NAT
ssh [email protected] 'ping -c 2 8.8.8.8'
Host k8s-control HostName <Pi 1's Wi-Fi IP> User ubuntu IdentityFile ~/.ssh/id_ed25519 Host worker-1 HostName 10.0.0.11 User ubuntu IdentityFile ~/.ssh/id_ed25519 ProxyJump k8s-control Host worker-2 HostName 10.0.0.12 User ubuntu IdentityFile ~/.ssh/id_ed25519 ProxyJump k8s-control Host worker-3 HostName 10.0.0.13 User ubuntu IdentityFile ~/.ssh/id_ed25519 ProxyJump k8s-control
Host k8s-control HostName <Pi 1's Wi-Fi IP> User ubuntu IdentityFile ~/.ssh/id_ed25519 Host worker-1 HostName 10.0.0.11 User ubuntu IdentityFile ~/.ssh/id_ed25519 ProxyJump k8s-control Host worker-2 HostName 10.0.0.12 User ubuntu IdentityFile ~/.ssh/id_ed25519 ProxyJump k8s-control Host worker-3 HostName 10.0.0.13 User ubuntu IdentityFile ~/.ssh/id_ed25519 ProxyJump k8s-control
Host k8s-control HostName <Pi 1's Wi-Fi IP> User ubuntu IdentityFile ~/.ssh/id_ed25519 Host worker-1 HostName 10.0.0.11 User ubuntu IdentityFile ~/.ssh/id_ed25519 ProxyJump k8s-control Host worker-2 HostName 10.0.0.12 User ubuntu IdentityFile ~/.ssh/id_ed25519 ProxyJump k8s-control Host worker-3 HostName 10.0.0.13 User ubuntu IdentityFile ~/.ssh/id_ed25519 ProxyJump k8s-control
sudo swapoff -a
sudo sed -i '/swap/d' /etc/fstab
sudo swapoff -a
sudo sed -i '/swap/d' /etc/fstab
sudo swapoff -a
sudo sed -i '/swap/d' /etc/fstab
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
cat <<EOF | sudo 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
sudo sysctl --system
cat <<EOF | sudo 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
sudo sysctl --system
cat <<EOF | sudo 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
sudo sysctl --system
sudo apt-get update && sudo apt-get install -y containerd # Generate the default config file
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml # Enable the systemd cgroup driver
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml sudo systemctl restart containerd
sudo systemctl enable containerd
sudo apt-get update && sudo apt-get install -y containerd # Generate the default config file
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml # Enable the systemd cgroup driver
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml sudo systemctl restart containerd
sudo systemctl enable containerd
sudo apt-get update && sudo apt-get install -y containerd # Generate the default config file
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml # Enable the systemd cgroup driver
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml sudo systemctl restart containerd
sudo systemctl enable containerd
# Import the signing key, this lets apt verify that packages
# actually come from the Kubernetes project and haven't been tampered with
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.32/deb/Release.key \ | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg # Add the repo
# The signed-by clause scopes the key to this repo only,
# so it can't be used to authenticate packages from other sources
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.32/deb/ /' \ | sudo tee /etc/apt/sources.list.d/kubernetes.list
# Import the signing key, this lets apt verify that packages
# actually come from the Kubernetes project and haven't been tampered with
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.32/deb/Release.key \ | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg # Add the repo
# The signed-by clause scopes the key to this repo only,
# so it can't be used to authenticate packages from other sources
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.32/deb/ /' \ | sudo tee /etc/apt/sources.list.d/kubernetes.list
# Import the signing key, this lets apt verify that packages
# actually come from the Kubernetes project and haven't been tampered with
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.32/deb/Release.key \ | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg # Add the repo
# The signed-by clause scopes the key to this repo only,
# so it can't be used to authenticate packages from other sources
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.32/deb/ /' \ | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
sudo kubeadm init \ --apiserver-advertise-address=10.0.0.1 \ --pod-network-cidr=10.244.0.0/16
sudo kubeadm init \ --apiserver-advertise-address=10.0.0.1 \ --pod-network-cidr=10.244.0.0/16
sudo kubeadm init \ --apiserver-advertise-address=10.0.0.1 \ --pod-network-cidr=10.244.0.0/16
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
kubectl get nodes
kubectl get nodes
kubectl get nodes
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
sudo kubeadm join 10.0.0.1:6443 \ --token <your-token> \ --discovery-token-ca-cert-hash sha256:<your-hash>
sudo kubeadm join 10.0.0.1:6443 \ --token <your-token> \ --discovery-token-ca-cert-hash sha256:<your-hash>
sudo kubeadm join 10.0.0.1:6443 \ --token <your-token> \ --discovery-token-ca-cert-hash sha256:<your-hash>
kubectl get nodes -o wide
kubectl get nodes -o wide
kubectl get nodes -o wide
kubectl get pods -A
kubectl get pods -A
kubectl get pods -A - 4x Raspberry Pi 4 (or Pi 5) (4GB RAM or more). With 4GB per board, there's enough headroom to run workloads on top. 8GB model is nice to have but not necessary.
- 4x microSD cards (32GB). The OS, container images, and Kubernetes binaries all live on these. 32GB gives you comfortable room.
- A multi-port USB-C charger (at least 15W per port). Each Pi 4 draws up to 15W under load. A 4-port USB-C PD GaN charger powers the whole cluster from a single wall outlet.
- 4x USB-C cables.
- An ethernet switch.
- 4x ethernet cables.
- A microSD card reader, a monitor (the Pi 4 has two micro-HDMI ports) and a USB keyboard. You'll only need the monitor and keyboard briefly during initial setup. Once SSH is configured, everything happens remotely from your laptop.
- A cluster case. Something like the UCTRONICS Raspberry Pi cluster enclosure keeps your four boards stacked neatly with proper airflow. Optional but recommended plus it looks great on a desk. - Enable SSH with public-key authentication. Generate an ed25519 key pair on your laptop (ssh-keygen -t ed25519) if you don't have one, and paste the public key here.
- Set the username. - Pi 1: k8s-control (this will be our control plane and gateway)
- Pi 2: worker-1
- Pi 3: worker-2
- Pi 4: worker-3 - Location New York
- Education NYU
- Joined Jul 2, 2017