Building Scalable Applications with Kubernetes

Building Scalable Applications with Kubernetes

Introduction to Kubernetes

Kubernetes is an open-source container orchestration platform that automates the deployment, scaling, and management of containerized applications. It provides a framework for managing and coordinating containerized workloads across a cluster of nodes.

In Kubernetes, a cluster is a set of nodes that run containerized applications. Nodes are individual machines (virtual or physical) that form the foundation of a Kubernetes cluster. The master node controls and manages the cluster, while the worker nodes host the applications.

key Kubernetes components

API Server: The central management point of the cluster, responsible for handling API requests and serving as the primary interface for interacting with the cluster.

etcd: A distributed key-value store that stores the cluster's configuration data and serves as its backing store.

Scheduler: Assigns workloads to nodes based on resource requirements, policies, and constraints.

Controller Manager: Ensures that the desired state of objects in the cluster matches the current state and manages various controllers, such as the ReplicaSet controller and Deployment controller.

Container Runtime: Executes containers on nodes, such as Docker or containerd.

Kubelet: Runs on each node and communicates with the master node. It manages containers, ensures their health, and reports node status to the master.

Kubernetes Concepts

Pods

In Kubernetes, containers are not directly deployed on the worker nodes. Instead, Kubernetes encapsulates the containers within a Kubernetes object called pods. A Pod is the smallest deployable unit in Kubernetes. It represents a single instance of a running process in the cluster. Pods can contain one or more containers that share the same network namespace, IPC namespace, and storage volumes.

Here's an example of a pod definition in YAML (pod-definition.yaml):

apiVersion: v1

kind: Pod

metadata:

  name: my-pod

spec:

  containers:

    - name: my-container

      image: my-image:tag

To run a pod using the YAML definition, you can use the following command:

kubectl apply -f pod-definition.yaml

ReplicaSets

ReplicaSets ensure that a specified number of pod replicas are running at any given time. They provide fault tolerance and enable the scaling of pods.

Here's an example of a ReplicaSet definition (replicaset-definition.yaml):

apiVersion: apps/v1

kind: ReplicaSet

metadata:

  name: my-replicaset

spec:

  replicas: 3

  selector:

    matchLabels:

      app: my-app

  template:

    metadata:

      labels:

        app: my-app

    spec:

      containers:

        - name: my-container

          image: my-image:tag

To create a ReplicaSet, you can use the following commands:

kubectl apply -f replicaset-definition.yaml

Scaling can also be achieved using the command line, as shown below:

kubectl scale replicaset my-replicaset --replicas=5

Deployments

Deployments provide declarative updates for Pods and ReplicaSets. They manage the rollout and rollback of application updates.

Here's an example of a Deployment definition (deployment-definition.yaml):

apiVersion: apps/v1

kind: Deployment

metadata:

  name: my-deployment

spec:

  replicas: 3

  selector:

    matchLabels:

      app: my-app

  template:

    metadata:

      labels:

        app: my-app

    spec:

      containers:

        - name: my-container

          image: my-image:tag

To create a Deployment and perform updates and rollbacks, you can use the following:

kubectl apply -f deployment-definition.yaml

The command below is used to update the image of a container in a deployment:

kubectl set image deployment/my-deployment my-container=my-new-image:tag

This command is used to check the status of a rollout for a deployment:

kubectl rollout status deployment/my-deployment

This is used to rollback a deployment to a previous revision:

kubectl rollout undo deployment/my-deployment

Service

In Kubernetes, a Service is a fundamental building block that enables networking and communication between different components (such as pods) within a cluster

Let's explore three types of Kubernetes Services:

NodePort

Exposes the service on a static port on each node in the cluster. The service is accessible via <NodeIP>:<NodePort>.

Here's an example (service-definition.yaml):

apiVersion: v1

kind: Service

metadata:

  name: my-nodeport-service

spec:

  type: NodePort

  selector:

    app: my-app

  ports:

    - protocol: TCP

      port: 80

      targetPort: 8080

      nodePort: 30080

ClusterIP

Exposes the service internally within the cluster using a cluster-internal IP. The service is only accessible from within the cluster.

Here's an example (service-definition.yaml):

apiVersion: v1

kind: Service

metadata:

  name: my-clusterip-service

spec:

  type: ClusterIP

  selector:

    app: my-app

  ports:

    - protocol: TCP

      port: 80

      targetPort: 8080

LoadBalancer

Provides external access to the service using a cloud provider's load balancer. Here's an example (service-definition.yaml):

apiVersion: v1

kind: Service

metadata:

  name: my-loadbalancer-service

spec:

  type: LoadBalancer

  selector:

    app: my-app

  ports:

    - protocol: TCP

      port: 80

      targetPort: 8080

To create and run the services, you can use the following commands:

kubectl apply -f service-definition.yaml

kubectl get services

Key Differences Between the Services

The major difference between NodePort, ClusterIP, and LoadBalancer in Kubernetes lies in how they expose services and handle external access. Here's a breakdown of each:

  1. NodePort:

    • Exposes the service on a static port on each worker node in the cluster.

    • The service is accessible externally using the worker nodes' IP addresses and the assigned static port.

    • Traffic coming to any worker node on that port is forwarded to the corresponding service and its pods.

    • Suitable for development or testing environments, but not recommended for production scenarios with large-scale deployments.

  2. ClusterIP:

    • Provides a stable internal IP address for accessing the service within the cluster.

    • The service is only accessible from within the cluster and not from outside.

    • Traffic sent to the ClusterIP is load balanced to the pods belonging to the service.

    • Suitable for internal communication between different components within the cluster.

  3. LoadBalancer:

    • Exposes the service using an external load balancer provided by the cloud provider.

    • The load balancer distributes traffic to the service across multiple pods.

    • The load balancer obtains a public IP address that allows external clients to access the service.

    • Typically used in production environments to expose services to the external world.

Microservice Architecture

Say we want to deploy a sample-voting app using the microservices architecture. We'll assume that the necessary Docker images are available in a Docker repository. The app consists of:

  • Front-end web app for the voting interface.

  • Another front-end web app for viewing the voting results.

  • Redis database connected to the voting interface for recording votes.

  • PostgreSQL database connected to the result app to store the vote percentages.

  • Worker app that links Redis and PostgreSQL databases.

Here's an illustration of how you can define and deploy these components:

Voting Interface Deployment

Create a Deployment for the voting interface:

apiVersion: apps/v1

kind: Deployment

metadata:

  name: voting-interface

spec:

  replicas: 1

  selector:

    matchLabels:

      app: voting-interface

  template:

    metadata:

      labels:

        app: voting-interface

    spec:

      containers:

        - name: voting-interface

          image: dockerrepo/voting-interface:latest

          ports:

            - containerPort: 80

Voting Interface Service

Create a ClusterIP Service for the voting interface:

apiVersion: v1

kind: Service

metadata:

  name: voting-interface-service

spec:

  selector:

    app: voting-interface

  ports:

    - protocol: TCP

      port: 80

      targetPort: 80

Result App Deployment

Create a Deployment for the result app:

apiVersion: apps/v1

kind: Deployment

metadata:

  name: result-app

spec:

  replicas: 1

  selector:

    matchLabels:

      app: result-app

  template:

    metadata:

      labels:

        app: result-app

    spec:

      containers:

        - name: result-app

          image: dockerrepo/result-app:latest

          ports:

            - containerPort: 80

Result App Service

Create a ClusterIP Service for the result app:

apiVersion: v1

kind: Service

metadata:

  name: result-app-service

spec:

  selector:

    app: result-app

  ports:

    - protocol: TCP

      port: 80

      targetPort: 80

Redis Deployment

Create a Deployment for Redis:

apiVersion: apps/v1

kind: Deployment

metadata:

  name: redis-db

spec:

  replicas: 1

  selector:

    matchLabels:

      app: redis-db

  template:

    metadata:

      labels:

        app: redis-db

    spec:

      containers:

        - name: redis-db

          image: dockerrepo/redis-db:latest

          ports:

            - containerPort: 6379

PostgreSQL Deployment

Create a Deployment for PostgreSQL:

apiVersion: apps/v1

kind: Deployment

metadata:

  name: postgres-db

spec:

  replicas: 1

  selector:

    matchLabels:

      app: postgres-db

  template:

    metadata:

      labels:

        app: postgres-db

    spec:

      containers:

        - name: postgres-db

          image: dockerrepo/postgres-db:latest

          ports:

            - containerPort: 5432

Worker App Deployment

Create a Deployment for the worker app:

apiVersion: apps/v1

kind: Deployment

metadata:

  name: worker-app

spec:

  replicas: 1

  selector:

    matchLabels:

      app: worker-app

  template:

    metadata:

      labels:

        app: worker-app

    spec:

      containers:

        - name: worker-app

          image: dockerrepo/worker-app:latest

After creating the deployment and service definitions for each component, you can deploy them using the following commands:

kubectl apply -f voting-interface-deployment.yaml

kubectl apply -f voting-interface-service.yaml

kubectl apply -f result-app-deployment.yaml

kubectl apply -f result-app-service.yaml

kubectl apply -f redis-deployment.yaml

kubectl apply -f postgres-deployment.yaml

kubectl apply -f worker-app-deployment.yaml

This will deploy the sample-voting app using Kubernetes with the defined microservices architecture. You can access the voting interface and result app using their corresponding service endpoints.

Explanation

Here's how the components of the app connect as they come alive:

  1. Front-end Voting Interface:

    • The front-end voting interface is deployed as a Deployment and exposed through a Service.

    • When the voting interface pod comes alive, it registers with the Kubernetes Service and obtains an internal IP.

    • Other components can communicate with the voting interface by using the Service's internal IP and port.

  2. Result App:

    • The result app is also deployed as a Deployment and exposed through a Service.

    • When the result app pod comes alive, it registers with the Kubernetes Service and obtains an internal IP.

    • The result app can communicate with the voting interface through the voting interface's Service IP and port.

  3. Redis Database:

    • The Redis database, used to record the votes, is deployed as a Deployment.

    • The voting interface pod connects to the Redis database using the Redis database's DNS name or IP.

    • The DNS name or IP can be obtained by the voting interface pod through environment variables or configuration.

  4. PostgreSQL Database:

    • The PostgreSQL database, used to store the percentage of votes casted, is deployed as a Deployment.

    • The result app pod connects to the PostgreSQL database using the PostgreSQL database's DNS name or IP.

    • The DNS name or IP can be obtained by the result app pod through environment variables or configuration.

  5. Worker App:

    • The worker app, responsible for linking Redis and PostgreSQL databases, is deployed as a Deployment.

    • The worker app pod connects to both the Redis and PostgreSQL databases using their respective DNS names or IPs.

    • The DNS names or IPs can be obtained by the worker app pod through environment variables or configuration.

Key points

Kubernetes is a powerful container orchestration platform that simplifies the management of containerized applications. It uses various key components such as nodes, clusters, and the master node to deploy and manage applications. Pods, ReplicaSets, and Deployments are essential concepts in Kubernetes, enabling the deployment, scaling, and management of application replicas.

Networking in Kubernetes is facilitated through Services, which provide connectivity, load balancing, and service discovery within the cluster. Service types like NodePort, ClusterIP, and LoadBalancer offer different ways to expose and access services both internally and externally.

Microservice architecture in Kubernetes involves deploying individual components as separate microservices. These components can communicate with each other using Services and connect to databases and other services using DNS names, IPs, or environment variables.

Overall, Kubernetes simplifies the deployment, scaling, and management of applications, allowing for efficient utilization of resources and seamless orchestration of containerized workloads. With its robust features and flexibility, Kubernetes has become a popular choice for building and managing scalable, distributed systems.