
Fundamental Kubernetes Resources
Kubernetes creates a powerful abstraction to provide life cycle management of scalable and robust cloud-native applications. Master and node components, as discussed in the previous chapters, work continuously to fulfill the desired state of workloads defined by the users using the Kubernetes API and client tools. In this section, different Kubernetes concepts and resources are explained with their essentials and real-life practices.
The Pod
The pod is the building block of Kubernetes computation objects. A pod consists of containers that are tightly coupled and should be treated as a single application. These containers in the same pod are always scheduled on the same node since they share volume and networking interfaces. Therefore, the pod can be imagined as an encapsulated set of containers that should work together and share the same life cycle, such as scaling up or down together.
Pods can be defined with just one container and its associated metadata and runtime environments. In the following pod definition, a pod with the name my-first-pod is presented. There is only one container with the Docker image of busybox and the command for the Hello DevOps! output:
apiVersion: v1
kind: Pod
metadata:
name: my-first-pod
spec:
containers:
- name: main
image: busybox
command: ['sh', '-c', 'echo Hello DevOps! && sleep 3600']
When this pod definition is submitted, Kubernetes schedules this pod to a node in the cluster. The kubelet service running in the respective node creates the container with the requirements defined and checks the status continuously by interacting the container runtime. Furthermore, a pod can have more than one containers that should work together and share resources.
In the following pod definition, two containers are defined to share a volume. Furthermore, the following pod follows a pattern of having one main container, nginx, to serve the files, and has a sidecar container, debian, to prepare and manage the served files:
apiVersion: v1
kind: Pod
metadata:
name: multiple-containers
spec:
volumes:
- name: shared
emptyDir: {}
containers:
- name: main
image: nginx
volumeMounts:
- name: shared
mountPath: /usr/share/nginx/html
- name: sidecar
image: debian
volumeMounts:
- name: shared
mountPath: /shared
command: ["/bin/sh"]
args: ["-c", "echo Hello from the sidecar container > /shared/index.html && sleep 3600"]
In this pod definition, an empty volume is defined with the name shared and mounted into two containers with different paths. The debian container writes Hello from the sidecar container to index.html in this volume, whereas the nginx container uses this volume to serve its contents, as illustrated in Figure 3.18:

Figure 3.18: Shared volume between containers
Pods are the primary and fundamental building blocks of Kubernetes resources, so they are generally managed by higher levels of resources, such as replication sets, deployments, and stateful sets. In the following sections, we'll examine these higher-level resources and how they fulfill sophisticated scaling and life cycle management requirements.
Replication Sets
Replication sets (ReplicaSet) are the Kubernetes resources that maintain a set of replica pods running in the cluster. Kubernetes is designed to enable and support high availability. Therefore, it is expected to have the same pod instances running in the cluster as those defined in the replication sets. Similar to pods, replication sets are building blocks of life cycle management resources, such as deployments. They are used with other high-level resources to scale up or down or roll out new versions of the applications. A replication set definition looks similar to a pod definition, because it encapsulates a pod specification:
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: high-available-hello
spec:
replicas: 3
selector:
matchLabels:
app: hello
template:
metadata:
labels:
app: hello
spec:
containers:
- name: main
image: busybox
command: ['sh', '-c', 'echo Hello DevOps! && sleep 3600']
In this replication set definition, the same pod specification from my-first-pod is used. There are two critical points to check in this definition; replicas and matchLabels.
The replicas field defines the desired number of the pods that should be running in the cluster. Kubernetes controllers inside kube-controller-manager create and manage the pods to fulfill this request.
The matchLabels field defines a set of labels to match with the pods that should be replicated. Labels in Kubernetes are semantic tags attached to Kubernetes resources to group them. Controllers use these labels to target a group of resources and manage them. For instance, in this example, replication set controllers will check for the number of pods with the label app:hello, since they are mentioned in matchLabels.
Replication sets are the fundamental resources that make Kubernetes applications highly available and resilient to failures in the cluster. In our discussion of the next resource, replication sets will be used to achieve higher life cycle requirements.
Deployment
Deployments are one of the most potent Kubernetes resources that makes it easier to manage containerized applications at large scales. A deployment specification looks similar to a replication set with an encapsulated pod definition:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-first-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
Although it looks similar to a replication set definition, the power of the deployments comes from the capabilities of the deployment controllers. By creating a deployment or changing the fields in a deployment specification, it is possible to manage these life cycle operations:
- Rolling out a new application: When the deployment is sent to the Kubernetes API, replication sets are defined, and the application is rolled to the cluster.
- Rolling out an update to running application: When a change is made to deployment specification, these changes are propagated by deleting old replication sets and creating new ones. This rollout of new versions is managed at a controlled rate so that there is no downtime when you add another environment variable to your pod specification.
- Rollback to an older version: If any problems occur while rolling out new releases, it is always possible to roll back the changes, since deployment controllers store history.
- Scaling up or down a running application: It is possible to change the number of replicas to scale down or up manually.
Deployments are high-level Kubernetes resources that enable complex life cycle operations. They are essential and among the most commonly used Kubernetes resources for deploying scalable, reliable, and highly available ephemeral workloads. In the next resource, we will discuss how to handle stateful applications, such as databases in Kubernetes with stateful sets.
Stateful Sets
Kubernetes enables both stateless ephemeral and stateful applications to be run with the same level of scalability and robustness, thanks to stateful sets (StatefulSet). Stateful sets fulfill the enhanced requirements of data-oriented applications, such as databases, with the help of persistent volumes. The stateful set definition looks similar to deployment and includes volume claim parts to create persistent volumes, as follows:
apiVersion: apps/v1beta2
kind: StatefulSet
metadata:
name: my-first-statefulset
spec:
selector:
matchLabels:
app: nginx
serviceName: "nginx"
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
In my-first-statefulset, a persistent volume, www, is created for every pod instance and mounted to the /usr/share/nginx/html path inside containers.
When a stateful set is sent to the Kubernetes API, the controllers create ordered pods with the defined volumes. Special care of the stateful set pods prevents data loss when the pods are rescheduled to another node. This is because the same ordered volumes are bound to the same ordered pods, as illustrated in Figure 3.19. In addition, volumes attached to these pods stay in the system when the stateful set is scaled down or deleted:

Figure 3.19: Stateful set and volume handling in Kubernetes
The stateful set controller and ordered execution of pods and volumes make stateful applications run in Kubernetes in a scalable and reliable way. With all of the fundamental resources of pods, replication sets, deployments, and stateful sets, it is possible to deploy any complex cloud-native application in Kubernetes. With the rich functionality of the Kubernetes API and toolsets, it is also possible to operate these applications in the cloud. In the following activity, a blog application and its database are installed in Kubernetes by using primary resources and the kubectl tool.
Activity 3: Installing a WordPress Blog and Database on Kubernetes
The aim of this activity to install and manage a MySQL database and a WordPress blog in Kubernetes in a cloud-native way. WordPress is based on PHP and is a free and open source content management system. It needs a MySQL database as the data source for user and content management. In this activity, database and blog containers should be stateful to persist their data, as expected in a production system.
Using the stateful set examples and previous kubectl exercises, it is expected that you will have a stateful set with two containers running and communicating with each other. Since the blog will be running inside Kubernetes, you need to access this by using the port-forward capabilities of kubectl. With the successful initialization of the WordPress container, it is expected that you will see the following setup screens:

Figure 3.20: Setup Steps – WordPress Install
After the setup, the new blog should be up and running:

Figure 3.21: Home page – WordPress blog
Execute the following steps to complete this activity:
- Create a two-container stateful set definition inside the wordpress-database.yaml file with the following specifications:
The name should be wordpress-database and the replica count can be set to 1. The database container should have the name of database and the container image of mysql:5.7. Publish the container to port 3306 and mount the data volume to the /var/lib/mysql path. In addition, set the following environment variables:
Figure 3.22: Environment variables
Create a blog container with the name blog using the latest WordPress container image and publish the container to port 80. In addition, set the following environment variables:
Figure 3.23: Environment variables
Include a volume claim with the name data and 1GB of storage.
Deploy the wordpress-database stateful set into the Kubernetes cluster.
- Check the status of the wordpress-database-0 pod and wait until it is ready.
- Create a proxy to the local system from the blog container using the port-forward command of kubectl.
- Open the forwarded address in the browser and fill in the WordPress setup form.
- Open the forwarded address in the browser and check that your new blog is running in containers.
- Stop the port forwarding started in step 4 and remove the stateful set.
Note
The solution to this activity can be found on Page 309.
Through this activity, we have operated a WordPress blog and its database in a Kubernetes cluster. For the blog and the database, only a stateful set is defined and sent to the cluster. Scheduling, networking, and storage operations were handled by Kubernetes in a couple of seconds, and the blog was ready. In addition, Kubernetes is always checking for node failures, and could reschedule the blog and database to another node without any data loss. Even for a single blog instance, it shows how powerful the Kubernetes resources and controllers are in creating scalable and reliable cloud-native applications.