Deployment Artifacts

Now that we have validated an operational Kubernetes environment, let’s focus on deploying workloads to it and see how it reflects in individual artifacts.

Exercise - check status of artifacts

In the previous exercise you have seen how “kubectl get” generally lists instances of certain object types. You can also combine the options by separation with comma. E.g. to list the currently running Deployments, ReplicaSets and Pods type:

kubectl get deployment,replicaset,pod

Alternatively, you can use short names to achieve the same result:

kubectl get deploy,rs,po

What did you get as a result of this command?

Solution

For now you should get output similar to the following:

$ kubectl get deploy,rs,po
No resources found in <your_namespace> namespace.
Warning

Do not add any spaces after the commas!

Exercise - use the watch command

Something very helpful is the external or built-in watch option for kubectl commands. This will keep the query for resources open and repeat it in a predefined interval.

Run:

watch -n1 kubectl get deployment,replicaset,pod

See:

Every 1.0s: kubectl get deployment,replicaset,pod

No resources found in <your_namespace> namespace.

This means the “kubectl get” call will be repeated every second. Right now there is nothing deployed, so there is not much to see.

If you want, you can use the built-in watch command of kubectl. Beware, it can only watch a specific single resource type like this:

kubectl get pod -w (which should not yield any output at first, but then display any changes as they occur)

Press Ctrl+C to break out of either version.

Exercise - Open a separate terminal session

It is recommended to run multiple terminal sessions here in parallel, so you can keep the watch command open and execute commands at the same time.

So duplicate your session and run the watch command in one, before doing the next exercise.

Exercise - deploy your first workload

Try and use the same initial container image, which was used for Docker first as well:

kubectl create deployment sampleapp --image novatec/technologyconsulting-hello-container:v0.1

Warning

With older versions of kubectl it was sufficient to run kubectl run sampleapp .... However, starting with kubectl v1.18 this will only run a single pod and not a deployment.

  • A Deployment describes the desired state of a deployed workload. It consists of a ReplicaSet and one or several Pods created by it. Yes, that word has many possible alternative meanings, but this is what it is in a Kubernetes context.
  • A ReplicaSet maintains a stable set of replica Pods running at any given time. As such, it is often used to guarantee the availability of a specified number of identical Pods.
  • Pods are the smallest deployable units of computing that can be created and managed in Kubernetes. They consist of one or several containers working together, and generally they are not created directly but rather via Deployments.

Milestone: K8S/DEPLOYMENT/SAMPLEAPP

Now look at the objects created:

watch -n1 kubectl get deploy,rs,po

You will see a few things changing here:

Every 1.0s: kubectl get deploy,rs,po

NAME                        READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/sampleapp   1/1     1            1           95s

NAME                                   DESIRED   CURRENT   READY   AGE
replicaset.apps/sampleapp-74964cf7b8   1         1         1       95s

NAME                             READY   STATUS    RESTARTS   AGE
pod/sampleapp-74964cf7b8-kjwl7   1/1     Running   0          95s

What has happened here is conceptually really comparable to what happened when working with Docker:

sequenceDiagram
    participant U as User
    participant K as Kubernetes
    participant R as Remote Registry
    U->>K: "kubectl create ..."
    opt fetching image
        K->>R: image pull
        R-->>K: image
    end
    loop execute
        K->>K: run Pod in background until terminated
    end
    K-->>U: release terminal

Exercise - get more information

Repeat the kubectl command specifying the switch -o wide.

kubectl get deploy,rs,po -o wide

This option adds some columns of information (different from type to type).

$ kubectl get deploy,rs,po -o wide
NAME                        READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS                             IMAGES                                              SELECTOR
deployment.apps/sampleapp   1/1     1            1           2m23s   technologyconsulting-hello-container   novatec/technologyconsulting-hello-container:v0.1   app=sampleapp

NAME                                   DESIRED   CURRENT   READY   AGE     CONTAINERS                             IMAGES                                              SELECTOR
replicaset.apps/sampleapp-74964cf7b8   1         1         1       2m23s   technologyconsulting-hello-container   novatec/technologyconsulting-hello-container:v0.1   app=sampleapp,pod-template-hash=74964cf7b8

NAME                             READY   STATUS    RESTARTS   AGE     IP            NODE                               NOMINATED NODE   READINESS GATES
pod/sampleapp-74964cf7b8-kjwl7   1/1     Running   0          2m23s   10.244.1.10   aks-fer2pool-24896946-vmss000001   <none>           <none>

An alternative is to run it in the following (lazy) way:

kubectl get all -o wide

Note

Please note that unlike what the name suggests this won’t actually list all resources but rather only all of those you’d (more or less) reasonably expect to see, following certain [rules][kubectl-rules]. You can check kubectl api-resources for a full list of available resource types.

Tip

It’s possible to use the ‘k’ alias instead of writing ‘kubectl’. This is an useful hack in your .bashrc!

Exercise - Recap

What you see is the information about Deployments, ReplicaSets and Pods. The Pods are on the section of the output. You can identify a naming structure.

  • The Deployment will be called sampleapp
  • The ReplicaSets will be called sampleapp-<ReplicaSetId>
  • The Pods will be called sample sampleapp-<ReplicaSetId>-<PodId>

Note down the sample sampleapp-<ReplicaSetId>-<PodId> or copy it to your local clipboard. But please also note that there exists TAB-autocompletion for kubectl commands as well, allowing to get <ReplicaSetId>-<PodId> ad-hoc as shown below.

Exercise - Logging

The application is now running in a container in a Pod controlled by a ReplicaSet controlled by a Deployment. However it is still not directly accessible from outside. Pods in the same namespace could theoretically access it, but let’s look at some options to interact with the application:

To validate if the application is running successfully, you can check the logs of the pods using:

kubectl logs sampleapp-<ReplicaSetId>-<PodId>

e.g.

kubectl logs sampleapp-<TAB> or just kubectl logs sample<TAB> or even just kubectl logs <TAB>

(or even kubectl logs deployment/sampleapp if you don’t want to address an individual Pod)

which might then autocomplete to

kubectl logs sampleapp-74964cf7b8-kjwl7

You should see a log containing the information:

$ kubectl logs sampleapp-74964cf7b8-kjwl7
__  ____  __  _____   ___  __ ____  ______
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2023-12-01 10:23:19,470 INFO  [io.quarkus] (main) quarkus-hello 1.0.0-SNAPSHOT native (powered by Quarkus 3.1.3.Final) started in 0.052s. Listening on: http://0.0.0.0:8080
2023-12-01 10:23:19,470 INFO  [io.quarkus] (main) Profile prod activated.
2023-12-01 10:23:19,470 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy-reactive, smallrye-context-propagation, vertx]

The whole sequence can be displayed as follows:

sequenceDiagram
    participant U as User
    participant K as Kubernetes
    participant P as Pod
    U->>K: "kubectl logs ..."
    K->>P: retrieve logs
    K-->>U: return logs

Exercise - port-forwarding

As you know there is an application listening on port 8080, apply a port-forwarding to this app by using:

kubectl port-forward sampleapp-<ReplicaSetId>-<PodId> 8080:8080 >> /dev/null &

e.g.:

kubectl port-forward sampleapp-74964cf7b8-kjwl7 8080:8080 >> /dev/null &

This port-forwarding allows us to access the specified pod directly from our workstation, bypassing access restrictions, so we can check whether the application works in principle. For actually exposing the application to regular external access there are better means as we will see later on.

Note: The “appendix” to the command using >> /dev/null & will switch the process into the background and redirect its output so it will not disturb the CLI.

Now try to connect to the app using the following command:

curl localhost:8080/hello; echo

The curl command should return a familiar output:

$ curl localhost:8080/hello; echo
Hello World (from sampleapp-74964cf7b8-kjwl7) to somebody

The whole sequence can be displayed as follows:

sequenceDiagram
    participant U as User
    participant K as Kubernetes
    participant P as Pod
    U->>K: "kubectl port-forward ..."
    K->>P: creates port-forward
    loop execute
        K->>K: keep port mapping
    end
    K-->>U: release terminal
    U->>P: "curl ..."
    P-->>U: "Hello World to somebody"

Exercise - Execute a shell

An alternative way to communicate with the application is opening a terminal in the container in a similar style as in Docker:

kubectl exec -it sampleapp-<ReplicaSetId>-<PodId> -- <command>

e.g.:

$ kubectl exec -it sampleapp-74964cf7b8-kjwl7 -- /bin/bash
bash-4.4$

Here you can execute calls within the container again:

ls /work

You will see the application, but the files we created in the Docker exercise are not there.

Why?

Solution

Because you are using the unmodified version of the image (v0.1)

Exit the terminal.

Tip

Of course it’s rather unwieldy to dig out the specific ReplicaSetId and PodId for addressing an individual Pod with such an exec. So if you don’t mind in which individual Pod this gets executed just issue

kubectl exec -it deployment/sampleapp -- <command>

instead.

Exercise - Clean up

To clean up the deployment artifacts in Kubernetes simply execute

kubectl delete deployment sampleapp

In the watch command output you will see that the ReplicaSet and Pods will be removed, too.

Milestone: K8S/DEPLOYMENT/CLEANUP

One thing that tends to get forgotten is the port-forwarding, which might block the local port 8080 at a later point.

Execute the following call to remove the port-forwarding

pkill -f 'kubectl port-forward sampleapp-'