Part 1 and Part 2 covered pods and services, which are two foundational building blocks to run an app in Kubernetes. This post will describe a third piece called Deployment
that helps you run the pod with more reliability.
A Deployment
is a better way to run your pod, especially in a production environment. It describes how the pod is deployed, accessed, upgraded and scaled. Under the covers it also creates a helper, called a ReplicaSet
, to ensure the pod continues running in its requested state. The ReplicaSet
is the critical secret sauce that provides auto-healing. If an instance of the pod terminates, the ReplicaSet
starts a new instance to replace it.
Here’s a deployment object spec to run the hello pod covered in Part 1 that we’ll store in a file named hello-world-deployment.yaml
.
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-deployment
labels:
app: hello-website
spec:
replicas: 3
selector:
matchLabels:
app: hello-website
template:
metadata:
labels:
app: hello-website
spec:
containers:
- name: hello-container
image: docker.io/busybox:latest
command: ["/bin/sh"]
args: ["-c", "while true; do echo 'Hello'; sleep 3; done"]
- name: web-service
image: docker.io/nginx:latest
ports:
- containerPort: 80
You can see that the container definition is the same as it was in the Part 1 pod example. The deployment spec adds a replicas
field to specify how many instances of the pod to run. It also adds controls for how updates are applied, which will be covered later.
Apply the deployment file, as we did for the pod, and a deployment resource is created in the cluster.
~ » kubectl apply -f hello-world-deployment.yaml
deployment.apps/hello-deployment created
~ » kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
hello-deployment 3/3 3 3 32s
A ReplicaSet
is also created to manage the pod instances and make sure the correct number are running.
~ » kubectl get rs
NAME DESIRED CURRENT READY AGE
hello-deployment-987c7866b 3 3 3 48s
~ » kubectl get pods
NAME READY STATUS RESTARTS AGE
hello-deployment-987c7866b-bhxh9 2/2 Running 0 37s
hello-deployment-987c7866b-qnqdg 2/2 Running 0 37s
hello-deployment-987c7866b-kl28b 2/2 Running 0 37s
You can see the ReplicaSet
in action if you delete one of the pod instances. Here we’ll delete hello-deployment-987c7866b-bhxh9
.
~ » kubectl delete pod hello-deployment-987c7866b-bhxh9
pod "hello-deployment-987c7866b-bhxh9" deleted
Very briefly, the ready count will drop to 2. But very quickly it will be back up to 3 as the ReplicaSet
launches a new instance to replace the one we deleted.
~ » kubectl get rs
NAME DESIRED CURRENT READY AGE
hello-deployment-987c7866b 3 3 2 3m23s
~ » kubectl get rs
NAME DESIRED CURRENT READY AGE
hello-deployment-987c7866b 3 3 3 116s
~ » kubectl get pods
NAME READY STATUS RESTARTS AGE
hello-deployment-987c7866b-qnqdg 2/2 Running 0 2m13s
hello-deployment-987c7866b-kl28b 2/2 Running 0 2m13s
hello-deployment-987c7866b-hqvmc 2/2 Running 0 63s
We can change the desired instance count several ways, such as editing the resource definition file and reapplying it. It’s also simple to adjust it directly with the kubectl scale command as follows.
~ » kubectl scale deployment hello-deployment --replicas=5
deployment.apps/hello-deployment scaled
~ » kubectl get rs
NAME DESIRED CURRENT READY AGE
hello-deployment-987c7866b 5 5 3 4m20s
~ » kubectl get rs
NAME DESIRED CURRENT READY AGE
hello-deployment-987c7866b 5 5 5 4m32s
~ » kubectl get pods
NAME READY STATUS RESTARTS AGE
hello-deployment-987c7866b-kl28b 2/2 Running 0 4m40s
hello-deployment-987c7866b-hqvmc 2/2 Running 0 3m30s
hello-deployment-987c7866b-2hj6d 2/2 Running 0 79s
hello-deployment-987c7866b-hmbxn 2/2 Running 0 24s
hello-deployment-987c7866b-qbfcq 2/2 Running 0 24s
Along with the deployment definition and status, a kubectl describe command will show events that have happened for it. Below shows events for the initial scaling when created plus the change when we scaled to 5.
~ » kubectl describe deployment hello-deployment
Name: hello-deployment
Namespace: default
CreationTimestamp: Fri, 01 Apr 2022 14:01:37 -0700
Labels: app=hello-website
Annotations: deployment.kubernetes.io/revision: 1
Selector: app=hello-website
Replicas: 5 desired | 5 updated | 5 total | 5 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=hello-website
Containers:
hello-container:
Image: docker.io/busybox:latest
Port: <none>
Host Port: <none>
Command:
/bin/sh
Args:
-c
while true; do echo 'Hello'; sleep 3; done
Environment: <none>
Mounts: <none>
web-service:
Image: docker.io/nginx:latest
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Progressing True NewReplicaSetAvailable
Available True MinimumReplicasAvailable
OldReplicaSets: <none>
NewReplicaSet: hello-deployment-987c7866b (5/5 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 18m deployment-controller Scaled up replica set hello-deployment-987c7866b to 3
Normal ScalingReplicaSet 14m deployment-controller Scaled up replica set hello-deployment-987c7866b to 5
There are a number of options or strategies for how pod instances are replaced when applying an update. That might be a good topic for a future post. For now, a couple simple examples are Recreate and RollingUpdate.
Recreate strategy will immediately delete all instances of the old version and create new instances using the new version. An example resource definition is below. Obviously this strategy causes availability downtime since all instances are deleted immediately, so it’s not acceptable for all apps.
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-deployment
labels:
app: hello-website
spec:
replicas: 3
strategy:
type: Recreate
selector:
matchLabels:
app: hello-website
template:
metadata:
labels:
app: hello-website
spec:
containers:
- name: hello-container
image: docker.io/busybox:latest
command: ["/bin/sh"]
args: ["-c", "while true; do echo 'Hello'; sleep 3; done"]
- name: web-service
image: docker.io/nginx:latest
ports:
- containerPort: 80
RollingUpdate strategy improves on that by only deleting and creating a few instances at a time, in a rolling wave until all instances have been updated. An example resource definition is below. There are extra fields, such as MaxSurge
and MaxUnavailable
, that let us specify how aggressive the rolling update happens.
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-deployment
labels:
app: hello-website
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 2 # how many pod instances to add at one time
maxUnavailable: 0 # how many pod instances can be unavailable at one time
selector:
matchLabels:
app: hello-website
template:
metadata:
labels:
app: hello-website
spec:
containers:
- name: hello-container
image: docker.io/busybox:latest
command: ["/bin/sh"]
args: ["-c", "while true; do echo 'Hello'; sleep 3; done"]
- name: web-service
image: docker.io/nginx:latest
ports:
- containerPort: 80
The Kubernetes Deployment
is the most common way to run workloads. It runs your pod in a way that automatically heals and facilitates dynamic scaling.
In this 3 part series, you saw some basic building blocks for running your app in Kubernetes. After defining a few basic components, your app can be deployed and running in no time.