Misadventures in Kubernetes: Creating Workers
Source: Dev.to
We built a control plane from scratch—useful but also hard. The control plane is just the brain; we still need worker nodes (the muscle) to run our containerized applications. Manually configuring each worker would be a nightmare, so this part of the series shows how to create a Golden Image from a pre‑configured machine and rapidly provision identical workers.
Creating a Golden Image
The goal is to stop repetitive manual configuration. When we set up the control node we:
- Loaded the
overlayandbr_netfilterkernel modules. - Tweaked network bridge settings for iptables.
- Disabled swap (required for
kubelet). - Installed containerd and set
systemdas the cgroup driver.
All of these prerequisites—OS tuning and Kubernetes binaries (kubeadm, kubelet, kubectl)—are baked into the k8s-seed VM. A Golden Image is a frozen snapshot of that perfect setup, enabling automated, image‑based provisioning.
Step 1: Generalize the Seed Instance
Before using k8s-seed as a template we must clear unique identifiers so each new VM gets its own identity.
-
SSH into the seed machine
gcloud compute ssh k8s-seed --zone=us-central1-a -
Clean up unique identifiers
sudo cloud-init clean --seed --logs --machine-id exit -
Stop the instance (run from your local machine)
gcloud compute instances stop k8s-seed --zone=us-central1-a
Step 2: Create the Custom Image
Create a Google Compute Engine image from the stopped seed disk.
gcloud compute images create k8s-node-image-v1 \
--source-disk=k8s-seed \
--source-disk-zone=us-central1-a \
--family=k8s-node-family
The --family flag lets you reference the “latest” version of the image later.
Step 3: Provision Worker Nodes
Spin up new workers instantly using the golden image.
gcloud compute instances create worker-1 \
--zone=us-central1-a \
--image-family=k8s-node-family \
--tags=k8s-worker
gcloud compute instances create worker-2 \
--zone=us-central1-a \
--image-family=k8s-node-family \
--tags=k8s-worker
Step 4: Join the Cluster
Workers need to register with the control plane using kubeadm join.
-
Locate your join command (saved from
kubeadm initon the control node):kubeadm join 10.128.0.x:6443 --token <token> --discovery-token-ca-cert-hash sha256:<hash> -
Run the command on each worker (example for
worker-1):gcloud compute ssh worker-1 --zone=us-central1-a sudo kubeadm join 10.128.0.x:6443 --token <token> --discovery-token-ca-cert-hash sha256:<hash> exitRepeat the same steps for
worker-2. -
Verify the nodes
kubectl get nodesBoth workers should appear with a
Readystatus.
For Worker 2: Repeat the Same Steps
gcloud compute ssh worker-2 --zone=us-central1-a
sudo kubeadm join ... # Paste your command here
exit
Step 5: Verify the Cluster
From the control plane, confirm that all nodes are registered.
kubectl get nodes
Expected output:
NAME STATUS ROLES AGE VERSION
control-plane Ready control-plane 10m v1.35.0
worker-1 Ready <none> 2m v1.35.0
worker-2 Ready <none> 1m v1.35.0
The Easy Way (GKE)
If you used Google Kubernetes Engine, the entire process could be replaced with a single command:
gcloud container clusters create k8s-easy-cluster \
--zone us-central1-a \
--num-nodes 3 \
--machine-type e2-medium
What’s Next?
Your multi‑node cluster is up, but it’s still static. If a worker crashes, you must manually replace it, and scaling requires manual provisioning and kubeadm join. Future posts may cover automating node lifecycle and scaling to achieve a truly cloud‑native, resilient cluster.
Go Deeper
- The Original Blueprint: Inspired by Kelsey Hightower’s guide, Kubernetes The Hard Way.
- Official Documentation: The authoritative source for all components, Kubernetes.io Documentation.
