Ubuntu Kubernetes Cluster: A Step-by-Step Guide

by Jhon Lennon 48 views

Hey there, fellow tech enthusiasts! Ever felt the urge to dive deep into the magical world of container orchestration? Well, you've come to the right place, guys! Today, we're going to walk through the exciting process of creating a Kubernetes cluster on Ubuntu. This isn't just about following some dry commands; we're going to break it down so it's super easy to understand and, dare I say, even fun! Kubernetes, or K8s as the cool kids call it, is a powerful open-source system for automating deployment, scaling, and management of containerized applications. And doing it on Ubuntu, a super popular and stable Linux distribution, just makes perfect sense. So, grab your favorite beverage, settle in, and let's get this cluster built!

Why Build Your Own Kubernetes Cluster?

Before we get our hands dirty, let's chat for a sec about why you might want to build your own Kubernetes cluster on Ubuntu. Sure, there are managed services out there, but there's a ton of value in setting it up yourself. Firstly, it’s an awesome learning experience. You’ll gain a deep understanding of how Kubernetes works under the hood, from networking and storage to pod scheduling and node management. This hands-on knowledge is invaluable, whether you're aiming for a career in DevOps, cloud engineering, or just want to level up your sysadmin skills. Secondly, it gives you ultimate control. You can customize every aspect of your cluster to fit your specific needs, optimize performance, and experiment with different configurations without the constraints of a managed service. Plus, let's be honest, there's a certain satisfaction that comes from building something complex and powerful from the ground up. It’s like building your own custom race car – you know every bolt and wire, and it performs exactly how you want it to. For developers, having a local or self-hosted K8s environment means faster iteration cycles, easier testing of deployments, and a more realistic staging environment before pushing to production. It empowers you to tinker, break things (in a safe environment, of course!), and learn at your own pace. So, while managed Kubernetes services are fantastic for production environments where you need reliability and scalability without the operational overhead, building your own is undeniably the best way to truly master Kubernetes. We'll be focusing on setting up a basic, functional cluster that you can expand upon. Think of this as your foundation, your launchpad into the world of K8s!

Prerequisites: What You'll Need Before We Start

Alright guys, before we jump headfirst into setting up our Kubernetes cluster on Ubuntu, let's make sure we have all our ducks in a row. Having the right prerequisites sorted will make this whole process a breeze, trust me. We're not talking about anything too crazy, just a few essential things to get us started on the right foot. First and foremost, you'll need at least two Ubuntu machines. Why two? Well, Kubernetes works on a master-worker (or control-plane-node) architecture. You need one machine to act as your control plane (the brain of the operation) and at least one to act as a worker node (where your actual applications will run). For testing and learning purposes, you can even use virtual machines (VMs) on your local machine using tools like VirtualBox or VMware. Just make sure each VM has a decent amount of RAM (at least 2GB per node is a good starting point) and is running a supported version of Ubuntu Server. We recommend Ubuntu 20.04 LTS or 22.04 LTS for stability. Ensure that these machines can communicate with each other over the network. This means they should be on the same network, or you need to configure your network settings appropriately (e.g., port forwarding if using VMs). SSH access to all your machines is also a must. You'll be running commands remotely, so make sure you can SSH into each Ubuntu server without any issues. It's also highly recommended to disable swap on all nodes. Kubernetes doesn't play nicely with swap, as it can lead to performance issues and unexpected behavior. You can disable it temporarily with sudo swapoff -a and permanently by editing the /etc/fstab file. Finally, you'll need a user account on each machine with sudo privileges. This user will be used to install necessary packages and configure the system. It’s also a good idea to set up static IP addresses for your nodes, or at least ensure they have consistent hostnames. This helps Kubernetes identify and communicate with them reliably. Don't worry if some of this sounds a bit technical; we'll guide you through each step. The key is preparation, and by ensuring these prerequisites are met, you’re setting yourself up for a much smoother Kubernetes cluster creation experience on your Ubuntu servers. Let’s get this party started!

Step 1: Preparing Your Ubuntu Nodes

Okay, team, let's get down to business and prepare our Ubuntu nodes for the Kubernetes cluster. This is a crucial step, so pay close attention! We need to make sure all our machines (both the future control plane and worker nodes) are running smoothly and are ready to host Kubernetes components. First things first, let's update our package lists and upgrade any installed packages. Log in to each of your Ubuntu machines via SSH and run these commands:

sudo apt update
sudo apt upgrade -y

This ensures you're running the latest software versions, which is always a good practice for security and stability. Next, we need to enable some kernel modules that Kubernetes relies on. These modules help manage networking and other low-level operations. We'll need to load the br_netfilter module and ensure it's loaded on boot. Run the following commands on all your nodes:

sudo modprobe overlay
sudo modprobe br_netfilter

# Persist these modules across reboots
echo 'overlay' | sudo tee -a /etc/modules-load.d/k8s.conf
echo 'br_netfilter' | sudo tee -a /etc/modules-load.d/k8s.conf

Now, for Kubernetes networking to work properly, particularly with container network interfaces (CNIs), we need to configure some sysctl parameters. These parameters ensure that traffic bridging across network interfaces is handled correctly. Run these commands on all your nodes:

sudo sysctl net.bridge.bridge-nf-call-iptables=1
sudo sysctl net.bridge.bridge-nf-call-ip6tables=1
sudo sysctl net.ipv4.ip_forward=1

# Persist these sysctl settings
echo 'net.bridge.bridge-nf-call-iptables  = 1' | sudo tee -a /etc/sysctl.d/k8s.conf
echo 'net.bridge.bridge-nf-call-ip6tables = 1' | sudo tee -a /etc/sysctl.d/k8s.conf
echo 'net.ipv4.ip_forward                 = 1' | sudo tee -a /etc/sysctl.d/k8s.conf

We also need to install container runtime. Kubernetes uses a container runtime to run containers. Containerd is a popular and robust choice. Let's install it on all your nodes:

sudo apt install -y containerd

After installing containerd, we need to configure it. This involves creating a default configuration file and enabling it. Run these commands:

sudo mkdir -p /etc/containerd
sudo containerd config default | sudo tee /etc/containerd/config.toml

# IMPORTANT: Set the SystemdCgroup to true for containerd
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml

sudo systemctl restart containerd
sudo systemctl enable containerd

Setting SystemdCgroup = true is crucial because Kubernetes relies on systemd for cgroup management. Finally, let's install kubeadm, kubelet, and kubectl. kubeadm is used to bootstrap the cluster, kubelet runs on each node and does the heavy lifting of running pods, and kubectl is the command-line tool for interacting with the cluster. We need to add the Kubernetes repository first:

# Install packages prerequisites
sudo apt install -y apt-transport-https curl

# Download the Google Cloud public signing key
curl -fsSL https://pkgs.k8s.io/core-/stable/1.28/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

# Add the Kubernetes apt repository
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core-/stable/1.28/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list

# Update package list again and install the tools
sudo apt update
sudo apt install -y kubelet kubeadm kubectl

# Hold these packages to prevent accidental upgrades
sudo apt-mark hold kubelet kubeadm kubectl

We use apt-mark hold to prevent these critical packages from being automatically upgraded when you run apt upgrade, which could break your cluster. Phew! That's a lot of preparation, but we've successfully set up our Ubuntu nodes. High five! This foundational work ensures your nodes are primed and ready for the next exciting phase: bootstrapping the Kubernetes cluster.

Step 2: Initializing the Control Plane (Master Node)

Alright guys, we've prepped our machines, and now it's time for the main event: initializing the control plane on our designated master node. This is where the magic really begins! Remember, you should only run these commands on the machine you've chosen to be your control plane. First, let’s use kubeadm init to kick things off. This command sets up the Kubernetes control plane components, like the API server, scheduler, and controller manager. We need to specify a pod network CIDR. This is an IP address range from which pods will be assigned IP addresses. A common choice is 10.244.0.0/16 if you plan to use Flannel as your CNI (which we'll discuss later). Make sure to replace YOUR_POD_CIDR with the actual CIDR if you choose differently. For now, let’s use the common one:

sudo kubeadm init --pod-network-cidr=10.244.0.0/16

This command might take a few minutes to complete. It will download necessary container images, set up etcd (the distributed key-value store that holds Kubernetes cluster data), and configure the control plane components. Once it's done, you'll see some important output. Pay close attention to this output! It contains the kubeadm join command you'll need later to connect your worker nodes to this cluster. It also provides instructions on how to configure kubectl to manage your cluster.

Here's what you should do immediately after kubeadm init finishes successfully:

  1. Configure kubectl for your user: Kubernetes commands are run using kubectl. To use it as a regular user (not root), you need to set up a config file. Run these commands:

mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown (id−u):(id -u):(id -g) $HOME/.kube/config ``` Now you should be able to run kubectl commands without sudo.

  1. Record the kubeadm join command: As mentioned, the output of kubeadm init will give you a command that looks something like this:
    kubeadm join <control-plane-ip>:6443 --token <some-token> --discovery-token-ca-cert-hash sha256:<some-hash>
    
    Copy this entire command and save it somewhere safe! You'll need it to add your worker nodes later. If you happen to lose it, don't panic! You can regenerate a token and get the discovery hash again on the control plane node using commands like kubeadm token create --print-join-command.

At this point, your control plane node is up and running, but it's not yet fully functional for scheduling pods. Why? Because we haven't installed a network plugin (Container Network Interface - CNI). Pods need a way to communicate with each other, and that's what a CNI handles. So, while your control plane is initialized, it's like having a powerful brain without any communication lines. We'll tackle that in the next step!

Step 3: Installing a Pod Network (CNI Plugin)

Alright, team, we've got our control plane humming, but our Kubernetes cluster on Ubuntu is still missing a crucial piece: a functioning network for our pods. This is where installing a pod network, or a Container Network Interface (CNI) plugin, comes in. Without a CNI, your pods won't be able to communicate with each other, which pretty much defeats the purpose of a cluster, right? There are several popular CNI plugins available, such as Flannel, Calico, Weave Net, and Cilium. For simplicity and ease of setup, Flannel is a great choice for beginners and is widely used.

Let's go ahead and install Flannel. Remember, you should be running these commands on your control plane node, and you should have kubectl configured (as we did in the previous step). First, make sure your kubectl context is set to the cluster you just initialized. You can check this with kubectl config current-context. Now, apply the Flannel manifest file. This file contains all the Kubernetes resources (like Deployments, DaemonSets, etc.) needed to run Flannel across your cluster.

kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml

This command downloads the Flannel configuration directly from its GitHub repository and applies it to your cluster. It will create the necessary pods to manage networking. This process usually takes a minute or two. Once applied, Flannel will start distributing network configurations to all nodes in your cluster, enabling pod-to-pod communication.

What's happening under the hood? Flannel essentially creates a virtual network that spans across all your nodes. It uses a backend technology (like VXLAN, which is commonly used by default) to encapsulate network traffic between pods on different nodes. Each pod gets a unique IP address from the 10.244.0.0/16 CIDR range we specified during kubeadm init. This makes it seem like all pods are on the same local network, regardless of which physical node they reside on.

After applying the Flannel manifest, you can check the status of the Flannel pods. They should be running:

kubectl get pods -n kube-flannel

You should see one kube-flannel-ds pod running on each node (initially just your control plane node). The ds stands for DaemonSet, meaning Flannel ensures one instance runs on every node.

Now, let's verify that our control plane node is ready. We can check the status of the nodes in our cluster:

kubectl get nodes

Initially, you'll likely see your control plane node listed with a STATUS of NotReady. This is because the Kubelet on the control plane node is waiting for a CNI plugin to be installed before it marks the node as ready. Once Flannel is up and running and has configured the network, you should see the status change to Ready!

NAME           STATUS   ROLES           AGE   VERSION
<your-node-name>   Ready    control-plane   5m    v1.28.x

Voilà! Your control plane is now initialized, and your cluster has a working network. You've successfully set up the core of your Kubernetes cluster on Ubuntu! But what about adding more power? That's where worker nodes come in. Let's move on to adding them!

Step 4: Adding Worker Nodes to Your Cluster

We've got a solid control plane running, and our Ubuntu Kubernetes cluster is starting to take shape! Now, it's time to beef it up by adding worker nodes. These nodes are where your actual applications (your pods) will be deployed and run. The more worker nodes you have, the more resources (CPU, RAM) your cluster has available, and the more resilient it becomes. This process is refreshingly straightforward, thanks to the kubeadm join command we saved earlier.

First, ensure that your worker nodes have also gone through the initial preparation steps (Step 1). They need the kernel modules loaded, sysctl settings applied, containerd installed and configured, and kubelet, kubeadm, and kubectl installed and held. It's critical that they are at the same software versions as your control plane node.

Now, grab the kubeadm join command that was outputted when you initialized your control plane. It looks something like this:

sudo kubeadm join <control-plane-ip>:6443 --token <some-token> --discovery-token-ca-cert-hash sha256:<some-hash>

Log in to each of your worker nodes via SSH and paste this exact command into the terminal. Then, execute it.

# Example - replace with your actual join command!
sudo kubeadm join 192.168.1.100:6443 --token abcdef.1234567890abcdef --discovery-token-ca-cert-hash sha256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef

When you run this command on a worker node, it contacts the control plane, authenticates using the token and hash, and gets configured to join the cluster. The kubelet service on the worker node will start, register itself with the control plane's API server, and become a part of your Kubernetes cluster.

What happens next?

After running the join command on a worker node, it will take a minute or two for it to fully integrate. You can verify its status by switching back to your control plane node and running the kubectl get nodes command:

kubectl get nodes

You should see your newly added worker node appear in the list. Initially, its status might be NotReady. This is perfectly normal! Just like the control plane node, the worker node needs the CNI plugin (which we installed using Flannel) to be running on it for networking to be fully functional. Since Flannel is deployed as a DaemonSet, it will automatically be deployed to your new worker node. Once the Flannel pod starts running on the worker node and networking is established, the node's status should change to Ready.

NAME           STATUS   ROLES           AGE   VERSION
<control-plane-name>   Ready    control-plane   10m   v1.28.x
<worker-node-1-name>     Ready    <none>          2m    v1.28.x
<worker-node-2-name>     Ready    <none>          1m    v1.28.x

Repeat the kubeadm join process for any additional worker nodes you want to add to your Ubuntu Kubernetes cluster. Each time, verify its status with kubectl get nodes.

Congratulations! You've now successfully added worker nodes to your Kubernetes cluster on Ubuntu and expanded its capacity. You have a multi-node cluster ready to host your containerized applications. Pretty neat, huh?

Verifying Your Kubernetes Cluster

Awesome job, everyone! We've successfully built a Kubernetes cluster on Ubuntu, initialized the control plane, installed a CNI, and even added worker nodes. But how do we know it's actually working? Let's do some quick verification of your Kubernetes cluster to make sure everything is shipshape.

First, let's check the status of all the nodes in our cluster. We already used this command, but it's the most fundamental check:

kubectl get nodes

Ensure that all your nodes (control plane and workers) are listed and have the STATUS as Ready. If any node is stuck in NotReady, double-check that the kubelet service is running on that node (sudo systemctl status kubelet) and that the CNI plugin pods (like kube-flannel-ds) are running on it as well (kubectl get pods -n kube-flannel).

Next, let's see what components are running within the kube-system namespace. This namespace holds all the core Kubernetes system components. It's a great way to see if critical services like etcd, kube-apiserver, kube-scheduler, and coredns are functioning correctly:

kubectl get pods -n kube-system

You should see pods related to coredns (for DNS resolution within the cluster), etcd, kube-apiserver, kube-controller-manager, kube-scheduler, and potentially others like kube-proxy. All these pods should be in a Running state.

To really test that our cluster can run applications, let's deploy a simple Nginx web server. We'll create a Deployment and expose it via a Service. This will demonstrate pod scheduling, service discovery, and network access.

  1. Create a Deployment: This tells Kubernetes how many replicas of our Nginx pod we want to run.

kubectl create deployment nginx-deployment --image=nginx --replicas=2

    This command creates a Deployment named `nginx-deployment` with two replicas of the Nginx container image.

2.  **Check the Deployment and Pods:** Verify that the Deployment was created and that the pods are running.
    ```bash
kubectl get deployments
kubectl get pods
You should see `nginx-deployment` listed with 2/2 available replicas, and two `nginx-deployment-...` pods in a `Running` state, likely spread across your worker nodes.
  1. Expose the Deployment with a Service: We'll create a Service to make Nginx accessible from outside the cluster. We'll use a NodePort service for simplicity, which exposes the service on a static port on each Node's IP address.

kubectl expose deployment nginx-deployment --port=80 --type=NodePort


4.  **Check the Service:** See the details of the created service, including the NodePort assigned.
    ```bash
kubectl get services
You'll see an output similar to this:
```
NAME               TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
nginx-deployment   NodePort   10.101.40.123   <none>        80:3XXXX/TCP   60s
```
Note the port mapping, specifically the `3XXXX` part. This is the port on your nodes that maps to port 80 inside the Nginx pods.
  1. Access Nginx: Now, open your web browser and navigate to http://<NodeIP>:<NodePort>, replacing <NodeIP> with the IP address of any of your nodes (control plane or worker) and <NodePort> with the 3XXXX port number you found. You should see the default Nginx welcome page!

This simple test confirms that your cluster can schedule pods, pods can communicate via services, and external traffic can reach your applications. We have successfully verified our Kubernetes cluster on Ubuntu!

Conclusion: Your Kubernetes Journey Has Begun!

And there you have it, folks! We've journeyed through the entire process of creating a Kubernetes cluster on Ubuntu, from setting up prerequisites and preparing our nodes to initializing the control plane, installing a vital CNI network plugin, adding worker nodes, and finally, verifying our creation with a test deployment. It might seem like a lot of steps, but by breaking it down, you can see that each part is manageable and logical. You now have a fully functional, albeit basic, Kubernetes cluster running on Ubuntu that you built with your own two hands! This is a massive achievement and a significant step forward in your understanding and mastery of container orchestration.

Remember, this is just the beginning. Your cluster is now a playground for experimentation. You can explore deploying more complex applications, setting up persistent storage, configuring advanced networking, integrating with CI/CD pipelines, and so much more. The possibilities are virtually endless! This hands-on experience is invaluable. It demystifies Kubernetes and builds confidence. Don't be afraid to tinker, try different configurations, and even break things (in this controlled environment, of course!) to learn how to fix them. The skills you've acquired by setting up a Kubernetes cluster on Ubuntu are highly sought after in the tech industry, paving the way for exciting career opportunities in DevOps, cloud engineering, and beyond.

Keep learning, keep experimenting, and enjoy the power and flexibility that Kubernetes brings to your application deployments. Happy orchestrating, guys!