Kubernetes Basics: Your First App Deployment

Tejashree Salvi
9 min readMay 22, 2023

Day 33/90 with Shubham Londhe

Photo by Joseph Barrientos on Unsplash

Looking to deploy your applications with ease and efficiency? In this blog post, we’ll walk you through the step-by-step process of deploying applications on Kubernetes, the popular container orchestration platform.

What is Kubernetes?

Kubernetes is like a super-hero for managing containers. But what are containers? Think of them as small, portal packages that hold all the things your application needs to run the code smoothly.

Now, Imagine you have a bunch of containers that need to run together to make your application work. eg(Front-end, Back-end, database, etc). Kubernetes comes to the rescue!

It helps you manage and control these containers, making sure they run in the right place, at the right time, and on the right machines as well.

Kubernetes acts like a brain that cor-ordinates and distributes these containers across multiple computers called Nodes.

With Kubernetes, you can easily deploy, update and roll back your application without any downtime. It also handles load balancing, so that traffic is evenly distributed among your containers, keeping your application responsive and reliable.

Kubernetes is a popular container orchestration tool.

Let’s deploy our First application using kubernetes.

Note: Please follow the steps for deployment

Step 1: Pre-requisites:

Installation of Kubernetes on master and worker nodes:

Step 2: Clone a repository

git clone https://github.com/tejashreeSalvi/django-todo-cicd.git

Let's look into the docker file in this repository:

FROM python:3

WORKDIR /data

RUN pip install django==3.2

COPY . .

RUN python manage.py migrate

EXPOSE 8000

CMD ["python","manage.py","runserver","0.0.0.0:8000"]

It’s a Django application running on port number 8000 and the entry point to the application is manage.py

What are Containers?

Containers are a runtime environment where your application code, with dependencies, is packaged and executed.

Containers are created from images.

Step 3: Build an Image from Dockerfile

docker build . -t trajendra/django-todo:latest
  • Go to the repository cd django-todo and run the above command to create an image.
  • Run the docker images command to get the list of images.

The image is successfully created.

Step 4: Push the docker image to a Registry

  • Login to the docker hub
docker login

After running the command it will prompt for username and password. Enter the details and you are successfully logged in.

  • Push the image to registry
docker push trajendra/django-todo:latest
  • Verify whether we have successfully been able to push the docker image to the registry.

WoW ! You have successfully created the image and pushed to registry. We will be creating containers from this image.

There are primary two ways to create and manage resources:

  1. Command-Line Interface
  2. YAML Manifests

Command-Line Interface:

The Kubernetes CLI referred to as kubectl is a tool that allows you to interact with the Kubernetes Cluster. It provides a set of commands to create, modify, and manage Kubernetes resources

For example:

kubectl create to create a resource, kubectl edit or kubectl patch to modify the resource and kubectl delete to delete any resources, kubectl get pods to get all the pods in the default namespace. There are many more commands.

YAML Manifests:

YAML manifests are configuration files written in YAML. These files define the desired state of Pods, Deployments, ConfigMaps, Secret, and many more.

We will be using YAML Manifests to deploy our application.

What is Pods?

The code of our application runs inside containers, which are encapsulated within Pods in Kubernetes.

  • A Pod can contain one or more containers, and all the containers share the same resources such as CPU, memory, and storage
  • Pods have their own unique IP address within the cluster, enabling other Pods or services to communicate with them.
  • Pods in Kubernetes are considered ephemeral, meaning they can be created, scaled, or terminated dynamically based on the desired state of the system.

1 Pod = 1 container is recommendand as best practices.

Task 1: Create your First Pod.

We will be creating a single pod using the YAML Manifests.

pod.yml

apiVersion: v1
kind: Pod
metadata:
name: django-app
spec:
containers:
- name: django-app
image: trajendra/django-todo:latest
ports:
- containerPort: 8000

In pod.yml file,

  • apiVersion: v1 specifies the Kubernetes API version being used.
  • kind: Pod indicates that you are creating a Pod object.
  • metadata contains metadata about the Pod, including its name.
  • spec defines the desired state of the Pod.
  • containers is an array of container objects running inside the Pod. In this case, there is only one container.
  • name: crud-backend-app sets the name of the container to "crud-backend-app".
  • image: trajendra/crudbackend:latest specifies the Docker image to be used for the container. In this case, it uses the image "trajendra/django-todo" with the "latest" tag.
  • ports defines the network ports that the container exposes. In this example, it exposes port 8000.
kubectl apply -f pod.yml

Run this command, Kubernetes will read our YAML file, and update any resources in our cluster accordingly. To see the newly created pod in action, you can run kubectl get pods. You should see the pod running.

Worker nodes are the machines in the Kubernetes cluster where Pods are scheduled and containers are run

  • Connect to Worker Node to verify if the container is successfully created.
docker ps
  • Let’s go inside the container and execute the curl command
docker exec -it 3551dae bash

Hey ! We have successfully been able to create containers via pod using YAML Manifests

Namespaces

Namespaces are logical groups created inside a Cluster. They allow multiple teams or applications to co-exist in the same cluster.

Each namespace has its own set of resources, including Pods, Services, Deployments, ConfigMaps, and more.

  • By default, Kubernetes provides a default namespace where resources are created if no specific namespace is specified
  • You can define RBAC (Role-Based Access Control) policies to grant different levels of access to resources based on namespaces.

Step 5: Create a Namespace

kubectl create namespace my-django-app

We will be creating my-django-app namespace for our application to include all the pods, services, deployment, and containers in this namespace.

Task 2: Create a Pod inside the Namespace

pod.yml

apiVersion: v1
kind: Pod
metadata:
name: django-app
namespace: my-django-app
spec:
containers:
- name: django-app
image: trajendra/django-todo:latest
ports:
- containerPort: 8000
  • To create the pod run the following command:
kubectl apply -f pod.yml
  • To verify the pods created inside the namespace
kubectl get pods -n=my-django-app

Deleting a Pod

kubectl delete -f pod.yml

Here, the pod will be deleted from the namespace my-django-app .

As we have added a namespace added in the metadata.

We just learned to create a pod inside the namespace.

Deployment

Our app runs inside kubernetes cluster. Application consists of multiple pods, each representing a component of the application. Instead of manually creating and managing individual pods for your application, you can use a deployment.

  • It ensures that the desired number of replicas of each component is always available and handles any necessary updates or scaling.
  • Kubernetes monitors the health of the Pods managed by a Deployment. If a Pod fails or becomes unresponsive, Kubernetes automatically replaces it to maintain the desired number of replicas.

Step 6: Create a Deployment

deployment.yml

apiVersion: apps/v1
kind: Deployment
metadata:
name: my-django-app-deployment
labels:
app: django-app
namespace: my-django-app
spec:
replicas: 3
selector:
matchLabels:
app: django-app
template:
metadata:
labels:
app: django-app
spec:
containers:
- name: django-app-container
image: trajendra/django-todo:latest
ports:
- containerPort: 8000

In Manifest, we have declared

kind: Defines the type of resource, which is a Deployment in this case.

selector: Specifies the labels used to select which Pods are part of this Deployment.

  • matchLabels: Defines the labels that Pods must have to be considered part of the Deployment. In this case, Pods with the label app: django-app will be included.
  • replicas: Defines the desired number of replicas (Pods) to maintain, set to 3 in this example.
  • template: Defines the Pod template used to create the Pods managed by the Deployment.

spec: Specifies the specifications for the Pods created from the template.

kubectl apply -f deployment.yml  # run the deployment

kubectl get pods -n=my-django-app # get the list of pods in namespace

You can then use the kubectl apply command to create or update the Deployment in your Kubernetes cluster.

To get the pods created inside a namespace kubectl get pods -n=my-django-app .

Scale

It is used to scale number of replicas(Pods) in the Deployments

Task 3: Create 10 replicas using Kubernetes CLI commands

kubectl scale deployment my-django-app-deployment --replicas=10 -n=my-django-app
  • --replicas=10: Sets the desired number of replicas (Pods) to 10. This means that Kubernetes will adjust the number of Pods in the Deployment to match this value.

It has created 10 pods in Cluster

Service

You have a deployment with multiple pods running a web application. Each pod has its own IP address, which can change dynamically. If you expose these pods directly to clients, the clients would need to keep track of each pod’s IP address and handle potential changes. This approach becomes impractical as the number of pods scales up or down. To solve this problem, you can create a service.

It provides a way to expose your application running in Pods to other services or external users within the cluster.

Step 7: Expose your application to external users

service.yml

apiVersion: v1
kind: Service
metadata:
name: my-django-app-service
namespace: my-django-app
spec:
type: NodePort
selector:
app: django-app
ports:
# By default and for convenience, the `targetPort` is set to the same value as the `port` field.
- port: 80
targetPort: 8000
# Optional field
# By default and for convenience, the Kubernetes control plane will allocate a port from a range (default: 30000-32767)
nodePort: 30009

NodePort: Exposes the Service on a port across each cluster node’s IP. Allows external access to the Service. We are using NodePort (30007–32767) range of IP allowed.

  • port: Specifies the port number on the Service itself. In this case, it is set to 80.
  • targetPort: Specifies the port number on the Pods that the Service should forward traffic to. In this example, it is set to 8000
  • nodePort: 30009 : it is set to 30009, meaning that traffic arriving at nodeIP:30009 will be directed to the Service.
kubectl apply -f service.yml

Edit the inbound rule in the security group of the EC2 instance and click on save rules.

Access the Application

To access the application: http://<public_ip_of_instance>:30009

Well, Done! we have successfully deployed a todo application on Kubernetes Cluster.

--

--