Self hosted Kubernetes cluster on bare metal

January 17, 2019
docker kubernetes

Self hosted Kubernetes cluster on bare metal

This post aims to show how to build a self hosted Kubernetes cluster composed from a master node and 2 or more worker nodes.

Also, this post assumes that all machines are running Ubuntu 16.04.5 LTS version, however most of the commands may run on older or newer versions.

Install Docker

Foremost, we must install docker on every node. There are 2 ways to install Docker: using Official Ubuntu Repositories and the Official Docker Way. I usually follow the installation steps provided in the official Docker documentation rather from Ubuntu repositories, as it is always possible to install a very specific version or the very latest version.

sudo apt-get install -y \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg2 \

curl -fsSL | sudo apt-key add -

sudo add-apt-repository \
   "deb [arch=amd64] \
   $(lsb_release -cs) \

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

Add the current user to the docker group in order to run docker commands as non-root user. Don’t forget to log out and log back in order this to have effect.

sudo groupadd docker
sudo usermod -aG docker $USER
sudo systemctl enable docker

Install Kubernetes dependencies

These steps must be performed on all nodes as well. They will install the following dependencies:

Therefore run the following:

curl -s | sudo apt-key add

sudo apt-add-repository "deb kubernetes-xenial main"

sudo apt-get update
sudo apt-get -y install kubeadm kubelet kubectl

Disable swap

Kubernetes aims for performance by utilizing resources as much as possible and it does not need swap as it will slow things down, hence we need to disable swap.

sudo swapoff -a

Initialize master node

We will be initializing the k8s master node from a config file. Save the below content in a file named kubeadm.yaml and replace the with the IP address of master node. The config file is kept as simple as possible and its explained in details.

kind: InitConfiguration
  advertiseAddress: # IP address the kubeapi server will run on
  bindPort: 6443 # port the kubeapi server will listen on
kind: ClusterConfiguration
  - <master_node_ip> # adds and extra name for API server and issues a certificate for it
controlPlaneEndpoint: <master_node_ip>:6443 # sets the endpoint that nodes use to communicate within the cluster

Having the config file, we can now initialize the master node.

kubeadm init --config=kubeadm.yaml

If the master node has been initialized successfully, you should see an output of the following:


Your Kubernetes master has initialized successfully!

To start using your cluster, you need to run (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 addon options listed at:

You can now join any number of machines by running the following on each node
as root:

  kubeadm join <master_node_ip>:6443 --token <token> --discovery-token-ca-cert-hash sha256:<hash>

As the installer suggests, run below commands in order to point kubectl to issue commands against the newly created cluster.

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

Install a CNI

Container Network Interface defines a specification on how to configure network interfaces for Linux containers. There are a few libraries that abide this (e.g. calico, flannel, weave) and they act as a bridge for communication between nodes within the Kubernetes cluster. In this case we’ll go with calico.

On the master node run the following:

kubectl apply -f
kubectl apply -f

Depending on the environment it may take some time to set up everything. Verify that the network has built up without errors and that all pods are running with:

kubectl get pods --all-namespaces

Join Kubernetes cluster

To join the Kubernetes cluster, on every node that will act as an worker run the kubeadm join command alongside the tokens, that was returned when the master node was initialized.

sudo su
kubeadm join <master_node_ip>:6443 --token <token> --discovery-token-ca-cert-hash sha256:<hash>

Great, now the worker nodes joined the cluster and we have a fully functional Kubernetes cluster. To double check that run:

kubectl get nodes

that should return something similar to this:

master       Ready    master   4m    v1.13.2
worker001    Ready    <none>   2m    v1.13.2
worker002    Ready    <none>   2m    v1.13.2
comments powered by Disqus