Kubernetes-101: Deployments, part 1
Tags: kubernetes
Published: 2023-03-31 10:00:00

We continue our Kubernetes journey by learning about a new object: the **Deployment**. When we learn about deployments we will quickly find out that we also have to learn about something called a **ReplicaSet**. Two new objects in one go!

In this article I will use a new convention. When I write the name of an object in Kubernetes I will use Pascal case: _Pod_, _Deployment_, _ReplicaSet_, etc.

## Deployments

A Deployment is an _abstraction_ on top of ReplicaSets and Pods. We are familiar with Pods from before! More about ReplicaSets below. To simplify things a bit you could say that when you create a Deployment you specify two things:

1. What Pod should the Deployment create?
2. How many replicas of this Pod should be created?

That sounds simple enough! If that is all a Deployment does, what is the difference from manually creating a number of Pods using what we saw in the previous two articles about Pods? To understand the beauty with Deployments we must also introduce the Deployment controller.

What is a controller? A controller is an agent in something called the controller pattern. A controller runs in a never-ending loop (a _control-loop_) and continuously makes sure a system is in a desired state. An example of such a system is cruise-control in your car. The control-loop in that case makes sure the car holds a given speed (your desired state). In the case of Kubernetes Deployments the Deployment controller takes a desired state as specified in the Deployment manifest, and makes sure the current state corresponds to this desired state. If I specify that I want two replicas of my Pod, then the Deployment controller will make sure there are two replicas of my Pod. If I delete one of my Pods, then a new Pod will appear to take its place.

In the previous two articles I mentioned that we do not usually create individual Pods using the method I showed you there. We can create Pods in a better way, by using a Deployment! A Deployment is the _most_ common way to create Pods. It is not the only way of doing it, later in this series of articles I will introduce additional objects that can create Pods: **DaemonSets**, **StatefulSets**, and **Jobs**. So when do we use a Deployment? Whenever we want to create a Pod, potentially with many replicas[^daemonsets].

### Creating a deployment declaratively

Let us get our hands dirty and create a Deployment using a Deployment manifest. As in previous articles we will use a simple Nginx container as our application. A simple Deployment manifest looks like this:

```yaml
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:latest
          ports:
            - containerPort: 80
```

This manifest should look familiar because most of it was part of our Pod manifest as well. Let me highlight a few things from the manifest:

- `apiVersion` is `apps/v1`, this is different from when we defined a Pod. In general each kind of object could have its own API version, so you should check the API reference to know what is the correct API version to use.
- `kind` is set to `Deployment`
- `spec.replicas` is set to `3`. This is the second of the two points I said you configure for a Deployment.
- `spec.selector.matchLabels` is where you specify what labels on a Pod this Deployment will look for when determining the current state.
- `spec.template` is the Pod manifest again! This is the first of the two points I said you configure for a Deployment.
- `spec.template.metadata.labels` is where I provide labels for this Pod template, these labels should match with the labels I told the Deployment to look for in `spec.selector.matchLabels`.

An important concept has casually been introduced here: **labels**. Labels are key-value pairs that are usually used for different kinds of selections. We will encounter labels a lot more in future articles!

A notable missing property in the `spec.template.metadata` part is the `name`. We do not specify a name for the Pod because then we would have a problem when we try to create several replicas of the Pod, since the Pod names must be unique.

We can create this Deployment by using the same command we used to create a Pod from our Pod manifest, with `kubectl apply`:

```console
$ kubectl apply -f deployment.yaml

deployment.apps/nginx-deployment created
```

We can list all of our Deployments with `kubectl get deployments`:

```console
$ kubectl get deployments

NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           21s
```

Similarly to how we could use the short version `po` instead of `pods` we can write `deploy` instead of `deployments`:

```console
$ kubectl get deploy

NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           32s
```

If you followed my idea about setting an alias `alias k="kubectl"` in your terminal you can shorten the previous command to `k get deploy`. If you want to take it to the extreme you could define another alias: `alias kgd="kubectl get deploy"` to shorten it even more!

We can get all details about our Deployment by using `kubectl describe`:

```console
$ kubectl describe deployment nginx-deployment
```

The output of this command is verbose, so I will not repeat it here. What happens if we list our Pods now? Let's see:

```console
$ kubectl get pods

NAME                               READY   STATUS    RESTARTS   AGE
nginx-deployment-cd55c47f5-b695d   1/1     Running   0          40s
nginx-deployment-cd55c47f5-fvjtt   1/1     Running   0          40s
nginx-deployment-cd55c47f5-ql8pq   1/1     Running   0          40s
```

We have three copies of them! We can see that the names of the Pods start with the name of the Deployment: `nginx-deployment`. Where does the `cd55c47f5` part come from? Let us run `kubectl get` to see if we have the thing called a ReplicaSet:

```console
$ kubectl get replicasets

NAME                         DESIRED   CURRENT   READY   AGE
nginx-deployment-cd55c47f5   3         3         3       2m53s
```

So it seems like we have a ReplicaSet with the name of `nginx-deployment-cd55c47f5`. This is the resource that our Deployment actually created for us. The Pods themselves were technically created by this ReplicaSet object. See that the name of the ReplicaSet is `nginx-deployment-cd55c47f5`? That is where the `cd55c47f5` part of the Pod names come from. The last part of the Pod names are autogenerated and different for each Pod (`b695d`, `fvjtt`, and `ql8pq`).

Similarly to how we can shorten `pods` to `po`, and `deployments` to `deploy`, we can shorten `replicasets` to `rs`. So the previous command could have been run as `kubectl get rs`. This time the short version really pays off, imagine having to write `replicasets` all the time?

What happens if we delete one of our Pods? We delete the Pod named `nginx-deployment-cd55c47f5-ql8pq` using `kubectl delete pod`:

```console
$ kubectl delete pod nginx-deployment-cd55c47f5-ql8pq

pod "nginx-deployment-cd55c47f5-ql8pq" deleted
```

That worked. What happens if we list all our Pods again?

```console
$ kubectl get pods

NAME                               READY   STATUS    RESTARTS   AGE
nginx-deployment-cd55c47f5-b695d   1/1     Running   0          6m54s
nginx-deployment-cd55c47f5-fvjtt   1/1     Running   0          6m54s
nginx-deployment-cd55c47f5-mqrkl   1/1     Running   0          40s
```

A new Pod has appeared! The controller pattern is at play here. If we lose a Pod for any reason, a new one appears to replace it. One could almost say that we have a system that is resilient to failure[^failure].

How do we delete our Pods, if they keep reappearing all the time? We must delete the Deployment, and we do that with `kubectl delete`:

```console
$ kubectl delete -f deployment.yaml

deployment.apps "nginx-deployment" deleted
```

What would happen if we delete the ReplicaSet that the Deployment has created for us? I am not really sure, I just think that you will end up having to manually delete your Pods in the end. It is _not_ recommended to do any modifications to the ReplicaSet resource. If you need to change something, change the Deployment and re-apply the Deployment manifest.

There are a few more interesting things we can do with Deployments, but we will look at some of them in the next article.

Once we have a Deployment, do we have a redundant system? Partly. We have a way to set up several replicas of our Pods, but we do not yet have a way to distribute incoming traffic between our replicas. In a future article we will learn about Kubernetes **Services** which will handle this for us.

## Loose end: What is a ReplicaSet?

We have briefly seen the ReplicaSet in the previous section, where we were supposed to talk about Deployments and not about ReplicaSets. However, it is difficult not to talk about ReplicaSets when describing what a Deployment is. A ReplicaSet has one task: keep a given number of identical (replicas) pods running at all time. How does it know what pod it should create replicas of? If we take a look at the manifest for a ReplicaSet[^replicaset] it will become clear:

```yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: web-rs
  labels:
    tier: web
spec:
  replicas: 3
  selector:
    matchLabels:
      tier: web
  template:
    metadata:
      labels:
        tier: web
    spec:
      containers:
        - name: nginx
          image: nginx:latest
```

As we can see this manifest looks almost identical to the Deployment manifest above, so we don't have to go through the details of it. The reason they look so similar is because I have not added any of the unique details for any of the manifests. As mentioned earlier we will see more of the Deployment manifest in the next article in this series.

A general observation we can make about Kubernetes manifests at this stage is that each manifest has the following properties:

- `apiVersion`
- `kind`
- `metadata.name`
- `spec`

So each manifest specifies an API version, what kind of object we are working with, a name that we can use to identify the object, and a specification that is different depending on what kind of object we are creating. These properties are good to remember!

Back to ReplicaSets: in real-life it is not very common to directly create ReplicaSets. It is common that you create Deployments that create ReplicaSets for you. So at this stage of your Kubernetes journey it is not relevant to focus too much on the ReplicaSet, you can instead think of the Deployment as the one who is in charge. But I still want you to know how this works behind the scenes. So let us end this section with an illustration of what happens when we create a Deployment with a desired Pod count of three:

![deployment](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y01uoqxiwoq0sysew2d3.png)

## Summary

Even though Deployments are relatively straight-forward we managed to learn a lot about them in this article. We now know that a Deployment creates something called a ReplicaSet, and the ReplicaSet in turn creates a configurable number of Pods. We now know that when we want to create a Pod, we should really be using a Deployment instead. We know how to declaratively create a Deployment, and we know how to use `kubectl` to list our Deployments and to get detailed information about a certain Deployment.

In the next article in this series I will expand a bit about what more we can do with Deployments.

[^replicaset]: The full API is available at https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/replica-set-v1/
[^daemonsets]: Except when we require the special abilities that DaemonSets and StatefulSets offer!
[^failure]: That is only partly true. If you have a serious problem in your application that makes your containers crash, your Pods will just keep on crashing over and over again. The Deployment makes sure new Pods keep appearing, but you will need to fix your application!