I wrote an article not long ago showing how to get started with Kubernetes.

In that article we started pods by passing options as command line arguments to kubectl create. This works for demostration purposes, but it’s not usually the way Kubernetes deployments are managed.

Having files that describe the characteristics of our deployment allows us to add these files to source control, which gives us the benefit of keeping track of changes as well as making it easy to change things like the number of pods without having to remember all other details.

For this article, I’ll assume you already have a Kubernetes cluster and a configured kubectl client. If you don’t have these ready, you might want to take a look at my introduction to Kubernetes to get those set up.

Kubernetes Objects

A Kubernetes Object is a unit we can manage with kubectl; Examples of objects are: pods, deployments, services, etc.

Config files

Let’s start by looking at a kubectl command with inline arguments:

1
2
3
kubectl create deployment echo-server-deployment \
  --image=ealen/echo-server \
  --replicas=1

The minimal configuration file required to create the same deployment looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: apps/v1
kind: Deployment
metadata:
  name: echo-server-deployment
spec:
  selector:
    matchLabels:
      app: echo-server-deployment
  replicas: 1
  template:
    metadata:
      labels:
        app: echo-server-deployment
    spec:
      containers:
      - image: ealen/echo-server
        name: echo-server

The apiVersion field defines the schema of the file and the kind field tells us that this is defining a deployment.

It’s necessary to specify a name for our object. We do that in the top level metadata section:

1
2
metadata:
  name: echo-server-deployment

Next is our deployment spec. The selector section is used to specify the pods that are managed by this deployment. This must match the pods labels:

1
2
3
4
spec:
  selector:
    matchLabels:
      app: echo-server-deployment

The replicas field is pretty self explanatory.

The template section describes the pods that will be created. We start by adding a label to identify the pods created by this deployment:

1
2
3
4
  template:
    metadata:
      labels:
        app: echo-server-deployment

Followed by our pods spec:

1
2
3
4
    spec:
      containers:
      - image: ealen/echo-server
        name: echo-server

If we save this to a file named deployment.yaml, we can use this command to create the deployment:

1
kubectl create -f deployment.yaml

If we want to change the number of replicas in our deployment, we can just update the number in the configuration file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: apps/v1
kind: Deployment
metadata:
  name: echo-server-deployment
spec:
  selector:
    matchLabels:
      app: echo-server-deployment
  replicas: 3
  template:
    metadata:
      labels:
        app: echo-server-deployment
    spec:
      containers:
      - image: ealen/echo-server
        name: echo-server

To update our deployment, we use this command:

1
kubectl replace -f deployment.yaml

The replace command replaces the configuration, but the pods are still updated in a safe manner (rolling update). If we list the pods, we can see that the original pod wasn’t touched (The AGE is older) but two new pods were created:

1
2
3
4
5
kubectl get pods
NAME                                     READY   STATUS    RESTARTS   AGE
echo-server-deployment-c4dd99ffc-4ptq6   1/1     Running   0          3m2s
echo-server-deployment-c4dd99ffc-9svg6   1/1     Running   0          4s
echo-server-deployment-c4dd99ffc-n5d5p   1/1     Running   0          4s

Since our configuration is very minimal, a lot of configuration options use their default value. To see the whole runtime configuration, we can use:

1
kubectl get -f deployment.yaml -o yaml

The output looks something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    deployment.kubernetes.io/revision: "1"
  creationTimestamp: "2021-11-17T00:41:10Z"
  generation: 2
  name: echo-server-deployment
  namespace: default
  resourceVersion: "20568"
  uid: 62954d9a-3645-4d34-a897-821ec8428a5d
spec:
  progressDeadlineSeconds: 600
  replicas: 3
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: echo-server-deployment
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: echo-server-deployment
    spec:
      containers:
      - image: ealen/echo-server
        imagePullPolicy: Always
        name: echo-server
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30
status:
  availableReplicas: 3
  conditions:
  - lastTransitionTime: "2021-11-17T00:41:10Z"
    lastUpdateTime: "2021-11-17T00:41:12Z"
    message: ReplicaSet "echo-server-deployment-c4dd99ffc" has successfully progressed.
    reason: NewReplicaSetAvailable
    status: "True"
    type: Progressing
  - lastTransitionTime: "2021-11-17T00:44:11Z"
    lastUpdateTime: "2021-11-17T00:44:11Z"
    message: Deployment has minimum availability.
    reason: MinimumReplicasAvailable
    status: "True"
    type: Available
  observedGeneration: 2
  readyReplicas: 3
  replicas: 3
  updatedReplicas: 3

We can see all the fields defined in our file, plus a lot more that are just grabbing the default value.

Finally, we can delete our deployment with:

1
kubectl delete -f deployment.yaml

Conclusion

In this article we learned how to use the imperative configuration files to manage Kubernetes objects. We only covered deployments, but other objects work similarly.

The Full documentation for the configuration API can be used to learn all the options that can be configured for all the available objects.

[ architecture  automation  docker  linux  productivity  ]
Instrumenting an Istio Cluster With Jaeger Tracing
Introduction to Jaeger Tracing
Monitoring Kubernetes Resources with Fabric8 Informers
Resource Management in Kubernetes - Requests and Limits
Kubernetes ConfigMaps