- Kubernetes logs: Pod logs, container logs, Docker logs, kubelet logs, and master node logs - Mon, Sep 25 2023
- Kubernetes DaemonSets - Wed, Sep 6 2023
- Static Pods in Kubernetes - Fri, Sep 1 2023
To run your containerized application in a Kubernetes cluster, you need to run a Pod because, unlike Docker, Kubernetes doesn't run containers directly. In this post, you will learn how to create a Pod in Kubernetes. For this demo, I assume you already have a Kubernetes cluster up and running. If you don't, read this post.
Cluster information
Before jumping right into creating a Pod, let me quickly tell you about my lab setup. I configured a Kubernetes cluster using the kubeadm tool. There is one master node and two worker nodes, as you can see in the screenshot below.
kubectl get nodes -o wide
To follow along, you need a Kubernetes cluster, whether it is a single-node or a multinode cluster.
Imperative vs. declarative configuration
In Kubernetes, there are two ways of working with objects: imperative and declarative. In the imperative configuration, we use the kubectl run, kubectl create, or kubectl scale commands with various arguments directly in the command-line console, which helps get things done quickly. However, this method is not ideal for the production environment because the changes made this way are usually hard to track. This is why it is preferred only for learning and experimenting. However, admins tend to use imperative commands to perform various one-off tasks quite often.
In the declarative configuration, you need to write all the instructions in a yaml manifest (or configuration) file and then specify it using the kubectl apply command to create the Kubernetes object. To modify the object, you can simply edit the configuration file and use the kubectl apply command again. The apply command is smart enough to detect the changes and apply them accordingly. Another benefit of declarative configuration is its support for directories. You can specify the path of a directory containing multiple configuration files with the kubectl apply command, and it takes care of creating (or updating) everything for you automatically.
For the sake of this post, I will cover both the imperative and declarative approaches to creating a Pod in Kubernetes.
Create a Pod using the imperative approach
Let's first discuss how to create a Pod with imperative commands. First, make sure you are connected to your Kubernetes cluster or directly logged on to the master node with SSH. To create a Pod, use the kubectl run command, as shown below:
kubectl run webapp-pod --image=nginx
This command creates a new Pod named webapp-pod in the default namespace using the nginx image from the Docker Hub public registry.
To create the Pod in a different namespace, you can specify it with the --namespace (or -n) parameter (e.g., --namespace=dev). A namespace is a way of isolating a group of resources in a Kubernetes cluster. Furthermore, while creating a Pod, you can also add labels using the --labels parameter (e.g., --labels="app=webapp,tier=front-end"), which helps you identify the relevant Pods when you have a lot of them running in the cluster.
View a Pod
Once your Pod is created, you can view it with the following command:
kubectl get pods webapp-pod --watch
The --watch (-w) flag allows you to view the status changes. You can see in the screenshot above that the status of our webapp-pod changed from ContainerCreating to Running. Pods that are critical for the Kubernetes cluster are stored in the kube-system namespace. To view all the Pods running in the Kubernetes cluster from all namespaces, you can use the --all-namespaces flag, as shown below:
kubectl get pods --all-namespaces
Since we have more than one worker node in the cluster, how would we know which node our webapp-pod is placed on? To check this, you can use the following command:
kubectl get pods webapp-pod --output wide
The --output (or -o) wide option gives you a bit more information that is normally not shown, as you can see in the screenshot. This way, we know that the webapp-pod is placed on the kube-srv3 node, and the IP address of the Pod is 10.244.2.30.
Inspect a Pod
To get detailed information about the Pod, you can inspect it using the kubectl describe command, as shown below:
kubectl describe pod webapp-pod
This command gives a lot of information, such as what container(s) are running in the Pod, the container image, the container state, etc. If you take a look at the Events section at the end, you will see the most recent events. This is important for troubleshooting.
For example, imagine a situation in which your Pod is stuck in the Pending state. In this case, you will use the kubectl describe command, and the events section will tell you why the Pod is stuck in the Pending state. To view the logs of a container running inside a Pod, use the kubectl logs command, as shown in the screenshot below:
Delete a Pod
Note that deleting a Pod will terminate any application running inside it. To delete a Pod, use the kubectl delete pod command, as shown below:
kubectl delete pod webapp-pod
Depending upon what is running inside your Pod, the command could take a while to complete.
Create a Pod using the declarative approach
As stated earlier, imperative configuration works for learning or one-off tasks. The declarative configuration makes life much easier, but it comes with the complexity of writing the configuration files. Kubernetes has native support for YAML and JSON configuration files.
Create a YAML config file
Writing configuration files can be hard, particularly for beginners. Fortunately, there is a quick and easy way to generate configuration files in Kubernetes. To do so, we will use the imperative command that we used in the previous section and tweak it a little bit, as shown below:
kubectl run webapp-pod --image=nginx --dry-run=client --output=yaml
Here, we used the --dry-run=client parameter to display the Kubernetes object configuration (Pod, in this case) without actually creating it. The --output=yaml (or -o) caused the output to be displayed in YAML format. To view the output in JSON format, you would use --output=json instead. That's it. You can now easily redirect this output to create a YAML or JSON file, as needed. That is what I did in the second command.
Let's take a quick look at the fields of the YAML file. In a Kubernetes configuration file, at a minimum, you need to have 4 main sections. They are marked with red arrows in the above screenshot.
- apiVersion: Specifies which Kubernetes API version to use
- kind: Specifies what kind of object to create
- metadata: Specifies metadata to uniquely identify the object in the cluster
- spec: The specifications and state of the object to create
Note that YAML is case-sensitive and white space-sensitive, so indentation is really important in your configuration files. To avoid potential issues due to incorrect indentation, it is recommended to use the --dry-run=client method to generate the YAML files for other Kubernetes objects, and manually update the generated files when needed.
Create the Pod with a YAML file
Once you have the YAML configuration file ready, you can create the Pod using the kubectl apply command, as shown below:
kubectl apply -f webapp-pod.yaml
The --filename (or -f) option is used to specify the path of the configuration file(s). Remember, the kubectl apply command also lets you specify the path of a directory containing multiple configuration files.
Alternatively, you can also use kubectl create -f webapp-pod.yaml command, but since we are discussing the declarative configuration in this section, the kubectl apply command is more suitable here. You will realize the benefits of the kubectl apply command when we cover Kubernetes deployments in upcoming posts. For now, just keep in mind that kubectl create will throw an error if the object already exists, whereas the kubectl apply command does not throw an error, of course, unless you try to update immutable metadata. This is discussed in the next section.
Modify a running Pod
Once a Pod is created, you might want to make some changes to it later. Note that there is not much you can change in a running Pod because most of the Pod metadata is immutable. At the time of writing, Kubernetes only supports updating the following fields for a running Pod:
- spec.containers[*].image
- spec.initContainers[*].image
- spec.activeDeadlineSeconds
- spec.tolerations
As you can see, all of these fields are part of the spec section that we saw in the YAML configuration.
To modify a running Pod, you can use the kubectl edit command, as shown below:
kubectl edit pod webapp-pod
This command opens a live Kubernetes resource (Pod, in our case) for in-place editing in the default editor, which is vi in Linux and Notepad in Windows. To use a different editor (e.g., nano), you can set the KUBE_EDITOR variable, as shown below:
KUBE_EDITOR="nano" kubectl edit pod webapp-pod
Remember, if you change an unsupported field, you will not be able to save the live object configuration. Your changes will be saved to a temporary file instead. See the screenshot below for reference.
The screenshot shows that I tried to change an unsupported field in the live Pod configuration. My changes were saved to a temporary file, whereas the original Pod configuration remained unchanged.
In this case, to modify a Pod, you first need to delete the existing Pod and create a new one with the updated configuration. You can use different approaches depending on how the existing Pod was originally created.
For this demo, we used an imperative kubectl run command, so we can now delete the existing Pod and use the temporary file with the kubectl create command, as shown below, to create a new Pod with the updated configuration:
kubectl create -f /tmp/kubectl-edit-105925122.yaml
Alternatively, you can also use the kubectl replace command with the --force parameter, which automatically deletes the existing Pod and creates a new one for you.
kubectl replace -f /tmp/kubectl-edit-105925122.yaml --force
If the Pod had been created using the declarative configuration in the first place, it would have been a lot easier. You would just need to edit the original YAML file and use the kubectl apply command. This is why the declarative configuration is always preferred whenever possible.
You just learned how to create a Pod in Kubernetes. Note that manually creating a Pod, as shown in this post, is primarily intended for educational purposes, so you get the idea of Pods in Kubernetes. In the real world, you must create a deployment in Kubernetes because it takes care of deleting, updating, and recreating Pods for you.
In my upcoming Kubernetes guide post, I will provide a detailed explanation on how to create a Kubernetes Deployment.