Chapter 4. Creating and Modifying Fundamental Workloads

In this chapter, we present recipes that show you how to manage fundamental Kubernetes workload types: pods and deployments. We show how to create deployments and pods via CLI commands and from a YAML manifest, and explain how to scale and update a deployment.

4.1 Creating a Deployment Using kubectl run

Problem

You want to quickly launch a long-running application such as a web server.

Solution

Use the kubectl run command, a generator that creates a deployment manifest on the fly. For example, to create a deployment that runs the Ghost microblogging platform do the following:

$ kubectl run ghost --image=ghost:0.9

$ kubectl get deploy/ghost
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
ghost     1         1         1            0           16s

Discussion

The kubectl run command can take a number of arguments to configure additional parameters of the deployments. For example, you can do the following:

  • Set environment variables with --env

  • Define container ports with --port

  • Define a command to run using --command

  • Automatically create an associated service with --expose

  • Define the number of pods using --replicas

Typical usages are as follows. To launch Ghost serving on port 2368 and create a service along with it, enter:

$ kubectl run ghost --image=ghost:0.9 --port=2368 --expose

To launch MySQL with the root password set, enter:

$ kubectl run mysql --image=mysql:5.5 --env=MYSQL_ROOT_PASSWORD=root

To launch a busybox container and execute the command sleep 3600 on start, enter:

$ kubectl run myshell --image=busybox --command -- sh -c "sleep 3600"

See also kubectl run --help for more details about the available arguments.

4.2 Creating Objects from File Manifests

Problem

Rather than creating an object via a generator such as kubectl run, you want to explicitly state its properties and then create it.

Solution

Use kubectl create like so:

$ kubectl create -f <manifest>

In Recipe 6.3 you’ll see how to create a namespace using a YAML manifest. This is one of the simplest examples as the manifest is very short. It can be written in YAML or JSON—for example, with a YAML manifest file myns.yaml like so:

apiVersion:    v1
kind:          namespace
metadata:
  name:        myns

You can create this object with kubectl create -f myns.yaml.

Discussion

You can point kubectl create to a URL instead, or a filename in your local filesystem. For example, to create the frontend for the canonical Guestbook application, get the URL of the raw YAML that defines the application in a single manifest and enter:

$ kubectl create -f https://raw.githubusercontent.com/kubernetes/kubernetes/ \
                    master/examples/guestbook/frontend-deployment.yaml

4.3 Writing a Pod Manifest from Scratch

Problem

You want to write a pod manifest from scratch and not use a generator such as kubectl run.

Solution

A Pod is an /api/v1 object and, like any other Kubernetes object, its manifest file contains the following fields:

  • apiVersion, which specifies the API version

  • kind, which indicates the type of the object

  • metadata, which provides some metadata about the object

  • spec, which provides the object specification

The pod manifest contains an array of containers and an optional array of volumes (see Chapter 8). In its simplest form, with a single container and no volume, it looks as follows:

apiVersion:    v1
kind:          Pod
metadata:
  name:        oreilly
spec:
  containers:
  - name:      oreilly
    image:     nginx

Save this YAML manifest in a file called oreilly.yaml and then use kubectl to create it:

$ kubectl create -f oreilly.yaml

Discussion

The API specification of a pod is much richer than what is shown in the Solution, which is the most basic functioning pod. For example, a pod can contain multiple containers, as shown here:

apiVersion: v1
kind:       Pod
metadata:
  name:     oreilly
spec:
  containers:
  - name:   oreilly
    image:  nginx
  - name:   safari
    image:  redis

A pod can also contain volume definitions to load data in the containers (see Recipe 8.1), as well as probes to check the health of the containerized application (see Recipe 11.2 and Recipe 11.3).

A description of the thinking behind many of the specification fields and a link to the full API object specification is detailed in the documentation.

Note

Unless for very specific reasons, never create a pod on its own. Use a Deployment object (see Recipe 4.4) to supervise pods—it will watch over the pods through another object called a ReplicaSet.

4.4 Launching a Deployment Using a Manifest

Problem

You want to have full control over how a (long-running) app is launched and supervised.

Solution

Write a manifest using the Deployment object in it. For the basics, see also Recipe 4.3.

Let’s say you have manifest file called fancyapp.yaml with the following content:

apiVersion:       extensions/v1beta1
kind:             Deployment
metadata:
  name:           fancyapp
spec:
  replicas:       5
  template:
    metadata:
      labels:
        app:      fancy
        env:      development
    spec:
      containers:
      - name:     sise
        image:    mhausenblas/simpleservice:0.5.0
        ports:
        - containerPort: 9876
        env:
        - name:   SIMPLE_SERVICE_VERSION
          value:  "0.9"

As you can see, there are a couple of things you might want to do explicitly when launching the app:

  • Set the number of pods (replicas), or identical copies, that should be launched and supervised.

  • Label it, such as with env=development (see also Recipe 6.5 and Recipe 6.6).

  • Set environment variables, such as SIMPLE_SERVICE_VERSION.

Now let’s have a look at what the deployment entails:

$ kubectl create -f fancyapp.yaml
deployment "fancyapp" created

$ kubectl get deploy
NAME       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
fancyapp   5         5         5            0           8s

$ kubectl get rs
NAME                  DESIRED   CURRENT   READY     AGE
fancyapp-1223770997   5         5         0         13s

$ kubectl get po
NAME                        READY     STATUS              RESTARTS   AGE
fancyapp-1223770997-18msl   0/1       ContainerCreating   0          15s
fancyapp-1223770997-1zdg4   0/1       ContainerCreating   0          15s
fancyapp-1223770997-6rqn2   0/1       ContainerCreating   0          15s
fancyapp-1223770997-7bnbh   0/1       ContainerCreating   0          15s
fancyapp-1223770997-qxg4v   0/1       ContainerCreating   0          15s

And if we repeat this a few seconds later:

$ kubectl get po
NAME                        READY     STATUS    RESTARTS   AGE
fancyapp-1223770997-18msl   1/1       Running   0          1m
fancyapp-1223770997-1zdg4   1/1       Running   0          1m
fancyapp-1223770997-6rqn2   1/1       Running   0          1m
fancyapp-1223770997-7bnbh   1/1       Running   0          1m
fancyapp-1223770997-qxg4v   1/1       Running   0          1m
Warning

When you want to get rid of a deployment, and with it the replica sets and pods it supervises, execute a command like kubectl delete deploy/fancyapp. Do not try to delete individual pods, as they will be recreated by the deployment. This is something that often confuses beginners.

Deployments allow you to scale the app (see Recipe 9.1) as well as roll out a new version or roll back to a previous version. They are, in general, good for stateless apps that require pods with identical characteristics.

Discussion

A deployment is a supervisor for pods and replica sets (RSs), giving you fine-grained control over how and when a new pod version is rolled out or rolled back to a previous state. The RSs and pods that a deployment supervises are generally of no interest to you unless, for example, you need to debug a pod (see Recipe 12.5). Figure 4-1 illustrates how you can move back and forth between deployment revisions.

Deployment Revisions
Figure 4-1. Deployment revisions

Note that RS will, going forward, replace the original replication controller (RC), so it’s a good thing to start thinking in terms of RSs rather than RCs. For now, the only difference is that RSs support set-based labels/querying, but we can expect that there will be more features added to the RS and the RC will eventually be deprecated.

Finally, to generate the manifest, you can use the kubectl create command and the --dry-run option. It will allow you to generate the manifest in YAML or JSON format and save the manifest for later use. For example, to create the manifest of a deployment called fancy-app using the Docker image nginx, issue the following command:

$ kubectl create deployment fancyapp --image nginx -o json --dry-run
{
    "kind": "Deployment",
    "apiVersion": "extensions/v1beta1",
    "metadata": {
        "name": "fancy-app",
        "creationTimestamp": null,
        "labels": {
            "app": "fancy-app"
        }
    },
...

See Also

4.5 Updating a Deployment

Problem

You have a deployment and want to roll out a new version of your app.

Solution

Update your deployment and let the default update strategy, RollingUpdate, automatically handle the rollout.

For example, suppose you create a new container image and want to update the deployment based on it:

$ kubectl run sise --image=mhausenblas/simpleservice:0.4.0
deployment "sise" created

$ kubectl set image deployment sise mhausenblas/simpleservice:0.5.0
deployment "sise" image updated

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

$ kubectl rollout history deployment sise
deployments "sise"
REVISION        CHANGE-CAUSE
1               <none>
2               <none>

You’ve now successfully rolled out a new revision of your deployment where only the container image used has changed. All other properties of the deployment, such as the number of replicas, stay unchanged. But what if you want to update other aspects of the deployment, such as changing environment variables? You can use a number of kubectl commands to update the deployment. For example, to add a port definition to the current deployment, you can use kubectl edit:

$ kubectl edit deploy sise

This command will open the current deployment in your default editor, or, when set and exported, in the editor specified by the environment variable KUBE_EDITOR.

Say you want to add the following port definition:

...
  ports:
  - containerPort: 9876
...

The result of the editing process (in this case, with KUBE_EDITOR set to vi) is shown in Figure 4-2.

Editing Deployment
Figure 4-2. Editing a deployment

Once you save and exit the editor, Kubernetes kicks off a new deployment, now with the port defined. Let’s verify that:

$ kubectl rollout history deployment sise
deployments "sise"
REVISION        CHANGE-CAUSE
1               <none>
2               <none>
3               <none>

Indeed, we see that revision 3 has been rolled out with the changes we introduced with kubectl edit. The reason the CHANGE-CAUSE column is empty is that you didn’t use kubectl create with the --record option. If you want to see what triggered a revision, add this option.

As mentioned earlier, there are more kubectl commands that you can use to update your deployment:

  • Use kubectl apply to update a deployment (or create it if it doesn’t exist) from a manifest file—for example, kubectl apply -f simpleservice.yaml.

  • Use kubectl replace to replace a deployment from a manifest file—for example, kubectl replace -f simpleservice.yaml. Note that unlike apply, in order to use replace, the deployment must already exist.

  • Use kubectl patch to update a specific key—for example:

    kubectl patch deployment sise -p '{"spec": {"template":
    {"spec": {"containers":
    [{"name": "sise", "image": "mhausenblas/simpleservice:0.5.0"}]}}}}'

What if you make a mistake or experience issues with the new version of the deployment? Luckily, Kubernetes makes it really easy to roll back to a known good state using the kubectl rollout undo command. For example, suppose the last edit was a mistake and you want to roll back to revision 2. You can do this with the following command:

$ kubectl rollout undo deployment sise ‐‐to‐revision=2

You can then verify that the port definition has been removed with kubectl get deploy/sise -o yaml.

Note

The rollout of a deployment is only triggered if parts of the pod template (that is, keys below .spec.template) are changed, such as environment variables, ports, or the container image. Changes to aspects of the deployments, such as the replica count, do not trigger a new deployment.

Get Kubernetes Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.