Kubernetes has become the orchestration platform of choice in the enterprise. When you need the ability to quickly scale containerized workloads, few tools match its power and flexibility. But getting a cluster up and running from scratch can feel daunting. This guide walks you through deploying a three-node Kubernetes cluster on Ubuntu Server using kubeadm — the official bootstrapping tool.
The example topology uses three machines:
- kubemaster — 192.168.1.218 (control-plane node)
- kubenode1 — 192.168.1.219 (worker node)
- kubenode2 — 192.168.1.220 (worker node)
You can add as many worker nodes as your environment requires. Adjust IP addresses and hostnames to match your own network.
Prerequisites
- Three machines running Ubuntu Server 18.04 (fully updated)
- A user account with
sudoprivileges on each machine - Network connectivity between all three nodes
- Unique hostnames assigned to each machine (covered below)
Step 1 — Install Docker on All Three Nodes
Kubernetes requires a container runtime. On Ubuntu 18.04, docker.io is available directly from the default repositories. Run the following on every node:
sudo apt-get install docker.io -yCode language: JavaScript (javascript)
Add your current user to the docker group so you can run Docker commands without sudo:
sudo usermod -aG docker $USERCode language: PHP (php)
Log out and log back in for the group membership change to take effect. Then enable and start the Docker service:
sudo systemctl start docker
sudo systemctl enable docker
Step 2 — Add the Kubernetes Repository and Install kubeadm
Kubernetes tools are not included in Ubuntu’s default repositories. Add the official Kubernetes apt repository on every node:
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add
sudo apt-add-repository "deb http://apt.kubernetes.io/ kubernetes-xenial main"Code language: JavaScript (javascript)
Then install the three core Kubernetes utilities on every node:
sudo apt-get install kubeadm kubelet kubectlCode language: JavaScript (javascript)
- kubeadm — bootstraps and manages the cluster lifecycle
- kubelet — the node agent that runs on every node
- kubectl — the command-line client for interacting with the cluster
Step 3 — Configure Hostnames and the Hosts File
Assign a unique, descriptive hostname to each machine. Run this command on each node, substituting the appropriate hostname:
sudo hostnamectl set-hostname HOSTNAMECode language: JavaScript (javascript)
For example, on the master node:
sudo hostnamectl set-hostname kubemasterCode language: JavaScript (javascript)
Next, edit /etc/hosts on every node so that all machines can resolve each other by name:
sudo nano /etc/hosts
Append the following lines, replacing the IP addresses with your own:
192.168.1.218 kubemaster
192.168.1.219 kubenode1
192.168.1.220 kubenode2Code language: CSS (css)
Save and close the file.
Step 4 — Disable Swap on All Nodes
Kubernetes requires swap to be disabled. The kubelet will fail to start if swap is active. To disable it persistently, open /etc/fstab:
sudo nano /etc/fstab
Comment out any line that references a swap partition by prepending it with a # character. Save and close the file. Then disable swap immediately for the current session:
sudo swapoff -a
Step 5 — Initialize the Control-Plane Node
Run the following command on the master node only. The --pod-network-cidr flag specifies the IP range for the pod network. The value you use must match the requirements of the pod network add-on you choose. For Flannel, the conventional CIDR is 10.244.0.0/16:
sudo kubeadm init --pod-network-cidr=10.244.0.0/16
When initialization completes successfully, kubeadm prints a kubeadm join command at the end of its output. Copy this command — you will need it to join your worker nodes to the cluster.
Configure kubectl Access on the Master
Still on the master node, set up your user’s kubeconfig file so you can interact with the cluster using kubectl:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/configCode language: PHP (php)
Step 6 — Deploy a Pod Network (Flannel)
A pod network add-on is required before any pods — including DNS — can be scheduled. This example uses Flannel, a simple overlay network. Apply the Flannel manifest from the master node:
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.ymlCode language: JavaScript (javascript)
Wait until the Flannel pods reach a Running state before proceeding:
kubectl get pods --all-namespacesCode language: JavaScript (javascript)
Step 7 — Join the Worker Nodes to the Cluster
On each worker node (kubenode1 and kubenode2), run the kubeadm join command that was printed during the master initialization in Step 5. It will look similar to:
sudo kubeadm join 192.168.1.218:6443 --token <token> \
--discovery-token-ca-cert-hash sha256:<hash>Code language: CSS (css)
Use the exact command and token values that kubeadm init produced in your environment.
Step 8 — Verify the Cluster
Back on the master, confirm that all nodes have joined and are in the Ready state:
kubectl get nodesCode language: JavaScript (javascript)
Expected output (after a minute or two for the nodes to become ready):
NAME STATUS ROLES AGE VERSION
kubemaster Ready control-plane,master 5m v1.x.x
kubenode1 Ready <none> 2m v1.x.x
kubenode2 Ready <none> 2m v1.x.xCode language: CSS (css)
Your cluster is now operational. You can deploy workloads using kubectl apply or kubectl run.
Next Steps
- Explore managed Kubernetes offerings (Amazon EKS, Google GKE, Azure AKS) to reduce operational overhead in production environments.
- Set up the Kubernetes Dashboard for a visual overview of cluster resources.
- Investigate Helm for templated, repeatable application deployments.
- Review the official kubeadm cluster creation documentation for production hardening guidance.