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?
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 isapps/v1
at the time of writing this article. - Line 2 –
kind
defines the type of resource we are going to create. In this case thekind
isDeployment
.
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 labelapp=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
andkind
. 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 theselector
undermatchLabels
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 theselector
. 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 likekubectl get deployments -l app=mysql
to view all the deployments that hasapp=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.