Kubernetes

Scaling a Microservice Application in Kubernetes

Damian Igbe, Phd
March 24, 2022, 6:42 p.m.

Subscribe to Newsletter

Be first to know about new blogs, training offers, and company news.

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.

The Pod Object (Pod of containers)

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.

Attributes of a Pod Object

When a pod  object is created, whether it contains just one container or more than one container:

  • Kubernetes allocates IP addresses to the Pod and not the individual containers.
  • The containers in the Pod share the same IP address allocated to the Pod.
  • To distinguish the containers running in a pod, the containers must have their individual Ports. In that way, containers can address each other by using localhost and the port of the container to access.
  • Containers in a Pod share the same volume. To the Pods, a volume is like a shared medium, and information stored in the volume by one pod can be seen by the other pod. This diagram illustrates how a pod looks.

 

Creating a Pod

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:

  1. Create a YAML file describing the attributes that you would like the Pod to have.
  2. Use kubectl create to create the object.

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:

  • Viewed the YAML file for the Pod. You can read here to understand the syntax of a Pod object.
  • Created the Pod object
  • Checked the IP address for the Pod
  • And then use curl to view the voting app running on port 80.

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:

  • Scale the application (ReplicaSet and Deployment objects will solve that)
  • Expose the Pod to the outside world  (The service object will solve that)

The ReplicaSet Object

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:

  • Metadata section
  • A selector that specifies how to identify Pods
  • A number indicating the desired replica for the application
  • A Pod template specifying the data of new Pods to create

Scaling Pods Using ReplicaSet Object

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:

  • Scaled the ReplicaSet from one Pod to 4 Pods.
  • Checked that they are all in Running state.
  • Note that:
    • Each of the replicas is just a copy of the previous.
    • We can access any of them as we did before and they should all have the same content.

ReplicaSet as a Monitor

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.

Conclusion

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:

  • As the voting app evolves, what if we would like to update the images in real life without downtime?
  • How about DevOps practices like Blue/Green deployment?

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.

Zero-to-Hero Program: We Train and Mentor you to land your first Tech role