Kubernetes has many moving parts, and it is essential to wrap your head around quite a few of them if you want to work within Kubernetes efficiently. One of these important aspects is “metadata,” namely labels, selectors, and annotations. These three types of metadata each have their role to play when configuring and working with Kubernetes, whether it is stitching multiple resources together or just providing some more context for developers and DevOps engineers.
In this article, you will see some examples of different types of metadata in action and understand how to work with them—adding, editing, and removing them in various ways—as well as some of the benefits they can each provide.
Labels are a type of metadata in Kubernetes that take on the form of a key-value pair attached to objects such as pods and services. Labels are often used to describe identifying aspects of the object, possibly for use by the user at a later stage. However, like other metadata, labels do not directly change any functionality as they imply no semantics to Kubernetes by default. One of the nice things about labels is that they let you map your own data structures onto objects in a loosely coupled fashion.
For example, your team might have different “release” types, such as “alpha,” “beta,” and “stable.” This would be a solid use case for labels, allowing you to indicate which of these release types a given object falls under. Although selectors can use labels for identification purposes, it is important to remember that they are not unique, as many objects carry the same labels.
You can add labels to your resources in a few different ways. The first and most common way is to add them directly to your config files to set them when the resource is created or updated. To do this, you can specify label values at <terminal inline>metadata.labels<terminal inline> like so:
The other way to work with labels is via the kubectl CLI tool. This is handy for making small tweaks to your resources, but it is important to remember that the changes will not be reflected back to your config files automatically.
To add a label to an existing resource, you can use the following command:
You can also remove the label using this command:
Finally, you can also use the edit command to change your running configurations in a more imperative way using <terminal inline>kubectl edit pod/metadata-demo<terminal inline>. This will open your CLI editor of choice and allow you to add and remove labels and other details. When you save and exit, the changes will apply.
As their name suggests, label selectors allow you to identify the objects you have tagged with particular labels. Label selectors can either be equality-based or set-based. Equality-based label selectors work by specifying an exact value that you want to match against. If you provide multiple selectors, all of them must be satisfied to qualify as a match.
Set-based selectors work in a similar fashion, except you can specify multiple values in a single selector, and only one of them needs to match for the object to qualify.
Selectors come in handy for a few different things. Probably the most common usage is for grouping the correct resources for something like a service. Consider the following configuration file where a deployment will create a pod with a particular label, and that label is then used by a service to determine its association:
The significant part of this config is the <terminal inline>selector<terminal inline> field for the service, which tells the service which pods it should associate with and send traffic to.
Selectors are also commonly used for more “human” operations via the command-line tool. In clusters with many resources running, it can be beneficial to use the selectors to discriminate and quickly identify the resources you are interested in. For example, suppose you wanted to find all of the resources in the above configuration file. In that case, you could use the aforementioned set-based selectors to see everything with one of the matching labels, like so:
If you are only looking for a specific label, the syntax is similar, if simpler:
Annotations are another type of metadata you can use in Kubernetes. While labels can be used to identify and select objects, annotations cannot. Their intended use is to store arbitrary, non-identifying information about objects. This data is often used to provide context about objects to the human operators of the system. One good example of how you can use annotations is the a8r projects, which establish a convention for “using annotations to help developers manage Kubernetes services.” This includes annotations such as <terminal inline>a8r.io/description<terminal inline> and <terminal inline>a8r.io/bugs<terminal inline>, among others. These can be used to store an unstructured text description of the service for humans, as well as a link to an external bug-tracker for the service, respectively.
Because annotations have fewer restrictions on them than labels do, you can store special characters not permitted by labels, as well as large, structured values if your use case requires it. This can be seen when you use the <terminal inline>kubectl edit<terminal inline> command. For example, when editing a deployment configuration, you can see that the previous configuration state is stored in an annotation as structured data:
Like labels, you can add annotations in a few ways, namely via config files or the kubectl command line. Say, for example, you wanted to add some of those a8r.io annotations onto the config example above. That might look something like this if you want to do it as config:
Or, it might look like this, if you want to do it via the CLI:
Finally, there is always the option of using the <terminal inline>kubectl edit<terminal inline> command to alter the configuration on the fly.
While annotations do not inherently imply semantics to the Kubernetes core, it is still possible for them to affect operation in some cases. A good example of this is with the NGINX Ingress controller (among others). The NGINX Ingress controller allows you to add Kubernetes annotations onto your ingress objects to affect their behavior. Most of these map cleanly to the configuration options available in NGINX, and as such, it is a nice way to allow mapping NGINX specific concepts onto your Kubernetes resources. The NGINX Ingress controller is then able to read these annotations and apply them as needed. An example of this is the <terminal inline>nginx.ingress.kubernetes.io/rewrite-target<terminal inline> annotation. Much like the a8r.io annotations discussed above, this NGINX annotation is prefixed with a specific scope to avoid conflicts with other annotations that may be similarly named.
There are multiple types of metadata in Kubernetes, with just as many ways to work with them and even more use cases. Metadata is essential to managing larger deployments and keeping everything organized, as it gives you a way to impose your own organizational model onto the Kubernetes resources in a loosely coupled fashion, without directly implying semantics.
As exemplified in features like human-readable annotations, this can provide a lot of value for other humans interacting with the system, as it guides them through the resources and paints a picture of how things work together. Metadata isn’t only useful for humans though, it also provides Kubernetes with a way to group, organize, and associate resources, allowing for larger and more complex structures to exist than would be reasonably viable without it.