Deploying a Kubernetes application
Deploying an application on Kubernetes is fundamentally similar to deploying an application outside of Kubernetes. All applications, whether containerized or not, must have configuration details around topics that include the following:
- Networking
- Persistent storage and file mounts
- Availability and redundancy
- Application configuration
- Security
Configuring these details on Kubernetes is done by interacting with the Kubernetes application programming interface (API).
The Kubernetes API serves as a set of endpoints that can be interacted with to view, modify, or delete different Kubernetes resources, many of which are used to configure different details of an application.
Let's discuss some of the basic API endpoints users can interact with to deploy and configure an application on Kubernetes.
Deployment
The first Kubernetes resource we will explore is called a Deployment. Deployments determine the basic details required to deploy an application on Kubernetes. One of these basic details consists of the container image that Kubernetes should deploy. Container images can be built on local workstations using tools such as docker, and jib but images can also be built right on Kubernetes using kaniko. Because Kubernetes does not expose a native API endpoint for building container images, we will not go into detail about how a container image is built prior to configuring a Deployment resource.
In addition to specifying the container image, Deployments also specify the number of replicas, or instances, of an application to deploy. When a Deployment is created, it spawns an intermediate resource, called a ReplicaSet. The ReplicaSet deploys as many instances of the application as determined by the replicas field on the Deployment. The application is deployed inside a container, which itself is deployed inside a construct called a Pod. A Pod is the smallest unit in Kubernetes and encapsulates at least one container.
Deployments can additionally define an application's resource limits, health checks, and volume mounts. When a Deployment is created, Kubernetes creates the following architecture:
Another basic API endpoint in Kubernetes is used to create Service resources, which we will discuss next.
Services
While Deployments are used to deploy an application to Kubernetes, they do not configure the networking components that allow an application to be communicated with Kubernetes exposes a separate API endpoint used to define the networking layer, called a Service. Services allow users and other applications to talk to each other by allocating a static IP address to a Service endpoint. The Service endpoint can then be configured to route traffic to one or more application instances. This kind of configuration provides load balancing and high availability.
An example architecture using a Service is described in the following diagram. Notice that the Service sits in between the client and the Pods to provide load balancing and high availability:
As a final example, we will discuss the PersistentVolumeClaim API endpoint.
PersistentVolumeClaim
Microservice-style applications embrace being self-sufficient by maintaining their state in an ephemeral manner. However, there are numerous use cases where data must live beyond the life span of a single container. Kubernetes addresses this issue by providing a subsystem for abstracting the underlying details of how storage is provided and how it is consumed. To allocate persistent storage for their application, users can create a PersistentVolumeClaim endpoint, which specifies the type and amount of storage that is desired. Kubernetes administrators are responsible for either statically allocating storage, expressed as PersistentVolume, or dynamically provisioning storage using StorageClass, which allocates PersistentVolume in response to a PersistentVolumeClaim endpoint. PersistentVolume captures all of the necessary storage details, including the type (such as network file system [NFS], internet small computer systems interface [iSCSI], or from a cloud provider), along with the size of the storage. From a user's perspective, regardless of which method of the PersistentVolume allocation method or storage backend that is used within the cluster, they do not need to manage the underlying details of managing storage. The ability to leverage persistent storage within Kubernetes increases the number of potential applications that can be deployed on the platform.
An example of persistent storage being provisioned is depicted in the following diagram. The diagram assumes that an administrator has configured dynamic provisioning via StorageClass:
There are many more resources in Kubernetes, but by now, you have probably got the picture. The question now is how are these resources actually created?
We will explore this question further in the next section.
Approaches in resource management
In order to deploy an application on Kubernetes, we need to interact with the Kubernetes API to create resources. kubectl is the tool we use to talk to the Kubernetes API. kubectl is a command-line interface (CLI) tool used to abstract the complexity of the Kubernetes API from end users, allowing them to more efficiently work on the platform.
Let's discuss how kubectl can be used to manage Kubernetes resources.
Imperative and declarative configuration
The kubectl tool provides a series of subcommands to create and modify resources in an imperative fashion. The following is a small list of these commands:
- create
- describe
- edit
- delete
The kubectl commands follow a common format:
kubectl <verb> <noun> <arguments>
The verb refers to one of the kubectl subcommands and the noun refers to a particular Kubernetes resource. For example, the following command can be run to create a Deployment:
kubectl create deployment my-deployment --image=busybox
This would instruct kubectl to talk to the Deployment API and create a new Deployment called my-deployment, using the busybox image from Docker Hub.
You could use kubectl to get more information on the Deployment that was created by using the describe subcommand:
kubectl describe deployment my-deployment
This command would retrieve information about the Deployment and format the result in a readable format that allows developers to inspect the live my-deployment Deployment on Kubernetes.
If a change to the Deployment was desired, a developer could use the edit subcommand to modify it in place:
kubectl edit deployment my-deployment
This command would open a text editor, allowing you to modify the Deployment.
When it comes to deleting the resource, the user can run the delete subcommand:
kubectl delete deployment my-deployment
This would instruct the API to delete the Deployment called my-deployment.
Kubernetes resources, once created, exist in the cluster as JSON resource files, which can be exported as YAML files for greater human readability. An example resource in YAML format can be seen here:
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox
spec:
replicas: 1
selector:
matchLabels:
app: busybox
template:
metadata:
labels:
app: busybox
spec:
containers:
- name: main
image: busybox
args:
- sleep
- infinity
The preceding YAML format presents a very basic use case. It deploys the busybox image from Docker Hub and runs the sleep command indefinitely to keep the Pod running.
While it may be easier to create resources imperatively using the kubectl subcommands we have just described, Kubernetes allows you to directly manage the YAML resources in a declarative fashion to gain more control over resource creation. The kubectl subcommands do not always let you configure all the possible resource options, but creating the YAML files directly allows you to more flexibly create resources and fill in the gaps that the kubectl subcommands may contain.
When creating resources declaratively, users first write out the resource they want to create in YAML format. Next, they use the kubectl tool to apply the resource against the Kubernetes API. While in imperative configuration developers use kubectl subcommands to manage resources, declarative configuration relies primarily on only one subcommand—apply.
Declarative configuration often takes the following form:
kubectl apply -f my-deployment.yaml
This command gives Kubernetes a YAML resource that contains a resource specification, although the JSON format can be used as well. Kubernetes infers the action to perform on resources (create or modify) based on whether or not they exist.
An application may be configured declaratively by following these steps:
- First, the user can create a file called deployment.yaml and provide a YAML-formatted specification for the deployment. We will use the same example as before:
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox
spec:
replicas: 1
selector:
matchLabels:
app: busybox
template:
metadata:
labels:
app: busybox
spec:
containers:
- name: main
image: busybox
args:
- sleep
- infinity
- The Deployment can then be created with the following command:
kubectl apply -f deployment.yaml
Upon running this command, Kubernetes will attempt to create the Deployment in the way you specified.
- If you wanted to make a change to the Deployment, say by changing the number of replicas to 2, you would first modify the deployment.yaml file:
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox
spec:
replicas: 2
selector:
matchLabels:
app: busybox
template:
metadata:
labels:
app: busybox
spec:
containers:
- name: main
image: busybox
args:
- sleep
- infinity
- You would then apply the change with kubectl apply:
kubectl apply -f deployment.yaml
After running that command, Kubernetes would apply the provided Deployment declaration over the previously applied deployment. At this point, the application would scale up from a replica value of 1 to 2.
- When it comes to deleting an application, the Kubernetes documentation actually recommends doing so in an imperative manner; that is, using the delete subcommand instead of apply:
kubectl delete -f deployment.yaml
- The delete subcommand can be made more declarative by passing in the -f flag and a filename. This gives kubectl the name of the resource to delete that is declared in a specific file and it allows the developers to continue managing resources with declarative YAML files.
With an understanding of how Kubernetes resources are created, let's now discuss some of the challenges involved in resource configuration.