Kubernetes 101: Part 2

So, in case we have 1 container in a pod, it might get destroyed

To solve this issue, we can have multiple pods using replication controller.

Now, if one container gets destroyed, other is up and running.

We can also have replication controller for 1 pod too.

So, when this pod will be destroyed,

replication controller will delete this one and create another.

Also, to balance the loads , we can have multiple pods .

When the demand increases, it can also distribute load in other pods residing in other nodes.

Replication controller vs replica set

Replication controller was user earlier but now we use replica set to distribute loads and others.

A replication controller’s yaml file might look like this

apiVersion, kind, metadata and spec are basic 4 parts. Here , kind has ReplicationController enabled.

Replication controllers do have pod instructions. So, we will add that within template. So, that it can create a pod (container instruction is kept in the pod ) once a pod is unhealthy.

For example, this is a pod yaml file

We can modify our rc-definition file now (excluding apiVersion and kind)

We also need to mention how many replicas we can have

Now you can see if the replication controller is created or not and how many replicas we have within that.

SO, these replicas are basically pods. We have 3 pods created by the replication controller

Now, let’s talk about replicaset

here the apiVersion has to be apps/V1

Then rest other are almost same

but that’s not the end.We have another thing to add called “selector” which defines what pods fall under this replicaset

Here this replica set looks for pods which has label front-end

Then you can create the pods/replicas using this

But where did we use selector???

Assume that you have 3 pods created and you are monitoring that

So, assume you are monitoring pods which has label “front-end”. We already mentioned the labels when creating the pod.

Now, if one pod /set is destroyed, replicaset will create a new pod.

Now, what about scaling the replicas?

We can modify the replicaset file (upgrade 3 to 6) and then use

kubectl replace -f <replicaset yaml>

or, without modifying we can just make the change from command line

kubectl scale —replicas=6 <yaml file>

But the issue is, the replicaset-definition.yml file won’t be edited to 6. It will still be 3.

Here is a code of replicaset which used template from this pod.yaml file and used it as a template as well.

Now, once we create that, we can these.

Now there is a point. This replicaset has the goal to create 3 pods which will have label “frontend”

Gladly we used to have a nginx pod which had this pod. So, we already have 1 with frontend label. We need 2 more, right?

When the replica set was created, these 2 got created as well. You can easily say that these are actually part of the replica set by seeing their names.

What if I delete the nginx pod? Shouldn’t we have new pod here?? Will it have the nginx or something related to replicaset?

Let’s check

Once we deleted the nginx pod, automatically one new pod got created.

Now, you can see it’s up and running

If you want to see what this replicaset did by now, you can see the events generated by this

Now, what if we decide to create a pod using the pod.yaml file we had (which has label : frontend)

Let’s guess, should that happen? because replicaset was told that we expect 3 replicas having label frontend.

So, my guess is : We can’t create it

Let’s check

Ahhaa! the pod is created.

But, now, we should have 3 pods right? Let’s check

Yes!! Basically the new pod got created, but as it had the label “frontend”, it was terminated.

Now, if we check if that termination was done by the replicaset or not, we can see

We can see that the new nginx pod was deleted by the replicaset.

Deployments

Deployment gives us opportunity to upgrade the underlying instances seamlessly using rolling updates, pause or resume changes as required.

The only change we have is “kind : Deployment”

Now, if we create the deployment, a replicaset will be created with the same name and some numbers associated with it. And surely, it will maintain the replicas/pods

Let’s code this down

We have created deployment.yaml and pod.yaml file

deployment.yaml has kind: Deployment set

Then we create the deployment.

Here the deployment name should be myapp-deployment

Now, you can see we have created the deployment and the replicaset was automatically created and got the name (myapp-deployment-7f….)

then we are expecting 3 more pods to be created by this deployment.

They are ==> myapp-deployment-7f…….-2x….,myapp-deployment-7f…….-bb…., myapp-deployment-7f…….-78x….

So, replica set and pods got the name from deployment name.

You can also see if deployment actually created a replicaset from the Events portion

We can also see all information using kubectl get all

Rollout and versioning

When we create a deployment first, it creates a rollout called “revision 1”

When we update the content and new deployment is created, a new rollout gets created called “revision 2”

For all of these 10 pods generated in deployment, this can be the command to check their rollout

Deployment strategy

Assume you have 5 pods,

You can destroy old 5 pods and create new 5 pods.

But the issue is, if anything of our product is dependent on any of the old 5 pods, our product will be down.

So, this strategy is bad for us

What if we take down 1 server at a time and create the new one then?

In this way, all of our servers/pods won’t be out of service.

And this is called “Rolling Update”

By default Rolling Update is the default updating policy

For now, assume that we have this as our deployment yaml file

Let’s update the image to nginx:1.7.1

Then apply the changes

Once we use rolling update, we can see

The events where we see replica set had pod up and down.

So, in general a deployment has a replica set

One a person upgrades the deployment.yaml file and apply that Which means Upgrading, it creates a new replica set and generates new pod there. Also removes old pods as well.

Here one pod from replica set 1 gets down and 1 pod from replica set 2 gets up.

In this way, all pods from replica -set-1 goes down and all pods from replica-set-2 goes up

We can also verify this by checking the replica sets

What if the new version has issue and we want to get back to the old version??

Yes, we can undo the changes

So, new pods will be destroyed and old ones will be up

Once done, we can see the output

Also, you can now see the old replica set has all 5 pods

To sum up, once we make changes and deploy, we can enabling rolling to the pods.

Then if we delete/update them and again want to make them up, rollback/other deployment strategy works.

Here, we have checked if we have pods or not. Then we deployed this file

Then we wanted to check the rollout status

Let’s delete this deployment

Now, I am going to create the deployment and check the rollout status command as fast as possible to see how they are created.

Here, you can see 3 out of 6 replicas are available, then 4, then 5 and then 6

Actually the deployment file mentioned to create 6 pods, and when we wanted to create this deployment, by default it used “RollingUpdate” strategy to create/update the pods

We can also record the reasons/commands which causes new deployments.

To do that, let’s delete the deployment

Now, let’s create the deployment again using - - record

We can now see the change caused the deployment. Here, the cause was the command we used to create the deployment.

Let’s update the images from nginx to nginx:1.26.2-alpine

Here we did set the image of nginx as nginx:1.26.2-alpine and you can see how old replica/pods are terminated and at the same time updated.

So, these are the pods updated

Let’s pick one of the pods and we can see that image is set as nginx:1.26.2-alpine

We can now check the history / cause of the deployment

We can also undo the deployment and go back to the earlier version

Now, we are expecting to see the nginx images to be used instead of nginx:1.26.2-alpine

let’s check one of the pods?

If we now check the history, we can see revision 2 and 3 created

Here, 3 is exactly what Revision 1 was. As it has been applied again, it has got the new number and same commands used.

Again, assume that we have a deployment responsible for the website

Let’s check what image was used for that,

Then we check one of the pods details:

Here, kodekloud/webapp-color:v1 image was used

Let’s see what deployment and strategy we have.

Here we can see StrategyType: RollingUpdate

Now, we will change the image an kubectl edit deployment frontend

Let’s upgrade it to kodekloud/webapp-color:v2

Once done, we will save and exit.

After the update, the website now looks like this

Let’s change the deployment strategy to Recreate

We did set the strategy type to Recreate.

Let’s change the image to kodekloud/webapp-color:v3

Saved and exited!

The website is now red

So, to sum up, we have learned two rolling update method: One was “Recreate” : In this way all old pods get down at the same time and after a while new pods get up all at the same time

For the second one, “Rolling update”: In this way, one pod goes down and another was is created with new update.

But we have more rolling update strategy

Blue/Green Deployment

In this way, we have old pods as blue and new pods as green. All of the traffics are sent to blue pods.

Once the tests do pass on green pods, the traffics are sent to green pods.

It’s applied in service meshes like Istio

How does it work?

First of all, we deploy a blue deployment with a selector (versions:v1), we also deploy a service with the same selector. Then we route traffic from the service to the blue deployment.

Then we create another deployment which has the latest pods. Once all of the tests pass on the new deployment (with a selector version:v2) , we change the selector of the service so that it routes traffic to the new deployment.

How to code that?

Here we created a deploy for the blue pods. We also created a service. In both cases, we used the same selector so that the service can track the pods.

Then we will create another deployment with new version

Once the testing is done, we will change the selector in the service. In this way, we can route the traffic to the new deployment.

Canary Deployment

In this way, we route a small portion of traffic to the new version

If everything looks goo, we update the original versions (app:1.0) with the newer version (app:2.0)

Then we get rid of the experimental one

How to apply this one?

First we create a deployment and a service with same selector . The selector routes all traffic to the primary deployment

As our target is to send traffic to both deployment, lets have a common selector

Then we update the selector for the service. So, both deployment gets equal traffic

But do we want that? We want less traffic to be sent to canary deployment.So, let’s remove pods from here.

As there are less pods, surely there are less traffic going to canary deployment.

The issue here is, we can’t fix the percentage of traffic on the canary deployment. To solve this, istio service mesh gives us the opportunity to select the desired percentage of traffic.

Firstly, we create these 3 files

Stateful Sets

Deployment and stateful sets are almost the same. In deployment, pods which are part of the replica sets are created at the same time. Whereas in replica set, pods are created one by one. Once a pod is successfully running,another one gets deployed.

So, cases like multiple containers with read and write access can be created using stateful set.

Assume that, there is one mysql server running and we have decided to copy the data to two new servers

The way we can clone other servers are:

Firstly clone data from master to slave -1 (left). Then enable replication from master to slave-1 so that, if any new data arrives in master, slave-1 gets the update.

Then we wait for slave-1 to be ready and clone the data from slave-1 to slave-2 rather than from master to slave-2. We have to avoid clone from master several times as there are networking and other settings which is going to be hard then.

Once the clone is done for slave-2, enable replication from master to slave-2 rather than slave-1 to slave-2.

On the slave servers, properly configure the master server address (MASTER_HOST=mysql-master)

If we had to use deployment to deploy them, it would have been impossible as pods/servers are launched all at the same time. Also the IP address changes for the pods as they may get deleted.

If we use replicaset, servers/pods will have unique name and once a pod is launched successfully, another one gets created. So, it is helpful for us.

How to create that?

Here we had to add headless server name and kind as StatefulSet

Then we can create the pods , scale it (—replicas=5), scale down it (—replicas=3), or delete pod

When you scale , it scales one by one. When it scales down,it starts removing pods from the back (in a reverse order)

We can change the order , we need to set podManagementPolicy to Parallel so that, it does not follow any order.

Headless services

We did mention headless service in the StatefulSet’s yamnl file

serviceNaame: mysql-h

But why?

As mentioned, our goal was to point the slave servers to point to master server. But the way we can do is, using service

Let’s first learn what a normal service can do. A normal service acts as a load balancer and distribute traffic across all pods in the statefulstes. The service here has a cluster IP and the DNS name associated with it is like “mysql.default.svc.cluster.local”

Any other application can now contact the service

So, anyone can serve the application but the writes must only be processes through master server (mysql-0)

Other servers can be used for reading just. What if the application want to reach the master server or slave server directly?

How can it reach that? Surely not using IP and DNS of the pod servers as those may change.

So, here comes headless service. Once we create that(mysql-h) , it assigns each pod a DNS.

The application can now reach the pod using that DNS. Remember, this service does not work as a load balancer.

How to create the headless service?

Here, notice that we have set clusterIP as None. This is how we can create a headless service. To create a DNS record for the pods, we need to set

subdomain : <headless-server-name> and

hostname: <a name> in the pod definition file (not in the statefulset definition file)

If we set the subdomainand & hostname in the deployment file (Note: For deployment. Not as a statefulset), we get the DNS set for all pods using subdomain and hostname.

The problem is, all of the 3 pods have the same DNS now. But we want them to be different and that’s where StatefulSet helps.

Here you don’t even need to set the subdomain and hostname. It automatically creates DNS for all the pods. It uses the unique pod name.headless_servername.svc.cluster.local as the DNS

So, we have unique DNS now all of the pods whereas we could never get that using deplpoyment. So, thanks to statefulsets.

Storage in StatefulSets

When we specify Persistent Volume Claim (PVC) under the pod definition, all pods created by that stateful set tries to use the same volume.

But if we want to keep different volumes for each pod, we need to specify PVC for each pod

Each PVC needs a Persistent Volume (PV) and they may below one or multiple storage classes (SC).

So, how to create different PVC for each pod?

Add this pvc-definition.yaml file to the end the statefulset-definition.yaml

Here under volumeClaimTemplate, we have pasted

So, how does it work?

When the first pod is created, a PVC is created.The PVC is associated to a Storage Class. The Storage class provisions a volume on the GCE (here we use GCE )

Then PV is created and associates the PV with the volume and binds the PVC to the PV.

Then the second pod is created and PVC is created . The storage class then provisions a new volume , associates that to PV and binds the PV to the PVC.

Same goes for the third one

Jobs

When a pod is created here, it runs the computation task

And once done, it exists. Now, the pod status turns to “Completed”

Then it re-creates the pod inorder to keep thar running. Thus it restarts again.

This restart happens untill a threshold is reached.

Why is it restarting? Because in the definition file, there is a setting called restartPolicy set as Always

We can change this value to Never.

Now, assume that we want multiple pods to process at parallel

How can we do that? We know that a replicaset can allow us create multiple pods. Should we use that? No!

A job is used to run a set of pods to perform a given task to completion. Whereas, replicaset only deals with pod creation.

So, how to create a job?

Create a job and check the result (“5”) from the container. Then the job should be deleted.

To have multiple pods with the task, we need to specify completions as 3

What if the container fails to complete the tasks?

If failed, it creates a new pod untill it gets desired successful tasks

There is another way we can deploy pods . We can deploy them parallely.

Here you can see 3 pods have been deployed first and 2 of them got succeeded. Then it deploys 1 pod at a time un till it gets a successful pod

Cron Jobs

A job that can be scheduled. For example, creating a job and schedule it and it does the task when the time comes.

This time, we need to specify the schedule in this format

Once the schedule is set, it runs the job on the desired time

Then we can create the job

Networking

A node has a IP address

Each pod gets an internal IP address. (Containers don’t have any IP address)

When kubernetes is configured, a internal private network gets created.

Now, talking about two different nodes (they have their IP)

Assuming each has their own private network and a pod associated with it

But the internal IP networks are same here. So, it’s not possible to contact each other as they have the same IP

We need to follow these rules to solve this issue

There are some solutions which provide this networking setup

Assuming we are using one of the solutions here

Now, this solution will provide IP to each network and will be different.So, communicating each other became easier now!

Kubernetes Services

Services helps communicating between backend, frontend and helps connecting to external data source.

Assume that the python based container in the pod has a webpage and you want to access the webpage from your laptop

We can’t contact the pod as our laptop’s IP and pod’s one is way different.

So, how to contact the pod?

To solve this issue, what we can do is, we can use a service which will listen to a port and pass the traffic to the pod.

Now, using the node IP and the port 192.168.1.2:30008, we can contact the pod from our laptop.

Service types

There are some of the types:

  1. NodePort: The example we used was a NodePort

Here the service has port 80 to contact with pod with it’s port 80. The service has it’s IP as well which is Cluster IP (10.106.1.1)

How to create a service?

Here we define, kind as Service and specifically, spec section is the import portion. Check the image to set the value for ports.

The NodePort range is between 30000-32767

We have specified the TargetPort but what if we have thousands of ports ? How to know which port has this TargetPort?

To solve this issue , we will use label and selectors to connect to our desired pod

So, we use the selector section to use the pod or track the pod.

Then we can create the service and use it

What if we have multiple pods?

Here all the pods have label myapp and service looked for the myapp selector.

So, the service will contact with all of the three pods. Now, it will work as a load balancer and distribute load among several pods.

Kubernetes automatically creates a service that spans across all the nodes in the cluster and maps the target port to the same node port (30008) on all nodes in the cluster .

When pods are removed or added, service automatically gets updated . So, it’s highly adaptive.

Let’s code this down:

Let’s create a folder called service which has service-def.yaml file

We specified the ports, set kind as Service. Let’s create the service

Now create the service and check the service

If we now know the IP of our pods, we can use : <pod’s ip>:30004

As we are using minikube in our pc, we can also use

minikube service myapp-service --url

This is one of the pod’s IP and we can use it alongside 30004 port

As our pods are based on nginx, we can now check the nginx website.

  1. ClusterIP

    We can have pods for front end, backend and others

    They can contact but the IP’s can go down if the pods are down. So, these IPs are not trustworthy.

    We can assign services some clusterIP and thus using those, pods can communicate and that IP won’t change.

    Here we have specified which pods to target in selector, also mentioned the ports (target ports and port)

    Then we can run it and check the services!

  2. LoadBalancer

    Assume that we have 4 node cluster and blue nodes are for vote taking and green two are for the results. Simply a voting machine.

    We have two services which listens to port 30035 and 31061

    Using all of the <node’s IP> : port (30035 or, 31061), we can access to

    Assume the voters have access to two websites

    But how to connect the nodes with this URL?

    We can use AWS or, Azure or GCP and use their supportive load balancer here.

    Let’s do some coding:

    In general kubernetes creates a clusterIP service

    We can explore the details , targetports, labels it used (selector)