Understanding Kubernetes Pods and Containers

Understanding Kubernetes Pods and Containers

kubernetes_pods_containers

Here in this article we will demonstrate the usage Pods which are the most basic deployable units in Kubernetes Cluster. We will also look at and understand about the containers that are encapsulated within a pod and the types of containers that can be launched based on the use case.

Test Environment

Fedora 36 server
Kubernetes Cluster v1.22.5

What are Pods

Pods are the very basic deployable units that we can create in kubernetes cluster. Pods encapsulate and run one or more containers. There is a host process associated with each container that is running within a Pod. Pod is just a logical unit like a wrapper around those containers. These containers share the storage and network resouces. Pod is similar to set of containers with shared namespaces and shared filesystem volumes.

App Containers – Pods with single container

Step1: Run and Describe a nginx pod

As a first step let’s create an instance of nginx image as shown below.

[admin@kubemaster kubernetes]$ kubectl run nginx-inst1 --image=nginx
pod/nginx-inst1 created

Now that our nginx instance is created let us try to get some details about the instance using the below command.

[admin@kubemaster kubernetes]$ kubectl describe pod nginx-inst1
Name:             nginx-inst1
Namespace:        default
Priority:         0
Service Account:  default
Node:             kubenode/192.168.122.49
Start Time:       Sat, 08 Oct 2022 19:35:51 +0530
Labels:           run=nginx-inst1
Annotations:      <none>
Status:           Running
IP:               10.0.1.5
IPs:
  IP:  10.0.1.5
Containers:
  nginx-inst1:
    Container ID:   cri-o://a821354dcdb232c943c2c266ee26f6485cc9ba6c8ef16407859c440cc49b37ce
    Image:          nginx
    Image ID:       docker.io/library/nginx@sha256:2f770d2fe27bc85f68fd7fe6a63900ef7076bc703022fe81b980377fe3d27b70
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Sat, 08 Oct 2022 19:36:25 +0530
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-8pxl6 (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  kube-api-access-8pxl6:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   BestEffort
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  77s   default-scheduler  Successfully assigned default/nginx-inst1 to kubenode
  Normal  Pulling    76s   kubelet            Pulling image "nginx"
  Normal  Pulled     43s   kubelet            Successfully pulled image "nginx" in 33.527070421s
  Normal  Created    43s   kubelet            Created container nginx-inst1
  Normal  Started    43s   kubelet            Started container nginx-inst1

The above output provides us with most of the details about the pod. But if you want to get detailed information in either yaml or json format we can use the below commands.

[admin@kubemaster kubernetes]$ kubectl get pod nginx-inst1 -o yaml
[admin@kubemaster kubernetes]$ kubectl get pod nginx-inst1 -o json

Step2: Execute into Pod Containers

From the above nginx pod that we just created we can see that its a single container pod as there is only one continer named nginx within the pod.

As per the below command we are trying to get a interactive bash shell from the running nginx container.

[admin@kubemaster kubernetes]$ kubectl exec -it nginx-inst1 -- bash
root@nginx-inst1:/# 

Let us try to check some details about the Pod

Hostname

The hostname of the nginx container is same as the name of the pod.

root@nginx-inst1:/# hostname
nginx-inst1

IP Address

The IP address can be captured from the /etc/hosts file as shown below. This IP address will be allocated based on the POD CIDR range that we provide during the kubernetes cluster initialization.

root@nginx-inst1:/# cat /etc/hosts 
# Kubernetes-managed hosts file.
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
fe00::0	ip6-mcastprefix
fe00::1	ip6-allnodes
fe00::2	ip6-allrouters
10.0.1.5	nginx-inst1

By default the Pod uses a default serviceaccount. serviceaccounts are system id that are used by pods to communicate with the API server.

Here is the secret token for our default serviceaccount that the nginx pod is using.

root@nginx-inst1:/# cat /var/run/secrets/kubernetes.io/serviceaccount/token 
eyJhbGciOiJSUzI1NiIsImtpZCI6IjFOckw3anZrY1FLM1BOWGdpV2dvVzF0dzNPWjZmSS1WUmlYUnFoWkhHSTAifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNjk2NzczOTUxLCJpYXQiOjE2NjUyMzc5NTEsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0IiwicG9kIjp7Im5hbWUiOiJuZ2lueC1pbnN0MSIsInVpZCI6ImFiOTEyMjgxLTMxYmItNGU2MS1hMGFjLTcwOTQyMTBmZTRhZSJ9LCJzZXJ2aWNlYWNjb3VudCI6eyJuYW1lIjoiZGVmYXVsdCIsInVpZCI6IjU4MmQzNzIwLTY1ZDAtNGIwOC04ZjE2LWIyMTJhODg4MDY0ZiJ9LCJ3YXJuYWZ0ZXIiOjE2NjUyNDE1NTh9LCJuYmYiOjE2NjUyMzc5NTEsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.X9cLOh2Vu44rYzdeERYlj5SEQG0KLfS0g03r4tZ7RezWDJOtZboUmrnIQ9jbjjQqLouVXL5Imt-5oVSp1wxBCdfPQKiOoCZmHUsqVnhsHicRnliyKzhsm8kTea1fGo2McP0VNQFDgmcHd7-Bm-l5HwSsVrbyqXHYIsDn1Fi94y0f5h5OMLQ4-v55geoCrKQgg0K9kZdlnJPu1pAlbj-osa4OBMWLiECHbVtv_ueYpPdlScEmx4QggA9V3e_KEfqOwaFSoAX3ER7WJW1oJO2lFmfq2U-TLPTQXNdEr93vELsqG669gdGihmjqq5G0eYy9zB_TnU-UPY6LcLDxCJRH5A

Step3: Replicate Pod

Here we will try to create another instance of the nginx pod to scale the nginx instance horizontally.

[admin@kubemaster kubernetes]$ kubectl run nginx-inst2 --image=nginx
pod/nginx-inst2 created

Now let’s try to get the details of the IP address that is allocated and the node on which these two pods are scheduled as shown below.

[admin@kubemaster kubernetes]$ kubectl get pods -o wide
NAME          READY   STATUS    RESTARTS   AGE     IP           NODE       NOMINATED NODE   READINESS GATES
nginx-inst1   1/1     Running   0          4m10s   10.0.1.5     kubenode   <none>           <none>
nginx-inst2   1/1     Running   0          18s     10.0.1.172   kubenode   <none>           <none>

As you can see the pods are allocated with an IP address from the subnet “10.0.1.x”.

Pods natively provide two kinds of shared resources for their constituent containers: networking and storage.

Step4: Termination of Pod

By default when a pod deletion request is submitted to API server, kubelet tries to grace-fully shutdown the container process with pid 1 in the pod with a default grace period of 30 sec after which kubelet triggers the forcible shutdown with SIGKILL signal.

[admin@kubemaster kubernetes]$ kubectl delete pod nginx-inst1  # This termination of pod tries a grace full shutdown of the container process

If we want to forcibally terminate the pod we can pass grace period with 0 sec and –force flags as shown below.

[admin@kubemaster kubernetes]$ kubectl delete pod nginx-inst2 --force --grace-period=0
Warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
pod "nginx-inst2" force deleted

Init Containers – Pods with multiple containers

A Pod can have multiple containers running apps within it but Init Containers are a specialized type of containers that are executed before any App Containers are started. The Init Containers need to be executed successfully to their completion state in the order they are defined.

Init containers do not support lifecycle, livenessProbe, readinessProbe, or startupProbe because they must run to completion before the Pod can be ready. Init containers can run with a different view of the filesystem than app containers in the same Pod. Consequently, they can be given access to Secrets that app containers cannot access. Init containers can securely run utilities or custom code that would otherwise make an app container image less secure. By keeping unnecessary tools separate you can limit the attack surface of your app container image.

Step1: Create a Pod with Init Container

Here is a very basic init container demo in which we have one init container which will loop through a count of 100. Once this loops completes the init container should get into completion state and the app container should be initiated and get into running state.

[admin@kubemaster workloads]$ cat initcontainerapp.yml 
apiVersion: v1
kind: Pod
metadata:
  name: initcontainer-demo
  labels:
    app.kubernetes.io/name: initcontainer-demo
spec:
  containers:
  - name: myapp
    image: busybox:1.28
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox:1.28
    command: ['sh', '-c', "for i in `seq 1 100`; do echo $i; sleep 1; done"]

Let’s apply this yaml definition file as shown below.

[admin@kubemaster workloads]$ kubectl apply -f initcontainerapp.yml 
pod/initcontainer-demo created

Now if we check the pod status it is still not in running state as the init container is still running in the pod.

[admin@kubemaster workloads]$ kubectl get pods
NAME                 READY   STATUS     RESTARTS   AGE
initcontainer-demo   0/1     Init:0/1   0          30s

We can check the logs of the init-myservice container as shown below. Once this init-myservice container count reaches to 100, the init-myservices container execution completes.

[admin@kubemaster workloads]$ kubectl logs initcontainer-demo -c init-myservice
1
2
3
4
...
100

As shown below the pod is now in running state means the app container is initiated and in running state now.

[admin@kubemaster workloads]$ kubectl get pods
NAME                 READY   STATUS    RESTARTS   AGE
initcontainer-demo   1/1     Running   0          104s

We can check the status of each container using the kubectl describe command as shown below.

[admin@kubemaster workloads]$ kubectl describe pod initcontainer-demo
Name:             initcontainer-demo
Namespace:        default
Priority:         0
Service Account:  default
Node:             kubenode/192.168.122.49
Start Time:       Sat, 08 Oct 2022 20:34:26 +0530
Labels:           app.kubernetes.io/name=initcontainer-demo
Annotations:      <none>
Status:           Running
IP:               10.0.1.123
IPs:
  IP:  10.0.1.123
Init Containers:
  init-myservice:
    Container ID:  cri-o://089101d6704582c964447b7ea62d587214ceaa5ed2518b3bf551d68ff00f7e00
    Image:         busybox:1.28
    Image ID:      docker.io/library/busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47
    Port:          <none>
    Host Port:     <none>
    Command:
      sh
      -c
      for i in `seq 1 100`; do echo $i; sleep 1; done
    State:          Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      Sat, 08 Oct 2022 20:34:27 +0530
      Finished:     Sat, 08 Oct 2022 20:36:07 +0530
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-vbsx4 (ro)
Containers:
  myapp:
    Container ID:  cri-o://8097eebf3c608dab378e5dd3cb6f68117b3993e9d1fdb853e49c48c94d3f0a24
    Image:         busybox:1.28
    Image ID:      docker.io/library/busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47
    Port:          <none>
    Host Port:     <none>
    Command:
      sh
      -c
      echo The app is running! && sleep 3600
    State:          Running
      Started:      Sat, 08 Oct 2022 20:36:08 +0530
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-vbsx4 (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  kube-api-access-vbsx4:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   BestEffort
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age    From               Message
  ----    ------     ----   ----               -------
  Normal  Scheduled  4m2s   default-scheduler  Successfully assigned default/initcontainer-demo to kubenode
  Normal  Pulled     4m2s   kubelet            Container image "busybox:1.28" already present on machine
  Normal  Created    4m2s   kubelet            Created container init-myservice
  Normal  Started    4m2s   kubelet            Started container init-myservice
  Normal  Pulled     2m21s  kubelet            Container image "busybox:1.28" already present on machine
  Normal  Created    2m21s  kubelet            Created container myapp
  Normal  Started    2m21s  kubelet            Started container myapp

Ephemeral Containers – Pods with Debugging Containers

Pods are the basic deployable units which can only be disposed and replaced with a new Pod. We cannot add a new container to an already existing Pod. In order to debug a faulty container in a Pod we need to deploy a ephemeral container with some utility tools which can help us to debug a faulty container in a pod.

Ephemeral containers are created using a special ephemeral containers handler in the API rather than by adding them directly to pod.spec, so it’s not possible to add an ephemeral container using kubectl edit.

Ephemeral containers are useful for interactive troubleshooting when kubectl exec is insufficient because a container has crashed or a container image doesn’t include debugging utilities.
Distroless images are a kind of minimal container images which are built with a reduced attack surface and exposure to bugs and vulnerabilities.

Let’s try to create a pod with a distroless image which does not contain the required utilities to debug the container.

[admin@kubemaster workloads]$ kubectl run ephemeral-demo --image=registry.k8s.io/pause:3.1 --restart=Never
[admin@kubemaster workloads]$ kubectl get pods
NAME                 READY   STATUS    RESTARTS   AGE
ephemeral-demo       1/1     Running   0          14s

Now if we try to get shell into the ephemeral-demo pod we will get an error as the shell utilities are not available in this distroless image.

[admin@kubemaster workloads]$ kubectl exec -it ephemeral-demo -- sh
ERRO[0000] exec failed: unable to start container process: exec: "sh": executable file not found in $PATH 
command terminated with exit code 255

Now in order get a view of the ephemeral pod container we will use kubectl debug to instantiate a debugging container and attach it to the pod ephemeral-demo as shown below.

[admin@kubemaster workloads]$ kubectl debug -it ephemeral-demo --image=busybox:1.28 --target=ephemeral-demo
Targeting container "ephemeral-demo". If you don't see processes from this container it may be because the container runtime doesn't support this feature.
Defaulting debug container name to debugger-cqtwt.
If you don't see a command prompt, try pressing enter.
/ # 

This command adds a new busybox container and attaches to it. The –target parameter targets the process namespace of another container. Please note that there are other methods to carry out debugging of the pod containers as mentioned in the Documentation – https://kubernetes.io/docs/tasks/debug/debug-application/debug-running-pod/#ephemeral-container.

Hope you enjoyed reading this article. Thank you..