This is part 2 of the series on Managing Microservices with Kubernetes. You can read part 1 here,
In part 1, we understood how Kubernetes can be used to deploy a Microservice. In that blog, I mentioned that a couple of Kubernetes objects are used to deploy the voting application- Namespaces, Labels and selectors, Pods, ReplicaSets, Deployment, and Service Objects. In this blog, I will take you through the journey of how these objects are used to deploy the microservice.
To understand a Pod object, let me run this commend:
cloudexperts@master1:~$ kubectl get pods -n vote NAME READY STATUS RESTARTS AGE db-6789fcc76c-kkfnk 1/1 Running 0 13h redis-554668f9bf-7qt2x 1/1 Running 0 13h result-79bf6bc748-rhrv8 1/1 Running 46 13h vote-7478984bfb-544p7 1/1 Running 0 13h worker-dd46d7584-4dzzx 1/1 Running 1 13h
The command output indicates that 5 pods are running, and each pod represents each of the microservice that constitutes the voting-app. From this, we can see that, at the basic level, a container in Docker and a Pod in Kubernetes can mean the same thing. However, a Pod can contain more than one container and in that sense, a Pod is an envelope, a wrapper around containers. This is similar to a pod of beans where the case is the pod and the bean seeds are the containers. In the listing of the above command, if you look under READY, you’ll see 1/1 on all the pods. This means that the Pod contains 1 container and the container is running. To see the container, you can describe the Pod object like so:
cloudexperts@master1:~$ kubectl describe pod vote-7478984bfb-544p7 -n vote Name: vote-7478984bfb-544p7 Namespace: vote Priority: 0 Node: node02/192.168.0.10 Start Time: Mon, 08 Jun 2020 21:51:35 -0500 Labels: app=vote pod-template-hash=7478984bfb Annotations: <none> Status: Running IP: 10.36.0.2 IPs: IP: 10.36.0.2 Controlled By: ReplicaSet/vote-7478984bfb Containers: vote: Container ID: docker://f66bda85b72e14757f6d95b5a26cf37c5816f2382bb6f34333c7ab3b8b4a7b44 Image: dockersamples/examplevotingapp_vote:before Image ID: docker-pullable://dockersamples/examplevotingapp_vote@sha256:8e64b18b2c87de902f2b72321c89b4af4e2b942d76d0b772532ff27ec4c6ebf6 Port: 80/TCP Host Port: 0/TCP State: Running Started: Mon, 08 Jun 2020 21:51:41 -0500 Ready: True Restart Count: 0 Environment: <none> Mounts: /var/run/secrets/kubernetes.io/serviceaccount from default-token-7rb9h (ro) Conditions: Type Status Initialized True Ready True ContainersReady True PodScheduled True Volumes: default-token-7rb9h: Type: Secret (a volume populated by a Secret) SecretName: default-token-7rb9h Optional: false QoS Class: BestEffort Node-Selectors: <none> Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s node.kubernetes.io/unreachable:NoExecute for 300s Events: <none>
The section in red gives the attributes of the container. It is using the image dockersamples/examplevotingapp_vote:before, which is located at Docker hub, the Container Image Registry. The owner of the image is dockersamples, the name of the image is examplevotingapp_vote and the image version/tag is before.
Most Pods will contain just one container and when a Pod contains more than one container, there is usually the main container and the rest are usually helper containers, helping the main container in one way or the other. This follows the best practice of container designs where a container does just one thing and does it well. In this way, the container/microservice can be scaled independently of the other containers. Resources can also be allocated to the Pod independent of the other Pods/microservices.
When a pod object is created, whether it contains just one container or more than one container:
Now that we know that the Pod is the most basic object in Kubernetes, let us create a Pod from scratch. We will do this in 2 steps:
Actually, the above 2 steps describe how to create an object in Kubernetes. Tell Kubernetes your desired profile of the object and let Kubernetes create it.
Here is a YAML file we will use to create the vote-app. Remember that this microservice application has 5 microservices and to create all of them, we will need to repeat the below steps for all the 5 YAML files. An alternative to creating 5 YAML files is to create a single YAML file with 5 sections, each section describing a pod. Each section is separated by 3 dashes like – – -.
apiVersion: v1 kind: Pod metadata: name: voting-app labels: app: voting-app Namespace: vote spec: containers: - name: vote image: dockersamples/examplevotingapp_vote:before ports: - containerPort: 80 name: vote
cloudexperts@master1:~/manifests$ kubectl create -f vote-pod.yaml cloudexperts@master1:~/manifests$ kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES voting-app 1/1 Running 0 18s 10.36.0.3 node02 <none> <none> cloudexperts@master1:~/manifests$ curl 10.36.0.3 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Cats vs Dogs!</title> <base href="/index.html"> <meta name = "viewport" content = "width=device-width, initial-scale = 1.0"> <meta name="keywords" content="docker-compose, docker, stack"> <meta name="author" content="Tutum dev team"> <link rel='stylesheet' href="/static/stylesheets/style.css" /> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css"> </head> <body> <div id="content-container"> <div id="content-container-center"> <h3>Cats vs Dogs!</h3> <form id="choice" name='form' method="POST" action="/"> <button id="a" type="submit" name="vote" class="a" value="a">Cats</button> <button id="b" type="submit" name="vote" class="b" value="b">Dogs</button> </form> <div id="tip"> (Tip: you can change your vote) </div> <div id="hostname"> Processed by container ID voting-app </div> </div> </div> <script src="http://code.jquery.com/jquery-latest.min.js" type="text/javascript"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.js"></script> </body> </html>
Above, we performed the following:
Note that we can follow the same process to create the other 4 microservices as the YAML files are all provided.
We now have the pod running but there are things that we cannot do at this point, some limitations. For example, we cannot:
With Replicasets, you can scale the vote-app microservices and ReplicaSet will also ensure that the specified number of pod replicas are running at any given time. ReplicaSet is a controller object. As a controller object, it will engage in closed-loop monitoring of the Pod to ensure that the Pod and the desired number of replicas running at all times.
When you create the YAML file for ReplicaSet, you describe your ‘desired state’ of the object. The ReplicaSet object will keep monitoring the ‘current state’ of the object for any deviation from the ‘desired state ‘ and bring it back to the desired state if any difference. Note that a ReplicaSet also creates a Pod so instead of just creating a pod directly using the Pod object (as we did earlier), it is better to create a ReplicaSet object. In fact, hardly will you just create a standalone/orphan Pod as this is always done through the ReplicaSet.
Here is the YAML file for the ReplicaSet object.
apiVersion: apps/v1 kind: ReplicaSet metadata: labels: app: vote name: vote namespace: spec: replicas: 1 selector: matchLabels: app: vote template: metadata: labels: app: vote spec: containers: - image: dockersamples/examplevotingapp_vote:before name: vote ports: - containerPort: 80 name: vote
This YAML file has 4 sections:
Now let’s scale this pod through the ReplicaSet object:
cloudexperts@master1:~/manifests$ kubectl get rs NAME DESIRED CURRENT READY AGE vote 1 1 1 11s cloudexperts@master1:~/manifests$ kubectl scale rs vote --replicas=4 replicaset.apps/vote scaled cloudexperts@master1:~/manifests$ kubectl get rs NAME DESIRED CURRENT READY AGE vote 4 4 4 24m cloudexperts@master1:~/manifests$ kubectl get pods NAME READY STATUS RESTARTS AGE vote-7srcc 1/1 Running 0 39s vote-qh484 1/1 Running 0 39s vote-rqbvk 1/1 Running 0 25m vote-w6gn9 1/1 Running 0 39s
Above, we did the following:
Here I will delete one of the pods running and we will see it replaced immediately.
cloudexperts@master1:~/example-voting-app/k8s-specifications$ kubectl get pods -n vote | grep vote vote-48bjg 1/1 Running 0 98s vote-lzd69 1/1 Running 0 98s vote-qffsr 1/1 Running 0 98s vote-xkrgv 1/1 Running 0 2m31s cloudexperts@master1:~/example-voting-app/k8s-specifications$ kubectl delete pod vote-48bjg -n vote pod "vote-48bjg" deleted cloudexperts@master1:~/example-voting-app/k8s-specifications$ kubectl get pods -n vote | grep vote vote-bn5tc 1/1 Running 0 13s vote-lzd69 1/1 Running 0 3m20s vote-qffsr 1/1 Running 0 3m20s vote-xkrgv 1/1 Running 0 4m13s
Above you can see that the first pod was deleted but it immediately got replaced so that 4 pods are always running.
Note that each time you create a deployment object, a Replicaset object is also created and if you try to scale the replicaset created by the deployment object, the deployment object will replace any pods created by the replica set object.
With the Pod created through the Pod object, we can not scale it while we can scale pods created through the replicaset object. However, there are more things we need to do that Replicaset cannot help us:
To do the above we would need another Kubernetes abstraction called the Deployment object. With the Deployment object, we no longer need to use the ReplicaSet object directly. Instead, we can use the Deployment object to control the ReplicaSet and the Pod. The deployment object will be addressed in the next blog. Stay tuned.