It is an unspoken consensus that 2020 was a stormy year. As this new year unravels, it is a fact that painful memories were inscribed on the minds and on the hearts of a lot of people around the globe. Lessons have been learnt, opportunities have been realized but what is most important is that you are safe. As 2021 unfolds, we hope that such the dark clouds that shrouded the past year will clear and pure sunshine will be allowed to illuminate our hopes and dreams.

In this guide, we shall be focusing on Kubernetes. Yes, this platform that continues to transform how we deploy and manage business applications. Whether it is CI/CD or you prefer the manual way, Kubernetes still remains the best choice to handle, manage, scale, and orchestrate your applications. For those who do not know, Kubernetes will be deprecating dockershim in future releases which means they will not be supporting docker as the container runtime. Before you throw tantrums, it is important to note that the change will not be affecting how you build and deploy your images. Docker contains a lot of components that Kubernetes has no use of bearing in mind that they need a simple light-weight container runtime to launch and run containers.

As you have already guessed, you can continue building your images using docker and Kubernetes will pull and run them using other container run times such as containerd or CRI-O. Containerd and CRI-O are pretty lightweight and they plug in fantastically to the Container Runtime Interface specifications.

Installation pre-requisites

In order for this deployment to start and succeed, we are going to need an extra server or computer that will be used as the installation server. This machine will contain Kubespray files and will connect to your servers where kubernetes will be installed and proceed to setup Kubernetes in them. The deployment architecture is simplified by the diagram below with one master, one etcd and two worker nodes.


Make sure you generate SSH keys on the builder machine and copy your public key to the Debian 10 servers where Kubernetes will be built.

Having said all of that, we are going to install Kubernetes on Debian 10 servers using Kubespray and we shall be using containerd as the container runtime. The following steps will suffice to get your cluster ready to accommodate your apps.

Step 1: Prepare your servers

Preparing your servers is a crucial step which ensures that every aspect of the deployment runs smoothly till the very end. In this step, we shall be doing simple updates and making sure that important packages have been installed. Issue the commands below in each of your servers to kick everything off on a clean slate.

sudo apt update
sudo apt upgrade

Step 2: Clone Kubespray Git repository and add configurations

In this step, we are going to fetch Kubespray files in our local machine (the installer machine) then make the necessay configurations by choosing containerd as the container run time as well as populating the requisite files with the details of our servers (etc, masters, workers).

$ cd ~
$ git clone
Cloning into 'kubespray'...

Change to the project directory:

$ cd kubespray

This directory contains the inventory files and playbooks used to deploy Kubernetes.

Step 3: Prepare Local machine

On the Local machine where you’ll run deployment from, you need to install pip Python package manager.

curl -o
python3 --user

Step 4: Create Kubernetes Cluster inventory file and Install dependencies

The inventory is composed of 3 groups:

  • kube-node : list of kubernetes nodes where the pods will run.
  • kube-master : list of servers where kubernetes master components (apiserver, scheduler, controller) will run.
  • etcd: list of servers to compose the etcd server. You should have at least 3 servers for failover purpose.

There are also two special groups:

  • calico-rr : explained for advanced Calico networking cases
  • bastion : configure a bastion host if your nodes are not directly reachable

Create an inventory file:

cp -rfp inventory/sample inventory/mycluster

Define your inventory with your server’s IP addresses and map to correct node purpose.

$ vim inventory/mycluster/inventory.ini
master0   ansible_host= ip=
worker1   ansible_host= ip=
worker2   ansible_host= ip=

# ## configure a bastion host if your nodes are not directly reachable
# bastion ansible_host=x.x.x.x ansible_user=some_user





Add A records to /etc/hosts on your workstation.

$ sudo vim /etc/hosts master0 worker1 worker2

If your private ssh key has passphrase, save it before starting deployment.

$ eval `ssh-agent -s` && ssh-add
Agent pid 4516
Enter passphrase for /home/centos/.ssh/id_rsa: 
Identity added: /home/centos/.ssh/id_rsa (/home/centos/.ssh/id_rsa)

Install dependencies from requirements.txt

# Python 2.x
sudo pip install --user -r requirements.txt

# Python 3.x
sudo pip3 install -r requirements.txt

Confirm ansible installation.

$ ansible --version
ansible 2.9.6
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/tech/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  executable location = /usr/bin/ansible
  python version = 3.8.5 (default, Jul 28 2020, 12:59:40) [GCC 9.3.0]

Review and change parameters under inventory/mycluster/group_vars

We shall review and change parameters under inventory/mycluster/group_vars to ensure that Kubespray uses containerd.

##Change from docker to containerd at around line 176 and add the two lines below

$ vim inventory/mycluster/group_vars/k8s-cluster/k8s-cluster.yml

container_manager: containerd
etcd_deployment_type: host
kubelet_deployment_type: host

Step 5: Deploy Kubernetes Cluster with Kubespray Ansible Playbook

Now execute the playbook to deploy Production ready Kubernetes with Ansible. Please note that the target servers must have access to the Internet in order to pull images.

Start the deployment by running the command:

ansible-playbook -i inventory/mycluster/inventory.ini --become \
--user=tech --become-user=root cluster.yml

Replace “tech” with the remote user ansible will connect to the nodes as. You should not get failed task in execution.

Installation progress should look like below

kubespray progress

The very last messages will look like the screenshot shared below.

kubespray recap

Once the playbook executes to the tail end, login to the master node and check cluster status.

$ sudo kubectl cluster-info
Kubernetes master is running at

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

You can also check the nodes

$ sudo kubectl get nodes

master0   Ready    master   11m   v1.19.5
worker1   Ready    <none>   10m   v1.19.5
worker2   Ready    <none>   10m   v1.19.5

$ sudo kubectl get endpoints -n kube-system

NAME                      ENDPOINTS                                                     AGE
coredns         ,, + 3 more...   23m
kube-controller-manager   <none>                                                        27m
kube-scheduler            <none>                                                        27m

Step 6: Install Kubernetes Dashboard & Access

This is an optional step in case you do not have other options to access your Kubernetes cluster via a cool interface like Lens or VMware Octant .To get the dashboard installed, follow the detailed guide below.

How To Install Kubernetes Dashboard with NodePort

And once it is working, you will need to create an admin user to access your cluster. Use the guide below to fix that:

Create Admin User to Access Kubernetes Dashboard

If you like, you can also use Active Directory to authenticate your users using how to Authenticate Kubernetes Dashboard Users With Active Directory.

Step 7: Install Nginx-Ingress controller

In this step, we are going to include an ingress controller to help us access our services from outside the cluster. The simplest approach to enabling external access to services is using the NodePort service type. The disadvantage of NodePort is that services must use a limited range of ports (by default, in the 30000 to 32767 range), and just a single port can be mapped to a single service.

The ingress resource makes it possible to expose multiple services using a singular external endpoint, a load balancer, or both at once. Taking this approach, teams can enact host, prefix, and other rules to route traffic to defined service resources however they prefer.

We shall therefore install Nginx Ingress controller and configure it so that we can be able to access one sample application we will setup. To install Nginx Ingress controller, download the manifest below and apply it in your cluster


Then install using kubectl

sudo kubectl apply -f deploy.yaml

Confirm that the ingress controller pod is running after sometime

$ sudo kubectl get pods -n ingress-nginx
NAME                                        READY   STATUS      RESTARTS   AGE
ingress-nginx-admission-create-f7d8c        0/1     Completed   0          109m
ingress-nginx-admission-patch-4fxtb         0/1     Completed   0          109m
ingress-nginx-controller-85df779996-b9c2k   1/1     Running     0          109m

Next, we are going to deploy httpbin application then use our ingress controller to access it. Fetch httpbin as follows


Then install using kubectl

sudo kubectl apply -f httpbin.yaml

In case you would wish it to be deployed in a different namespace, simply edit the file before applying it.

Step 8: Add Ingress Rule to access your service

Until now, we have a working ingress controller and a sample deployment (httpbin) that we shall use to test how it works. Create the following Ingress resource that will target httpbin. If you read the manifest we fetched earlier, you will notice that it creates a service called “httpbin” and will be listening at port 8000. Armed with that information, let us create an Ingress thus:

$ vim httpbin-ingress.yaml

kind: Ingress
  name: httpbin-ingress
  namespace: default
  annotations: /
    - host:
          - path: /
            pathType: Prefix
                name: httpbin
                  number: 8000

Save the file then apply it in your cluster. Note that “” must be resolving to the IP of the ingress as shown below.

$ sudo kubectl apply -f httpbin-ingress.yaml configured

Then confirm that the ingress was created successfully

$ sudo kubectl get ing
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use Ingress
NAME              CLASS    HOSTS                           ADDRESS          PORTS   AGE
approot           <none>   80      168m
httpbin-ingress   <none>   80      108m

What is happening here is that, any traffic that will come from “” root url, it will be routed to the httpbin service automatically. Is that not pretty!

What else are we left to do than to test if we can reach our serivice? First, let us investigate how our Ingress Controller service looks like.

$ sudo kubectl get svc -n ingress-nginx
NAME                                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller             NodePort    <none>        80:30242/TCP,443:31635/TCP   123m       
ingress-nginx-controller-admission   ClusterIP   <none>        443/TCP                      123m 

As you will notice, our “ingress-nginx-controller” is exposed via NodePort. That is very good information because we can be really frustrated while accessing our applications in other ways. With that in mind, let us now open up our browser, and point it to our application at // And ensure that port 30242 has been opened in “” node’s firewall. You should see something eye popping as shown below:

kubespray httpbin ingress


Kubespray makes the deployment of Kubernetes a cinch. Thanks to the team that developed the playbooks involved in achieving this complex deployment, we now have a ready platform just waiting for your applications that will serve the world.

In case you have a bigger cluster you intend to setup, simply place the various components (etcd, master, workers etc) in the deployment scripts and Kubespray will handle the rest. May your year flourish, your endeavor bear good fruits and your investments pay off. Let us face it with fortitude, with laughter, hard work and grace.

Other guides you might enjoy:

How to Install Active Directory Domain Services in Windows Server 2019

Monitor Docker Containers and Kubernetes using Weave Scope

Kubectl Cheat Sheet for Kubernetes Admins & CKA Exam Prep

Send Logs to Splunk on Kubernetes using Splunk Forwarder

How To Ship Kubernetes Logs to External Elasticsearch

Persistent Storage for Kubernetes with Ceph RBD

Your support is our everlasting motivation,
that cup of coffee is what keeps us going!

As we continue to grow, we would wish to reach and impact more people who visit and take advantage of the guides we have on our blog. This is a big task for us and we are so far extremely grateful for the kind people who have shown amazing support for our work over the time we have been online.

Thank You for your support as we work to give you the best of guides and articles. Click below to buy us a coffee.


  1. A tip for users that utilize users with sudo privileges requiring a password, which is more secure. Add a -K or –ask-become-pass when initiated the playbook execution or it will fail.


Please enter your comment!
Please enter your name here