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?
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 95sWhat 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 somebodyThe 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?
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-'