OK. Kubernetes. Or K8s. Where do I start? I’ve been struggling with K8s for a while now. I get that it’s a platform for running your containerised apps. Oh, quick spoiler, this post will not cover stateful apps (databases) or persistent volumes on K8s. So, a platform you say? I’ve only ran a single container on a single machine. I’ve seen Docker Swarm examples where you need/want to run multiple containers together on the same network but I think you still commit to running on a single machine. K8s provides a platform to deploy your containers across many servers. You tell K8s what image to deploy and how many you want running and it makes sure that happens automagically.

So from a platform perspective I understand what it provides (not fully, obviously, but enough to know why it exists and is so popular). But everything I’ve read and watched online tells me about the features of K8s and how to use it assuming it’s been deployed for you (public cloud or turnkey on-premises solution). As an infrasturcture guy I couldn’t get past not knowing what it looked like under the covers. I don’t need to go very deep, I just wanted to find out how I install the thing myself and get it to run a container. And I just happen to have a container I can use too!

My feeling was that if I could install and run K8s on my own VMs I’d break through the mental barrier I have and be able to understand K8s sufficiently to use cloud solutions or our on-premises offering. And then we start to look at persistent volumes and running databases there!

Before I go on, here are a few links to things you can read or watch to get an overview of K8s if required:

  • The Illustrated Children’s Guide to Kubernetes comes highly recommended. It helped me a bit but I was still lost. I also didn’t fully understand the benefit of pods as it suggests you’re likely to only run one container in a pod at a time.
  • The tutorial on the Kubernetes website is pretty good.
  • There are plenty other intros to K8s all over the place.
  • Something I watched most recently and gave me the help I needed to understand pods was a talk by Carson Anderson available here. Within the first 4 minutes I finally got the point of pods.

Now, back to the original plan - build my own K8s cluster. A bit of googling and I came across this great article from Aravind on how to do exactly that. I tried following it exactly but I had problems with the CentOS repo on RHEL thing so used CentOS machines instead. I’m going to reproduce what Aravind wrote here. I don’t normally do that but I had to make a couple of changes to get it working and I’d like a single source to reproduce it all.

Prepare 2 CentOS 7 machines

Disable swap

swapoff -a
sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab

Install docker on both machines

yum -y update 
yum -y install docker
systemctl enable docker
systemctl start docker

Install K8s on both machines

cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
setenforce 0
yum -y install kubelet kubeadm kubectl
systemctl start kubelet
systemctl enable kubelet

Fix SELINUX if necessary on both machines

vi /etc/selinux/config
     SELINUX=permissive ##Change if it is enforceing

Some hacks for reasons

cat <<EOF >  /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system
echo 1 > /proc/sys/net/ipv4/ip_forward

Following steps should be carried out on the master node only

Configure and Enable Networking to the cluster

kubeadm init --pod-network-cidr=10.244.0.0/16

Output will be something similar to the following:

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 10.74.55.2:6443 --token pb7yrw.mzftioym0fg4hx6a \
    --discovery-token-ca-cert-hash sha256:b6f29385d1ea6df06c8968e174faf595ec4bd0969092b29dc04fd2e6cd942c7a

Create an OS user

adduser dinodba
passwd dinodba

Give sudo privs

gpasswd -a dinodba wheel

Run on the master as the dinodba user

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

On the master node only

Enable the Kubernetes cluster using flannel to get the config in yaml.
The link supplied by Aravind didn’t work for me. I found the correct link on the K8s website. It’s actually part of their instructions to do this kind of install - it’s just not as easy to follow as Aravind’s article.

The command that worked for me is below. You should confirm the URL is still correct on the K8s website.

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/a70459be0084506e4ec919aa1c114638878db11b/Documentation/kube-flannel.yml

Verify the cluser

kubectl get nodes

Now run the join command from the master setup on the worker node.

kubeadm join 10.0.2.203:6443 --token 49ub6n.b97ie9hxthvfyjtx --discovery-token-ca-cert-hash sha256:09e35eb11e535c64171d50059a584ea209a8d2479d00de30c566f47dbc7128cf

Verify on the master:

kubectl get nodes

NAME                         STATUS    ROLES     AGE       VERSION
ip-10-0-0-139.vpc.internal   Ready     <none>    25s       v1.11.1
ip-10-0-2-203.vpc.internal   Ready     master    17h       v1.11.1

It’s working!

It wasn’t that easy for me. I had issues with swap not being disabled. I also had the flannel networking challenge. But we got there.

Now we want to deploy our container to our K8s platform. I don’t want to go to the hassle of putting my container in a registry and pulling from that registry at this stage.

I used the container from my own docker post that grabs twitter tweets and inserts them into an Oracle database. I had to spin up a database (thankfully quick and easy where I am) as my previous one had been destroyed and then I copied the stream.py, Dockerfile and instant client software to the K8s cluser and built the image. I ran it locally in docker to check it worked before attempting to deploy on K8s.

Prepare K8s deployment yaml

Via a bit of googling I’ve now lost I found a simple yaml example:

Create pod.yaml file:

---
 apiVersion: v1
 kind: Pod
 metadata:
   name: tweet-capture
   labels:
     app: python
 spec:
   containers:
     - name: stream
       image: my-stream-app
       args: ["-s", "ORCL", "-m", "diplodicus.dinodba.com", "-n", "1521", "-w", "oracle,docker,dinosaur"]
       imagePullPolicy: Never

And then this is deployed with a simple command:

kubectl create -f pod.yaml

And the status can be viewed with the following command:

kubectl describe pod tweet-capture

And you can view logs with this command:

kubectl logs tweet-capture

Issues

What you’re reading is the working example. It wasn’t that easy for me. Firstly the deploy didn’t work.

Build the image on all nodes

I was getting ErrImagePull and then ImagePullBackOff errors that took me too long to diagnose. It should be getting the image locally but it wasn’t finding it. Eventually a google search led me to the obvious information that the docker image only existed on the master node at this point. I’m running the kubectl create command on the master node but the master node is scheduling the pod on the worker node and the worker node doesn’t have access to the image. The simple fix was to build the image on the worker node too. Incidentally, these messages were available in the describe pod output.

args

I forgot to include the arguments. So once the pod was successfully deployed it didn’t work. The output of kubectl logs showed that it couldn’t find the database. Now I had to work out how to pass these arguments to K8s. A wee bit of googling and some trial and error got me to what you see above.

Next Steps

I’d like to play with this a wee bit more before I move away from my own K8s deployment. Maybe scale things. Or put the DB in a container too. This has been the breakthrough I needed for K8s. I’ll be a lot more confident using a K8s platform now and I have no interest in continuing to manage my own!