How To Create a Kubernetes Cluster on Ubuntu 18.04

What is Kubernetes

In this tutorial, we are going to start talking about orchestration tools, and we’re going to begin specifically with Kubernetes. Kubernetes is a container orchestration tool, and what a container orchestration tool does is it allows you to easily build and manage your container infrastructure and automation. So when we have a lot of containers and we want to do different automation around those containers, we want to deploy containers, we want to spin up containers on various hosts. Orchestration tools allow us to build automation around all of that and do it much more easily than if we were doing it all manually. So if we want to do things like self healing applications, that is applications that automatically correct problems as they arise or automated scaling where we spin up additional containers in response to real time data about the usage of our applications or if we want to do things like easy automated deployments than we definitely want to look into an orchestration tool like Kubernetes. Kubernetes is currently the industry leading container orchestration tool. So it’s the most popular one. It has a huge number of features, and it is a very powerful tool to enable you to really leverage the benefits of containers with all of the automation that they enable. If you want to learn more about Kubernetes, feel free to check out the official Kubernetes documentation at Kubernetes.io. Now we’re going to dive right in and get hands on with Kubernetes by building a Kubernetes cluster.

Installing Docker

The first step in setting up a new cluster is to install a container runtime such as Docker. We will be installing Docker on our three servers in preparation for standing up a Kubernetes cluster. After completing this tutorial, you should have three servers, all with Docker up and running.

Here are the commands we are going to use to setup docker:

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"

sudo apt-get update

sudo apt-get install -y docker-ce=18.06.1~ce~3-0~ubuntu

$ sudo apt-mark hold docker-ce
docker-ce set on hold.

You can verify that docker is working by running this command on all servers:

$ sudo docker version
Client:
 Version:           18.06.1-ce
 API version:       1.38
 Go version:        go1.10.3
 Git commit:        e68fc7a
 Built:             Tue Aug 21 17:24:51 2018
 OS/Arch:           linux/amd64
 Experimental:      false

Server:
 Engine:
  Version:          18.06.1-ce
  API version:      1.38 (minimum version 1.12)
  Go version:       go1.10.3
  Git commit:       e68fc7a
  Built:            Tue Aug 21 17:23:15 2018
  OS/Arch:          linux/amd64
  Experimental:     false

Installing Kubeadm, Kubelet, and Kubectl

Now that Docker is installed, we are ready to install the Kubernetes components. In this lesson, I will guide you through the process of installing Kubeadm, Kubelet, and Kubectl on all three playground servers. After completing this lesson, you should be ready for the next step, which is to bootstrap the cluster.

Here are the commands used to install the Kubernetes components in this lesson. Run these on all three servers.

Kubeadm: This is a tool which automates a large portion of the process of setting up a cluster. It will make our job much easier.
Kubelet: The essential conponent of Kubernetes that handles running containers on a node. Every server that will be running containers needs kubelet.
Kubectl: Command-line tool for interacting with the cluster once it is up. I will use this to manage the cluster.

NOTE: There are some issues being reported when installing version 1.12.2-00 from the Kubernetes ubuntu repositories. You can work around this by using version 1.52.7-00 for kubelet, kubeadm, and kubectl.

$ curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
OK

$ cat << EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
> deb https://apt.kubernetes.io/ kubernetes-xenial main
> EOF

sudo apt-get update

sudo apt-get install -y kubelet=1.15.7-00 kubeadm=1.15.7-00 kubectl=1.15.7-00

sudo apt-mark hold kubelet kubeadm kubectl

After installing these components, verify that Kubeadm is working by getting the version info.

$ kubeadm version
kubeadm version: &version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.7", GitCommit:"6c143d35bb11d74970e7bc0b6c45b6bfdffc0bd4", GitTreeState:"clean", BuildDate:"2019-12-11T12:40:15Z", GoVersion:"go1.12.12", Compiler:"gc", Platform:"linux/amd64"}

$ systemctl status kubelet
● kubelet.service - kubelet: The Kubernetes Node Agent
   Loaded: loaded (/lib/systemd/system/kubelet.service; enabled; vendor preset: enabled)
  Drop-In: /etc/systemd/system/kubelet.service.d
           └─10-kubeadm.conf
   Active: active (running) since Sat 2020-09-12 22:41:06 UTC; 2h 9min ago
     Docs: https://kubernetes.io/docs/home/
 Main PID: 13315 (kubelet)
    Tasks: 17 (limit: 2313)
   CGroup: /system.slice/kubelet.service
           └─13315 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.co

Making Active the Cluster

Now we are ready to get a real Kubernetes cluster up and running! In this lesson, we will bootstrap the cluster on the Kube master node. Then, we will join each of the two worker nodes to the cluster, forming an actual multi-node Kubernetes cluster.

On the Kube master node, initialize the cluster:

$ sudo kubeadm init --pod-network-cidr=10.244.0.0/16
I0912 22:40:22.039846   12698 version.go:248] remote version is much newer: v1.19.1; falling back to: stable-1.15
[init] Using Kubernetes version: v1.15.12
[preflight] Running pre-flight checks
---
Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 172.31.24.191:6443 --token ei5f3h.ffmlt3ube5qb2b76 \
    --discovery-token-ca-cert-hash sha256:45252cd1ebf00694e5b9f0084363702741d6ed6f1bad026b3ef37df393254d39

That command may take a few minutes to complete. When it is done, set up the local kubeconfig:

$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config

Verify that the cluster is responsive and that Kubectl is working. You should get Server Version as well as Client Version. It should look something like this:

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.7", GitCommit:"6c143d35bb11d74970e7bc0b6c45b6bfdffc0bd4", GitTreeState:"clean", BuildDate:"2019-12-11T12:42:56Z", GoVersion:"go1.12.12", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.12", GitCommit:"e2a822d9f3c2fdb5c9bfbe64313cf9f657f0a725", GitTreeState:"clean", BuildDate:"2020-05-06T05:09:48Z", GoVersion:"go1.12.17", Compiler:"gc", Platform:"linux/amd64"}

$ kubectl get nodes
NAME     STATUS     ROLES    AGE     VERSION
master   NotReady   master   8m48s   v1.15.7

The kubeadm init command should output a kubeadm join command containing a token and hash. Copy that command and run it with sudo on both worker nodes. It should look something like this:

Worker Node 1

$ sudo kubeadm join 172.31.24.191:6443 --token ei5f3h.ffmlt3ube5qb2b76 \
>     --discovery-token-ca-cert-hash sha256:45252cd1ebf00694e5b9f0084363702741d6ed6f1bad026b3ef37df393254d39
[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml'
[kubelet-start] Downloading configuration for the kubelet from the "kubelet-config-1.15" ConfigMap in the kube-system namespace
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Activating the kubelet service
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...

This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the control-plane to see this node join the cluster.

Worker Node 2

$ sudo kubeadm join 172.31.24.191:6443 --token ei5f3h.ffmlt3ube5qb2b76 \
>     --discovery-token-ca-cert-hash sha256:45252cd1ebf00694e5b9f0084363702741d6ed6f1bad026b3ef37df393254d39
[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml'
[kubelet-start] Downloading configuration for the kubelet from the "kubelet-config-1.15" ConfigMap in the kube-system namespace
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Activating the kubelet service
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...

This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the control-plane to see this node join the cluster.

Verifying on Master

Verify that all nodes have successfully joined the cluster:

$ kubectl get nodes
NAME     STATUS     ROLES    AGE     VERSION
master   NotReady   master   12m     v1.15.7
node1    NotReady   <none>   3m1s    v1.15.7
node2    NotReady   <none>   2m20s   v1.15.7

Configuring Networking with Flannel

Once the Kubernetes cluster is set up, we still need to configure cluster networking in order to make the cluster fully functional. In this lesson, we will walk through the process of configuring a cluster network using Flannel. You can find more information on Flannel at the official site: https://coreos.com/flannel/docs/latest/.

On all three nodes, run the following:

$ echo "net.bridge.bridge-nf-call-iptables=1" | sudo tee -a /etc/sysctl.conf
net.bridge.bridge-nf-call-iptables=1

$ sudo sysctl -p
net.bridge.bridge-nf-call-iptables = 1

Install Flannel in the cluster by running this only on the Master node:

$ kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/bc79dd1505b0c8681ece4de4c0d86c5cd2643275/Documentation/kube-flannel.yml
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
serviceaccount/flannel created
configmap/kube-flannel-cfg created
daemonset.extensions/kube-flannel-ds-amd64 created
daemonset.extensions/kube-flannel-ds-arm64 created
daemonset.extensions/kube-flannel-ds-arm created
daemonset.extensions/kube-flannel-ds-ppc64le created
daemonset.extensions/kube-flannel-ds-s390x created

Verify that all the nodes now have a STATUS of Ready:

$ kubectl get nodes

You should see all three of your servers listed, and all should have a STATUS of Ready. It should look something like this:

$ kubectl get nodes
NAME     STATUS   ROLES    AGE   VERSION
master   Ready    master   33m   v1.15.7
node1    Ready    <none>   24m   v1.15.7
node2    Ready    <none>   23m   v1.15.7

Note:* It may take a few moments for all nodes to enter the Ready status, so if they are not all Ready, wait a few moments and try again.

It is also a good idea to verify that the Flannel pods are up and running. Run this command to get a list of system pods:

$ kubectl get pods -n kube-system
NAME                             READY   STATUS    RESTARTS   AGE
coredns-5d4dd4b4db-tpqjx         1/1     Running   0          35m
coredns-5d4dd4b4db-wk8qs         1/1     Running   0          35m
etcd-master                      1/1     Running   0          34m
kube-apiserver-master            1/1     Running   0          34m
kube-controller-manager-master   1/1     Running   0          34m
kube-flannel-ds-amd64-5dm6b      1/1     Running   0          2m26s
kube-flannel-ds-amd64-cxll8      1/1     Running   0          2m26s
kube-flannel-ds-amd64-s8b94      1/1     Running   0          2m26s
kube-proxy-fw9fm                 1/1     Running   0          35m
kube-proxy-ncgsj                 1/1     Running   0          25m
kube-proxy-swx4g                 1/1     Running   0          25m
kube-scheduler-master            1/1     Running   0          34m

You should have three pods with flannel in the name, and all three should have a status of Running.

Conclusion

That’s all you need to know about creating Kubernetes cluster on Ubuntu 18.04 server. There are more tutorials that will go into deeper explanation of using Kubernetes thoroughly, but this is a simple article to just get familiar with creating Kubernetes cluster on linux based server.

Avatar photo

Asif Khan

Responsible and proactive professional with more than 14 years of experience in IT systems, open source software applications, DevOps, Linux systems, and cloud operations. My main goals are to automate things, keep them safe, and make sure they are strong. I am very good at planning and building the infrastructure for services that people really want. I was drawn to the fast-paced world of cloud computing because it has resources that can be scaled up or down as needed. One of my best skills is being able to use a lot of different DevOps tools to set up, release management, and microservices ecosystems, as well as for provisioning, orchestration, and configuration management.