Kubernetes Tutorial for Beginners
Origin of Kubernetes
What is Kubernetes?
Key Kubernetes features
Kubernetes Cluster
Kubernetes cluster setup
How to set up a Kubernetes cluster using Minikube?
Kubernetes Architecture
Components of a Kubernetes cluster
Control plane
Data plane
Kubernetes Objects
Basic Kubernetes Objects
Real-Life Case Scenario
What happens when you create a Kubernetes Deployment?
Conclusion
Telepresence: Rapid debugging & testing of Kubernetes services
Want to get started with Kubernetes? You are in luck! This detailed tutorial on Kubernetes will teach you Kubernetes basics, how Kubernetes originated, its architecture, concepts, and more!
Origin of Kubernetes
Although the first release of Kubernetes occurred in 2015, its origin is traced to a project called Borg which was created by the folks at Google in 2003. The major problem that led to this project was maintaining free services while managing massive infrastructure hardware without degrading the business revenue. In addition, the attraction of talented engineers to Google led to the rethinking of ways to optimize commodity hardware performance, which led to Borg’s birth.
The Borg system was a small-scale project with an initial team of 3–4 engineers collaborating on the new version of the Google search engine. Being an internal tool, Borg was viewed as a competitive advantage for cost savings and developer(s) productivity. As a result, it remained an internal tool within Google and was later succeeded by the Omega project in 2013.
In 2013, Joe Beda, Brendan Burns, and Craig McLuckie, with the design and development experience of Borg and Omega, looked into the popularity of Docker and decided to create a cloud-native project that combined the Borg and Omega project with Docker. Though the idea was difficult to execute, its importance came to light, and in mid-2014, Google released Kubernetes as the first open-source version of the Borg project. In 2015, Google released Kubernetes version 1.0 and partnered with The Linux Foundation to form the Cloud Native Computing Foundation (CNCF). So, you can think of Kubernetes as an open-source system run by the Cloud Native Computing Foundation.
For context, the name Kubernetes originated from a Greek word meaning helmsman or pilot, and K8s is an abbreviation of Kubernetes. The 8 in the K8s is the total number of letters between the letter “K” and “s” in Kubernetes.
What is Kubernetes?
Kubernetes is a container orchestrator that helps you automate, deploy, scale, and manage your containerized applications. For example, if a container fails, Kubernetes will restart it, and if a container isn’t responding to your user-defined health check, Kubernetes can kill it and stop showing it to your users until it’s ready to serve traffic again.
Key Kubernetes features
Kubernetes has so many amazing features. But here are some of the most important ones:
- Self-healing: Kubernetes enables the killing and restarting of containers that fail user-defined health checks while isolating them from the end-user’s reachability until they are ready to receive traffic.
- Automated rollouts and rollbacks: Changes to a container application are progressively rolled out while monitoring its health to ensure it doesn’t kill all your instances simultaneously. If something goes wrong, Kubernetes will roll back the changes.
- Service discovery: Kubernetes can expose a container using the DNS name or using their own IP address. It can also automatically manage DNS records by specifying and transferring services to their required hosts.
- Load balancing: Kubernetes load balances and distribute network traffic to containers to maintain stable deployment.
- Scaling: Kubernetes scales your application up and down (stateful application) or in and out (stateless applications) through CLI commands, a web UI, or performance metric.
- Resource Monitoring and Logging: Kubernetes monitors the state of resources within the cluster and logs their operations.
- Secret and configuration management: Kubernetes allows developers to store and manage sensitive information (passwords, OAuth tokens, and SSH keys) without rebuilding container images and exposing secrets in their stack configuration.
Kubernetes Cluster
The first step in using Kubernetes is a cluster’s provisioning (creation).
A Kubernetes cluster is a collection of virtual or physical machines, called nodes, which manage and run containerized applications. The nodes that make up a Kubernetes cluster are grouped into master nodes and worker nodes. The master nodes manage the operations within the Kubernetes cluster, while the worker nodes are responsible for running containerized applications. I’ll explain more about this in the Kubernetes Architecture section of this article.
Kubernetes cluster setup
It is possible to provision a cluster manually; however, this is a tedious task that implies the distribution and execution of each master and worker component (to be discussed in the Kubernetes Architecture). As a result, multiple tools emerged to handle the automatic provisioning of a cluster. The process of automatically provisioning a Kubernetes cluster is known as bootstrapping.
The use case (development or production) determines the tool used for bootstrapping. Examples of tools used to bootstrap a Kubernetes cluster are as follows:
Development-grade clusters:
Production-grade clusters:
Most cloud service providers offer Kubernetes-as-a-Service. Some of such services include:
- Amazon Elastic Kubernetes Service
- Google Kubernetes Engine
- Azure Kubernetes Service
- DigitalOcean Kubernetes
- IBM Kubernetes Service
- Oracle Container Engine for Kubernetes
How to set up a Kubernetes cluster using Minikube?
This section will explore how to set up a Kubernetes cluster using Minikube. If you’d like to use other tools/platforms to create your cluster, click on the link shared earlier.
If you’re not ready to set up a Kubernetes cluster yet, that’s fine. You can skip this section and move to the next session, as you can always return to it later. But if not, let’s set up a cluster with Minikube.
Minikube is used to bootstrap a single-node Kubernetes cluster for development purposes. We will set up this cluster using an Ubuntu 20.04 virtual machine on AWS. The minimum requirements to set up a Minikube cluster are as follows:
- 2 CPUs or more
- 2GB of free memory
- 20GB of free disk space
- Internet connection
- Container or virtual machine managers, such as Docker, Hyperkit, Hyper-V, KVM, Parallels, Podman, VirtualBox, or VMware Fusion/Workstation
It’s one thing to have our cluster setup and another to interact with our cluster. In Kubernetes, the most common way to interact with a cluster is by using a Kubernetes command-line tool called kubectl, which allows a user to run commands against a cluster.
As an analogy, imagine how the school library system works with the: student (developer), librarian (kubectl), and library (cluster). The way the librarian serves as the means of communication between a student and the library is the same way
kubectl
Minikube
kubectl
Step 1: Update the ubuntu operating system
sudo apt update
Step 2: Create a
k8s.sh
nano k8s.sh
and then add the following commands and then save and exit the nano editor.
#!/bin/bash# Download Kubectl binary releasecurl -LO https://dl.k8s.io/release/v1.21.0/bin/linux/amd64/kubectl# Download Kubectl checksumcurl -LO https://dl.k8s.io/v1.21.0/bin/linux/amd64/kubectl.sha256# Run checksum again kubectlecho "$(<kubectl.sha256) kubectl" | sha256sum --check# Install kubectlsudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl# Display kubectl client versionkubectl version --client
Step 3: Change the
k8s.sh
chmod 700 k8s.sh./k8s.sh
Step 4: Create a
docker.sh
nano docker.sh
and then add the following commands and then save and exit the nano editor.
#!/bin/bash# Install dockersudo apt install docker.io -y# Add ubuntu user to the docker groupsudo usermod -aG docker $USER# Allow access to docker by other applicationssudo chmod 666 /var/run/docker.sock
Step 5: Change the
docker.sh
chmod 700 docker.sh./docker.sh
Step 6: Create SSH Key for
minikube
ssh-keygen -f .ssh/id_rsa
Step 7: Install a dependency for
minikube
sudo apt install conntrack -y
Connection tracking (“”) is used to keep track of all logical network connections or flows. It is essential for performant complex networking of Kubernetes where nodes need to track connection information between thousands of pods and services
Step 8: Install
minikube
curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/
Step 9: Start
minikube
sudo minikube start --vm-driver=none
N/B: We’re using
-— vm-driver=none
minikube
minikube
Step 10: Confirm your
minikube
sudo minikube status
Your output should look like this, meaning that the cluster has been set up successfully.
minikubetype: Control Planehost: Runningkubelet: Runningapiserver: Runningkubeconfig: Configured
Now, let’s make slight changes to ensure we have a great user experience. Ifor a better user experience. We had initially been using
sudo
minikube
/root
minikube
# migrate minikube from root to ubuntu usersudo cp -r /root/.minikube $HOME && sudo rm -R /root/.minikube# Give ubuntu user ownership to .minikube directorysudo chown -R $USER:$USER $HOME/.minikube
Run Minikube status and confirm it outputs the same result we got with the sudo command. The next thing we are going to do is make the
kubectl commands
# migrate kubeconfig folder from root to ubuntu usersudo cp -r /root/.kube $HOME && sudo rm -R /root/.kube# Give ubuntu user ownership to .kube directorysudo chown -R $USER:$USER $HOME/.kube# Change the /root parameters to /home/ubuntu within the .kube/config filesed -i "s|/root|$HOME|g" .kube/config
Great work! Now the cluster has been set up, let’s confirm that kubectl can interact with
minikube
kubectl get all
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEservice/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 111m
Great work setting up your first Kubernetes cluster! The next section describes the components of a Kubernetes cluster.
Kubernetes Architecture
Kubernetes is a platform used for container orchestration, and as for every platform, there is always an architectural design behind the hood which showcases its principle of operation. In this section, we will look at the key components that constitute the Kubernetes cluster which is explained as the Kubernetes architecture.
Components of a Kubernetes cluster
As previously mentioned, the master and worker nodes within a Kubernetes cluster contain various components. The components of the master node collectively form the Control Plane, and the components of the worker node collectively form the Data Plane.
The control plane serves as the command center in the cluster, while the data plane serves as the soldier executing commands. Let’s explore these components in detail.
Kubernetes architectural diagram (Source: Kubernetes)
Control plane
The control plane components are responsible for granting external access to a Kubernetes cluster, storing the state of Kubernetes objects, scheduling objects to worker nodes, and detecting and responding to events within the cluster.
The control plane comprises these 5 components:
kube-apiserver
etcd
kube-scheduler
kube-controller-manager
cloud-controller-manager
kube-apiserver
The
kube-apiserver
The
kube-apiserver
kubectl
User access to Kubernetes cluster
etcd
The
etcd
kube-apiserver
etcd
The
etcd
etcd
kube-scheduler
The
kube-scheduler
kube-scheduler
Pod specifications subject the scheduler to factors required for its attachment. The factors for scheduling decisions include resource requirements and limits, hardware & software constraints, affinity and anti-affinity specifications, data locality, inter-workload interference, and deadlines.
kube-controller-manager
The
kube-controller-manager
kube-controller-manager
The
kube-controller-manager
cloud-controller-manager
The
cloud-controller-manager
The
cloud-controller-manager
Data plane
The data plane is made up of worker nodes. Each worker node contains three components:
kubelet
kube-proxy
container runtime
Data plane (Source: The New Stack)
kubelet
The
kubelet
kube-apiserver
container-runtime
kubelet
container-runtime
The
container-runtime
kube-proxy
The
kube-proxy
Kubernetes Objects
Kubernetes objects are persistent entities in the Kubernetes system used to represent the state of your cluster. These objects are typically used to do the following amongst other things:
- Deploy and scale containerized applications
- Allocate network and disk resources
- Balance loads across containerized applications
- Describe the behavioral policies of containerized applications, etc.
Kubernetes objects are created using the kubectl command-line tool, a web user interface, or the Kubernetes API. These objects are then managed by the Kubernetes system, which ensures they always exist in the desired state of your cluster. Based on my experience, one of the most common techniques used for creating Kubernetes objects is the kubectl command-line tool.
Kubernetes objects are managed using the imperative approach (defines actions) or the declarative approach (defines desired state).
Basic Kubernetes Objects
There are so many Kubernetes objects. However, this section of the article will focus on the basic ones you need to know to get started with Kubernetes.
Pod
A pod is the smallest deployable unit of Kubernetes. A pod contains one or more containers with shared storage and networking resources, created with the same specification.
Kubernetes Pod (Source: kubernetes.io)
A Kubernetes pod
pods
Imagine you wish to deploy a simple blog application and also obtain the logs from the application. To achieve this, you’ll have to create a pod with two containers — the main container will be your blog application, and the side-car container will be the logging agent.
To create a simple pod using the imperative approach, run the command below on your terminal:
kubectl run techtrends --image=joshbolten/techtrends
Your output
pod/techtrends created
Confirm the pod is running
kubectl get pods
Your output
NAME READY STATUS RESTARTS AGEtechtrends 1/1 Running 0 42s
As a little tip, if you wish to generate a template file for the
techtrends
pod
kubectl run techtrends --image=joshbolten/techtrends --dry-run=client -o yaml > pod.yaml
The newly created
pod.yaml
kubectl apply -f pod.yaml
If you want to get more details about the newly created
pod
kubectl describe pod techtrends
To delete the pod, run this command:
kubectl delete pod techtrends
The abbreviation for a
pod
po
kubectl get po
ReplicationController
A
ReplicationController
ReplicationController
A pod created with the replica controller will automatically be replaced on failure. It is highly recommended to use a
ReplicationController
deployment
To create a ReplicationController declaratively, create a file named
rc.yaml
apiVersion: v1kind: ReplicationControllermetadata:name: amb-applabels:app: amb-appspec:# modify replicas according to your casereplicas: 3selector:app: amb-apptemplate:metadata:labels:app: amb-appspec:containers:- name: techtrendsimage: joshbolten/techtrendsports:- name: tech-portcontainerPort: 3111protocol: TCP
Now, apply the file to your Kubernetes cluster by running this command:
kubectl apply -f rc.yaml
In doing so, you should get the output below:
replicationcontroller/amb-app created
To see the number of replication controllers running on your Kubernetes cluster, run this command:
kubectl get rc
In doing so, you should get an output similar to the one below:
NAME DESIRED CURRENT READY AGEamb-app 3 3 3 43s
To delete the replication controller we just created, run the command below:
kubectl delete rc amb-app
ReplicaSet
The
ReplicaSet
ReplicationController
- The supports equality-based selectors, whereas the
ReplicationController
supports both equality and set-based selectors.ReplicaSet
- The uses this format
ReplicationController
to match pods while the ReplicaSet uses this formatselector.label.<label_name>
.selector.matchLabels.<label_name>
- The label selector of a is added like this:
ReplicationController
env = testtier != frontend
while the label selector for a
replicaset
env in (prod, test)tier notin (frontend, backend)
At the time of this writing, it is recommended to use
ReplicaSets
ReplicationControllers
Deployment
Deployments
ReplicaControllers
ReplicaSets
Deployments
In simple words, you can think of a
deployment
Pods
ReplicaSets
To create a deployment using the imperative approach, run the command below:
kubectl create deployment techtrends --image=joshbolten/techtrends --replicas=4
- This will create a deployment named using the
techtrends
container image.joshbolten/techtrends
- This deployment will create 4 pod replicas since we passed 4 into the field.
—- replicas
To see the list of deployments in your Kubernetes cluster, run the command:
kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGEtechtrends 4/4 4 4 12s
Volumes
A volume is a directory accessible to the containers in a pod to read and write data. There are two types of volumes in Kubernetes: ephemeral and persistent volumes. The ephemeral volume exists with the lifetime of a pod (i.e., the destruction of a pod destroys its ephemeral volume). In contrast, the persistent volumes exist beyond the lifetime of its attached pod.
Kubernetes Volume (Source: startkubernetes.com)
To use a persistent volume, a persistent volume claim must be created and attached to the volume of the pod it is attached to. Volumes are used to mount application configuration, secrets, external storage, etc., to a running container.
Statefulset
A
statefulset
mysql-0
mysql-1
mysql-2
Imagine you have a robust application where you want to have a primary database set aside to perform read and write operations to a persistent storage. Because you wish to scale this database, you will need to create replicas without changing the state of the data stored by the primary database. To achieve this, you will need an object that handles both stateful applications and gives unique identifiers to each replica while scaling out. This is where Statefulset comes in as opposed to deployment, giving random IDs to its pods.
Statefulset Object (Source: loft.sh)
The scaling process of a
statefulset
Service
A
service
Service object (Source: thoughtbot.com)
There are four types of services in Kubernetes, namely:
- Cluster IP: The service type is the default Kubernetes service. This service type is used only to access internal traffic. Kubernetes assigns a cluster-internal IP on this service to allow communication within the cluster. For internal communication of an application’s components, this service type can be used.
Cluster-IP
- Nodeport: A service is used to externally expose an application running on a node (server) by assigning the node a static port. This port (dynamically assigned by Kubernetes or the user) is used as the entry-point to the application from the internet (
NodePort
). By standard, Kubernetes allows nodes exposure to a limited range of port numbers fromnode_ip:nodePort
. Although this service exposes the application to the internet, it is best used for testing and debugging purposes and not for production. The major limitations of using this service are its limited port numbers and maintenance required for changes in30000 to 32767
due to node failure.node_ip
- Load balancer: The service exposes applications using a cloud provider’s load balancer. Kubernetes uses a cloud provider API to create an application load balancer that directs traffic from the internet to pods running an application. This service type is used when using a cloud provider to host your Kubernetes cluster. The application load balancer is used to expose only one service, which means that for 10 services, you need 10 different load balancers.
LoadBalancer
External name: This maps a service to an external domain using a Canonical Name (CNAME) record. The external name mapping is performed using the
spec.externalName
spec.selector
Services and use-cases (Source: Ashish Patel)
Let’s expose our
techtrends
kubectl expose deployment techtrends --type=NodePort --name=techtrends-service --port=3000 --target-port=3111
From the above command:
- : Name of the service to be exposed
—-name
- : service type to be exposed
—-type
- : port used to expose service
—-port
- : application port
—-target-port
Confirm the service created by running this command:
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEkubernetes ClusterIP 10.96.0.1 <none> 443/TCP 24mtechtrends-service NodePort 10.96.13.251 <none> 3000:31877/TCP 32s
Behind the hood, Kubernetes maps a port within the range of
30000–32767
port 3000
port 3111
N/B:
port 31877
Assuming you have a public IP of
52.40.234.189
52.40.234.189:31877
Here is what happened:
- The traffic from the internet (your browser) was routed from port (node port) externally to the
31877
on porttechtrends-service
.3000
- The routed the traffic to port
techtrends-service
of your techtrends application.3111
Ingress
An
ingress
ingress
ingress
Ingress Controller
The
ingress controller
ingress
ConfigMap
A
ConfigMap
configmaps
Secrets
A
secret
Secrets
etcd
Real-Life Case Scenario
Wach is a new intern is Brightworks LLC, and on the first day of his onboarding week, the lead engineer briefed him on an upcoming minimum viable product (MVP) focused on improving the people’s department. After the interesting meeting, Wach was given access to the product codebase. As a dynamic engineer, who had just finished learning about the basics of Kubernetes, he decided to create a proof of concept (POC) to operationalize the frontend using Kubernetes. He had successfully packaged the front end and pushed the docker image to Dockerhub.
Now, it’s time to put his Kubernetes knowledge to a little test. To achieve his goal, he decided to go with the following workflow:
- Create a Kubernetes cluster with Minikube
- Create a Kubernetes deployment for the frontend application using the declarative approach
- Create a NodePort service for the frontend deployment using the declarative approach
- Access the frontend application and navigate through it
Wach followed the guide from Ambassador Labs on how to create a Kubernetes cluster with Minikube and moved forward to create a file named
udapeople-frontend.yaml
In the
udapeople-frontend.yaml
apiVersion: apps/v1kind: Deploymentmetadata:name: udapeople-frontend-deploymentnamespace: defaultlabels:app: udapeopletier: front-endspec:selector:matchLabels:app: udapeople-frontendreplicas: 3template:metadata:labels:app: udapeople-frontendspec:containers:- name: udapeople-feimage: wache/udapeople-frontend:1.0ports:- containerPort: 3000name: uda-fe-port---apiVersion: v1kind: Servicemetadata:name: udapeople-frontend-servicenamespace: defaultspec:selector:app: udapeople-frontendtype: NodePortports:- name: fe-svcprotocol: TCPport: 3500targetPort: uda-fe-portnodePort: 31000
The manifest file creates a deployment object named
udapeople-frontend-deployment
udapeople-frontend-service
The deployment object deploys the
udapeople frontend
replicaset
port 3000
udapeople-frontend
pods
3500
3500
31000
minikube
Wach applies his configuration to the Kubernetes cluster using this command:
kubectl apply -f udapeople-frontend.yaml
deployment.apps/udapeople-frontend-deployment createdservice/udapeople-frontend-service created
He decides to confirm if the
deployment
kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGEudapeople-frontend-deployment 3/3 3 3 67s
He then goes further to confirm if the
service
kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEkubernetes ClusterIP 10.96.0.1 <none> 443/TCP 8m10sudapeople-frontend-service NodePort 10.108.195.185 <none> 3500:31000/TCP 2m6s
With this, Wach has now successfully deployed the
udapeople frontend
Prior to having access to the application, the nodeport 31000 must be allowed for inbound traffic through the security group used to setup your minikube cluster
On Wach’s favorite browser, he decides to confirm the application reachability using the instance public URL and port by entering
http://ec2-34-210-242-5.us-west-2.compute.amazonaws.com:31000/
The public ip of the instance could also be used to access the application
http://34.210.242.5:31000/
Great! Wach is happy and can explore the application. While exploring the application, Wach decided to document what happened behind the hood in the process of creating a deployment object.
What happens when you create a Kubernetes Deployment?
At this point, we understand the core components of a Kubernetes cluster and its basic concepts and have also seen a real-life case scenario of how to use the deployment and service Kubernetes object. Now, you need to understand exactly what happens behind the scenes when you create a Kubernetes deployment.
When Wach created the deployment using the
udapeople-frontend-deployment.yaml
- The client sends an HTTP POST request to
kubectl
which validates the user and exposes the Kubernetes API for Deployment.kube-apiserver
- The stores the deployment manifest in
kube-apiserver
while the deployment controller listens to changes to theetcd
gets notified of the deployment manifest.kube-apiserver
- The deployment controller creates a ReplicaSet using the deployment specs and sends a response to the to store in the
kube-apiserver
.etcd
- The ReplicaSet controller gets notified of the new changes on the . With the replica counts and pod selector, the ReplicaSet controller creates the required pods’ state. A response is sent to the
kube-apiserver
to store the pods' state in thekube-apiserver
.etcd
- The creates the pod object without assigning it to a node. The newly created pods trigger the
kube-apiserver
for scheduling to a worker node based on its specification.kube-scheduler
- The sends a response for pods attachment to the
kube-scheduler
. This response is updated in thekube-apiserver
. Theetcd
sends the Pod specifications to thekube-apiserver
of the appropriate worker node.kubelet
- The uses the pod specification to create the pods within the worker node and notify the container runtime to run the required amount of containers with its appropriate configurations. After all the containers are created and attached to the pods, the
kubelet
updates the pod status to thekubelet
, and thekube-apiserver
updates the data in thekube-apiserver
.etcd
- On successful creation of containers, the network configuration of the worker node is updated by the .
kubeproxy
The illustration below shows a basic walkthrough of what happens when pods are created using the deployment blueprint. From the successful creation of containers, Kubernetes takes care of the orchestration for you.
Background processes of pods creation using the deployment blueprint (Source: Nived Velayudhan)
Conclusion
The use of Kubernetes depends on the architectural pattern of your application as it sits between Infrastructure-as-a-Service and Platform-as-a-Service. Kubernetes is required to be understood by platform engineers and application developers to make modern software delivery seamless.
The core concepts learned here are a stepping stone to your journey of container orchestration with Kubernetes. Keep learning!
Telepresence: Rapid debugging & testing of Kubernetes services
Building and testing your microservice-based application becomes complex when you can no longer run everything locally due to resource requirements. Moving to the cloud for testing is a no-brainer, but how do you synchronize your local changes against your remote Kubernetes environment? By using Telepresence!
Telepresence redirects network traffic from a service in the cloud to a service or container on your local machine, merging the benefits of cloud and local test environments. This means you can run integration tests locally instead of waiting on a remote deployment.
Try Telepresence today