Updated: Getting Started with Installing Kubernetes

Let’s get you started on your Kubernetes journey with installing Kubernetes and creating a cluster in virtual machines.

Kubernetes is a distributed system, you will be creating a cluster which will have a master node that is in charge of all operations in your cluster. In this walkthrough we’ll create three workers which will run our applications. This cluster topology is, by no means, production ready. If you’re looking for production cluster builds check out Kubernetes documentation. [Here][1] and [here][2]. The primary components that need high availability in a Kubernetes cluster are the [API Server][3] which controls the state of the cluster and the [etcd][4] database which persists the state of the cluster. You can learn more about Kubernetes cluster components [here][5]. If you want to dive into Kubernetes more check out my Pluralsight Courses [here][6]! Where I have a dedicated course on [Installation and Configuration][7].

In our demonstration here, the master is where the API Server, etcd, and the other control plan functions will live. The workers/nodes, will be joined to the cluster and run our application workloads. 

Get your infrastructure sorted

I’m using 4 Ubuntu Virtual machines in VMware Fusion on my Mac. Each with 2vCPUs and 2GB of RAM running Ubuntu 16.04.5. Ubuntu 18 requires a slightly different install. Documented [here][8]. In there you will add the Docker repository, then install Docker from there. The instructions below get Docker from Ubuntu’s repository. You will also need to disable the swap on any system which you will run the kubelet, which in our case is all systems. To do so you need to turn swap off with sudo swapoff -a and edit /etc/fstab removing or commenting out the swap volume entry. 

  • c1-master1 – 172.16.94.15
  • c1-node1 – DHCP
  • c1-node2 – DHCP
  • c1-node3 – DHCP

Ensure that each host has a unique name and that all nodes can have network reachability between each other. Take note of the IPs, because you will need to log into each node with SSH. If you need assistance getting your environment ready, check out my training on Pluralsight to get you started [here][9]! I have courses on installation, command line basics all the way up through advanced topics on networking and performance.

Overview of the cluster creation process

  • Install Kubernetes packages on all nodes

  • Add Kubernetes’ apt repositories

  • Install the required software packages for Kubernetes

  • Download deployment files for your Pod Network

  • Create a Kubernetes cluster on the Master

  • We’re going to use a utility called kubeadm to create our cluster with a basic configuration

  • Install a Pod Network

  • Join our three worker nodes to our cluster

Install Kubernetes Packages

Let’s start off with installing the required Kubernetes packages on to all of the nodes in our system. This is going to require logging into each server via SSH (or console), adding the Kubernetes apt repositories and installing the required packages. Perform the following tasks on ALL nodes in your cluster, the master and the three workers. If you add more nodes, you will need to install these packages on those nodes too. Add the gpg key for the Kubernetes apt repository to your local system

curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
Add the Kubernetes apt repository to your local repository locations
cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
Next, we’ll update our apt package lists
sudo apt-get update
Install the required packages
sudo apt-get install -y docker.io kubelet kubeadm kubectl
Then we need to tell apt to not update these packages. 
sudo apt-mark hold docker.io kubelet kubeadm kubectl
With Docker installed, we need to make one adjustment to its configuration changing the cgroup driver to systemd.
sudo bash -c 'cat > /etc/docker/daemon.json <<EOF
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2"
}
EOF'
With that file created, go ahead and reload the systemd configuration and restart the docker daemon.
sudo systemctl daemon-reload
sudo systemctl restart docker
Here’s what you just installed
  • kubelet – On each node in the cluster, this is in charge of starting and stopping pods in response to the state defined on the API Server on the master 
  • kubeadm – Primary command line utility for creating your cluster
  • kubectl – Primary command line utility for working with your cluster
  • docker – Remember, that Kubernetes is a container orchestrator so we’ll need a container runtime to run your containers. We’re using Docker. You can use other container runtimes if required

Download the YAML files for your Pod Network

Now, only on the Master, let’s download the YAML deployment file for your Pod network and get our cluster created. Networking in Kubernetes is different than what you’d expect. For Pods to be on different nodes to be able to communicate with each other on the same IP network, you’ll want to create a Pod network. Which essentially is an overlay network that gives you a uniform address space for Pods to operate in. The decision of which Pod network to use, or even if you need one is very dependent on your local or cloud infrastructure. For this demo, I’m going to use the Calico Pod network overlay. The code below will download the Pod manifest in YAML and we’ll deploy those into our cluster. This creates a DaemonSet. A DaemonSet is a Kubernetes Controller that will start the specified Pod on all or some of the nodes in the cluster. In this case, the Calico network Pod will be deployed on all nodes in our cluster. So as we join nodes, you might see some delay in nodes becoming Ready…this is because the container is being pulled and started on the node.
 
Download the YAML for the Pod network
wget https://docs.projectcalico.org/master/manifests/calico.yaml
If you need to change the address of your Pod network edit calico.yaml, look for the name: CALICO_IPV4POOL_CIDR and set the value: to your specified CIDR range. It’s 192.168.0.0/16 by default. 

Creating a Kubernetes Cluster

Now we’re ready to create our Kubernetes cluster, we’re going to use kubeadm to help us get this done. It’s a community-based tool that does a lot of the heavy lifting for you.
 
To create a cluster do this, here we’re specifying a CIDR range to match that in our calico.yaml file.
sudo kubeadm init --pod-network-cidr=192.168.0.0/16
What’s happening behind the scenes with kubeadm init:
  • Creates a certificate authority – Kubernetes uses certificates to secure communication between components, verify the identity of Nodes in the cluster and authenticate users.
  • Creates kubeconfig files – On the Master, this will create configuration files for various Kubernetes cluster components
  • Pulls Control Plane container images – the services implementing the cluster components are deployed into the cluster as containers. Very cool! You can, of course, run these as local system daemons on the hosts, but Kubernetes suggests keeping them inside containers
  • Bootstraps the Control Plane Pods – starts up the pods and creates static manifests on the master start automatically when the master node starts up
  • Taints the Master to just system pods – this means the master will run (schedule) only system Pods, not user Pods. This is ideal for production. In testing, you may want to untaint the master, you’ll really want to do this if you’re running a single node cluster. See this link for details on that.
  • Generates a bootstrap token – used to join worker nodes to the cluster
  • Starts any add-ons – the most common add-ons are the DNS pod and the master’s kube-proxy
If you see this output, you’re good to go! Keep that join command handy. We’ll need it in a second.
[init] Using Kubernetes version: v1.16.1
[preflight] Running pre-flight checks
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
…output omitted… 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: /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.16.94.20:6443 --token czpkcj.ncl6p005orlie95h \ --discovery-token-ca-cert-hash sha256:3e21bb225c0986330ba11dd37c51fcd6542928964832705e13b84354872270bd

The output from your cluster creation is very important, it’s going to give you the code needed to access your cluster, the code needed to create your Pod network and also the code needed to join worker nodes to your cluster (just go ahead and copy this into a text file right now). Let’s go through each of those together.

Configuring your cluster for access from the Master node as a non-privileged user

This will allow you to log into your system with a regular account and administer your cluster.

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

Create your Pod network

Now that your cluster is created, you can deploy the YAML files for your Pod network. You must do this prior to adding more nodes to your cluster and certainly before starting any Pods on those nodes. We are going to use kubectl apply -f calico.yaml to deploy the Pod network from the YAML manifest we downloaded earlier. 

kubectl apply -f calico.yaml
configmap/calico-config created customresourcedefinition.apiextensions.k8s.io/felixconfigurations.crd.projectcalico.org created customresourcedefinition.apiextensions.k8s.io/ipamblocks.crd.projectcalico.org created customresourcedefinition.apiextensions.k8s.io/blockaffinities.crd.projectcalico.org created customresourcedefinition.apiextensions.k8s.io/ipamhandles.crd.projectcalico.org created customresourcedefinition.apiextensions.k8s.io/ipamconfigs.crd.projectcalico.org created customresourcedefinition.apiextensions.k8s.io/bgppeers.crd.projectcalico.org created customresourcedefinition.apiextensions.k8s.io/bgpconfigurations.crd.projectcalico.org created customresourcedefinition.apiextensions.k8s.io/ippools.crd.projectcalico.org created customresourcedefinition.apiextensions.k8s.io/hostendpoints.crd.projectcalico.org created customresourcedefinition.apiextensions.k8s.io/clusterinformations.crd.projectcalico.org created customresourcedefinition.apiextensions.k8s.io/globalnetworkpolicies.crd.projectcalico.org created customresourcedefinition.apiextensions.k8s.io/globalnetworksets.crd.projectcalico.org created customresourcedefinition.apiextensions.k8s.io/networkpolicies.crd.projectcalico.org created customresourcedefinition.apiextensions.k8s.io/networksets.crd.projectcalico.org created clusterrole.rbac.authorization.k8s.io/calico-kube-controllers created clusterrolebinding.rbac.authorization.k8s.io/calico-kube-controllers created clusterrole.rbac.authorization.k8s.io/calico-node created clusterrolebinding.rbac.authorization.k8s.io/calico-node created daemonset.apps/calico-node created serviceaccount/calico-node created deployment.apps/calico-kube-controllers created serviceaccount/calico-kube-controllers created
Before moving forward, check for the creation of the Calico pods and also the DNS pods, once these are created and the STATUS is Running then you can proceed. In this output here you can also see the other components of your Kubernetes cluster. You see the Pods running etcd, API Server, the Controller Manager, kube-proxy and the Scheduler.
kubectl get pods --all-namespaces
NAMESPACE     NAME                                      READY   STATUS    RESTARTS   AGE
kube-system   calico-kube-controllers-7594bb948-4mgqd   1/1     Running   0          2m58s
kube-system   calico-node-qpcv7                         1/1     Running   0          2m58s
kube-system   coredns-5644d7b6d9-2lxgt                  1/1     Running   0          3m42s
kube-system   coredns-5644d7b6d9-g5tfc                  1/1     Running   0          3m42s
kube-system   etcd-c2-master1                           1/1     Running   0          2m50s
kube-system   kube-apiserver-c2-master1                 1/1     Running   0          2m41s
kube-system   kube-controller-manager-c2-master1        1/1     Running   0          3m5s
kube-system   kube-proxy-d2c6s                          1/1     Running   0          3m42s
kube-system   kube-scheduler-c2-master1                 1/1     Running   0          2m44s

Joining worker nodes to your cluster

Now on each of the worker nodes, let’s use kubeadm join to join the worker nodes to the cluster. Go back to the output of kubeadm init and copy the string from that output be sure to put a sudo on the front before you do this on each node. The process below is called a TLS bootstrap. This securely joins the node to the cluster over TLS and authenticates the host with server certificates.
sudo kubeadm join 172.16.94.20:6443 \
>     --token czpkcj.ncl6p005orlie95h \
>     --discovery-token-ca-cert-hash sha256:3e21bb225c0986330ba11dd37c51fcd6542928964832705e13b84354872270bd
[sudo] password for aen:
[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.16" 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.

If you didn’t keep the token or the CA Cert Hash in the earlier steps, go back to the master and run these commands. Also note, that join token is only valid for 24 hours. 
 
To get the current join token
kubeadm token list
To get the CA Cert Hash
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'
Back on the master, check on the status of your nodes joining the cluster. These nodes are currently NotReady, behind the scenes they’re pulling the Calico Pods and setting up the Pod network.
kubectl get nodes
NAME         STATUS     ROLES    AGE   VERSION
c1-master1   Ready      master   11m   v1.16.1
c1-node1     NotReady   <none>   63s   v1.16.1
c1-node2 NotReady <none> 57s v1.16.1
C1-node3 NotReady <none> 33s v1.16.1
And here we are with a fully functional Kubernetes cluster! All nodes joined and Ready.
kubectl get nodes
NAME         STATUS   ROLES    AGE     VERSION
c1-master1   Ready    master   12m     v1.16.1
c1-node1     Ready    <none>   3m04s   v1.16.1
c1-node2 Ready <none> 2m31s v1.16.1
C1-node3 Ready <none> 1m28s v1.16.1
Please feel free to contact me with any questions regarding Kubernetes, Linux and other SQL Server related issues at: aen@centinosystems.com 
[1]: https://kubernetes.io/docs/setup/independent/high-availability/ [2]: https://kubernetes.io/docs/setup/independent/setup-ha-etcd-with-kubeadm/ [3]: https://kubernetes.io/docs/concepts/overview/components/#kube-apiserver [4]: https://kubernetes.io/docs/concepts/overview/components/#etcd [5]: https://kubernetes.io/docs/concepts/overview/components/ [6]: https://www.pluralsight.com/profile/author/anthony-nocentino [7]: https://www.pluralsight.com/library/courses/kubernetes-installation-configuration-fundamentals/table-of-contents [8]: https://docs.docker.com/v17.09/engine/installation/linux/docker-ce/ubuntu/#install-using-the-repository [9]: http://www.centinosystems.com/blog/sql/my-current-training-courses-on-pluralsight/