Kubernetes Pod, ReplicaSet and Deployment
What is Kubernetes?
Kubernetes is an open source container orchestration engine for automating deployment, scaling, and management of containerized applications. It’s supported by all hyperscaller cloud providers and widely used by different companies. Amazon, Google, IBM, Microsoft, Oracle, Red Hat, SUSE, Platform9, IONOS and VMware offer Kubernetes-based platforms or infrastructure as a service (IaaS) that deploy Kubernetes.
Pod
A Pod is the smallest Kubernetes deployable computing unit. It contains one or more containers with shared storage and network. Usually, Pods have a one-to-one relationship with containers. To scale up, we add more Pods and to scale down, we delete pods. We don't add more containers to a pod for scaling purposes.
A Pod can have multiple containers with different types. We use a multi-container Pod when the application needs a helper container to run side by side with it. This helper container will be created when the application container is created and it will be deleted if the application container is deleted. They also share the same network (which means that they can communicate with each other using localhost) and same storage (using volumes).
We can create a pod using the following command,
$ kubectl run nginx --image nginx
nginx
using the nginx
docker imageThis command will create a Pod named nginx
using the nginx
docker image available in docker-hub. To confirm the pod is created successfully, We can run the following command that will list pods in the default namespace,
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 15s
We also can create Pods using yaml configuration file,
apiVersion: v1
kind: Pod
metadata:
name: redis-pod
spec:
containers:
- name: redis-container
image: redis
ports:
- containerPort: 6379
To create this redis Pod, run the following command,
$ kubectl create -f redis-pod.yaml
We also can create a multi-container Pod using yaml file,
apiVersion: v1
kind: Pod
metadata:
labels:
run: web-app
name: web-app
namespace: default
spec:
containers:
- image: web-app
imagePullPolicy: Always
name: web-app
- image: log-agent
imagePullPolicy: IfNotPresent
name: log-agent
priority: 0
restartPolicy: Always
To create this Pod, run the following command,
$ kubectl create -f multi-container-pod.yaml
This will deploy a logging agent alongside with our web app container to process the logs and send it to a central logging service for example. This pattern is called sidecar
.
Replicaset
A replicaset ensures that the specified number of Pods (replicas) are running at all times. If a Pod goes down for any reason, It automatically creates a new one using the template specified in the yaml file. Here is an example replicaset definition file,
apiVersion: apps/v1
kind: ReplicaSet
metadata:
labels:
app: nginx
name: nginx-replicaset
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx
imagePullPolicy: Always
name: nginx
dnsPolicy: ClusterFirst
restartPolicy: Always
To create this repicaset, run the following command,
$ kubectl create -f nginx-replicaset.yaml
Note: In order for the replicaset to work, the spec.selector.matchLabels
must match the spec.template.labels
because the replicaset uses this template to create pods when needed.
Deployment
A Deployment is the recommended choice when it comes to deploy stateless applications in Kubernetes. It automatically creates a replicaset under the hood to ensure the specified number of Pods are running at all times. It also describe how to deploy a new version using deployment strategy, here is an example yaml definition file for a deployment,
apiVersion: apps/v1
kind: Deployment
metadata:
name: example-nginx-deploy
labels:
app: nginx-deploy
spec:
replicas: 5
strategy:
type: RollingUpdate
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx-container
image: nginx:1.14.2
ports:
- containerPort: 8080
To create this nginx Deployment, run the following command,
nginx-deploy.yaml redis-pod.yaml
This will create a deployment named example-nginx-deploy
using the nginx
docker image available in docker-hub. To confirm the deployment is created successfully, We can run the following command that will list deployments in the default namespace,
$ kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
example-nginx-deploy 5/5 5 5 63s
This will create a ReplicaSet
under the hood to make sure all replicas are up all time,
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
example-nginx-deploy-bbc95f979 5 5 5 2m19s
It will also create 5 replicas using the same docker image specified in the yaml definition file, It uses the name of the deployment as a prefix for the name of the pod as shown below,
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
example-nginx-deploy-bbc95f979-2g7kh 1/1 Running 0 97s
example-nginx-deploy-bbc95f979-dlq8l 1/1 Running 0 2m6s
example-nginx-deploy-bbc95f979-gb97h 1/1 Running 0 97s
example-nginx-deploy-bbc95f979-n6xdj 1/1 Running 0 97s
example-nginx-deploy-bbc95f979-pwphh 1/1 Running 0 97s
Note: In order for the deployment to work, the spec.selector.matchLabels
must match the spec.template.labels
because the deployment uses this template to create pods when the current number of pods doesn't match the desired number of pods.
Scaling a Deployment
When creating a Deployment, We need to specify the number or replicas (default is one). Sometimes we need to scale up (increase the number of replicas) or scale down (decrease the number of replicas) manually. We can do this by running the following commands:
Scaling up example-nginx-deply
from 5 replicas to 10 replicas:
$ kubectl scale deployment example-nginx-deploy --replicas 10
deployment.apps/example-nginx-deploy scaled
example-nginx-deply
from 5 replicas to 10 replicasTo confirm, We can list all pods in the default namespace. As you can see below, There are five new replicas created.
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
example-nginx-deploy-5674dfc4b7-7zzpr 0/1 ContainerCreating 0 3s
example-nginx-deploy-5674dfc4b7-8dhhx 0/1 ContainerCreating 0 3s
example-nginx-deploy-5674dfc4b7-8v8mw 1/1 Running 0 32m
example-nginx-deploy-5674dfc4b7-95j9p 1/1 Running 0 32m
example-nginx-deploy-5674dfc4b7-gl97j 0/1 ContainerCreating 0 3s
example-nginx-deploy-5674dfc4b7-gtq4w 1/1 Running 0 32m
example-nginx-deploy-5674dfc4b7-kcj2f 0/1 ContainerCreating 0 3s
example-nginx-deploy-5674dfc4b7-m4wkq 0/1 ContainerCreating 0 3s
example-nginx-deploy-5674dfc4b7-tsw6s 1/1 Running 0 32m
example-nginx-deploy-5674dfc4b7-xfg7q 1/1 Running 0 32m
Scaling down example-nginx-deply
from 10 replicas to 3 replicas:
$ kubectl scale deployment example-nginx-deploy --replicas 3
deployment.apps/example-nginx-deploy scaled
example-nginx-deply
from 10 replicas to 3 replicasTo confirm, We can list all pods in the default namespace. As you can see below, There are only three replicas running and the rest are terminating.
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
example-nginx-deploy-5674dfc4b7-6pncj 1/1 Terminating 0 33s
example-nginx-deploy-5674dfc4b7-8v8mw 1/1 Running 0 39m
example-nginx-deploy-5674dfc4b7-95j9p 1/1 Running 0 39m
example-nginx-deploy-5674dfc4b7-9r7kv 1/1 Terminating 0 33s
example-nginx-deploy-5674dfc4b7-gtq4w 1/1 Running 0 39m
example-nginx-deploy-5674dfc4b7-lgwnj 1/1 Terminating 0 33s
example-nginx-deploy-5674dfc4b7-m4hc8 1/1 Terminating 0 33s
example-nginx-deploy-5674dfc4b7-zr485 1/1 Terminating 0 33s
Zero downtime Deployment
The main point of using a deployment in Kubernetes is that we can easily deploy a new version of our application using the deployment strategy defined in the deployment and we can also roll it back easily if this new version didn't work as expected with zero downtime.
We use .spec.strategy
to specify how we want to deploy this new version. We have two types of deployment strategies, Recreate
and RollingUpdate
. RollingUpdate
is the default value.
Recreate
Deployment Strategy means that all current pods with the old version will be killed and new pods will be created using the new version. This will cause the application to be down for sometime (depends on how long it takes to start the app). This is not the preferred option because of the downtime.
RollingUpdate
Deployment Strategy means that it will run both versions (the old and new one) for sometime until the deployment is completed. based on the .spec.strategy.rollingUpdate.maxUnavailable
and .spec.strategy.rollingUpdate.maxSurge
values, it will create new pods with the new version and delete old pods with the old version. if we configured this correctly, we can assure that the deployment is zero downtime deployment.
Here is an example to make it more clear,
spec:
replicas: 10
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 30%
maxUnavailable: 20%
In this example deployment, we have ten replicas and RollingUpdate strategy with maxSurge
is 30% (it can also be an absolute number) which means that this deployment can run 30% more replicas if needed (instead of creating 10 replicas, it can create 13 replicas temporary for both old and new versions) and maxUnavailable
is 20% (it can also be an absolute number) which is the maximum number of unavailable pods during the update process (20% of ten replicas is two pods)
The default value for maxSurge
and maxUnavailable
is 25%.
Rollout and Rollback a Deployment
To deploy a new version of a deployment we use the following command,
$ kubectl set image deployment/example-nginx-deploy nginx-container=nginx:1.16.1 --record
deployment.apps/example-nginx-deploy image updated
Note: The record flag is deprecated but there is no alternative to it yet. for more info check this.
To check the status of the deployment,
$ kubectl rollout status deployment/example-nginx-deploy
Waiting for deployment "example-nginx-deploy" rollout to finish: 3 out of 5 new replicas have been updated...
Waiting for deployment "example-nginx-deploy" rollout to finish: 3 out of 5 new replicas have been updated...
Waiting for deployment "example-nginx-deploy" rollout to finish: 3 out of 5 new replicas have been updated...
Waiting for deployment "example-nginx-deploy" rollout to finish: 3 out of 5 new replicas have been updated...
Waiting for deployment "example-nginx-deploy" rollout to finish: 3 out of 5 new replicas have been updated...
Waiting for deployment "example-nginx-deploy" rollout to finish: 4 out of 5 new replicas have been updated...
Waiting for deployment "example-nginx-deploy" rollout to finish: 4 out of 5 new replicas have been updated...
Waiting for deployment "example-nginx-deploy" rollout to finish: 4 out of 5 new replicas have been updated...
Waiting for deployment "example-nginx-deploy" rollout to finish: 4 out of 5 new replicas have been updated...
Waiting for deployment "example-nginx-deploy" rollout to finish: 4 out of 5 new replicas have been updated...
Waiting for deployment "example-nginx-deploy" rollout to finish: 2 old replicas are pending termination...
Waiting for deployment "example-nginx-deploy" rollout to finish: 2 old replicas are pending termination...
Waiting for deployment "example-nginx-deploy" rollout to finish: 2 old replicas are pending termination...
Waiting for deployment "example-nginx-deploy" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "example-nginx-deploy" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "example-nginx-deploy" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "example-nginx-deploy" rollout to finish: 4 of 5 updated replicas are available...
deployment "example-nginx-deploy" successfully rolled out
As you can see from the logs, it first created two pods with the new version. Once they are up and running it starts to terminate old pods (using old version) and create new pods (using new version) one by one to make sure the application is up and running at all times.
we can see this more clearly from the deployment events,
$ kubectl describe deployments
Name: example-nginx-deploy
Namespace: default
CreationTimestamp: Sun, 18 Sep 2022 11:42:41 +0200
Labels: app=example-nginx-deploy
Annotations: deployment.kubernetes.io/revision: 2
Selector: app=example-nginx-deploy
Replicas: 5 desired | 5 updated | 5 total | 5 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 20% max unavailable, 30% max surge
Pod Template:
Labels: app=example-nginx-deploy
Containers:
nginx:
Image: nginx:1.16.1
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: example-nginx-deploy-78788d9bbd (5/5 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 8m13s deployment-controller Scaled up replica set example-nginx-deploy-78788d9bbd to 2
Normal ScalingReplicaSet 8m13s deployment-controller Scaled down replica set example-nginx-deploy-bbc95f979 to 4
Normal ScalingReplicaSet 8m13s deployment-controller Scaled up replica set example-nginx-deploy-78788d9bbd to 3
Normal ScalingReplicaSet 4m57s deployment-controller Scaled down replica set example-nginx-deploy-bbc95f979 to 3
Normal ScalingReplicaSet 4m57s deployment-controller Scaled up replica set example-nginx-deploy-78788d9bbd to 4
Normal ScalingReplicaSet 4m53s deployment-controller Scaled down replica set example-nginx-deploy-bbc95f979 to 2
Normal ScalingReplicaSet 4m53s deployment-controller Scaled up replica set example-nginx-deploy-78788d9bbd to 5
Normal ScalingReplicaSet 4m49s deployment-controller Scaled down replica set example-nginx-deploy-bbc95f979 to 1
Normal ScalingReplicaSet 4m46s deployment-controller Scaled down replica set example-nginx-deploy-bbc95f979 to 0
Let's say nginx 1.16.1 is a buggy version and we want to rollback to the previous version. First we need to check the rollout history,
kubectl rollout history deployment/example-nginx-deploy
deployment.apps/example-nginx-deploy
REVISION CHANGE-CAUSE
1 <none>
3 kubectl set image deployment/example-nginx-deploy nginx=nginx:1.14.2 --record=true
4 kubectl set image deployment/example-nginx-deploy nginx=nginx:1.16.1 --record=true
To rollback to previous version, Run the following command,
kubectl rollout undo deployment/example-nginx-deploy
deployment.apps/example-nginx-deploy rolled back
Conclusion
A Pod is the smallest Kubernetes deployable computing unit. It contains one or more containers with shared storage and network. A replicaset ensures that the specified number of Pods (replicas) are running at all times. If a Pod goes down for any reason, It automatically creates a new one using the template specified in the yaml file. A Deployment is the recommended choice when it comes to deploy stateless applications in Kubernetes. It automatically creates a replicaset under the hood to ensure the specified number of Pods are running at all times. It can be easily scaled up or down using kubectl scale
command. It also enables us to easily rollout a new version of our application using the specified deployment strategy and rollback to previous version if needed.