In the past two articles, we have talked about the Pod and ReplicaSet (RS) API objects in Kubernetes. It is known that Pod is the minimum scheduling unit in Kubernetes, and ReplicaSet is a basic controller that controls the number of Pod copies. The article left a topic at the end:

In Kubernetes, it is common to use a Deployment controller rather than ReplicaSet directly. Deployment is a more advanced controller that manages ReplicaSet and provides horizontal scaling/shrinking, Pod declarative updates, application versioning, and many other functions.

So Go projects deployed to Kubernetes clusters scale/shrink horizontally and apply new management through the Deployment controller, which uses its own control loop to ensure that the current state in the cluster is always equal to the expected state defined by the Deployment object.

I will use “Kubernetes get started practice – deployment run Go project” article used projects as a demonstration project, demonstrating how Kubernetes application services for horizontal expansion, release update, version rollback and other operations, in the demo process together to discuss the following topics:

  • What is theDeploymentThe controller
  • DeploymentHow does it work?
  • How to createDeployment.
  • How to useDeploymentScroll to update the app.
  • How to useDeploymentRoll back the application version.

What is the Deployment

In Kubernetes, it is recommended to deploy Pod and RS using Deployment, as it has many built-in features to facilitate cluster management, such as:

  • Easy deployment of RS (replica set)
  • Clean up old RS no longer needed
  • Expand/shrink the number of pods in RS
  • Dynamic updatePod(Replace old pods with new pods based on updates to Pod template definitions)
  • Roll back to the previousDeploymentversion
  • Ensure continuity of service

Take the following definition of Deployment object as an example. The first part is the definition of its own meta information (name, labels) and the second part is the definition of ReplicaSet object (spec.replica=3….). ReplicaSet contains the Pod definition (spec.template) :

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: Nginx: 1.7.9
        ports:
        - containerPort: 80
Copy the code

In terms of the specific implementation, the relationship between Deployment, ReplicaSet and Pod and the management level can be described in a diagram:

How Deployment works

There are many kinds of controllers in Kubernetes, and each controller is responsible for some kind of orchestration function in its own way. Deployment, is one of these controllers. They all follow a common choreography pattern in the Kubernetes project, namely, the Control loop, in which each controller is responsible for the choreography function that it implements within the control loop.

Next, taking the Deployment defined above as an example, I will briefly describe the working principle to you:

  • The Deployment controller gets all the pods with the “app: nginx” tag from the Etcd and counts them, which is the actual state;
  • The Replicas field value of the Deployment object is the expected state, and the Deployment controller compares the two states;
  • Based on the comparison,DeploymentDetermine whether to create a Pod, delete an existing Pod, or do nothing.

This is choreographed for the number of Pod copies, but the dynamic update of Pod and rollback of the Deployment object version are covered below. In a word, the core idea of the controller is to continuously tune the actual state to the defined desired state through the control loop. Once the desired state is updated, the tuning logic in the control loop will be triggered.

How to create Deployment

Before creating Deployment, you need to declare its object definition. Let’s briefly explain what each part means using the Deployment definition used in the previous article “Kubernetes Getting Started — Deploying and Running the Go Project” :

apiVersion: apps/v1
kind: Deployment
metadata: Metadata for # Deployment
  name: my-go-app
spec:
  replicas: 1 # ReplicaSet section definition
  selector:
    matchLabels:
      app: go-app
  template: # Pod template definition
    metadata:
      labels:
        app: go-app
    spec: Container related definitions in Pod
      containers:
        - name: go-app-container
          image: kevinyan001/kube-go-app
          resources:
            limits:
              memory: "128Mi"
              cpu: "100m"
          ports:
            - containerPort: 3000
Copy the code
  • The API Version declares the API version of the object, and Kubernetes goes back to the corresponding package to load the library file.

  • Kind declares the class of the object, essentially telling Kubernetes what object to load.

  • Metadata is the metadata of our object.

  • Spec.replicas defines how many Pod replicas a replica set has, while spec.selectors are the rules by which the replica set matches pods.

  • Spec. Template is the definition of a Pod template, which is the definition of a complete Pod object.

  • Spec. Template. Spec is a container definition for pods.

In the container configuration, 128 mi for limits. Memory means 128 megabytes allocated to the container, while 1000m for limits. CPU means 1 core. 100m is allocated to the container 0.1 core, this practice on my own computer try not to allocate too large, or it will not start up at all.

Once the declaration file is written, use the kubectl create command to create the Deployment object, which is how all API objects in Kubernetes are created.

➜ kubectl create -f deployment.yaml -- Record deployment.apps/my-go-app created ➜Copy the code

Minikube- A Kubernetes cluster that runs on a laptop.

Before moving on to more advanced Deployment choreography, let’s make sure that Deployment is up and running with the following two commands:

  • Kubectl rollout Status Deployment tells us about the state change of the Deployment object.

    ➜ kubectl rollout status deployment my-go-app
    deployment "my-go-app" successfully rolled out
    Copy the code
  • Kubectl Get Deployment shows the expected number of copies and the number of copies being updated, as well as the number of pods currently available for service. Because we only specified one replica in the definition, there is currently only one Pod.

    kubectl get deployment my-go-app
    NAME        READY   UP-TO-DATE   AVAILABLE   AGE
    my-go-app   1/1     1            1           13m
    Copy the code
  • Kubectl get Replicaset checks the status of the Replicaset created by Deployment for Pod.

    kubectl get replicaset          
    NAME                   DESIRED   CURRENT   READY   AGE
    my-go-app-864496b67b   1         1         1       19m
    Copy the code

    By default, Deployment adds pod-template-hash to the name of the ReplicaSet it creates. Like my-Go-app-864496B67b here

  • Finally, kubectl get pod command can check the status of the POD replica created by ReplicaSet.

    NAME                         READY   STATUS             RESTARTS   AGE
    my-go-app-864496b67b-ctkf9   1/1     Running            0          25m
    Copy the code

Use Deployment to roll updates to the application

Deployment operates on the number and properties of the ReplicaSet through a “controller mode”, which implements the “horizontal scale/shrink” and “rolling update” scheduling actions.

Horizontal expansion/contraction

Horizontal scaling/scaling is very easy to implement, Deployment just needs to change the number of Pod copies of ReplicaSet it controls. For example, if you change this value from 1 to 3, the Deployment ReplicaSet will automatically create two new PODS based on the changed value, and the “horizontal scaling” will do the opposite. The instructions for this operation are also very simple, namely kubectl scale, for example:

➜ kubectl scale --replicas=3 deployment my-go-app -- Record deployment.apps/my-go-app scaledCopy the code

Kubectl rollout status Deployment my-go-app kubectl rollout status deployment my-go-app

kubectl rollout status deployment my-go-app    
Waiting for deployment "my-go-app" rollout to finish: 1 of 3 updated replicas are available...
Waiting for deployment "my-go-app" rollout to finish: 2 of 3 updated replicas are available...
deployment "my-go-app" successfully rolled out
Copy the code

You can observe that the ReplicaSet Name does not change with the following command:

➜   kubectl get replicaset                         
NAME                   DESIRED   CURRENT   READY   AGE
my-go-app-864496b67b   3         3         3       53m
Copy the code

This proves that Deployment horizontal scaling and scaling replica sets does not create a new ReplicaSet, but when updating a Pod template, such as changing the container’s image, Deployment creates a new version of ReplicaSet instead of the old one.

Scroll to update

In the Deployment definition above, the container image in the Pod template is kevinyan001/ Kube-Go-app. Then for example, our GO project code is updated to package the image Kevinyan001 / Kube-Go-app with the latest code :v0.1. The process of deploying a new image of the Go project triggers a rolling update of the Deployment.

There are two ways to update the image, one is to update the image name in deployment.yaml and then execute kubectl apply -f deployment.yaml. Jenkins and other continuous succession tools in companies do this. Another way is to use kubectl set image command, for the convenience of demonstration here is to use the second way of Pod rolling update.

➜ kubectl set image deployment my-go-app go-app-container=kevinyan001/ Kube-GO-app :v0.1 -- Record deployment.apps/my-go-app image updatedCopy the code

Checking the status of the ReplicaSet from the command line after a rolling update shows the process of Deployment replacing the old ReplicaSet object with the new one.

➜ kubectl get replicaset NAME DESIRED CURRENT READY AGE My-Go-app-6749DBc697 3 3 2 19s My-Go-app-864496b67b 1 1 72m ➜ kubectl get replicaset NAME DESIRED CURRENT READY AGE my-go-app-6749dbc697 3 3 3 24s my-go-app-864496b67b 0 0 0 72mCopy the code

The details of this rolling update can be seen from the Deployment Events:

➜ kubectl describe Deployment My-go-app Name: My-go-app Namespace: Default CreationTimestamp: Sat, 29 Aug 2020 00:31:56 +0800 Events: ..... Normal ScalingReplicaSet 37h deployment-controller Scaled up replica set my-go-app-6749dbc697 to 1 Normal ScalingReplicaSet 37h deployment-controller Scaled down replica set my-go-app-864496b67b to 2 Normal ScalingReplicaSet 37h deployment-controller Scaled up replica set my-go-app-6749dbc697 to 2 Normal ScalingReplicaSet 37h (x2 over 37h) deployment-controller Scaled down replica set my-go-app-864496b67b to 1 Normal ScalingReplicaSet 37h deployment-controller Scaled up replica set my-go-app-6749dbc697 to 3 Normal ScalingReplicaSet 37h deployment-controller  Scaled down replica set my-go-app-864496b67b to 0Copy the code

When you change the Pod definition in Deployment, Deployment will use the changed Pod template to create a new ReplicaSet (hash= 6749DBc697). The initial number of Pod copies of the new ReplicaSet is 0. The Deployment then starts scaling the number of Pod copies controlled by the new ReplicaSet from zero to one, i.e. **” scale horizontally “** out of one copy. The Deployment then reduces the number of old Pod copies controlled by the old ReplicaSet (hash= 864496B67b) by one, i.e. “horizontally shrinks” to two copies. Doing this alternately completes the version upgrade process for this set of PODS. Like this, the process of upgrading one Pod version after another that is running in a cluster is called a “rolling update.”

A diagram of the process would look something like this

To ensure continuity of service, Deployment also ensures that only a specified percentage of pods are offline in any time window. It also ensures that only a specified proportion of new pods are created in any time window. Both ratios are configurable and default to 25% of the spec.relicas value in the desired state. So, in the Deployment example above, which has three Pod copies, the controller will always ensure that at least two pods are available and at most four pods are present in the cluster during the “rolling update” process. This policy can be set through a field in the Deployment object, RollingUpdateStrategy:


apiVersion: apps/v1
kind: Deployment
.
spec:
.
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
Copy the code

Roll back the Deployment version

All of the above change commands are executed using the **–record** parameter, which enables Kubernetes to record the command executed at the time of the change in the Deployment change log.

Kubectl rollout History Deployment my-go-app to see the deployment update record:

➜ kubectl rollout history Deployment my-go-app deployment.apps/my-go-app REVISION change-cause 1 Kubectl scale deployment my-go-app --replicas=3 --record=true 2 kubectl set image deployment my-go-app Go - - the container = kevinyan001 app/kube - go - app: v0.1 - record = trueCopy the code

If there is a problem with the scrolling Go project image, we want to Go back to the previous version. The **–record** parameter helps us record the revision number in the execution command and update record to find the revision number we want to roll back.

The kubectl rollout undo command completes the version rollback of the Deployment object once the revision number has been determined.

kubectl rollout undo  deployment my-go-app --to-revision=1
deployment.apps "my-go-app"
Copy the code

The old ReplicaSet(Hash =864496b67b) now has 3 pods, and the new ReplicaSet(hash= 6749DBc697) now has 0 pods.

➜ kubectl get RS NAME DESIRED CURRENT READY AGE My-go-app-6749DBc697 0 0 0 3m33s My-go-app-864496b67b 33 3 4m30sCopy the code

Prove that Deployment does not delete the old version of ReplicaSet after the last rolling update, but reserves it for roll-back, so ReplicaSet acts as an infrastructure-level application version management.

When I look at the change record after rollback, I find that there is no content with revision No. 1, but more content with revision No. 3. The change content of this version is actually the change content in revision No. 1 before rollback.

➜ kubectl rollout history Deployment my-go-app deployment.apps/my-go-app REVISION change-cause 2 kubectl set image Deployment my-go-app go-app container= Kevinyan001 / Kube-GO-app :v0.1 -- Record =true 3 Kubectl scale deployment my-go-app --replicas=3 --record=trueCopy the code

Controls the number of ReplicaSet versions

You may have already thought of a problem: isn’t it redundant or even a waste of resources for us to generate a new ReplicaSet object every time we update our Deployment? So, the Kubernetes project also provides an instruction that causes multiple update operations to the Deployment to only generate a ReplicaSet object. To do this, you need to execute a Kubectl rollout pause directive before updating the Deployment. Its usage is as follows:

➜ kubectl rollout Pause Deployment My-go-app deployment.apps/ my-Go-app pausedCopy the code

What this command does is put the Deployment into a “hold” state. Since the Deployment is in a “pause” state at this point, none of our changes to the Deployment will trigger a new “rolling update” or create a new ReplicaSet. After we have made all the changes to the Deployment, just execute kubectl rollout Resume to restore it, as shown below:

➜ kubectl rollout resume deployment my-go-app
deployment.apps/my-go-app resumed
Copy the code

As the application version increases, Kubernetes stores many, many different Replicasets for the same Deployment. So, Deployment object has a field, called the spec. RevisionHistoryLimit, Kubernetes is reserved for Deployment of “historical version number. If you set it to 0, you can no longer roll back.

conclusion

The Kubernetes project’s Deployment design, instead of abstractions the application, allows us to describe the application with a Deployment object and control the version of the application using the Kubectl rollout command.

Deployment also ensures continuity of the service by ensuring that only a specified proportion of pods are offline and only a specified proportion of new pods are created in any time window during rolling updates, thus ensuring smooth updates of the service. Hopefully, you will be able to follow the demonstration in today’s article to grasp the use of the various capabilities provided by Deployment. The image I used in the article has been uploaded to DockerHub. If the network is limited and the image cannot be pulled, you can scan the code below and reply to K8S to obtain the source code of the project and the Dockerfile used to build the image.