Kubernetes Objects

Kubernetes Objects are persistent entities in the cluster. These objects are used to represent the state of the cluster.

The following are some of the Kubernetes Objects:

  • pods
  • Namespaces
  • ReplicationController (Manages Pods)
  • DeploymentController (Manages Pods)
  • StatefulSets
  • DaemonSets
  • Services
  • ConfigMaps
  • Volumes

Docker World:

Before starting, let’s look at what is docker container first?

Any application requires software to run, some sort of configuration (between environments) and dependent libraries. Docker makes it easy to package all of them together and ship it as one standalone package. Docker Container image is a lightweight, executable software that package application, environment and dependent libraries. Docker Container image become containers at runtime. We can create any number of containers from Container Image. In terms of Java world, Container Image is a Java Class and running containers are the objects that we can instantiate by using Class.

Before starting Kubernetes Objects, I would encourage to go through Kubernetes Architecture (my second post of K8s journey) to understand the K8s components and how they interact with each other.

Let’s start our discussion with basic building block of K8s…

Pods:

In Docker World, Every Microservice is deployed as Container. In K8s world, A Pod is the basic building block of K8s Objects. A pod is a colocated group of containers. A pod can contain single container as well. But when it contains multiple containers, all of the containers are running on single worker node. A pod won’t distribute across multiple worker nodes.

Now, the question of why do we need colocated containers in the same pod?

  • There are certain cross cutting concerns which you need to take care before bringing up actual application container. For example, let’s say your application stores all its secret/password configuration in vault. To integrate with vault you need short lived vault token so that you can retrieve the passwords during runtime. Now you can bring up the first container which is responsible to integrate with vault and query for vault token and write it to a shared volume. Actual app container can read this token which was in shared volume and get the secret/passwords needed.
  • Another example, Let’s say our application is responsible for serving traffic for web content. We can divide the app into two containers, One container is responsible for serving the content, and another container is responsible for loading the content from external systems.
  • Another example, Our app container is responsible for handling the ecommerce store, another container is responsible for collecting the logs and sending it to a central location.

It’s not necessary that, we need to have multiple containers running under one pod. If there is a need K8s supports with colocated containers (grouping) under one pod which will share the same linux namespace. We can mount a shared volume which will be available for all the containers under one pod that can read/write the data on shared volume. Containers that are running under one Pod share the IP address allocated and port space, which means the IP address is allocated for the pod not for each container under the same pod.

Assumption: You have a kubernetes cluster running, either on google cloud, on-prem or as minikube for experimenting. You have installed kubectl, which is a command line tool for accessing k8s cluster.

Enough of theory, Let’s look at how to create a Pod?

Create Pod:

We can use kubectl or Rest api (to the api server) to submit the pod manifest to K8s to create a resource (Or Object). Ultimately kubectl also connects to api server and submit the pod manifest using Rest api only.

The commands are pretty simple to use and you need to write a manifest either in YAML or JSON notation. To create a Pod:

kubectl create <pod-manifest>

Pod manifest sample: spring-app.yaml

spring-app.yaml
  1. kind: Represent the type of k8s object created. It can be a Pod, DaemonSet, Deployments or Service.
  2. version: K8s api version used to create the resource, It can be v1, v1beta and v2. Some of the resources can be released under beta and available for general public usage.
  3. metadata: provides information about the resource like name of the pod, namespace under which the pod will be running,
    labels and annotations.
  4. spec: consists of the core information about pod. Here we will tell k8s what would be the expected state of resource, Like container image, number of replicas, environment variables and volumes.
  5. status: consists of information about the running pod, status of each container. Status field is supplied and updated by Kubernetes after creation.

When we create an object in K8s, we need to provide the object spec (manifest) which describes the object desired state. The status describes the actual state of the object.

Probably it would make sense now to understand the pod manifest that we have submitted to k8s master. The type of resource that we are creating is a pod and it has only one container as mentioned in the spec, and we are referring a sample image springdemoapp which is a spring boot java based application and the container is running on port 8080.

You can get the running pod information using:

kubectl get pods

Now you should see:

NAME         READY     STATUS    RESTARTS   AGE
spring-pod 1/1 Running 0 10m

You can get the complete yaml by specifying -o (output format) option. The returned pod manifest will consists of the additional fields added by K8s such as status, hostIp (k8s worker node) and podIp Address.

kubectl get pod spring-pod -o yaml

You can check the logs for your container using:

kubectl logs -f spring-pod --tail 200

You should see something like:

s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
c.m.main.SpringDemoAppApplication : Started SpringDemoAppApplication in 6.997 seconds (JVM running for 7.762)

If we have multiple containers running under one pod, to look at the logs for specific container we can use -c option as:

kubectl logs -f spring-pod --tail 200 -c spring-demoapp

Now that our pod is running successfully, how do we access our application? There are several different ways to access the application running on K8s. We can use hostPort or else port-forward or service based approach. We will discuss about service based approach in next medium post. Let’s start with how to access using hostPort.

Access Pod Using hostPort:

We need to tweak our spring-pod.yaml and add hostPort parameter to the ports section.

spring-app-expose.yaml

With this change our application is exposed to host network using hostIp (In case of gcloud, on-prem it’s the running worker node or in case of minikube you can get the host IP address using minikube status which will print the IP Address of k8s master) and hostPort at 9090.

We can validate by running spring boot health call at:

curl -v http://192.168.99.100:9090/health

The output should be:

Trying 192.168.99.100...
* Connected to 192.168.99.100 (192.168.99.100) port 9090 (#0)
> GET /health HTTP/1.1
< HTTP/1.1 200
{"status":"UP"}

Access Pod Using port-forward:

Using port-forward to access the application. port-forward option, I would recommend using it for only local testing.

kubectl port-forward spring-pod 10000:8080

After that you will see the following operation happening in the console, which will be exactly similar to hostPort option.

Forwarding from 127.0.0.1:10000 -> 8080
Forwarding from [::1]:10000 -> 8080

Now we can try the same request with 10000 port using:

curl -v http://127.0.0.1:10000/health

Now we see:

*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 10000 (#0)
> GET /health HTTP/1.1
> Host: 127.0.0.1:10000
< HTTP/1.1 200
{"status":"UP"}

View Pod Details:

We can get the running pod details, events, container status by using describe command:

kubectl describe pod spring-pod

Now view the details:

Name:         spring-labels
Namespace: default
Node: minikube/192.168.99.100
Labels: <none>
Annotations: <none>
Status: Running
IP: 172.17.0.6
Containers:
spring-demoapp:
Image: chkrishna/springdemoapp:latest
Port: 8080/TCP
State: Running
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 31s default-scheduler Successfully assigned spring-labels to minikube
Normal SuccessfulMountVolume 30s kubelet, minikube MountVolume.SetUp succeeded for volume "default-token-5fvlr"
Normal Pulling 30s kubelet, minikube pulling image "chkrishna/springdemoapp:latest"
Normal Pulled 29s kubelet, minikube Successfully pulled image "chkrishna/springdemoapp:latest"
Normal Created 29s kubelet, minikube Created container
Normal Started 29s kubelet, minikube Started container

Delete Pod:

We can delete the pod based on the name of the pod or by passing the same manifest that we have used to create the resource.

kubectl delete pod spring-pod
# OR
kubectl delete -f spring-app.yaml

Using Labels:

A label is a key/value pair which can be associated to a k8s object (resource). We can use these labels when selecting the resources using label selectors. We can associate any number of labels to a k8s object. We can add/delete/modify the labels even after creating the resource.

For example we can tag all the api services with appType as api, all the web applications with appType as web. Now create one more pod by adding labels.

Pod manifest: spring-labels.yaml

kubectl create -f spring-labels.yaml

Now to list all the pods along with labels, we can use --show-labels option.

kubectl get pods --show-labels

The output would be like this:

NAME            READY     STATUS    RESTARTS   AGE       LABELS
spring-labels 1/1 Running 0 35s appType=api
spring-pod 1/1 Running 0 1h <none>

We can add the labels after creating the pod as well. We can label our spring-pod also with appType label using:

kubectl label pod spring-pod appType=api
kubectl label pod spring-labels dept=billing

Now you should see:

pod "spring-pod" labeled

Now get all the pods again with --show-labels option:

kubectl get pods --show-labels

Your output should be:

NAME            READY     STATUS    RESTARTS   AGE       LABELS
spring-labels 1/1 Running 0 16m appType=api,dept=billing
spring-pod 1/1 Running 0 1h appType=api

To delete a label from running pod:

kubectl label pod spring-pod appType-

Now if we run the get pods with --show-labels option we will see the earlier status for spring-pod with label as <none>.

Similarly we can update the existing label of a pod using:

kubectl label pod spring-labels dept=customer_service --overwrite

To update existing label we have to use --overwrite option. Now get all the pods again with show-labels option

kubectl get pods --show-labels

The output should be:

NAME            READY     STATUS    RESTARTS   AGE       LABELS
spring-labels 1/1 Running 0 18m appType=api,dept=customer_service
spring-pod 1/1 Running 0 1h <none>

We can also list pods based on label selectors, For that we can use -l option.

kubectl get pods -l dept

The output should be:

NAME            READY     STATUS    RESTARTS   AGE       LABELS
spring-labels 1/1 Running 0 22m appType=api,dept=customer_service

We can narrow down the list based on specific value of label as well using:

kubectl get pods -l appType=api

The output should be:

NAME            READY     STATUS    RESTARTS   AGE       LABELS
spring-labels 1/1 Running 0 22m appType=api,dept=customer_service

We can also delete the pods using label selectors like:

kubectl delete pod -l appType=api

Now you should see:

pod "spring-labels" deleted

Experimenting Kubernetes:

If on-premise or gcloud k8s cluster is not an option for you to explore you don’t need to worry. You can experiment kubernetes using the following interactive terminals:

  1. Kubernetes interactive tutorials
  2. Katacoda

It’s a lot of information for now with only one resource (Pod), I will cover the remaining objects in next post (will add the link soon). Please let me know your feedback in the comments section.

Solution Architect @TSYS Good @ Java, Kubernetes, Kafka, AWS cloud, devops , architecture and complex problems