What is Pod in kubernetes or k8s?

Sanoj
16 min readNov 4, 2023

--

Welcome to our blog, we’ll explore the ins and outs of Kubernetes pods! 🚀🔍 If you’re curious about what a “pod” means in the world of Kubernetes

and want to grasp the concept easily, you’re in the right place. We’ll break down what Kubernetes pods are, how they work, and why they’re essential.

Let’s dive into the world of Kubernetes pods together! 🐳😃 #KubernetesPodsExplained

🚀 Pods are a basic scheduling unit in Kubernetes, representing the smallest unit. You cannot go beyond that, which means you cannot control containers as a unit.

🔒 If you want to control anything, it should be done at the pod level. Additionally, pods are often ephemeral in nature, meaning that a pod is not permanent.

🔄 So, if there is any issue with the pod, it will terminate, and Kubernetes will replace it with a new pod.

📝 Note: “ephemeral” means something that exists or lasts for a very short time. For example, imagine a soap bubble that you blow with a bubble wand. It’s a perfect example of something ephemeral because it only lasts for a few seconds before it pops and disappears. 🧼💨

But, not all pods are replaced; only the pods controlled by a controller manager undergo replacement.

So, what is the role of a controller manager 🤖? It continually maintains the desired number of workloads or pods alongside their current count. This means that only the pods managed by the controller are replaced.

If any pod dies 💥, the controller will replace it. Kubernetes does not run the container directly; instead, it encapsulates one or more containers into a higher-level structure called a pod.

Every pod has a name and is associated with a unique private IP address known as the POD IP. This IP address is managed by CNI (Container Network Interface) plugins, ensuring that each pod is assigned an IP address.

🧩 So, a pod can have multiple containers, and here each pod acts like a separate VM (virtual machine). So, every pod is an isolated space. Inside the pod, containers are not isolated; the pods are isolated, so containers act like they are in the same VM. So one container can talk to another container without leaving the pod because they (containers) communicate internally. 🗣️🏢

📦 If I summarized the above statements, a pod can have one or more tightly related containers that will always run together on the same worker node and in the same Linux namespace. 📄

Each pod is like a separate logical machine with its own IP, hostname, processes, and so on, running a single application. It’s just a sandbox to run containers in.

All the containers in a pod will appear to be running on the same logical machine, whereas containers in the other pods, even if they are running on the same worker node, will appear to be running on a different one.

Any containers inside a pod will share the same storage volumes and network resources and communicate using localhost. So, the meaning is this: Let’s suppose a pod has an IP address. Inside the pod, let’s say there are three containers.

Each container gets the same IP address as the pod. If you check the IP of a container, it won’t show anything because it operates at the pod level.

Then a question arises: how does one container talk to another container? Basically, every container inside a pod shares the same network and volume namespaces.

Let’s suppose that Container-1 is listening at port 80, Container-2 is listening at port 3000, and Container-3 is listening at port 8080.

Communication will happen by using localhost. For example, if Container-1 wants to communicate with Container-2, it will look like localhost:3000. If Container-2 wants to communicate with Container-3, it will look like localhost:8080.

Only the destination port will change to communicate with the other container. So now, you are probably thinking, ‘Okay, I’ve got this point, but what will happen if anyone from outside the pod wants to communicate with a container or any particular container?’

Let’s say an outsider or another pod wants to talk to Container-1. So, how will they talk?

So, that the user or pod will reach the desired pod (Pod IP) in which your container is running that you want to talk.

It should be written as POD IP: PORT_NUMBER. Then the outsider can immediately talk to the container.

using the example of above image you could run curl 10.0.1.1:80 to communicate to the container-1 , curl 10.0.1.1:3000 for conatiner-2, if you want to communicate between the two container you can use curl localhost:80,localhost:3000,localhost:8080 as per your desired container port number to calling another container.

Note: If one container in the pod occupies a particular port number, let’s say port 80, another container cannot occupy port 80. It is not possible, and it will never run because the pod is acting like a VM (virtual machine).

k8s (kubernetes) uses YAML to describe the desired state of the containers in a pod. This is also called a Pod Spec. These objects are passed to the kubelet through API server.

Pods are the unit of replication in kubernetes. If your application starts to get a lot of traffic and one pod can’t handle it all, Kubernetes can be set up to automatically create more copies of your pod within the cluster. This ensures your application can handle the increased load.

Now, let’s take a closer look at another aspect: the Lifecycle of a Pod.

Let’s say you’ve created a pod. It doesn’t come into existence directly; there are several steps it needs to go through. Think of a pod as a container for a moment. Within this container, there’s an image.

So, when you run a container, the image is first pulled from a registry or repository, such as Docker Hub, Nexus, JFrog, and so on.

In a pod’s lifecycle , it can go through different stages, starting from “Pending” and moving to “Running” You can find out what stage a pod is in by looking at its “phase” field

  1. Pending State: The pod is accepted by the kubernetes system but its container(s) is /are not created yet. so why it is not created? because maybe the image pulling process is still in process and it is not completed also it is waiting to be schedule.
  2. Running State: The pod is scheduled on a node and all its containers are created and at-least one container is in running state.
  3. Succeeded State: Let’s consider a scenario: You have a pod, and inside that pod, there’s a container with a specific job — to ping an IP address. It’s not an ongoing, infinite process; it’s a one-time task. Once the container finishes this task of pinging the IP address, the pod doesn’t stay in the “running” state. Instead, it transitions to a “succeeded” state. In this state, all the containers within the pod have completed their tasks successfully with a status of 0, and they won’t be restarted.
  4. Failed State: All container(s) in the pod have exited and at least one container returned a non-zero status.
  5. CrashLoopBackOff State: When a container repeatedly fails to start, Kubernetes will attempt to restart it multiple times. However, the term “BackOff” signifies that Kubernetes eventually stops trying to restart the container. If a pod goes into a failed state, Kubernetes initiates attempts to restart that container. If, due to some issue, the pod continues to fail, Kubernetes tries multiple times. If these restart attempts fail repeatedly, Kubernetes will stop trying, and the pod will be marked as “CrashLoopBackOff
  6. Unknown State: The pod’s status couldn’t be retrieved for some reason. This usually happens when there’s a problem communicating with the node where the pod is supposed to be running so kubernetes marked it as unknown state.

You can retrieve the phase of a pod using the following command.

kubectl get pod <pod_name> -o yaml | grep phase

We’ve gained an understanding of the pod’s lifecycle, but what about the lifecycle of an individual container? Within a pod, there are multiple containers, each with its own state. You can check the state of each container by using the command “kubectl describe pod <pod_name>”.

Lifecycle of a conatiner

Kubernets also tracks the state of each conatiner inside a Pod

Once the scheduler assigns a Pod to a Node, the kubelet starts creating containers for that pod using a conatiner runtime.

There are three possible container states:

  1. Waiting: while still pulling the conatiner image form a container image registry for example dockerhub, or applying Secret.
  2. Running: When container is running without any issues.
  3. Terminated: When container ran to completion or failed for some reason. You can use kubectl describe to see the reason whether it terminated with positive or negative , an exit code, and the start and finish time for that conatiners period of execution.

One more thing to note is that when we refer to the ‘terminated’ state, it signifies that the container has encountered a failure.

Kubernetes offers several options: whether to restart the failed containers, whether to restart only when it exited with a negative exit code (indicating a non-successful completion), or to restart always, even if the process exited with a positive exit code.

Alternatively, you can choose to completely turn off the restart policy. In any of these scenarios, your container will not be restarted.

Now, let’s explore the concept of the restart policy

So when a conatiner is terminated due to any reason so we have an option to restart it that is controled by restartPolicy.restartPolicy is not applicaple on Pod it is applicable to all conatiners not on a single container.

RestartPolicy:

  1. Restarting failed containers in the pod is controlled by restart policy
  2. The restartPolicy applies to all containers in the Pod
  3. The restart policy can have one of three values: “Always,” “OnFailure,” and “Never.”
  • “Always” indicates that the container will be restarted regardless of its exit status. For example, if your container’s primary task is to count from 1 to 10, and it successfully completes this task (reaching 10), it’s considered a success. However, if it only counts from 1 to 5 and not up to 10, it’s seen as a failure. If you apply the “Always” policy, the kubelet will restart the container after a successful count.
  • “OnFailure” means that the container will be restarted only when it fails (exits with a non-zero status code). In cases of failure, the kubelet will automatically restart the container. Otherwise, it won’t initiate a restart.
  • “Never” signifies that, regardless of the container’s exit status code, it will not be restarted again.

4. The default value is Always, thats the reason why your conatiner always restart after failing or if you set the policy as “onFailure” in both case container will restart again and again, but kubelet does not restart it infinite times.

5. Mention the restartPolicy in PodSpec

6. When containers within a Pod exit, the kubelet initiates a restart process with an increasing time delay (10s, 20s, 40s, …), which is capped at five minutes.

But what does this mean? Essentially, it signifies that upon the initial failure, the container is promptly restarted.

If it fails again, the kubelet will wait for 10 seconds before attempting another restart. If the container still encounters issues, it will wait for 20 seconds before another try.

This process continues, with the delay doubling each time, until a maximum of five minutes. After this threshold, the container is permanently marked as either ‘PodFailed’ or in a ‘CrashLoopBackOff’ state.

Why was this concept introduced? Its purpose is to prevent excessive strain on the Kubernetes cluster.

Now, let’s consider another scenario: if after five or six attempts, or about two minutes, your container manages to start automatically and enters a ‘running’ state.

However, if it fails again, it typically waits for four minutes before another restart is attempted. Yet, if the container starts and runs without any issues for the next ten minutes, the timer will reset back to zero.

So, even if it encounters a successful run on the fifth or sixth attempt, the timer will not be reset to zero because Kubernetes remains cautious of potential failures. Only after a continuous successful run for ten minutes will the timer reset to zero.

7. Once a container has executed for 10 minutes without any problems the kubelet resets the restart backoff timer for that container.

Now lets look at default policy of various contoller type

Job Controller Let’s start by talking about the first one, which is the job type. In a job, the goal is always to get it done successfully. If you have a job, you need to finish it.

In this case, you don’t want to restart the task over and over again because once the job is done, there’s no need to do it again.

So, the restart policy for a job should be set to “OnFailure” or “Never.” That means the job should only restart if something goes wrong, not in all situations.

Deployment and Replication Controllers Now, when it comes to deployment or replication controllers, it’s a bit different. Here, you want to make sure that if a container inside a pod fails, it should be automatically started again. This helps maintain the system’s desired state. So, for these controllers, the restart policy should always be set to “always.”

DaemonSet Controller Lastly, for the daemonset controller, the restart policy can be anyone. It doesn’t have to follow strict rules like the job or deployment controllers.

Now, let’s explore a scenario involving “CrashLoopBackOff” and gain a better understanding of it in the context of restartPolicy.

#pod.yml

apiVersion: v1
kind: Pod
metadata:
name: busybox
spec:
containers:
- name: busybox
image: busybox
command: ["/bin/sh"]
args: ["-c", 'sleep 10s; for num in 10 9 8 7 6 5 4 3 2 1; do echo $num ; done']
restartPolicy: Always

Explaination of the above code :

  • apiVersion: v1 and kind: Pod: These lines specify the Kubernetes API version and the resource type. In this case, it's a Pod, which is the smallest deployable unit in Kubernetes.
  • metadata section: This part contains metadata about the Pod, such as its name. The Pod is named "busybox."
  • spec section: This is where the specifications for the Pod are defined.
  • containers subsection: Inside the spec section, you define the containers running within the Pod. In this case, there is one container defined.
  • name: busybox: The name of the container is "busybox."
  • image: busybox: The Docker image used for this container is "busybox." Busybox is a lightweight Linux distribution often used for minimal containers.
  • command: ["/bin/sh"]: This specifies the command to run when the container starts. It runs the /bin/sh shell.
  • args: ["-c", 'sleep 10s; for num in 10 9 8 7 6 5 4 3 2 1; do echo $num ; done']: These are the arguments passed to the command. In this case, it's a shell command that sleeps for 10 seconds and then counts down from 10 to 1, printing each number.
  • restartPolicy: Always: This sets the restart policy for the Pod to "Always." This means that if the container terminates for any reason, the Pod will be restarted automatically. The container specified in this configuration is designed to count down from 10 to 1, so when it completes, the Pod restarts, and the process starts again.

this YAML configuration defines a Kubernetes Pod named “busybox” that runs a single container based on the “busybox” image.

The container executes a shell command to sleep for 10 seconds and then count down from 10 to 1.

The Pod is configured with a restart policy of “Always,” so it will keep restarting the container, effectively creating a loop of counting down from 10 to 1 in the shell.

In the above image ,you can observe a Pod that we’ve created. Its fundamental task is to count down from 10 to 1 and then it will stop because this is the only task that need to be done .

However, we’ve configured it with a restart policy set to “Always.” Consequently, the container again will restart, initiating the countdown from 10 to 1 repeatedly in a continuous cycle.

As a result of this behavior, Kubernetes interprets this container as experiencing recurring failures.

Kubernetes, in response, it will mark it as “CrashLoopBackOff” means that Kubernetes has made multiple attempts to launch the container, but it consistently fails to remain operational.

Therefore, it is marked as “CrashLoopBackOff” due to the container’s persistent failure to run successfully.

Alright, we’ve grasped the concept of ‘CrashLoopBackOff’. Now, let’s explore what happens when we set it to ‘OnFailure’.

apiVersion: v1
kind: Pod
metadata:
name: busybox
spec:
containers:
- name: busybox
image: busybox
command: ["/bin/sh"]
args: ["-c", 'sleep 10s; for num in 10 9 8 7 6 5 4 3 2 1; do echo $num ; done']
restartPolicy: OnFailure

In the image above, you can observe that the status of the Pod is now “Completed.” It won’t restart because the task has been successfully completed.

However, if during the container’s execution, let’s say it only counted down to 5 and then stopped, in that case, it would restart to complete the task.

Now, let’s discover what occurs when the restartPolicy is set to ‘Never’

As you can see in the image above, the status is marked as ‘Completed’ .In the case of (Never) as the restart policy, the container will never restart, no matter what is the success code. It remains inactive.

Now, let’s explore the concept of scaling pods.

  1. When scaling pods, it’s important to understand that all containers within the pod scale together. This means you cannot independently scale a single container within a pod.
  2. In Kubernetes, the unit of scale is the pod itself. You cannot individually scale containers within a pod. The entire pod is scaled up or down as a single unit.
  3. In Kubernetes, it’s generally recommended to have only one container per pod. Multiple container pods are relatively rare, and it’s considered best practice to maintain a one-to-one ratio of containers to pods.
  4. In Kubernetes, there is a concept of ‘init containers,’ which are sometimes used as a second container inside a pod to perform initialization tasks before the main container starts.

Now lets explore about Multi-Conatiner Pod Design Patterns

Init Containers:

  • Use case: Run to completion before the main container.
  • Example: Set up configuration files, wait for a database to be ready.

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

Sidecar Containers:

  • Use case: Provide supporting functionality to the main container in a multi-container pod.
  • Example: Log exporter that collects and forwards log data.

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

Ambassador Containers:

  • Use case: Act as an intermediary or proxy for communication with external services.
  • Example: Manage authentication, load balancing, or routing for requests to databases.

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

  • Adapter Containers:
  • Use case: Modify or adapt data format between containers.
  • Example: Convert log data from one format to another for centralized log management.

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — -

Explanation in Detail of the Above Points:

Init Containers: Init containers are a type of container that run to completion before the main container starts.

They are typically used for setup or initialization tasks that need to be completed before the main application container begins its work.

For example, you might use an init container to fetch configuration files, set up a database, or perform data migration tasks. Init containers are often used when you want to ensure that certain prerequisites are met before the main application starts.

Note:Init containers are not part of the multi-container design pattern; they are separate and are not meant to run concurrently with the main container. I just added it we are talking about multi-containers thats why.

Sidecar: The sidecar pattern involves running a secondary container alongside the main application container to provide additional functionality or services.

These sidecar containers are usually tightly coupled with the main application and support it in some way.

For Example , a common use case is a log exporter sidecar that collects and sends logs to a centralized logging system. Sidecar containers are a way to enhance the main container’s capabilities without modifying its code.

Ambassador: The ambassador pattern is a design pattern that involves using a separate container to act as an intermediary or proxy between the main container and external services.

The ambassador container helps in abstracting communication with external systems, load balancing, or performing transformations on data.

For example, you might use an ambassador container as a reverse proxy to route incoming requests to multiple backend services, making it easier to manage external interactions.

Adapter: An adapter container is used to modify or adapt data formats, protocols, or interfaces between different components in a system.

It serves as a bridge between the main application and external systems by translating data from one format to another.

For example, an adapter container might be used to convert log formats generated by the main application into a standardized format that can be easily ingested by a logging service.

This helps in ensuring compatibility between components that use different data formats.

Each of these container patterns serves a specific purpose and helps enhance the functionality, maintainability, and scalability of applications in Kubernetes.

Understanding Pods and their lifecycles is key to mastering Kubernetes. Just as Pods ensure your applications run smoothly, I hope this explanation has made Kubernetes more approachable.

Thank you for joining me on this journey!” 🚀🙏

--

--