The last part of this Kubernetes 101 series focused on ReplicaSets and Deployments and why it is better to use Deployments rather than Pods to manage your Kubernetes applications. In this part of the series, we will walk you through different types of Deployment strategies to give you the insight of their functionalities as well as to know which particular strategy would suit best a specific use case. It is advisable to have prior knowledge of the Kubernetes objects like Pods, Deployments and ReplicaSets to follow the hand-on practice.
Deployment Strategies
A Kubernetes Deployment strategy encompasses the methods of creating, upgrading, or downgrading to a different version of a Kubernetes application. For those that are familiar with software installation and update on PCs and laptops, the software application that is being updated remains inaccessible to the user during update or installation. Similarly in Kubernetes, the application will remain inaccessible to the users during an upgrade if a proper Deployment Strategy is not used during the creation of the application. In Kubernetes, new versions are continuously developed and deployed, which means at some point you will be upgrading from an old version to the new one. However, the applications must always be accessible to the users; thus, the need for a user-friendly upgrade strategy. In this guide, we will be looking at two strategies and outline which one best suits our applications.
Types of Deployment Strategies
Recreate: This strategy type will first destroy the existing Pods before new ones are created. During the period when the old application is down, and the new one is being brought up, the application is inaccessible to users. Looking at our previous exercises, we used the recreate strategy as it is in the YAML file. As seen under the events part of the Deployment description in part 6A, the three instances of the Pod were scaled down simultaneously before creating the new ones. Don’t forget that we updated the Deployment twice.
During the first update, the recreate strategy scaled the Pod my-deployment-97cfc859f down to 0, then brought up my-deployment-79f645dc59, and scaled it up to 3 Replicas. This was replicated during the second update by scaling the Pod my-deployment-79f645dc59 down to 0, bringing up a new one my-deployment-5997f87f5f, and scaling it to 3 Replicas. This type of strategy does not allow rolling back to the previous version because it has been destroyed before an update is performed. However, don’t fret; there is another Deployment strategy that overcomes this limitation.
RollingUpdate: In contrast to the recreate strategy, the RollingUpdate strategy, which can also be referred to as zero downtime rollouts, is the process of updating a Kubernetes object or application sequentially by replacing each previous Pod instance with a new one. Its functionalities entail taking down the older Pod instance and bringing up a new one consecutively during an upgrade by cycling through updating the Pods according to the parameters: maxSurge and maxUnavailable.
RollingUpdate gives users unhindered access to their applications during an update and allows rolling back to the previous version in case of an error during upgrade, bugs with the new version, or if the updated version is unstable. This strategy type is the default.
maxSurge: This is optional with the default value set to 25% or 1. It specifies the maximum number of Pods that can be created above the desired number.
maxUnavailable: This is also optional with the default value set to 25% or 1. It specifies the maximum number of unavailable Pods during an update.Both properties’ values can be represented using either an absolute number(2) or percentage(25%).
How To Create a Deployment with the RollingUpdates Strategy
Step 1) Modify your configuration file, copy, and paste the below configuration with correct indentation as the values of the strategy property of your deployment YAML file. Save and exit the terminal:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
The complete YAML file will look like this:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-deployment-container
image: nginx
Step 2) Check the status of the Pod. We can see all of our Pods are running:
$kubectl get pods
NAME READY STATUS RESTARTS AGE
my-deployment-97cfc859f-8m4zk 1/1 Running 0 59s
my-deployment-97cfc859f-b9j8w 1/1 Running 0 59s
my-deployment-97cfc859f-hbmxn 1/1 Running 0 59s
Step 3) Check the status of the Deployment:
$kubectl get deployments my-deployment
NAME READY UP-TO-DATE AVAILABLE AGE
my-deployment 3/3 3 3 51s
Step 4) Check the status of the latest rollout:
$ kubectl get replicasets
NAME DESIRED CURRENT READY AGE
my-deployment-97cfc859f 3 3 3 9m21s
Step 5) Next, you will update the Deployment’s container image from nginx to nginx:1.18.0:
$kubectl set image deployment/my-deployment my-deployment-container=nginx:1.18.0 --record
deployment.apps/my-deployment image updated
Check the Pod status. Once again we have all the pods running.
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
my-deployment-79f645dc59-2ft5f 1/1 Running 0 19s
my-deployment-79f645dc59-gcfgt 1/1 Running 0 34s
my-deployment-79f645dc59-wtd8k 1/1 Running 0 23s
Check the rollout current status. We can see that we now have two deployments. The new one and the old one.
$kubectl get replicaset
NAME DESIRED CURRENT READY AGE
my-deployment-79f645dc59 3 3 3 7m6s
my-deployment-97cfc859f 0 0 0 8m23s
Check the Deployment description:
$kubectl describe deployments my-deployment
Name: my-deployment
Namespace: default
CreationTimestamp: Thu, 30 Jul 2020 11:39:18 +0000
Labels: <none>
Annotations: deployment.kubernetes.io/revision: 2
kubernetes.io/change-cause: kubectl set image deployment/my-deployment my-deployment-container=nginx:1.18.0 --record=true
Selector: app=my-app
Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 0 max unavailable, 1 max surge
Pod Template:
Labels: app=my-app
Containers:
my-deployment-container:
Image: nginx:1.18.0
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: my-deployment-79f645dc59 (3/3 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 2m23s deployment-controller Scaled up replica set my-deployment-97cfc859f to 3
Normal ScalingReplicaSet 66s deployment-controller Scaled up replica set my-deployment-79f645dc59 to 1
Normal ScalingReplicaSet 55s deployment-controller Scaled down replica set my-deployment-97cfc859f to 2
Normal ScalingReplicaSet 55s deployment-controller Scaled up replica set my-deployment-79f645dc59 to 2
Normal ScalingReplicaSet 51s deployment-controller Scaled down replica set my-deployment-97cfc859f to 1
Normal ScalingReplicaSet 51s deployment-controller Scaled up replica set my-deployment-79f645dc59 to 3
Normal ScalingReplicaSet 46s deployment-controller Scaled down replica set my-deployment-97cfc859f to 0
Looking at the events property in the Deployment description, we see that the update was done sequentially. When the Deployment was created, a ReplicaSet my-deployment-97cfc859f was also created and scaled up to 3 replicas. After the update, another ReplicaSet my-deployment-79f645dc59 was created and scaled up to 1 replica, while the old ReplicaSet my-deployment-97cfc859f was scaled down to 2 replicas. It continued by scaling the new ReplicaSet to 2 and the old one to 1 replica respectively until the old one has been scaled down to 0 and the new one scaled up to 3 which is the desired number of the replicas. Finally, the new ReplicaSet will have 3 replicas available, while the old ReplicaSet will have 0.
Rolling Back to a Previous Version
There are times the new version might not be stable or may be filled with bugs. In this case, the application can be rolled back to the previous working or stable version. This can be performed by following these steps.
Step1) Check the rollout history:
$kubectl rollout history deployment my-deployment
deployment.apps/my-deployment
REVISION CHANGE-CAUSE
1 <none>
2 kubectl set image deployment/my-deployment my-deployment-container=nginx:1.18.0 --record=true
We updated the Deployment container image from nginx to nginx:1.18.0. However, you can revert to the previous version(nginx) from the current one(nginx:1.18.0).
Step 2) To rollback to the previous version:
$ kubectl rollout undo deployment my-deployment
deployment.apps/my-deployment rolled back
Step 3) To confirm the current version, check the Deployment description:
$kubectl describe my-deployment
Name: my-deployment
Namespace: default
CreationTimestamp: Thu, 30 Jul 2020 21:27:31 +0000
Labels: <none>
Annotations: deployment.kubernetes.io/revision: 3
Selector: app=my-app
Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 0 max unavailable, 1 max surge
Pod Template:
Labels: app=my-app
Containers:
my-deployment-container:
Image: nginx
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: my-deployment-97cfc859f (3/3 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 10m deployment-controller Scaled up replica set my-deployment-97cfc859f to 3
Normal ScalingReplicaSet 9m51s deployment-controller Scaled up replica set my-deployment-79f645dc59 to 1
Normal ScalingReplicaSet 9m44s deployment-controller Scaled down replica set my-deployment-97cfc859f to 2
Normal ScalingReplicaSet 9m44s deployment-controller Scaled up replica set my-deployment-79f645dc59 to 2
Normal ScalingReplicaSet 9m41s deployment-controller Scaled down replica set my-deployment-97cfc859f to 1
Normal ScalingReplicaSet 9m41s deployment-controller Scaled up replica set my-deployment-79f645dc59 to 3
Normal ScalingReplicaSet 9m38s deployment-controller Scaled down replica set my-deployment-97cfc859f to 0
Normal ScalingReplicaSet 53s deployment-controller Scaled up replica set my-deployment-97cfc859f to 1
Normal ScalingReplicaSet 50s deployment-controller Scaled down replica set my-deployment-79f645dc59 to 2
Normal ScalingReplicaSet 44s (x4 over 50s) deployment-controller (combined from similar events): Scaled down replica set my-deployment-79f645dc59 to 0
The new container image from the description is nginx which was used to create the Deployment before the update.
Deployment Pause and Resume
You can pause a Deployment to make multiple changes and fixes and then resume it. The pause will stop the rollouts trigger while the changes are being made. It will also put all the changes and fixes made in a queue until it is resumed.We will use our previous Deployment YAML manifest file for this exercise together with a running Kubernetes cluster and kubectl command-line tool configured to talk to the cluster. You can find out more about creating a Kubernetes cluster using our open source cluster lifecycle management tool KubeOne here. Follow the steps below to pause and resume a Deployment.
Step 1) Create a Deployment:
$ vim my-deployment.yaml
Copy the Deployment manifest YAML file, paste, save, and exit the terminal.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-deployment-container
image: nginx
Then run:
$kubectl create -f my-development.yaml ## To create the Deployment
deployment.apps/my-deployment created
Step 2) Check the Deployment:
$kubectl get deployments my-deployment
NAME READY UP-TO-DATE AVAILABLE AGE
my-deployment 3/3 3 3 4m13s
Step 3) Check the description:
$ kubectl describe deployments my-deployment
Name: my-deployment
Namespace: default
CreationTimestamp: Fri, 31 Jul 2020 10:15:47 +0000
Labels: <none>
Annotations: deployment.kubernetes.io/revision: 1
Selector: app=my-app
Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 0 max unavailable, 1 max surge
Pod Template:
Labels: app=my-app
Containers:
my-deployment-container:
Image: nginx
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: my-deployment-97cfc859f (3/3 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 8m6s deployment-controller Scaled up replica set my-deployment-97cfc859f to 3
As seen above, there is a running Deployment with 3 Pods.
Step 4) Pause the Deployment:
$kubectl rollout pause deployment.v1.apps/my-deployment
deployment.apps/my-deployment paused
Step 5) You can check if the Deployment is paused under the condition parameter in the Deployment description:
$kubectl get description my-deployment
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing Unknown DeploymentPaused
OldReplicaSets: <none>
NewReplicaSet: my-deployment-97cfc859f (3/3 replicas created)
Step 6) Update the Deployment by changing the container image to nginx:1.18.0 and scale up the replica to 5. You can make as many changes as you want before resuming the Deployment. To change the container image version to nginx:1.18.0, run the below command:
$ kubectl set image deployment/my-deployment my-deployment-container=nginx:1.18.0 --record
deployment.apps/my-deployment image updated
To scale up the Replicas to 5 from 3, run:
$kubectl scale deployment.v1.apps/my-deployment --replicas=5
deployment.apps/my-deployment scaled
Step 7) Check the rollout history:
$kubectl rollout history deployment.v1.apps/my-deployment
deployment.apps/my-deployment
REVISION CHANGE-CAUSE
1 <none>
No activities to display
Step 8) Check the status of the Deployment:
$kubectl get deployments my-deployment
NAME READY UP-TO-DATE AVAILABLE AGE
my-deployment 5/5 0 5 31m
The deployment has been scaled up to 5 replicas; however, the “UP-TO-DATE” column shows 0; which means that the 5 replicas are still in the queue and have not been updated because of the Deployment pause state.
Step 9) To resume the Deployment:
$ kubectl rollout resume deployment.v1.apps/my-deployment
deployment.apps/my-deployment resumed
Check the status of the Pods:
$kubectl get pods
NAME READY STATUS RESTARTS AGE
my-deployment-79f645dc59-vkcvg 0/1 ContainerCreating 0 12s
my-deployment-97cfc859f-5sqmz 1/1 Running 0 5m41s
my-deployment-97cfc859f-6w44n 1/1 Running 0 5m41s
my-deployment-97cfc859f-95r22 1/1 Running 0 2m2s
my-deployment-97cfc859f-f5rmn 1/1 Running 0 2m2s
my-deployment-97cfc859f-sqsmk 1/1 Running 0 5m41s
The Deployment has started creating a new Pod. Leave it for a few seconds and check the status of the Pod again.
$kubectl get pods
NAME READY STATUS RESTARTS AGE
my-deployment-79f645dc59-44lqk 1/1 Running 0 36s
my-deployment-79f645dc59-c55bk 1/1 Running 0 16s
my-deployment-79f645dc59-jmbbp 1/1 Running 0 30s
my-deployment-79f645dc59-kbvkr 1/1 Running 0 23s
my-deployment-79f645dc59-vkcvg 1/1 Running 0 49s
All the Pods have now been created and are running. The old ones have been replaced with the new ones.
Check the status of the Deployment
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
my-deployment 5/5 5 5 6m40s
The Deployment is now up to date with the desired replicas
Check the rollout history:
$kubectl rollout history deployment.v1.apps/my-deployment
deployment.apps/my-deployment
REVISION CHANGE-CAUSE
1 <none>
2 kubectl set image deployment/my-deployment my-deployment-container=nginx:1.18.0 --record=true
To clean up:
$kubectl delete my-deployment.yaml
deployment.apps "my-deployment" deleted
Check if it has been deleted:
$kubectl get deployments my-deployment
No resources found in default namespace.
Using Deployment Strategies
Deployment strategies are an essential feature in Kubernetes that give you more control of your application on how an update should be performed. Having seen the functionalities of both strategies, you can see that the RollingUpdate strategy usually suits applications better than the recreate strategy because we do not want the users to experience any downtime during an update.
Next, in our series, we will look at how to expose your app to the outside world using services. In that guide, you will learn Kubernetes services, types, and their usage. Furthermore we will go into details on keeping the state of app using volumes and volumeMounts. We will walk you through different types of volume and how to use them in a Pod. We’d love to hear from you! Please contact us with any thoughts or questions you might have about Deployments.
Learn More
- Read more on Deployment and ReplicaSets.
- Visit the official Kubernetes website for more resources on Deployment strategies.
- Learn more on RollingUpdate strategy here and here.