Understanding Kubernetes Services: Deploying a Django Todo Web App Step-by-Step
📍Introduction
Welcome to my Kubernetes blog series, where I share my Kubernetes learnings and try to deep dive into topics. In this blog, we will explore the K8s service in detail and understand the difference between them by doing a hands-on project of deploying a Django Todo Web Application. Let's get started!🚀
📍Types of Services:
There are 4 types of services:
ClusterIP
NodePort
LoadBalancer
ExternalName
We will be seeing about the first 3 services with a hands-on project.
✔Cluster IP
The default type of service is ClusterIP so there is no need to mention the type while defining this service type. ClusterIP service provides a service proxy using the Cluster IP address to access the application at the service port. This solves the issue of directly accessing the pod IP address which is dynamically allocated causing it to change when the pod is crashed. It provides access to the application inside the cluster therefore, you can access it only when you are logged in to your K8s Cluster.
If you are using Minikube you can log into the cluster using the minikube ssh
command. If you are using Kubeadm there is no need for login. It is mainly used for intra-cluster communication between pods or services. Examples: Database services, and microservices communication.
ClusterIP service provides the following functionalities:
Service Discovery
Load Balancing
✔NodePort
The NodePort service is mainly used to access our application outside the K8s cluster i.e. without logging into the cluster. When we define a NodePort
service, it exposes a port called nodePort
that we had defined in its manifest files on all the nodes, where we can access the application using the Node IP.
The nodePort
defined in its Manifest file ranges between 30000-32767. It can be used by the organization. It is useful for development and testing or when you need external access to services in a non-production environment. Examples: Web applications, and APIs for testing.
NodePort service provides the following functionalities:
Service Discovery
Load Balancing
Exposing to the organization.
✔LoadBalancer
We were accessing the application within the cluster or the organization using the above 2 services. To access the application outside the system by an external user who can use a Public IP we use the LoadBalancer service. There are some other methods to make the application running inside a cluster externally accessible. This service can only be used when you are running the Kubernetes cluster on any Cloud Service Provider Platform like AWS, Azure, Google Cloud, etc.
Kubernetes does not provide a load balancing component, LoadBalancer service helps us to use a Load balancing component of the Cloud Service. This component allows the external traffic coming from the User via the Public/External IP to access the application from the browser.
LoadBalancer service provides the following functionalities:
Service Discovery
Load Balancing
Exposing to the outside world
For the ExternalName service, you can visit the official Kubernetes documentation on services: K8s Services
Now let's do a hands-on project and understand the above services in practice.🚀
📍Django Todo Web Application
Step 1
: Set Up a Minikube or a Kubeadm cluster on your AWS EC2 instance. I will be executing this project on Minikube. You can refer to this guide: https://github.com/LondheShubham153/kubestarter
Step 2
: Clone the project using the git clone <HTTP_URL>
from this GitHub repository: https://github.com/LondheShubham153/django-todo-cicd
Step 3
: Go to the k8s
directory inside the project:
Step 4
: Modify the deployment.yml
file by adding a django
namespace. Copy the below manifest file inside the deployment.yml
file using the Vim editor.
apiVersion: apps/v1
kind: Deployment
metadata:
name: todo-deployment
namespace: django
labels:
app: todo-app
spec:
replicas: 3
selector:
matchLabels:
app: todo-app
template:
metadata:
namespace: django
labels:
app: todo-app
spec:
containers:
- name: todo-app
image: trainwithshubham/django-todo:latest
ports:
- containerPort: 8000
Step 5
: Similarly modify the service.yml
file by adding the django
namespace with the following content.
apiVersion: v1
kind: Service
metadata:
name: todo-service
namespace: django
spec:
type: NodePort
selector:
app: todo-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: 30007
Step 6
: Create the django
namespace using the command kubectl create namespace django
and verifying the namespace using the kubectl get namespace
command:
Step 7
: Apply the deployment manifest using the kubectl apply -f <fileame>
command and verify the applied deployment using kubectl get deployment -n django
:
Wait for the pods to be running keep verifying using the above command till you see the deployment and pods in the running state:
Once the pods are running we can access the application running on any of the pods using the pod IP.
Step 8
: Use the kubectl get pods -n django -o wide
command to get the pod IP:
Step 9
: Open a separate terminal of the same instance where Minikube is running, log in to the cluster using minikube ssh
command and access the application using the command curl -L <PodIP>:<containerport>
:
curl -L <podIP>:8000
You can try to access the application outside the cluster you cannot:
Let's see in practice how the pod IP is changed when a new pod is created once we delete a pod:
And if you try to access the pod with the IP 10.244.0.5
you cannot since that pod is deleted.
Accessing the application with the newly created Pod IP:
Here, we have deleted the pod manually but in production, there will be many scenarios where a pod gets crashed and if the user tries to access the pod using the Pod IP it will not be able to. Therefore, let's create a Service and apply it.
We will be creating a ClusterIP service i.e. the default service:
Step 10
: Create a svc.yml
file and copy the below Service Manifest file and apply it:
( Try to write the service file on your own, to learn how to write a manifest file you can refer to my blog: Kubernetes Fundamentals: Understanding Pods, Deployments, Services, and Manifests )
apiVersion: v1
kind: Service
metadata:
name: todo-service
namespace: django
spec:
selector:
app: todo-app
ports:
- protocol: TCP
port: 80
targetPort: 8000
type: ClusterIP
Verify the service using the command kubectl get svc -n django
:
Step 11
: Access the application using the ClusterIP
at the port defined in the service manifest file, where the service is listening at port 80
i.e. the default HTTP port, therefore, you can even directly access only using the ClusterIP (Login to your Minikube cluster ):
If you are using Kubeadm then no need to log in to the cluster.
Now that our service has been defined we can access the application regardless of the Pod IP changes since the service uses service discovery i.e. labels as a selector to apply the service.
Let's try and apply a Service of type NodePort and access the application outside the cluster without logging into it:
Step 12
: Delete the ClusterIP service using kubectl delete svc <service_name> -n <namespace>
:
Step 13
: Apply the service.yaml
file i.e. NodePort service present in the k8s
directory
Step 14
: Now, access the application using the NodeIP
at the nodePort
. Getting the NodeIP:
Step 15
: Access the application using curl -L <NodeIP>:<nodePort>
. In our case the nodePort is 30007
defined in the service file.
You can also try to access it on the browser using the same IP, make sure you allow the inbound traffic at port 30007
( https://<NodeIP>:<nodePort>
on the browser )
Now, let's try to access the application using a public IP with LoadBalancer service making it accessible for external users.
Step 16
: Delete the NodePort service using kubectl delete svc <service_name> -n <namespace>
:
Step 17
: Create a service2.yml
file and copy the below LoadBalancer Manifest file into it:
apiVersion: v1
kind: Service
metadata:
name: todo-service
namespace: django
spec:
selector:
app: todo-app
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 8000
Step 18
: Apply this service2.yml
manifest file:
Step 19
: Go to your second terminal and execute the minikube tunnel
command. Make sure that you have allowed incoming traffic to port 80
at the inbound rules in the security group of your EC2 instance.
Step 20
: After this, you will see the External IP:
Now, you can access the application using this External IP
at the port 80
or at 31748
📍Facing an issue
I am unable to access the application using the external IP from the above Load Balancer service from my browser:
I am unable to ping from my local machine to the External IP even after adding ICMP rule to the security group of my instance (One of the solutions I got from the ChatGPT)
I am unable to troubleshoot this issue, I would highly appreciate your advice or suggestions regarding how to troubleshoot this issue in the comments section.
📍Conclusion
Thank you for reading this blog! 📖 Hope you have gained some value. If you want to practice Kubernetes make sure you do it on Minikube or at killercoda.com where you get K8s playground for free.
If you enjoyed this blog and found it helpful, please give it a like 👍, share it with your friends, share your thoughts, and give me some valuable feedback.😇 Don't forget to follow me for more such blogs! 🌟