In this article, we’ll be exploring various Kubernetes objects in order to understand what they’re used for and the different options they give you. This article doesn’t go into every detail of objects, that would be way too much to cover in one article. However, after reading the article, you’ll have a clear understanding of objects, know when to use each, and their respective perks.
We’ll also look at some examples of how to create these objects. Even though there are several ways to create objects, we’ll be using YAML manifests so that we can have a clear understanding of the different object options and their use cases.
To practice with some examples that you’ll see in this article, you’ll need to have Kubernetes setup on your computer or use an online Kubernetes tool. We recommend you use the Kubernetes environment from www.katacoda.com. It’s a free interactive environment for practicing with different technologies.
Setting Up Your Work Environment In Katakoda
- Visit https://www.katacoda.com
- Scroll down to the section where you have playgrounds, and click on ‘Explore Playground’ in the Kubernetes card.
- On the next page, click on ‘START SCENARIO.’
- On the left-hand pane of the next page that appears, click on launch.sh to launch a new Kubernetes cluster for your practice.
- Once that is done, on the terminal, type kubectl get nodes to confirm that Kubernetes has started.
Trying Out Our Examples
Because Kubernetes uses similar commands for creating and getting objects, we’ll provide the various commands that you can use to create and get the objects we’ll be discussing.
- Create an object: the following command will create a pod using the manifest file for the pod-
kubectl apply -f ./application-pod.yamlYAML
- Describe an object: this will generate a verbose output describing the pod and all its details-
kubectl describe pod application-podYAML
- Get an object: this will list the production deployment-
kubectl get deployment productionYAML
- Get an object from all namespaces: this will list all the pods in all namespaces-
Kubectl get-pods --all-namespacesYAML
Even though we used pods here as our examples, you can use these commands to get and describe the various objects we’re discussing.
So, to test an object we’ve discussed, just create a YAML file for it, then use the command for creating the object above, then you can use any of the gets or describe commands to see the object you’ve created.
What Are Kubernetes Objects?
Kubernetes objects are entities provided by Kubernetes for deploying, maintaining, and scaling applications either on cloud or on-premise infrastructure. In this section, we’ll be discussing the most common Kubernetes objects that you’ll use in your deployments.
A pod is the most basic unit of the Kubernetes cluster. It usually contains one or more running containers. Pods are designed to be ephemeral in nature which means that they can be destroyed at any time. Containers in a pod share the same network, storage, and lifecycle. What this means is that they can communicate with each other directly, and will both be stopped and started at the same time.
apiVersion: v1 kind: Pod metadata: name: first-pod labels: name: first-pod app: hello-world-app spec: containers: - image: hello-world name: first-pod ports: - containerPort: 8080 protocol: TCP
Let’s break down and try to understand the pod definition we just created.
- apiVersion: This refers to the version of Kubernetes. There are several versions, and several objects are introduced with each version. Some common ones are v1, apps/v1, and extensions/v1beta1. You can check out the official Kubernetes documentation for the list of versions.
- Kind: This is the type of Kubernetes object. In this case (the example above), we’re creating a pod.
- Metadata: The metadata houses information that describes the object briefly. The information in the metadata usually contains the name you want to give the object (pod in our case), the labels, and the annotation. For the labels, you can define as many labels as you want, and you aren’t restricted to words to use as labels.
- Spec: The spec section is where you define the desired state of your object. In the case of a pod, it’s where you describe the state of your container. Some options you can specify in the spec section are:
- Container: Here you specify:
- The image of the application you want to run in your pods.
- The name of the container that you’ll run in your pod.
- The ports: containerPort is the port your application in your container is listening to.
Now, let’s create a pod with the pod definition:
- Copy the code above, paste it in your text editor, and save it with the filename: first-pod.yaml
- Now go to your terminal, make sure you’re in the directory where you created your file.
- Run the command
kubectl apply -f first-pod.yaml
kubectl get pods
kubectl describe first-pod
The Lifecycle Of A Pod
A pod passes through several phases in its lifetime. These phases are unknown, pending, running, succeeded, and failed.
If the pod is in the unknown state, it means the pod’s status could not be obtained.
- If the state is pending, it means that its containers haven’t been scheduled or created yet, even though it has been validated by the API-server and an entry has been created for it in etcd. The etcd is the primary data store for Kubernetes. It stores the cluster state in key-value pairs.
- A pod is in the running state, where container(s) have been scheduled on a node in the cluster and running.
- The succeeded state means the pod has finished the execution of its task without any errors. This is usually the case for Jobs that have run successfully. If that’s the case, the pods will not be restarted after their exit. If you don’t know what Jobs are in Kubernetes, don’t worry, we’ll be discussing them later in this article.
- The failed state indicates that either the containers finished running with an error, or something happened to stop the containers from executing successfully.
To check the state of a pod, use the kubectl describe pod <pod name> and check for phase under the status section.
The Replication Controller is used to create multiple copies of the same pod in a Kubernetes cluster. It helps ensure that at any given time, the desired number of pods specified are in the running state. If a pod stops or dies, the ReplicationController creates another one to replace it.
apiVersion: v1 kind: ReplicationController metadata: name: myapp spec: replicas: 2 selector: app: myapp template: metadata: name: first-pod labels: name: first-pod app: hello-world-app spec: containers: - image: hello-world name: first-pod ports: - containerPort: 8080 protocol: TCP
As you can see in our definition for the replication controller, the kind is ReplicationController, and under the spec, it says replicas: 2. This means that at any given time, if the application is healthy, 2 pods running your application container will be up. If any of the pods fail, the replication controller will immediately create another one to replace it.
The template section provides the characteristics of your pod. If you look at the spec section of our first pod definition, you’ll see that it looks just like the template section for the Replication Controller.
The Replicaset is the replacement for the Replication controller. They both perform the same functions. However, the Replicaset is much more powerful than the replication controller.
One of the major differences between a replicaset and replication controller is labels. Replication controllers use labels specified under the selector section while replicasets use matchLabels specified under the selector option. The implication of this is that replication controllers can only references pods whose key and value pair match. For example, if you specify environment: production under selector, any pod with that label will be matched but any other pod whose key is not environment, or value is not production, will not be matched. The replicaset on the other hand will match all pods with the same key, regardless of the value. For example, a replicaset with the key environment and value production will match any other pod with the key environment and those pods will be grouped. The selector is not required for replication controllers, but it is compulsory for replicasets.
Another difference between replication controllers and replicasets is the method used to update them. The replication controllers use the rolling update mechanism which means that the pod templates are changed for each pod, one at a time, until all the pods have been updated. Replicasets on the other hand, use the rollout approach. They are meant to serve as the backends for Deployments and implement the rolling update. Replicasets are not meant to be created on their own, but automatically when deployments are created.
Replication Controllers also belong to the V1 API version of Kubernetes while replicasets belong to the apps/V1 API version.
apiVersion: apps/v1 kind: ReplicaSet metadata: name: frontend labels: app: guestbook tier: frontend spec: replicas: 3 selector: matchLabels: tier: frontend template: metadata: labels: tier: frontend spec: containers: - name: php-redis image: gcr.io/google_samples/gb-frontend:v3
Deployments are Kubernetes objects that are used for managing pods. The first thing a Deployment does when it’s created is to create a replicaset. The replicaset creates pods according to the number specified in the replica option. If you look at the YAML definition for the Deployment below, you’ll notice that the spec section of the deployment looks just like the definition for a replicaset.
Deployments can be used to scale your application by increasing the number of running pods, or update the running application.
A Kubernetes Deployment can be in either of 3 states during its lifecycle.
The progressing state indicates that the deployment is still working on creating or scaling the pods. The completed state indicates that the deployment has finished its task successfully, while the failed state indicates that an error occurred with the deployment.
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80
DaemonSet is another Kubernetes object that’s used for managing pods. DaemonSets are used when you want to make sure that a particular pod runs on all nodes that belong to a cluster. This is helpful in situations where you want to run a monitoring or logging application on the nodes in the cluster.
As soon as a node is added, to the cluster, DaemonSet spins up the pod in the node and destroys the pod as soon as the node exits.
In his article Kubernetes DaemonSets 101, Mohamed Ahmed provides a detailed explanation of DaemonSets and how to work with them.
apiVersion: apps/v1 kind: DaemonSet metadata: name: fluentd-elasticsearch namespace: kube-system labels: k8s-app: fluentd-logging spec: selector: matchLabels: name: fluentd-elasticsearch template: metadata: labels: name: fluentd-elasticsearch spec: tolerations: - key: node-role.kubernetes.io/master effect: NoSchedule containers: - name: fluentd-elasticsearch image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2 resources: limits: memory: 200Mi requests: cpu: 100m memory: 200Mi volumeMounts: - name: varlog mountPath: /var/log - name: varlibdockercontainers mountPath: /var/lib/docker/containers readOnly: true terminationGracePeriodSeconds: 30 volumes: - name: varlog hostPath: path: /var/log - name: varlibdockercontainers hostPath: path: /var/lib/docker/containers
In the YAML above that describes the DaemonSet, we see that it has a namespace and we’ll be sure to talk more in-depth about namespaces later. We also see that it has two spec attributes: the first spec is for the DaemonSet, the second describes the pod.
Now, let’s take a look at the attributes that are in the pod spec:
- Tolerations: To understand tolerations, we have to look at another Kubernetes concept - taints. Taints are a way of indicating to the Kubernetes Scheduler that pods shouldn’t be scheduled on a particular node. The Kubernetes Scheduler is responsible for placing created pods in different nodes. When you put a taint on a node, you’re putting a mark on the node that tells the Kubernetes Scheduler to ignore that node. Only pods that have tolerance for that taint will be scheduled on that node. So toleration is just a way of ignoring the taints on a node in order to allow the pod to be scheduled on that node. You can taint a node by running the following command:
kubectl taint nodes my_node tainted=true:NoSchedule
To tolerate this taint, you have to add a toleration option to your pod.
toleration: Key: "tainted" Value: "true" Effect: "NoSchedule"
- Resources: The resource section is used to define specifications for the amount of resources the pod should consume in terms of the CPU and RAM of the node. This is important because containers need enough resources to be able to run without errors. On the other hand, some containers can take so many resources that they starve other containers of needed resources.
- Limits: Limits define the highest amount of resources that a container is allowed to consume. When a container is started, its memory limits are passed to the container runtime. If the container exceeds its limits, it may be terminated, and restarted if it is restartable.
- Requests: Requests define the initial amount of resources that are assigned to each running container. If a Container exceeds its memory request, the pods could be evicted out of the node if the node runs out of memory.
In his article Influencing Kubernetes Scheduler Decisions, Mohamed Ahmed talks extensively on managing resources in Kubernetes.
- VolumeMounts: Containers are ephemeral in nature - this means that once a container is terminated, all the data generated while the container was running will be lost. Sometimes, we may want to preserve the data generated by the container, that is where Volumes come in handy. A volume is just a location (directory) in your pod where all of your container’s data is being stored. It’s important to know that some volumes share the same lifecycle as the pod - an example is emptyDir. This means that the volumes are created and terminated with the pods. If you want to store your data permanently, you would have to look for an alternative storage solution. There are other volumes that are not terminated with the pods; an example is the awsElasticBlockStore which is merely mounted and will be unmounted once the pod is destroyed thus preserving its content.
A volume mount is a way of associating a particular volume with a container.
Volumes: - name: varlog hostPath: path: /var/log - name: varlibdockercontainers hostPath: path: /var/lib/docker/containers
In the above snippet from our original DaemonSet definition, you’ll notice that two volumes are defined. The first is a hostPath type of volume named varlog, and the second is also a hostPath volume, named varlibdockercontainers.
The hostPath volume uses the storage of your host computer, or node, to store the data associated with the volume. You can use a directory on your computer as the volume and mount it on the container. One of the use cases of the hostPath is when we need our containers to access data from docker internals as you see in the volume definition above.
- terminationGracePeriodSeconds: Kubernetes offers us a grace period before our pods are terminated. The default grace period is 30 seconds. The process for terminating a pod follows this order:
- The pod’s state is set to “terminating."
- A preStop hook is executed - this is a command that can be used to perform all the necessary actions you wish to perform before your application shutdown. This will help in minimizing the impact felt by a user of your application whose traffic has been redirected to the pod about to be terminated.
- The SIGTERM signal is sent to the pod. The SIGTERM is a signal sent to the containers informing them of their imminent termination in the specified time.
- The countdown for the pod’s termination period begins.
- If the preStop hook does not finish executing before the grace period is expired, a SIGKILL signal is sent which waits for an extra 2 seconds, then terminates the container and all its processes.
The terminationGracePeriodSeconds option specified in the pod template of the DaemonSet is used for extending or decreasing that grace period.
Namespaces are used to organize objects in a Kubernetes cluster. They enable you to group resources together and perform actions on those resources. One of the use cases can be the creation of different environments (staging and development) for your deployed application. For example, you can create a deployment called backend-application with the staging namespace and deploy the same application in another namespace called production. While both deployments have the same name, there are no conflicts because of the difference in their namespaces.
apiVersion: v1 kind: Namespace metadata: name: production _ _ _ apiVersion: v1 kind: Namespace metadata: name: staging
To use a namespace, create it and then reference it in the metadata section of your Kubernetes objects:
apiVersion: apps/v1 kind: DaemonSet metadata: name: fluentd-elasticsearch namespace: kube-system labels: k8s-app: fluentd-logging Spec:
ConfigMaps are used to separate configuration data from containers in your Kubernetes application. They offer you the ability to dynamically change data for containers at runtime. It’s important to note that ConfigMaps are meant to be used for storing sensitive information. If the data you want to pass to your application is sensitive, then it is recommended you use Secrets instead.
You can create ConfigMaps from literals (key-value pairs), files, or even directories.
- Create ConfigMaps from a directory: To properly understand this, we’ll look at an example. Follow these steps:
- Create a directory and name it people_config
- Inside the directory, create two files: boys.txt and girls.txt
- In the boys.txt file, add the following lines:
- Name: Samuel
- Toy: Car
- gender: male
- In the girls.txt file add the following lines:
- Name: Samantha
- Toy: doll
- gender: female
- Run the command below on your terminal, in the directory that you created:
kubectl create configmap people-config --from-file=people_config/YAML
After running the above command, run:
Kubectl describe configmap people-configYAML
The above command will provide a verbose description of the ConfigMaps you just created.
- Create ConfigMaps from files: The approach is the same as the one for creating ConfigMaps from a directory. But in this case, we specify the file path instead of a folder.
Kubectl create configmap boys-config --from-file=people_config/boys.txt
Create ConfigMaps from literals: To create a ConfigMap from a literal, you can use the following commands:
Kubectl create configmap boy-config --from-literal=name=Samuel --from-literal=gender=male --from-literal=age=13
To consume a ConfigMap in a pod, you simply reference it using the configMapRef option:
apiVersion: v1 kind: Pod metadata: name: dapi-test-pod spec: containers: - name: test-container image: k8s.gcr.io/busybox command: [ "/bin/sh", "-c", "env" ] envFrom: - configMapRef: name: people-config restartPolicy: Never
To inject the ConfigMap values into the command line, you have to specify the keys you want to be consumed using the configMapKeyRef.
apiVersion: v1 kind: Pod metadata: name: dapi-test-pod spec: containers: - name: test-container image: k8s.gcr.io/busybox command: [ "/bin/sh", "-c", "env" ] env: - name: MALE_AGE valueFrom: configMapKeyRef: name: people-config Key: age restartPolicy: Never
You can also generate ConfigMaps using a generator, but we’ll discuss that in another segment. You can also look at the official Kubernetes documentation for more information.
- We’ve successfully explored most of the basic Kubernetes objects that you would use when deploying your application. We looked at Pods, ReplicationSets, ReplicationControllers, Deployments, DaemonSets, Namespaces, and Configmaps. We also looked at the different options available when creating these objects.
- To be able to see a full list of all the objects available on Kubernetes, run the command below on your terminal: Kubectl api-resources
- From the output of that command, you’ll see that we haven’t even scratched the surface of the objects that Kubernetes makes available for managing our deployed applications.
- You can check out more of our articles here at Magalix to have a firm grasp of these Kubernetes objects and how they can help make your work easier.