Deployments in Kubernetes

In the previous article, we wrote our first manifest for Kubernetes, which was for a Pod. However, in Kubernetes we do not create naked Pods. If you don’t remember, naked Pods are Pods that are not bound to a ReplicaSet or a Deployment. The reason is, naked Pods will not be rescheduled in case of a node failure. In this article, we will look into Deployments in Kubernetes.

What are Deployments in Kubernetes?

Deployments in Kubernetes
K8s notation for Deployment

Deployment is a workload resource available in Kubernetes. Fundamentally, you specify a desired state in the Deployment and the Deployment controller in Kubernetes makes sure that desired state is maintained in a controlled rate.

All Deployments have a ReplicaSet attached to them. The job of a ReplicaSet is to maintain the specified number of stable replicas of a certain Pod. In other words, a ReplicaSet makes sure that the specified number of Pod replicas are running in the K8s cluster at any given time. Deployment is a higher level concept that manages ReplicaSets and it allows us to provide declarative updates to Pods.This means, that we do not directly interact with ReplicaSets in Kubernetes. Instead we work with Deployments.

For an example, let’s say we need 3 replicas of our application container running to manage the load. What we do is specify the number of replicas as 3 in the Deployment and Kubernetes will make sure that there are 3 stable Pods of our application running at any given time.

Write a Manifest for a Deployment

For this example, let’s create a MySQL Deployment using the official image on Docker Hub.

As you must know, a database is a stateful application. Deployments are not supposed to be used for stateful applications. Instead, there is another workload resource type in Kubernetes named StatefulSet which is suitable for stateful applications. For the purpose of this article, we would just use MySQL as an example because it is very easy to use and most of us are familiar with it. But it is important to be aware of this detail.

Manifest of a Deployment looks pretty much the same as a Pod, but there are some additional important attributes to it. Following is a sample manifest.

apiVersion: apps/v1
kind: Deployment

metadata:
  name: mysql
  labels:
    app: mysql

spec:
  replicas: 2
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql
        ports:
        - containerPort: 3306
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: charith

Let me explain this manifest line by line. But first, I would suggest you to go through the Pod manifest that we created for MySQL in the previous article. You would immediately notice that the “version and kind” section and the “metadata” section are pretty much identical.

Version and Kind
  • Line 1 – apiVersion defines the Kubernetes API version. This could be a different value depending on the resource type. For a Deployment, it is apps/v1 at the time of writing this article.
  • Line 2 – kind defines the type of resource we are going to create. In this case the kind is Deployment.
Metadata
  • Line 4 – the start of the metadata section.
  • Line 5 – here I have defined a name for my Deployment. You can give any name using alpha numeric characters and hyphens. Spaces are not allowed.
  • Line 6&7 – here we are assigning some labels to our Deployment. In this case, it would create a label like app=mysql. A label is a name-value pair.
Specification

The most important section of our Deployment manifest is the spec part.

  • Line 9 – the start of the specification.
  • Line 10 – replicas specifies the number of Pod replicas we need. Here I have specified 2. Which means Kubernetes will maintain 2 running Pods for this Deployment at any given time.
  • Line 11-13 – we need to tell the Deployment which Pods it is responsible for. That is the purpose of selector here. This basically tells the Deployment that it should manage any Pod that has the label app=mysql.
  • Line 14 – template here defines the template of the Pod that is bound to this Deployment.
  • Line 15 – EOF – If you have a look at the Pod manifest in the previous article, you would notice that this entire section until the end of the file is the same as the Pod manifest, without the apiVersion and kind. I am not going to explain the whole Pod manifest again as I have already explained it in the previous article.
  • Line 16&17 – the labels defined here in the Pod template are very important. Go back to lines 11-13 and have a look at the selector. Labels you define here should match the labels you have specified in the selector under matchLabels as that is how the Deployment knows which Pods it is responsible for.

    Don’t get confused about the labels in line 6, which is under the metadata section of the Deployment. Those labels have nothing to do with the selector. They are only there to be used as identifiers and serve no other purpose at the moment.
    Eg: You can use the labels in the Deployment to run a kubectl command like kubectl get deployments -l app=mysql to view all the deployments that has app=mysql as a label.
Selectors in Kubernetes is a whole other topic that merits a separate article, so I am not going to describe about selectors here at length.

Okay, now all we have to do is to apply this configuration file to the K8s cluster and see the results.

Create Deployments in Kubernetes

When we have a manifest file, we can simply run a kubectl apply command in a command line and it should show the output as below, if there are no errors.

$ kubectl apply -f mysql-deployment.yaml
deployment.apps/mysql created

As you can see, I have saved my configuration file with the manifest as ‘mysql-deployment.yaml’. Make sure to open the command line from the directory where the manifest file is.

Now if we run kubectl get deployments command, we can see the details of the Deployment as below.

$ kubectl get deployments
NAME            READY   UP-TO-DATE   AVAILABLE   AGE
mysql           2/2     2            2           25m

In the above output, we can see that it shows 2/2 are READY. What this means is that there are 2 stable Pods running for this Deployment. This is because we specified replicas as 2 in the manifest.

If there were other Deployments in the cluster, this command would list down all the available Deployments. Since there is only one Deployment in this cluster, it shows only one.

If we run kubectl get pods command, we can see the Pods as below.W

$ kubectl get pods
NAME                     READY   STATUS    RESTARTS       AGE
mysql-5788d89bbf-g5zk5   1/1     Running   0              23m
mysql-5788d89bbf-nvtpr   1/1     Running   0              23m

Here we can see the 2 Pods that were created by the Deployment.

We can create Deployments using imperative Kubectl commands as well, but its capabilities are quite limited.

What Happens If We Delete a Pod?

To understand the importance of Deployments, let’s do a small experiment and see what happens if we delete one of the Pods belonging to the Deployment.

I can delete a Pod using kubectl delete command as shown below. I need to specify the exact name of the Pod here.

$ kubectl delete pod mysql-5788d89bbf-g5zk5
pod "mysql-5788d89bbf-g5zk5" deleted

Once the Pod is deleted, if I run a kubectl get pods command immediately, I can see the output as shown below.

$ kubectl get pods
NAME                     READY   STATUS              RESTARTS        AGE
mysql-5788d89bbf-g5zk5   1/1     Terminating         0               23m
mysql-5788d89bbf-nvtpr   1/1     Running             0               23m
mysql-5788d89bbf-rjkfq   0/1     ContainerCreating   0               12s

Notice here that the deleted Pod is in Terminating state and a new Pod is being created in it’s place.

If I check again after a while, I can see that the deleted Pod is now gone and the new Pod is running.

$ kubectl get pods
NAME                     READY   STATUS    RESTARTS      AGE
mysql-5788d89bbf-nvtpr   1/1     Running   0             33m
mysql-5788d89bbf-rjkfq   1/1     Running   0             10m

So this is the most important thing about Deployments. As I mentioned in the beginning, the Deployment will try to maintain the desired state, which is 2 replicas. Since I deleted 1 replica, it will automatically schedule a new Pod to maintain the replica count specified in the manifest.

Now let’s run a kubectl get all command to see all the resources that were created by our Deployment.

$ kubectl get all
NAME                         READY   STATUS    RESTARTS      AGE
pod/mysql-5788d89bbf-nvtpr   1/1     Running   0             41m
pod/mysql-5788d89bbf-rjkfq   1/1     Running   0             18m

NAME                            TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
service/kubernetes              ClusterIP      10.96.0.1        <none>        443/TCP        14d

NAME                            READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/mysql           2/2     2            2           41m

NAME                                       DESIRED   CURRENT   READY   AGE
replicaset.apps/mysql-5788d89bbf           2         2         2       41m

As we can see, there are 2 Pods and the Deployment we created. In addition, we can also see the ReplicaSet as well. Notice that the DESIRED count shows as 2 to match the number of replicas we set in the Deployment manifest. You can ignore the Service as it is not relevant for our Deployment.

And that’s it for Deployments. The next article would be about another important resource type in Kubernetes – Services! When you have multiple applications deployed in a Kubernetes cluster, you need a way to enable communication with them, right? That’s what Services are for. Read the next article in the series Introduction to Services in Kubernetes.

Share this article if it was helpful!

Leave a Reply

Your email address will not be published. Required fields are marked *