Kubernetes-101: Pods, part 1
Tags: kubernetes
Published: 2023-03-17 11:00:00

This is my own story of what a Kubernetes pod is. There are a number of blog posts and documentation pages about this topic out there[^k8spod]. What can I possibly bring to the table with yet another story of what a pod is? Honestly, this might be more for my own sake of learning to document technical topics than it is for the benefit of you, the reader. However, maybe you find something of value in here.

Note that this article is about stand-alone pods. In future articles I will describe controller objects that creates and controls pods for us (for instance the **deployment** resource).

Also, a big caveat, if you have not used Kubernetes before this article will not teach you about the basics of setting up a cluster. I will discuss these topics in later articles, but for now I assume you either know some basics or know how to find relevant information online on how to do this. What I will say is that if you want a simple experience (and you are using a mac with `homebrew` and `docker` installed) you could get a working cluster by running the following commands:

```console
$ brew install minikube
$ brew install kubectl
$ minikube start
```

## Introduction

What is a **pod** anyway? A fitting definition of the word is:[^webster]

> a number of animals (such as whales) clustered together

In Kubernetes we are not handling _a number of animals_ (_such as whales_), instead we are handling _a number of containers_. A **container** is a running instance of an **image** (e.g. a Docker image) that contains your application source code and all the dependencies that are required to run your application. Returning to the word pod, and rewriting the previous definition a bit:

> a pod is a number of containers (such as Docker containers) clustered together

There we go! I said we are not handling a number of whales in Kubernetes, but it is interesting that Docker uses a whale as its icon.

With the basic definition out of the way, let us dig deeper into what a pod is, what we use pods for, and how we can create a pod in an imperative way and declarative way.

## When do we use pods in Kubernetes?

The short answer is: _all the time!_ I would argue that the pod is the single most important object in the Kubernetes world. Pods are the running instances of your application code[^container].

So when you have written your application code and built an image using Docker or some other containerization tool, you can create an instance of your application in the form of a container running inside of a pod in Kubernetes. However, your applications are not the only pods running in your cluster. There are a number of system pods running the whole time, as well as other helper pods you might set up depending on your needs. Basically, pods are the lowest level objects you will be dealing with on a day-to-day basis.

So how many pods do you need? This depends on a number of things. First of all, let's say you are building some sort of microservice architecture. Each service should have its own pod. Actually, it should probably have a number of identical pods running to allow for pod failures and to handle more traffic. Each pod can contain several containers, but that does not mean you should put all your microservices inside of a single pod. A pod should only contain containers that are intimately related with one another. Microservices should not be intimately related to each other - thus they should not be in the same pod. What is an example of intimately related containers? A common use-case is to have a sidecar container that performs a certain task. For instance, if you are using Prometheus for monitoring you might expose an endpoint on your container that exposes the values of a number of metrics of interest. Then you could run a Prometheus sidecar container that scrapes this endpoint, collects the metric values, and publishes the values to a Prometheus server. Thus your main application container does not need to know anything about how to publish metrics to Prometheus, it just needs to expose the metrics it knows about and the sidecar handles the rest.

In the rest of this article I will go through some examples of how to create pods in your Kubernetes cluster. What I will not show in this article is a better way of creating pods. In reality we do not create individual pod objects in Kubernetes, we would rather create other types of resources that in turn create pods for us. One example of this is the deployment resource. A deployment is an abstraction on top of pods where we can control things like how many copies of our pod we want to create. I will go through deployments in a later article.

## Working with Kubernetes pods

### Imperatively creating a pod in Kubernetes

There are two main ways of creating a pod in Kubernetes, an imperative way and a declarative way. We will look at the (preferred) declarative way [below](#declaratively-creating-a-pod-in-kubernetes). Here we will look at the imperative way.

The main way of interacting with Kubernetes is through the Kubernetes CLI tool called `kubectl`. Not sure how this name is pronounced, is it _kube-control_ or is it _kube-c-t-l_? Or something else completely? I digress! Let us run a command with `kubectl` to create a new pod running an Nginx web-server:

```console
$ kubectl run web --image=nginx:latest

pod/web created
```

That was easy. I have specified that I want to `run` a pod with the name `web` that should run the image `nginx:latest`[^nginx]. If I want to list the pod I just created I can run a `get` command:

```console
$ kubectl get pods

NAME    READY   STATUS    RESTARTS   AGE
web     1/1     Running   0          15s
```

Already at this point I feel that it is a good idea to talk a bit about shortcuts in the `kubectl` tool. The command I just ran, `kubectl get pods` is not a long command, but each type of resource in Kubernetes has a short name when using the `kubectl` tool[^kubectl]. For pods the short name is `po`, 50% less characters than in `pods`! So the last command could be written as:

```console
$ kubectl get po

NAME    READY   STATUS    RESTARTS   AGE
web     1/1     Running   0          15s
```

Anyway: there we have it, a `Running` pod! We see that the `READY` column reports `1/1`. This indicates that we have 1 container (out of a total of 1) that is up-and-running. If I want additional details of my pod I can run a `describe` command:

```console
$ kubectl describe pod web
```

I won't include the output of this command here since it is very verbose. Suffice to say that you get a lot of information related to your pod with this command. How can I delete my pod once I am done with it? For that I will use the `kubectl delete` command:

```console
$ kubectl delete pod web

pod "web" deleted
```

### Declaratively creating a pod in Kubernetes

The preferred way to work with pods (or anything else for that matter) in Kubernetes is a declarative approach. In a declarative approach we specify what we want our pod to look like using YAML, and then we give this specification to Kubernetes and Kubernetes makes sure that our pod looks the way we want it to look. It is a very powerful approach.

Without further ado, let us create a very simple Nginx web-server in the form of a pod:

```yaml
# pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: web
spec:
  containers:
    - name: web
      image: nginx:latest
      ports:
        - containerPort: 80
```

This is the declarative definition of a pod. All YAML documents that describe Kubernetes resources are called **manifests**. To break it down a bit further:

- I am using API version `v1`[^pod]
- I say that the kind of resource I want to create is a `Pod`
- I give some metadata about my pod, specifically giving it a `name` that I can refer to later
- I provide a specification `spec` of what my pod should contain
- I say that my pod should contain a single container inside of `containers`
- I give my container the `name` `web` (this name is different from the `metadata.name`, although I use the same value)
- I say that my container should use the image `nginx:latest`[^nginx]
- I specify that my container should be accessible on `containerPort` `80`

Now I need to tell Kubernetes that I want to have this pod created. I do this using `kubectl` and an `apply` command:

```console
$ kubectl apply -f pod.yaml

pod/web created
```

Similarly to the imperative case, I can check the status of my new pod with the `kubectl get` command:

```console
$ kubectl get po

NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          20s
```

It worked! If I want to change settings for my pod I can update the YAML file, and then run another `kubectl apply` command to update it. It is that simple. If I want to delete my pod I can run `kubectl delete`:

```console
$ kubectl delete -f pod.yaml

pod "web" deleted
```

### Declaratively running several containers in Kubernetes

Hang on a moment. In the [introduction](#introduction) we defined a pod as a number of containers clustered together. Let us expand on this for a moment. A pod _usually_ only contains a single container, like in the examples we have seen so far. However, it is possible, and not that uncommon, to run more than one container in the same pod. What would be a typical use-case for running two or more containers in the same pod, you ask? From my own experience in Kubernetes the **most** common use-case is to run a sidecar-container that performs a given task. For instance, the Prometheus use-case I discussed above.

As an example, let us set up a pod with an Nginx container as well as a Prometheus container:

```yaml
# pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: web
spec:
  containers:
    - name: web
      image: nginx:latest
      ports:
        - containerPort: 80
    - name: prometheus
      image: prom/prometheus:latest
      ports:
        - containerPort: 9090
```

Here we have two containers, one named `web` and one named `prometheus`. They run different `image`s and they expose different `containerPorts`. The two containers can communicate over `localhost` because they are in the same pod.

We apply the manifest to create the pod:

```console
$ kubectl apply -f pod.yaml

pod/web created
```

Then we check the status:

```console
$ kubectl get pods

NAME   READY   STATUS    RESTARTS   AGE
web    2/2     Running   0          22s
```

In this case we see that the `READY` column says `2/2`. This means both of our containers are ready to receive traffic. Actually, the Prometheus container is not really supposed to receive traffic in the way that the Nginx container is. But let us ignore that for now. If we want to delete the pod we run the `kubectl delete` command:

```console
$ kubectl delete -f pod.yaml

pod "web" deleted
```

## Summary

We have learned the basics about what pods are, what they are used for, and how to set them up. Along the way we have learned basic commands with `kubectl` and a little bit about YAML manifests. There are more parts of a pod that we have not covered yet, but we will gradually learn more in future articles in this series. Most importantly, we will learn how to interact with our pods.

[^k8spod]: First and foremost the official documentation at https://kubernetes.io/docs/concepts/workloads/pods/
[^webster]: Definition found at https://www.merriam-webster.com/dictionary/pod on 2022-12-12
[^pod]: The full definition of this API can be found at https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/
[^nginx]: I only have to specify the name of the image (`nginx`) and the tag (`latest`) I want to use. If I do not specify anything other than this Kubernetes will default to fetching the image from [Docker Hub](https://hub.docker.com/)
[^kubectl]: To make every command a lot shorter there is a custom of creating an alias for `kubectl` with `alias k=kubectl`, in that case the `kubectl get pods` command can be shortened to `k get po`. To make things even simpler you could create additional aliases, for instance `alias kgp="kubectl get pods"`
[^container]: Technically the containers inside of the pods are the running instances of your application