I’m studying for my CKAD exam, so this article is serving as a reference for me to cover the core concepts in that exam. This article will cover Pods, Namespaces, the kubectl CLI tool, and how we can use it to create objects in Kubernetes.

Kubernetes Primitives

These are the basic building blocks in Kubernetes for building and operating applications that you host on it. These could include Pod, Deployments, Services etc

Every Kubernetes primitive follows a structure which is reflected in the manifest of the object (This is usually a YAML file). These sections are usually found in each Kubernetes primitive:

  • API Version - The K8s API version that defines the structure of the primitive. You may see different prefixes for each version, and some API versions may be alpha, or beta versions. We can list API versions that are compatible for the version of our cluster by running kubectl api -versions
  • Kind - This defines the primitive type (Pod, Service, etc.).
  • Metadata - This describes high-level information about the object. This could include the name of the object, which namespace it lives in etc.
  • Spec - Or specification which declares the desired state. This includes what image should run the container, environment variables are needed etc.
  • Status - This describes the actual state of the object. The K8s controllers will try to always reflect the desired state into the actual state.

Interacting with the Kubernetes Cluster

kubectl is the primary tool that we use to interact with our Kubernetes cluster from the command line. A simple command will consist of the following:

kubectl [command] [TYPE] [NAME] [flags]

So a real kubectl command would look something like this:

kubectl get pod app -o app.yaml

Working with objects in Kubernetes

We can create objects in Kubernetes either imperatively or declaratively.

Imperatively, we don’t need a manifest definition. We can just use the kubectl run or kubectl create commands to create objects, If we need to provide configuration, we can do use via the command-line options, like so:

kubectl run webui --image=mywebuiiamge --port=80
# pod/webui created

Using the declarative approach, we define our Kubernetes objects using a manifest file. We then create the object using kubectl create or kubectl apply. This gives us the benefit of improved maintenance of our object.

kubectl create -f webui.yaml
# pod/webui created

You may have seen that instead of the create command, we can use the apply command instead. The create command will create a new object, and if you try to use create for an existing object, this will result in an error.

The apply command allows us to update an existing object in full, or incrementally. If you use apply for an object that doesn’t exist in your cluster, it’ll create the object:

kubectl apply -f webui.yaml
# pod/webui configured

We can delete Kubernetes objects using the kubectl delete command. This is useful when we want to delete objects that we don’t need anymore. We can delete an object either:

  • by providing the name of the object, or
  • by deleting the object by pointing to the manifest file that created it.
kubectl delete pod webui
# or
kubectl delete -f webui.yaml

We can edit the state of live objects in our cluster like so:

kubectl edit pod webui

Or we can replace the object. We can use the replace command to overwrite the configuration of our live objects with one that we’ve defined in our manifest file:

kubectl replace -f webui.yaml

Pods in Kubernetes

Pods are the primary primitive in Kubernetes. It lets you run containerized applications. In most cases, Pods and containers have a one-to-one mapping between them. There are some cases where you can have more than one container in a single Pod. Pods can also consume persistent storage, configuration, etc.

A container will package an application that includes its environment and configuration. It usually contains the operating system, source code, and dependencies.

We turn applications into containers by containerizing them (sounds obvious right?). We do this by defining instructions in a Dockerfile, which defines the build process for that application. From that Dockerfile, we build an image, and then publish that image to a registry for others to consume.

Creating Pods

Once our image is ready to be consumed, we can use it in our Pod definition. When we create our Pod, the container runtime engine (CRI) will check if the container images already exists locally, and if not, it will download it from a container image registry.

Image description

We can create pods using the run command. This creates a Pod imperatively. For example:

kubectl run webui --image=myacr/webui --restart=Never --port=8080

You can also create Pods from a YAML manifest file. For example, we could have the following Pod manifest:

apiVersion: v1
kind: Pod
metadata:
  name: webui
  labels:
    name: webui
    env: dev
spec:
  containers:
  - name: webui
    image: myacr/webui:latest
    resources:
      limits:
        memory: "128Mi"
        cpu: "500m"
    ports:
      - containerPort: 8080

And then create the Pod by using either kubectl create or kubectl apply:

kubectl create -f webui.yaml

Retrieving Pod details

Once our Pod is created, we can inspect it using the kubectl get command. To list all pods in our cluster, we can run the following:

kubectl get pods
NAME READY STATUS RESTARTS AGE
webui 1/1 Running 0 69s

If we want to query a specific pod, all we need to do is pass in the name like so:

kubectl get pods webui
NAME READY STATUS RESTARTS AGE
webui 1/1 Running 0 69s

When we list pods, we may not see it running right away. This is because Kubernetes has asynchronous control loops, so it takes a couple of seconds to retrieve the image and start the container. Pods have several phases in its lifecycle:

Status Description
Pending Pod has been accepted by Kubernetes system, but the container images have not been created
Running At least one container is running, or is starting
Succeeded All containers in the Pod have terminated successfully
Failed Containers in Pod have terminated, and at least one failed with an error
Unknown The state of the Pod is unknown

We can get more details from our Pod by running the kubectl describe command, for example:

kubectl describe pods webui

The output for this command will contain the metadata for the Pod, containers that it runs and the event log. This can be quite long, so if we want to retrieve specific parts of information, we can use the following:

kubectl describe pods webui | grep Image:
# produces Image: myacr/webui

We can also retrieve the log output of a container using kubectl logs like so:

kubectl logs webui

We can also use the -f flag to stream the logs in real time. One thing to keep in mind that if a container is restarted for whatever reason, we will lose the logs. The logs command only produces logs for the current container.

You can get the logs of the previous container by using the -p flag. this is handy when we want to identify the cause of the restart.

Commands in containers

To run commands in our containers, we can use the kubectl exec command to open a shell in our container, like so:

kubectl exec -it webui -- /bin/sh

We don’t have to provide a resource type, as this command only works for a Pod, and the two dashes (–) separate the exec command and what we want to run inside of the container.

Deleting Pods

Deleting pods can be done using the kubectl delete command:

kubectl delete pod webui

Kubernetes will try to delete the Pod gracefully, so it will finish active requests to the Pod so that end users aren’t disrupted. This can take between 5-30 seconds.

We can also delete our Pods by pointing the delete command to the YAML manifest that created it:

kubectl delete -f webui.yaml

Namespaces

Namespaces in Kubernetes are used to represent the scope for object names, and help isolate objects in Kubernetes by team/responsibility/applications etc.

We can use the kubectl to perform some basic operations in our cluster. To list them, we can run the following:

kubectl get namespaces

# Produces
NAME STATUS AGE
default Active 14d
kube-node-lease Active 14d
kube-public Active 14d
kube-system Active 14d

The default namespace hosts objects that aren’t assigned to a specific namespace. Namespaces that begin with a kube- prefix are not end-user namepsaces (As an app developer, you don’t need to work with these).

If we want to create a new namespace, we can use the create namespace command. For example:

kubectl create namespace my-new-namespace

We can also represent a namespace in a YAML manifest:

apiVersion: v1
kind: Namespace
metadata:
    name: my-new-namespace

Once our namespace has been created, we can create objects in it. We can specify the namespace to create the object by using the --namespace or -n flag:

kubectl get pods -n my-new-namespace

Finally, we can delete a namespace using kubectl delete. Deleting a namespace will also delete the objects in that namespace automatically:

kubectl delete namespace my-new-namespace

Conclusion

In this article, we covered Pods, Namespaces, the kubectl CLI tool, and how we can use it to create objects in Kubernetes.

If you want to learn more about the concepts we talked about here, check out the following articles:

If you have any questions on the above, feel free to reach out to me on twitter @willvelida

Until next time, Happy coding! 🤓🖥️