Kubernetes: Wrapping my head around the BuzzWord

Kubernetes: Wrapping my head around the BuzzWord

2019, Mar 07    

Alright folks, I have to admit that, getting my head over all the terminologies is something I have not yet completely done. Nonetheless, I was successfull in finally deploying a Kubernetes cluster on my local system, and running a scaled out a simple Hello world Flask application.

I would like to document this here, for those who might be wishing if someone wrote something similar.

Here is what we are going to do:

  1. List out what all things are to be installed
  2. Develop a simple Flask application (You may develop your own application)
  3. Dockerize it
  4. Deploy it inside a local Kubernetes cluster.
  5. Test out the API, and understand that the requests are distributed to all the nodes in the cluster.

So let’s get started.

1. Meeting dependencies:

  1. Python 3.6+ recommended [If you are following my application], optionally curl as well.
  2. Kubernetes
  3. Minikube
  4. Docker

Now you are all geared up. Let’s continue.

2. Flask Hello World app

You can skip this step if you already have an application in your hand, but for the simplicity of this topic and due to my lazyness :P, I am gonna just carve out a simple hello world json api for Flask

# app.py
from flask import Flask, jsonify

app = Flask(__name__)

@app.route("/")
def index():
    return jsonify(msg="Hello World")

Once the file is saved, let’s run it.

$ export FLASK_APP=app.py
$ flask run

If everything went well, this should spin up a Flask Development server. Let’s see if we get a JSON greeting

$ curl localhost:5000/
{"msg":"Hello World"}

3. Dockerize the Flask application

For kubernetes to pull container images, we will use a dockerhub public image. Inorder to do this, if you don’t have an account on it, make one now, here. Building a docker image is quite simple, let’s do that now.

We need to make a Dockerfile first

We will use a production grade WSGI HTTP server gunicorn instead of the default Python-Flask development server.

FROM python:3.7
MAINTAINER Sreenadh TC "[email protected]"
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt
ENTRYPOINT ["gunicorn"]
CMD ["-b 0.0.0.0:8000","--log-level=debug","app:app"]

Build image using the above Dockerfile.

$ docker build -t flask-app:latest .
$ docker run -d -p 8000:8000 flask-app:latest
$ docker ps

Here the docker build builds an image for us inside docker. The docker run command will actually run the underlying command gunicorn -b 0.0.0.0:8000 –log-level=debug app:app inside our docker python:3.7 container.

Test with curl or browser.

$ curl localhost:8000
{"msg":"Hello world"}

And viola, we have a running docker image now. Let’s push it to dockerub.

$ docker login docker.io
.
.
$ docker push <docker_username>/<repo_name>:<tag_version>
.
.

4. Deploying them to Kubernetes

If you are new to Kuberenetes, I strongly suggest you to read about the basic terminologies of it, here?. Head back here once you nail those theory. There is also a tutorial there to try out deploying some sample apps.

Alright, let’s continue our task. Inorder to deploy the Flask Server, we need to configure atleast a Deployment and Service. The deployment is spun up on the nodes and the service will expose it for us outside the Kubernetes cluster. We will now write some configurations for both of them.

It will be a good idea to make a new folder, say k8s to organize our Kubernetes configurations.

<!-- k8s_flask.deployment.yml -->

apiVersion: apps/v1
kind: Deployment
metadata:
  name: k8s-flask
spec:
  selector:
    matchLabels:
      app: k8s-flask
  replicas: 3
  template:
    metadata:
      labels:
        app: k8s-flask
    spec:
      containers:
      - name: k8s-flask
        image: sreenadhtc/flask-hello-world:0.1
        imagePullPolicy: "IfNotPresent"
        ports:
        - containerPort: 8000

The first three are mandatory fields for all Kubernetes configuration files. replicas spins up and makes sure that many nodes are always available inside the cluster. Inside the spec map, we provide the information for pulling the image from dockerhub, the port which our Flask application exposes.

<!-- k8s_flask.service.yml -->
apiVersion: v1
kind: Service
metadata:
  name: k8s-flask
spec:
  type: NodePort
  ports:
  - protocol: TCP
    name: http
    port: 8000
    targetPort: 8080
  selector:
      app: k8s-flask

Now, we have defined a Kubernetes Service, which will expose the ports (8000) of all the pods inside the replica set to a target port externally (8080). This means, whenever we send a request to localhost:8080, the Kubernetes Services internally routes the request into one of the three pods we have under the replica set.

Let’s tell Kubernetes to create these two parts for us inside our cluster.

$ kubectl create -f k8s_flask.deployment.yml
$ kubectl create -f k8s_flask.service.yml

Let’s ask kubectl about our deployment info

$ kubectl get services k8s-flask
NAME        TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
k8s-flask   NodePort   10.96.157.68   <none>        8000:32040/TCP   21s

$ kubectl get deployments k8s-flask
NAME        READY   UP-TO-DATE   AVAILABLE   AGE
k8s-flask   3/3     3            3           45s

Hmm. This doesn’t seem right. I mean, our service and all pods did spin up, but we don’t have an external IP to navigate to.

There is a gotcha here, for those who are doing this via a Minikube cluster on your local system. Minikube doesn’t support a LoadBalancer service which is the best approach to go about for exposing our cluster deployment, and the NodePort does not have an external IP. So, you might now be thinking, how we are going to test our deployment from outside the cluster. Luckily, the minikube cmdline gives an option to explore the cluster.

$ minikube service k8s-flask --url
http://192.168.99.102:31797

Aha!! Now we have an IP to navigate to! Let’s do a curl

$ curl http://192.168.99.102:31797/
{"msg":"Hello world"}

Since I was learning, and I hardly knew much more than this, it took hours for me to finally find this little caviet, which would have saved me a lot of time. Nevertheless, this blog is a result of persistent google searches and documentation reading!

I will be linking to all the useful blogs I read to accomplish this initial steps of stepping into the world of Kubernetes.

References and Special Mentions

  1. Sandeep Dinesh’s wonderfully explained Medium article
  2. Kubernetes Exposing external IP doc
  3. Joannah’s blog Running a Python application on K8s : A little bit outdates, but still helpful!
  4. The caviet that took me hours to figure out.

If there are mistakes here, correct me in the comments as am still learning this stuff. If you found this helpful, give it a tweet or a share!

Thanks for reading so far folks. You are awesome!!