Learn how to use Kubernetes Deployment to perfom rolling update


5 minute read

Deployment

The newer version of K8S, official suggests using Deployment instead of Replication Controller(rc) to perform a rolling update. Though, they are same in many ways, such as ensuring the homogeneous set of pods are always up/available and also they provide the ability to help the user to roll out the new images. However, Deployment provides more functionalities such as rollback support.

Relationship among Pods, Replica Sets and Deployment

Relationship among pod, replica set and deployment

A Deployment owns and manage multiples Replica Sets. And Replica Set manage the basic units in K8S - Pods.

Hands-on

Let’s create a simple Deployment with following nginx.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 3
  template:
    metadata:
      labels:
        service: http-server
    spec:
      containers:
      - name: nginx
        image: nginx:1.10.2
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80

Use kubectl to retrieve the current status of nginx deployment.

$ kubectl get deployment
NAME                DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx               3         3         3            3           8m

As I mentioned before, Deployment manages Replica Sets and Replica Set manages Pods.

├─ Deployment: <name>
│  └─ Replica Set: <name>-<rs>
│      └─ Pod: <name>-<rs>-<randomString>

K8S will create the replica set for us after the creation of deployment.

$ kubectl get rs
NAME                DESIRED   CURRENT   AGE
nginx-3322722759    3         3         8m

And the Replica Set will create pods after its been created.

$ kubectl get pod -l "service in (http-server)"
NAME                     READY     STATUS    RESTARTS   AGE
nginx-3322722759-7vp34   1/1       Running   0          14m
nginx-3322722759-ip5w2   1/1       Running   0          14m
nginx-3322722759-q97b7   1/1       Running   0          14m

Rolling Update

In order to support rolling update, we need to configure the update strategy first.

So we add following part into spec

minReadySeconds: 5
strategy:
  # indicate which strategy we want for rolling update
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 1
  • minReadySeconds:
    • the bootup time of your application, K8S waits specific time til the next pod creation.
    • K8S assume that your application is available once the pod created by default.
    • If you leave this field empty, the service may be unavailable after the update process cause all the application pods are not ready yet
  • maxSurge:
    • amount of pods more than the desired number of Pods
    • this fields can be an absolute number or the percentage
    • ex. maxSurge: 1 means that there will be at most 4 pods during the update process if replicas is 3
  • maxUnavailable:
    • amount of pods that can be unavailable during the update process
    • this fields can be a absolute number or the percentage
    • this fields cannot be 0 if maxSurge is set to 0
    • ex. maxUnavailable: 1 means that there will be at most 1 pod unavailable during the update process

The final nginx.yaml would be like the following

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-test
spec:
  replicas: 10
  selector:
    matchLabels:
      service: http-server
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  minReadySeconds: 5
  template:
    metadata:
      labels:
        service: http-server
    spec:
      containers:
      - name: nginx
        image: nginx:1.10.2
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80

Lets apply the new nginx.yaml

$ kubectl apply -f nginx.yaml --record

Now, for example, if we want to update the docker image, we have three ways to perform the rolling update.

  • set image
# format
$ kubectl set image deployment <deployment> <container>=<image> --record
# example
$ kubectl set image deployment nginx nginx=nginx:1.11.5 --record
  • replace

Modify the container image version in nginx.yaml (1.10.2)

spec:
  containers:
  - name: nginx
    # newer image version
    image: nginx:1.11.5
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 80

Using replace here instead of apply

# format
$ kubectl replace -f <yaml> --record
# example
$ kubectl replace -f new-nginx.yaml --record
  • edit
# format
$ kubectl edit deployment <deployment> --record
# example
$ kubectl edit deployment nginx --record

This command opens the editor, and you just need to change the image version in it.

# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  annotations:
    deployment.kubernetes.io/revision: "1"
    kubectl.kubernetes.io/last-applied-configuration: '{"kind":"Deployment","apiVersion":"extensions/v1beta1","metadata":{"name":"nginx","creationTimestamp":null},"spec":{"replicas":10,"template":{"metadata":{"creationTimestam
...
    spec:
      containers:
      - image: nginx:1.10.2
        imagePullPolicy: IfNotPresent
        name: nginx
...

Rollout Status

$ kubectl rollout status deployment nginx

Pause Rolling Update

$ kubectl rollout pause deployment <deployment>

Resume Rolling Update

$ kubectl rollout resume deployment <deployment>

Rollback

After the image update, your colleague finds the service become unstable you may want to go back to the previous version. Unfortunately, he/she dunno how the previous config looks like. Well, you don’t need the time machine, just let rollback to do its job.

At previous part, the parameter --record comes with command let the K8S record the command you typed, so that you can distinguish between the revisions.

$ kubectl apply -f nginx.yaml --record
deployment "nginx" configured

$ kubectl set image deployment nginx nginx=nginx:1.11.5 --record
deployment "nginx" image updated

$ kubectl rollout history deployment ngin
deployments "nginx":
REVISION  CHANGE-CAUSE
1   kubectl apply -f nginx.yaml --record
2   kubectl set image deployment nginx nginx=nginx:1.11.5 --record

Now, lets go back to revision 1

# to previous revision
$ kubectl rollout undo deployment <deployment>
# to specific revision
$ kubectl rollout undo deployment <deployment> --to-revision=<revision>
# exmaple
$ kubectl rollout undo deployment nginx --to-revision=1

All revision history is stored in the replica sets that deployment controls. If you want to keep more revision history, please set .spec.revisionHistoryLimit in yaml to specify the number of old Replica Sets to retain to allow rollback. (set this field at the first time apply)

...
spec:
  replicas: 10
  selector:
    matchLabels:
      service: http-server
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  minReadySeconds: 5
  revisionHistoryLimit: 10
...
$ kubectl rollout history deployment/nginx
deployments "nginx":
REVISION  CHANGE-CAUSE
2   kubectl set image deployment nginx nginx=nginx:1.11 --record
3   kubectl set image deployment nginx nginx=nginx:1.11.5 --record
4   kubectl set image deployment nginx nginx=nginx:1.10 --record
5   kubectl set image deployment nginx nginx=nginx:1.10.2 --record

Troubleshooting

  • Please add labels to the spec.template.metadata.labels
The Deployment "nginx" is invalid.

* spec.selector: Required value
* spec.template.metadata.labels: Invalid value: null: `selector` does not match template `labels`

Further Readings

Reference


comments powered by Disqus