Skip to content

Deployment

This tutorial goes through the deployment of a simple "Hello World" app, and explains what each resource means.

Before you can continue with this tutorial, make sure you have access to your training cluster and that you're familiar with running Pods in Kubernetes.

A fleet of Pods using a Deployment

Whenever you want to have long-running, stateless apps in Kubernetes, you should use a Deployment. It makes sure that you always have a specific number of your application containers running at all times. It can also manage upgrades to your apps gracefully. They're the primary way to run long-running services in Kubernetes.

Let's create a YAML manifest named demo-deployment.yaml with the following contents:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo
  labels:
    app: demo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: demo-pod
  template:
    metadata:
      labels:
        app: demo-pod
    spec:
      containers:
      - image: nginxinc/nginx-unprivileged
        name: nginx
        ports:
        - containerPort: 8080

In this YAML document:

  • The apiVersion is now apps/v1. This is distinct to Deployment kind.
  • We have a spec.template section, which describes the kind of Pod the deployment should create. The template matches the Pod YAML document we created earlier except for the namespace and name fields.
  • The spec.replicas field tells us how many instances of the Pod we should have running at the same time. We can also set this to any number including 1 to run a single Pod or even 0 to run no Pods at all.
  • The spec.selector field is used for querying for Pods that the Deployment should manage. Normally, you should set the matchLabels section to match the Pod labels in the template section. This way the Deployment will know how many replicas it has and which Pods it can shut down. As mentioned before, you can use any labels you want, but make sure the labels don't overlap with Pods that already exist in the same namespace. The labels for the Pod doesn't have to match the labels for the Deployment (i.e. metadata.labels.app).

Let's create the deployment in Kubernetes:

$ kubectl apply -f demo-deployment.yaml
deployment/demo created

Warning

Deployments are only meant for stateless applications: i.e. the ones that don't store any state on a disk or memory. In the future training sections, we'll learn how to manage stateful applications in Kubernetes.

Examining the deployment

We can use the kubectl rollout command to check the status of our deployment:

$ kubectl rollout status deployment demo
deployment "demo" successfully rolled out

Note that using kubectl rollout status is required to check that the Pods were rolled out correctly. The response from kubectl apply alone doesn't guarantee this.

We can see that the deployment did indeed create Pods for us:

$ kubectl get pod
NAME                        READY   STATUS    RESTARTS   AGE
demo-pod-79cc4b689c-qrcbn   1/1     Running   0          2m22s
demo-pod-79cc4b689c-xpmn5   1/1     Running   0          2m22s

We can also use get and describe to get more information on the Deployment itself:

$ kubectl get deployment
NAME   READY   UP-TO-DATE   AVAILABLE   AGE
demo   2/2     2            2           5m9s

$ kubectl describe deployment
Name:                   demo
Namespace:              tutorial
CreationTimestamp:      Thu, 17 Oct 2019 10:01:22 +0300
Labels:                 app=demo
...

Scaling up and down

Right now, we should have two Pods running for our demo app.

We can bring in more or delete existing Pods by tuning the replicas count. To do this, we can edit the demo-deployment.yaml file we created earlier, change the number of replicas, and use kubectl apply to update the deployment. Kubernetes will then create or delete Pods accordingly.

Alternatively, we can use kubectl scale command to perform the same thing directly from the command-line. Let's scale our deployment to 3 Pods:

$ kubectl scale deployment demo --replicas=3
demo deployment.apps/demo scaled

$ kubectl get pods
NAME                        READY   STATUS    RESTARTS   AGE
demo-pod-79cc4b689c-qrcbn   1/1     Running   0          16m
demo-pod-79cc4b689c-wvv55   1/1     Running   0          20s
demo-pod-79cc4b689c-xpmn5   1/1     Running   0          16m

The downside of using kubectl scale compared to kubectl apply is that any local configuration you have is no longer in sync with what Kubernetes has.

Note that trying to delete any of the Pods manually without changing the Deployment, Kubernetes will automatically create a new Pod to replace it.

$ kubectl delete pod demo-79cc4b689c-qrcbn
pod "demo-79cc4b689c-qrcbn" deleted

$ kubectl get pods
NAME                        READY   STATUS    RESTARTS   AGE
demo-pod-79cc4b689c-wvv55   1/1     Running   0          3m4s
demo-pod-79cc4b689c-xgrrp   1/1     Running   0          11s
demo-pod-79cc4b689c-xpmn5   1/1     Running   0          19m

Safely rolling out changes

Updating your application or the configuration of the application is a normal routine to go through. Deployments in Kubernetes include constructs that support zero-downtime upgrades for your application.

Let's edit the demo-deployment.yaml to add more details:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo
  labels:
    app: demo
spec:
  replicas: 3
  progressDeadlineSeconds: 20
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: demo-pod
  template:
    metadata:
      labels:
        app: demo-pod
    spec:
      containers:
      - image: nginxinc/nginx-unprivileged
        name: nginx
        ports:
        - containerPort: 8080
        readinessProbe:
          periodSeconds: 5
          successThreshold: 1
          httpGet:
            path: /
            port: 8080

We added three new things to the YAML:

  • progressDeadlineSeconds: The maximum number of seconds Kubernetes will wait per Pod until it considers the deployment a failure.
  • strategy: This determines the strategy for updating Pods when updating the deployment. In this case, we select RollingUpdate, which means that Kubernetes will gradually launch updated Pods and take out old Pods. Using maxSurge: 1 and maxUnavailable: 0 setting means that Kubernetes will launch at most one Pod at a time, and doesn't take out any Pods unless there's extra Pods running.
  • readinessProbe: This is the key element for deciding whether a Pod is ready or not. Kubernetes uses the probe method to continuously "ask", if the Pod is ready or not. Typically, you have a health endpoint in your application that responds 200 OK when it considers itself ready. In this case, Kubernetes polls the root path of the NGINX container for health.

You can also configure a liveness probe that will determine whether the Pod needs to be restarted or not. Normally, you'd want to use both, but the readiness probe should enough for the purpose of this example.

Let's update the deployment and check that it was rolled out correctly.

$ kubectl apply -f demo-deployment.yaml
deployment.apps/demo configured

$ kubectl rollout status deployment demo
deployment "demo" successfully rolled out

$ kubectl get pod
NAME                        READY   STATUS    RESTARTS   AGE
demo-pod-7f6597f97c-676g7   1/1     Running   0          58s
demo-pod-7f6597f97c-xwqxk   1/1     Running   0          49s
demo-pod-7f6597f97c-zgvs2   1/1     Running   0          39s

Great! Our deployment was rolled out successfully.

Let's try breaking the deployment to see how the deployment status changes. First, let's change the readiness probe HTTP path to something we know doesn't exist in the file demo-deployment.yaml.

...
httpGet:
  path: /does-not-exist
  port: 8080
...

Let's apply the change and check how the deployment behaves:

$ kubectl apply -f demo-deployment.yaml
deployment.apps/demo configured

$ kubectl rollout status deployment demo
Waiting for deployment "demo" rollout to finish: 1 old replicas are pending termination...
error: deployment "demo" exceeded its progress deadline

$ kubectl get pods
NAME                        READY   STATUS    RESTARTS   AGE
demo-pod-7f6597f97c-676g7   1/1     Running   0          2m6s
demo-pod-7f6597f97c-xwqxk   1/1     Running   0          117s
demo-pod-7f6597f97c-zgvs2   1/1     Running   0          107s
demo-pod-847df79d7d-hnb7j   0/1     Running   0          36s

As we can see from the output, the deployment change was applied successfully, but the rollout failed. When the rollout fails, we can see that there's one new Pod that was created, but not ready. The rest of the Pods are the old ones that were still kept available. If we apply a fixed version of the deployment manifest, we can get rid of the failed Pod.

The right way to get back to a working state is to use the deployment rollback feature. First, let's figure out the revision we want to get back to:

$ kubectl rollout history deployment demo
deployment.apps/demo
REVISION  CHANGE-CAUSE
1         <none>
2         <none>
3         <none>

We have three revisions of the deployment as expected. The last one is the one that failed, so let's roll back to revision 2.

$ kubectl rollout undo deployment demo --to-revision=2
deployment.extensions/demo

$ kubectl rollout status deployment demo
deployment "demo" successfully rolled out

$ kubectl get pod
NAME                        READY   STATUS    RESTARTS   AGE
demo-pod-7f6597f97c-676g7   1/1     Running   0          17m
demo-pod-7f6597f97c-xwqxk   1/1     Running   0          17m
demo-pod-7f6597f97c-zgvs2   1/1     Running   0          16m

We can see that the old Pods still remain.

Note

Do not delete the Deployment, we will be using it later.

Next

In the next section, we'll learn how to configure our applications in Kubernetes.