Kubernetes Deployment: Strategies, Explanation, and Examples
In Kubernetes, you can deploy a container in a pod with a simple one-line command. This simplicity is what makes Kubernetes a great container orchestration tool. However, when you want your application to scale up or down, maintain replicas, and use custom services, you need a deployment.
In this blog post, we’ll cover the role of deployments, their benefits, how to create them, and how to use them.
What Is Kubernetes Deployment?
According to the Kubernetes documentation:
A Deployment provides declarative updates for Pods and ReplicaSets.
You describe a desired state in a Deployment, and the Deployment Controller changes the actual state to the desired state at a controlled rate.”
Notice the words highlighted with bold text. Deployments are effectively managing a collection of Pods. Their focus is on "declarative updates," Going from point A to point B, from current state to desired state "at a controlled rate."
At the end of the day, Kubernetes is a complex automation machine. We feed it definitions and tell it, "Hey, I want these objects, and I want things to run this way!" It makes sure that objects exist and that things run that way.
Try the Kubernetes Deployments Lab for free
Try the Kubernetes Services Lab for free
The Role Kubernetes Deployments
It helps us change our applications' definitions. For instance, assume we don't want 100 Pods to run the Nginx version 1.22.0
anymore - we want the newer version, 1.22.1
. Deployments ensure that there are smooth transitions from the old definition to the newer definition.
Deployments can also be used to scale up and down the number of pods/replicas. For instance, we might want to run 100 Pods in the daytime when incoming traffic is high and a lower number at night when traffic is low. Deployments make it possible to automatically adjust the number of pods based on the users' traffic.
In a nutshell, Deployments help us periodically update large collections of Pods, scale the number of Pods as needed, and even let us undo an update action if things go wrong. This is called a rollback. We'll explore all of these possibilities in the last half of our blog; see actual commands we can use to do these things.
Deployment Strategies
Now imagine a Deployment that only handles 2 Pods. Let's say we want to upgrade the container definitions in our Pods. Instead of Nginx 1.22.0
, we will use 1.22.1
. There are two problems:
- Our Pods are actively receiving and processing users' requests.
- Kubernetes has to destroy the old Pods and create new ones that match our new definition.
But let's say this is fast. Kubernetes will destroy the two pods with Nginx 1.22.0
. In a few seconds, two new pods with Nginx 1.22.1
are created. The problem? In those few seconds, while this happens, application users can't reach our service.
But Deployments are smarter than that. They support different deployment strategies that tell Kubernetes how to transition a collection of Pods. Let's see an example that would solve the problem we mentioned before.
Rolling Update Strategy (also Known as Incremental)
Now imagine the same situation as above. 2 Pods that we want to transition to run a newer version of some app. Instead of blindly destroying the old Pods, this deployment strategy will be more organized.
First of all, it will not touch the old Pods in the beginning. It will leave them alone so they can do their job, serving our users, even while we update. The Deployment will start out with a safer action.
It will add the new Pods. Once one new Pod is created, it can start to reroute traffic from the old pods to the new one, running the newer app. Since one new pod is available, one or more old ones can be safely removed. This way, our users can always reach our service.
While Kubernetes is replacing Pods, some users will reach the old Pods. But once the new Pods are ready, all users' traffic will be directed to them. Old pods will be gradually removed until they're all gone.
For a setup with 100 Pods, it could go something like this: 3-4 new Pods get created, 3-4 old Pods get removed. Next, this step repeats, again and again, until bit by bit, all new Pods are added, and all old Pods are removed. At one point, we'd be in a situation where 50 new Pods are available, but 50 old Pods are still around. So it could be that 50% of our users are still reaching the old Pods, while 50% are reaching the new ones. This continues until 100% of old Pods are replaced with new Pods.
What is the advantage of this strategy? There is no downtime for our users.
The disadvantage? It can take a while until all Pods are replaced.
Here is an example of how we could pick this Deployment strategy in a manifest file:
spec:
replicas: 10
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
template:
...
maxSurge refers to the total number of Pods (old + new pods) that can exist during the update process. For instance, if we have a Deployment where we declare we want 100 Pods total. With a maxSurge of 25%, there can never be more than 125 total Pods during this update process.
maxUnavailable refers to how many Pods can be unavailable during the update process. Let's say we have the same 100 Pod Deployment. If maxUnavailable is set to 25%, there can never be fewer than 75 Pods during the update. This includes both old Pods and new Pods. It limits how many old Pods Kubernetes can remove while transitioning the Deployment to the new state.
Recreate Strategy
This is a two-stage process:
- First, all old Pods are deleted.
- Once all old Pods are destroyed, the new Pods are created.
This leads to downtime because once the Pods are deleted, the service is no longer available. Our users need to wait until all the new Pods are created to access the service again. That's why this strategy isn't recommended for production environments. Preferably, it should only be applied in testing environments.
Here's an example of a manifest file where we use the "Recreate" strategy for deployment.
spec:
replicas: 3
strategy:
type: Recreate
...
Blue / Green (or Red / Black) Deployments
With this strategy, you leave the old Deployment alone. This will be the so-called "blue version." Next, you also launch a "green version" with the new Deployment. So you have blue and green running side by side. The disadvantage is that you have double the number of pods you require. If there were 300 Pods in your blue Deployment, you'd also have 300 in your green one. However, only half of them are actively used, 300 in this case. But the advantages are many.
First, you can freely run tests on your new deployment and see if things run as expected. Once satisfied with the tests, you can transition your users to the green Deployment. And you get to the biggest advantage of this strategy: instant migration.
You just tell Kubernetes to reroute all users' traffic from the blue Deployment to the green one. And that's all there is to it. Instant upgrade, essentially, as far as user experience is concerned. Of course, the last step would be to remove the old blue Deployment.
Here's an example of how we could switch traffic from a blue to a green Deployment. In a nutshell, we tell Kubernetes: "Hey, when you use this Service, send traffic to apps with this selector label."
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
selector:
app: myapp
version: "03"
Canary
The canary deployment strategy involves routing a small group of users to the new version of an application, running on a smaller set of pods. This approach tests functionality on a smaller group of users instead of impacting the entire user base. Doing this contains the blast radius in the event of errors.
Thus, canary deployments help understand how new features will impact the overall system operation while containing the possible spillover to a small group of users.
This strategy is adopted to test new functionality in a production environment on real users. Once the application is tested and the results are satisfactory, replicas of the new version are scaled up while the old version is scaled down. We're essentially replacing the old version with the new one, but gradually.
Canary deployments can be rolled back very quickly if necessary. That's because they start out with a small percentage of new Pods, so a rollback has very few new Pods to revert.
Dark deployments or A/B Deployments
A dark deployment strategy is a variation of the canary deployment. The difference between a dark deployment and a canary is that dark deployment usually deals with features in the front end rather than the back end, as with canaries.
Dark deployments are also known as A/B Deployments. This approach introduces a new feature to a small set of users instead of all the users. The users are unaware they are treated as testers for a new feature; they are in the “dark” about their role as testers. At the same time, metrics are collected to track the user experience. Things like these can be monitored:
- How are the users interacting with the feature?
- Are they finding it intuitive and easy to use?
- How many choose to disable this new feature?
- And so on.
To learn more about when to use each of these deployment strategies, Check out this Kubernetes course:
Creating a Kubernetes Deployment
There are two ways we can create a Deployment. One uses the imperative approach, and another uses the declarative approach. We will cover both approaches.
In a nutshell, the imperative is when we use commands, and the declarative is when we write YAML manifest files.
How to Create a Deployment Using the Imperative Approach
To create a deployment, we can use the command below. We'll need to specify the deployment_name
and the image_name
that we want to be used in the Pods.
kubectl create deployment <deployment_name> --image=<image_name>
Creating an Nginx Deployment using the imperative approach
Using the imperative approach, we can now create an Nginx Deployment:
kubectl create deployment mywebsite --image=nginx
This command creates a deployment called "mywebsite". The deployment will initially have an nginx Pod. We can scale the deployment to the desired number of Pods we want. Kubernetes will do the work of maintaining the desired state of the deployment.
We can check the deployment details using:
$ kubectl get deployment mywebsite
Deleting the Nginx Deployment
We can delete the Deployment with the following command:
$ kubectl delete deployment/mywebsite
Note how we first had to specify the resource type, "deployment
", before actually specifying the deployment's name, "mywebsite
".
$ kubectl delete <resource_type>/<resource_name>
The resource_type
can be anything: Pod, Deployment, Service, Ingress, Config Map, Secret, Persistent Volume, etc.
How to Create a Deployment Using the Declarative Approach
Using the declarative approach, we can define a Deployment along with its configuration in a YAML file. Here's an example:
apiVersion: apps/v1
kind: Deployment
metadata:
name: mywebsite
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.22.0
ports:
- containerPort: 80
This configuration can be applied to create a Deployment exactly like the one we created using the imperative approach. If we save the content above in a file called deploy.yaml, we could then apply this to Kubernetes with the following command:
kubectl apply -f deploy.yaml
Update Kubernetes Deployment
We can use the set
command to make changes to an object's image, resources (compute resources such as CPU and memory), or other configuration fields.
For example, to update a Deployment from nginx version 1.22.0
to 1.22.1
run the following command:
kubectl set image deployment mywebsite nginx=nginx:1.22.1
Deployment Rollouts: Check History, Pause, Resume, or Undo/Rollback Changes
A Kubernetes rollout is the process of deploying new changes to a set of Pods managed by a Deployment. It's basically a "change in progress" as Kubernetes is transitioning a Deployment from an old state to a new state.
We can check the status of a rollout
using the rollout command as shown below:
kubectl rollout status deployment mywebsite
Waiting for deployment "mywebsite" rollout to finish: 1 out of 3 new replicas have been updated...
To pause a rollout, we can use the following command:
kubectl rollout pause deployment mywebsite
To resume a deployment, we can use the following command:
kubectl rollout resume deployment mywebsite
To see the history of rollouts a Deployment has been through:
kubectl rollout history deployment mywebsite
The history will show a list of so-called "revisions
". We'll see what these are in the next command after this one.
To see the details of a specific revision, for example, revision 1:
kubectl rollout history deployment mywebsite --revision 1
Whenever we make a change to a deployment, we arrive at something called a new revision. This serves as a sort of snapshot, a state we can return to if there's ever a need. To undo a deployment change and revert to the previous revision, we can use the following command:
kubectl rollout undo deployment mywebsite
To revert to a specific revision, we can use the following command:
kubectl rollout undo deployment mywebsite --to-revision 1
Scaling Kubernetes Deployment
Manually
Once a deployment is created, we can use the scale
command to scale the deployment up or down to match our requirements. The following command can be used to scale the deployment to 3 Pods (called replicas since these are identical):
kubectl scale deployment mywebsite --replicas=3
If we want to delete all the Pods in the Deployment without deleting the Deployment itself, we can scale the replicas to 0. This is a neat trick for keeping the deployment available while effectively deleting all Pods.
kubectl scale deployment mywebsite --replicas=0
To verify if the scale command took effect, you can check the status of this deployment:
kubectl get deployment mywebsite
NAME READY UP-TO-DATE AVAILABLE AGE
mywebsite 0/0 0 0 7m48s
By Configuring an Auto-Scaling Rule
A HorizontalPodAutoscaler (HPA) automatically updates a Deployment or StatefulSet. The HPA auto scales the resources based on the demand. HPA does not apply to objects that can't be scaled like a DaemonSet.
The HPA spins up new Pods in response to an increase in the load. Scaling is performed based on memory or CPU usage. We have the option of defining the max and min scale limits, which means that we can set the maximum number of Pods that can be scaled and the minimum number of Pods. Once the load decreases, the HPA scales down the pods.
Now, we can deploy an application on the cluster and then enable the horizontal pod autoscaler. This can be done using the following command:
kubectl autoscale deployment mywebsite --cpu-percent=50 --min=1 --max=10
The --cpu-percentage
flag is used to define the CPU percentage based upon which the autoscaling happens. For example, if the mywebsite
Pods exceed 50% CPU the autoscaler will create a new Pod. The --min
and --max
flags are used to define the minimum and maximum number of Pods that the autoscaler can scale to.
We can check the HPA by running the kubectl get hpa
command as shown below:
$ kubectl get hpa
NAME REFERENCE TARGET CURRENT MINPODS MAXPODS AGE
mywebsite Deployment/mywebsite/scale 50% 310% 1 10 2m
We hope this gives you a basic idea of what you can do with Deployments. If you're new to Kubernetes, you can check out this Kubernetes Course for Beginners.
More on Kubernetes:
- How To Scale DevOps: People, Processes, and Platforms
- 4 DevOps Metrics You Need To Track Quality And Performance
- Top 15 DevOps Automation Tools You Should Learn in 2024
- 10 Essential DevOps Tools You Should Learn in 2024
- 17 DevOps Monitoring Tools to Learn in 2024
- Which Certifications Should You Complete for Your Next Kubernetes Job in 2024?
- Understanding DevOps for Absolute Beginners