By having a Highly Available Kubernetes Pi Cluster, you will have full control over your production grade environment on-premise

HA Kubernetes Pi Cluster II

(Total Setup Time: 20 mins)


In this Part 2 guide, I will configure the HA Kubernetes Cluster using the previous external etcd setup.


Installing Docker

(5 mins)


Firstly, installs docker container runtimes to all master nodes:

sudo apt install docker.io
sudo usermod -aG docker ubuntu
su - ubuntu
sudo systemctl enable docker.service


Secondly, sets up the docker daemon:

# Run su commnad as root
sudo su -

# Set up the Docker daemon
cat > /etc/docker/daemon.json <<EOF
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  "storage-driver": "overlay2"


Thirdly, enables and restarts Docker

systemctl daemon-reload
systemctl enable docker
systemctl restart docker


Installing kudeadm

(10 mins)


Starts by adding Kubernetes repository to all master nodes:

sudo apt-get update && sudo apt-get install -y apt-transport-https curl

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

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


Next, installs kubeadm:

sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
sudo reboot


Initializing Kubernetes Master Nodes

(5 mins)


First, prepares the certs by following kubeadm HA setup:

sudo mkdir -p /etc/kubernetes/pki/etcd/
sudo cp /srv/etcd-certs/cacert.pem /etc/kubernetes/pki/etcd/
sudo cp /srv/etcd-certs/etcd.pem /etc/kubernetes/pki
sudo cp /srv/etcd-certs/etcd-key.pem /etc/kubernetes/pki


Second, inserts the following into kubeadm-config.yaml:

apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
kubernetesVersion: stable
controlPlaneEndpoint: "cluster-endpoint:6443"
    caFile: /etc/kubernetes/pki/etcd/cacert.pem
    certFile: /etc/kubernetes/pki/etcd.pem
    keyFile: /etc/kubernetes/pki/etcd-key.pem

This is my /etc/hosts for all master nodes:

# cluster-endpoint points to first master node localhost master1 master2 master3 cluster-endpoint


Third, initializes the master node by:

sudo kubeadm init --config kubeadm-config.yaml --upload-certs

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


Fourth, installs Calio from the list of networking addons:

curl https://docs.projectcalico.org/manifests/calico-etcd.yaml -o calico-etcd.yaml


Fifth, prepares TLS for calico by following config options:

cat /srv/etcd-certs/cacert.pem | base64 -w 0
cat /srv/etcd-certs/etcd.pem | base64 -w 0
sudo cat /srv/etcd-certs/etcd-key.pem | base64 -w 0

# Searches for the Secret section in calico.yaml and paste cat output from the cacert.pem, etcd.pem and etcd-key.pem
apiVersion: v1
kind: Secret
type: Opaque
  name: calico-etcd-secrets
  namespace: kube-system
  # Populate the following files with etcd TLS configuration if desired, but leave blank if
  # not using TLS for etcd.
  # This self-hosted install expects three files with the following names.  The values
  # should be base64 encoded strings of the entire contents of each file.
  etcd-key: LS0tLS1CRUdJTiB...VZBVEUgS0VZLS0tLS0=
  etcd-cert: LS0tLS1...ElGSUNBVEUtLS0tLQ==
  etcd-ca: LS0tLS1CRUdJTiBD...JRklDQVRFLS0tLS0=
# Source: calico/templates/calico-config.yaml
# This ConfigMap is used to configure a self-hosted Calico installation.
kind: ConfigMap
apiVersion: v1
  name: calico-config
  namespace: kube-system
  # Configure this with the location of your etcd cluster.
  etcd_endpoints: "https://cluster-endpoint:2379"
  # If you're using TLS enabled etcd uncomment the following.
  # You must also populate the Secret below with these files.
  etcd_ca: "/calico-secrets/etcd-ca"
  etcd_cert: "/calico-secrets/etcd-cert"
  etcd_key: "/calico-secrets/etcd-key"
kubectl apply -f calico.yaml
watch kubectl get pods --all-namespaces
kubectl get nodes -o wide


Lastly, runs this on the other 2 master nodes:

sudo kubeadm join cluster-endpoint:6443 --token puuck4.kjgqjif1xyy2397f \
    --discovery-token-ca-cert-hash sha256:50bb31b6f26ee6e98228f098fb9f50eaf7f1db67a011c90d6c764f7331cb90e1 \
    --control-plane --certificate-key 28ad20c49a7c6b8d06b2faa305fdf0c98d9763b932b929deb4704d57ec8ff959


This is my modified /etc/hosts:

# cluster-endpoint points to the loadbalancer IP localhost master1 master2 master3 cluster-endpoint


And that’s all to it! You now own a Highly Available Kubernetes Pi Cluster!



The connection to the server localhost:8080 was refused – did you specify the right host or port?

After kubeadm init, you need to run these commands:

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


Secret “kubeadm-certs” was not found in the “kube-system” Namespace. This Secret might have expired

If the certificate-key expires, generates a new one as below:

sudo kubeadm init phase upload-certs --upload-certs --config kubeadm-config.yaml


network: stat /var/lib/calico/nodename: no such file or directory

After resetting kubeadm, my kubernetes cluster runs successfully

sudo kubeadm reset
sudo rm -rf /etc/cni/net.d
sudo iptables --flush
sudo ipvsadm --clear
rm $HOME/.kube/config
sudo rm -rf /var/lib/cni

# External etcd clean up
sudo etcdctl del "" --prefix --cacert /srv/etcd-certs/cacert.pem --cert /srv/etcd-certs/etcd.pem  --key /srv/etcd-certs/etcd-key.pem


Re-joining cluster

When your SD failed and you wish to re-configure the faulty PI, you need to perform:

# Cordon the faulty node
kubectl cordon master3

# Drain the faulty one
kubectl drain master3 -delete-emptydir-data --ignore-daemonsets
(if it hungs, you may have to open a separate terminal and to delete the relevant pod)

# Delete the node
kubectl delete node master3 

# Retrieve bootstrap token
kubeadm token list

# Retrieve token cacert hash (sha256)
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt \
    | openssl rsa -pubin -outform der 2>/dev/null \
    | openssl dgst -sha256 -hex \
    | sed 's/^.* //'

# Generate new key
kubeadm init phase upload-certs --upload-certs --config kubeadm-config.yaml

# Forms the join as master command based on above results
kubeadm join --token 0abut7.guksf3dfp1k67osq --discovery-token-ca-cert-hash sha256:a263dfc32ddd3d16cc72f2f91a0786e869f9310646614dbdf6a103be5d42b9c7 --v=5 --control-plane --certificate-key 97e86ed12f1e1b4f97d51d1a1ff92485d678d5fb8725c3c73e66b17a660c68af