As organizations shift additional workloads to containers, a container orchestration system is required in order to facilitate automation. Kubernetes, the leading open source container orchestration system has gained massive support from small and large organizations across numerous industries.
The widespread adoption of Kubernetes has created a huge opportunity for talented engineers who understand Kubernetes and can managed complex workloads on K8s.
In this cheat sheet, we provide an in-depth guide on working with Kubernetes objects to familiarize you with how these persistent entities, that represent the state of your cluster, fit together.
What Are Kubernetes Objects?
Kubernetes objects are represented in JSON or YAML files and describe the state of your cluster. The state of the cluster defines what workloads should be running in it. You can create these objects, and then Kubernetes makes sure that your desired state is maintained. For example, if you create a deployment in Kubernetes, it makes sure it’s running even if the cluster restarts after a crash. That’s the beauty of Kubernetes.
There are two categories of objects in Kubernetes, which we’ll discuss more later on:
- basic objects: Pods, Service, Volumes, Namespace, etc., which are independent and don’t require other objects
- high-level objects (controllers): Deployments, Replication Controllers, ReplicaSets, StatefulSets, Jobs, etc., which are built on top of the basic objects
You can get to a desired state by creating an object and pushing it to the Kubernetes API with client-side tools like <terminal inline>kubectl<terminal inline>.
Now, let’s dive into a deeper exploration of how to best work with the objects that make Kubernetes function.
Object Spec and Status
In Kubernetes, object representation is stored in a manifest, as discussed above. Each kind of object has two specific properties:
- <terminal inline>spec<terminal inline>: This contains the details about the object. For example, for a pod, it would contain which container image it would run, the ports to expose, the labels, and more.
- <terminal inline>status<terminal inline>: This contains your object’s current state and is updated in real time by the Kubernetes control plane. If you delete your pod, the <terminal inline>status<terminal inline> changes to <terminal inline>Terminating<terminal inline>, and then the pod is no longer listed as it gets deleted. The status is automatically assigned to each object.
Each YAML configuration for an object contains a selected set of fields for it to be valid. These fields are called required fields, and they include the following:
- <terminal inline>apiversion<terminal inline>: This contains the instruction as to which version of the Kubernetes API should be used to create your object from the manifest.
- <terminal inline>kind<terminal inline>: As noted above, there are two categories of objects—basic and high-level objects. This field contains the details of the type of object; for example, if you want to create a pod in your cluster, the <terminal inline>kind<terminal inline> would be <terminal inline>Pod<terminal inline>.
- <terminal inline>metadata<terminal inline>: Metadata is defined as a set of data that gives information about other data. So this property defines parameters like <terminal inline>name<terminal inline>, <terminal inline>UID<terminal inline>, or <terminal inline>namespace<terminal inline> that would help you identify your object amid other objects of the same <terminal inline>kind<terminal inline>. You’ll read more about <terminal inline>UID<terminal inline> and <terminal inline>namespace<terminal inline> later in this article.
- <terminal inline>spec<terminal inline>: This, as discussed above, contains your object’s specifications. For example, it would contain what container image the pod would run, as well as what ports should be available for the object pod.
The manifest below contains all the required fields for a pod object for your reference in a YAML format:
Kubernetes Object Management
In the above section, you described a Kubernetes object using YAML manifests. Now, how should you deploy, edit, or delete it? Kubernetes objects can be managed either by imperative commands or declarative configuration. Using imperative commands with the kubectl command-line tool, you can create, update, and destroy Kubernetes objects directly without needing any manifest. This is very helpful when you’re deploying specific objects like a pod.
Here’s an example of some imperative commands:
However, when you need to deploy infrastructure, this method is not that helpful as you’d need to deploy many objects with many commands. In that case, you’d want to create manifests that would contain the specification of your objects, and <terminal inline>kubectl<terminal inline> would deploy them to your cluster at once. Here are some examples of declarative commands:
Now let’s take a look at a few operations that you can perform with your objects:
Regardless of the type of object, Kubernetes objects can be created with the help of this basic syntax:
Here, the <terminal inline>-f<terminal inline> flag stands for “file name,” and <terminal inline>apply<terminal inline> applies the configuration stated in your <terminal inline>object.yaml<terminal inline> to your cluster.
Editing objects is as simple as using kubectl and running this command:
The above command opens the raw configuration of the object in YAML representation from the Kubernetes API and launches your <terminal inline>$EDITOR<terminal inline> on the file. You can edit your object configuration and then get a new desired state when you save the changes. For example, you can edit the number of replicas in a deployment without redeploying a manifest from scratch.
Furthermore, updating objects from your manifest is as simple as using the <terminal inline>replace<terminal inline> command with kubectl. If you have your manifest for the initial deployment, you can use the following:
Deleting objects is quite similar to editing them. You can use this command:
If you have the object manifest, you can also use the manifest to delete the resource using this command:
You can use either of the above two commands as per your requirements, and <terminal inline>kubectl<terminal inline> will talk to the Kubernetes API to delete the objects specified.
Object Names and IDs
Kubernetes objects should follow a nomenclature and shouldn’t be duplicated in a
namespace. You don’t want something to be deleted when you don’t intend it to. Each object has two unique specifiers:
- <terminal inline>Name<terminal inline>: This is the unique name for your object, and it has some restrictions. You can read
more about the specifications in this documentation.
- <terminal inline>UID<terminal inline>: UID is a unique, system-generated string standardized as SO/IEC 9834-8 and as ITU-T X.667 for the whole lifetime of the cluster. It helps you distinguish between historical occurrences of similar entities and simplifies logs. UIDs are more reliable than the name as there might be an object of the same name in the history of the cluster, but the UIDs would be distinct.
Namespaces help you to divide your resources into isolation groups for ease and efficient resource management with the help of resource quotas. Creating a new namespace for your resources is simple as using the following command:
Regardless of how many namespaces you create, there will always be four initial namespaces present:
- <terminal inline>default<terminal inline>: where your resources you create would be deployed
- <terminal inline>kube-system<terminal inline>: where the objects created by the Kubernetes system would be stored
- <terminal inline>kube-public<terminal inline>: where the resources that should be visible and publicly readable should be stored
- <terminal inline>kube-node-lease<terminal inline>: where Node Lease objects are stored.
The namespace helps isolation, but there’s a limit to what objects it can isolate. For example, you can isolate your pods but not your nodes. You can have pods, deployments, service, and other objects in a namespace, but PersistentVolumes can’t be in any namespace. To see the full list, you can run this command:
To deploy resources in a specific namespace, you can use the following:
Not only do namespaces isolate your objects, but they are also hidden from each other until you configure DNS.
Labels and Selectors
Labels and selectors come in handy when you create a meaningful key-value relationship between your objects for segregation and across namespaces. Their beauty lies in the fact they allow for efficient queries and great organization.
In short, labels help you tag your pods, and selectors help you identify objects tagged with a label. Here, in metadata, we have two labels — <terminal inline>environment<terminal inline> and <terminal inline>app<terminal inline>:
You can have labels for your different environments like dev or testing, and they would help you manage the objects inside the labels together.
Apart from labels and selectors, Kubernetes also offers annotations. Annotations are another type of metadata that can’t be used to select objects but are there for the sole purpose of providing additional context to you or your logs and act as pointers to your logging, monitoring, and analytics systems.
Annotations are mapped in a key-value format, like labels, though they don’t have nomenclature limitations. Annotations can be large, small, structured, or unstructured data as there are no restrictions.
In the above example from the Kubernetes Documentation, you can see how <terminal inline>imageregistry<terminal inline> is used as an annotation to tell the operator that we are pulling the image from Docker Hub and not some other repository. Annotations provide context for the human operator, and it becomes easier for the operator to find the image from a specific registry if required.
Kubernetes field selectors can help you select objects based on one or more resource fields. Suppose you want to select all pods that are running in the default namespace. You’d use the following command to do so:
Selecting all objects in a namespace can be achieved by using the <terminal inline>-n<terminal inline> flag, but what if you want to select all objects in the cluster except the namespace? Field selectors are what can help you:
You can also use more than one field selector in your command for scenarios where you need a highly specific selection. A good example would be when you need to select all pods that are <terminal inline>running<terminal inline> and can <terminal inline>restart<terminal inline>.
Finalizers manage and enforce certain conditions after object deletion is initiated. To understand the importance of finalizers, you should first understand the deletion process. The process of deletion can be broken into three parts:
- Kubernetes receives delete command: When you issue the <terminal inline>kubectl delete <object> <object-name><terminal inline> command, Kubernetes marks the object for deletion. Now, your object is in a read-only ‘Terminating` state.
- Actions defined by finalizers: Other components take the actions defined by the finalizer, and after the steps are complete, the controller removes the associated finalizers from the target object.
- <terminal inline>metadata.finalizers<terminal inline> check: Kubernetes checks the <terminal inline>metadata.finalizers<terminal inline> field, and if it's empty, the object is considered deleted.
As you see, finalizers help delete your object, implement garbage collection, and prevent accidental deletions. They also notify the controller of removals. You can learn more about them here.
You can also create your own finalizers to delete objects, which sometimes get stuck in a terminating state. A good example would be this finalizer, which deletes your stuck namespace in one go.
Owners and Dependents
Owners and dependents are a parent-child relationship present in Kubernetes. Take a look at the following deployment object manifest:
The deployment object has replicas and pods objects linked to it. Deployment is the owner of replicas and pods, and the pods/replicas are dependents of the deployment object.
If you delete the owner, the dependents get deleted too. So the owner objects manage the dependents automatically, and any change in them translates to a change in the dependent. The relationship is managed by a metadata field called <terminal inline>ownerReference<terminal inline>.
Kubectl is not the only tool you can use to manage Kubernetes objects. There are dashboards, CLIs, and other tools that help you, and you can use one or a combination of them.
Recommended labels are a collection of labels that enable your tools to communicate with one another by describing items in a way that all of the tools can comprehend.
In the above example, you can see how we have added metadata labels that help to identify the required instance from our dashboard. You can select them and find all the objects related to your label.
In a production Kubernetes cluster, where thousands of objects are running, you need to manage them efficiently. Recommended labels are a suggestion from Kubernetes for efficiently standardizing the management of your objects.
Learn more about the recommendations here.
Now that you’ve learned about Kubernetes objects, you can understand how important they are.
Objects are one of your cluster’s fundamental units, and a good understanding of them will come in handy often. The best ways to learn more about them are to go through the official Kubernetes documentation and the ContainIQ blog.
If you’re looking for a tool for monitoring your Kubernetes deployments that can provide insights into cluster latency, resource management, and more to help you fine-tune your Kubernetes deployments, ContainIQ stands out as one of the most comprehensive tools currently available.