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
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
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
.
See Also
-
Kubernetes Pods reference documentation.
4.4 Launching a Deployment Using a Manifest
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.
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
-
Kubernetes Deployments in documentation
4.5 Updating a Deployment
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.
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 unlikeapply
, in order to usereplace
, 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.