how-to-deploy-a-multi-container-application-using-kubernetes-and-helm.html

How to Deploy a Multi-Container Application Using Kubernetes and Helm

In the world of modern software development, containerization has become a game-changer. It allows developers to package applications and their dependencies into a single unit, ensuring consistency across different environments. Kubernetes, a powerful orchestration tool, takes this a step further by managing these containers at scale. Meanwhile, Helm simplifies the process of deploying applications on Kubernetes. In this article, we will delve deep into deploying a multi-container application using Kubernetes and Helm, offering step-by-step instructions, coding examples, and troubleshooting tips.

What Are Kubernetes and Helm?

Kubernetes

Kubernetes (often abbreviated as K8s) is an open-source platform designed to automate deploying, scaling, and managing containerized applications. With Kubernetes, you can manage clusters of containers across multiple hosts, facilitating high availability, load balancing, and scaling.

Helm

Helm is a package manager for Kubernetes that streamlines the deployment process. It allows you to define, install, and upgrade even the most complex Kubernetes applications using a simple command-line interface. Helm uses charts, which are packages of pre-configured Kubernetes resources.

Use Cases for Multi-Container Applications

Multi-container applications are prevalent in microservices architectures, where individual components can be developed, deployed, and scaled independently. Common use cases include:

  • Web Applications: Separating the front-end, back-end, and database components.
  • Data Processing: Running data ingestion, processing, and storage containers.
  • CI/CD Pipelines: Utilizing different containers for building, testing, and deploying applications.

Prerequisites

Before we begin, ensure you have the following set up:

  • Kubernetes Cluster: You can use Minikube for local development or a cloud provider like GKE, EKS, or AKS.
  • Helm: Install Helm on your local machine.
  • kubectl: The command-line tool for interacting with Kubernetes.

Step-by-Step Guide to Deploying a Multi-Container Application

Step 1: Create the Application Structure

Let’s consider a sample application that consists of a front-end service (using React), a back-end service (using Node.js), and a database (PostgreSQL).

Create the directory structure:

mkdir multi-container-app
cd multi-container-app
mkdir -p frontend backend db

Step 2: Write Dockerfiles for Each Component

Frontend (React)

In the frontend directory, create a Dockerfile:

# frontend/Dockerfile
FROM node:14 as build
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build

FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html

Backend (Node.js)

In the backend directory, create a Dockerfile:

# backend/Dockerfile
FROM node:14
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "server.js"]

Database (PostgreSQL)

No Dockerfile is required here; we can use the official PostgreSQL image directly in our Helm chart.

Step 3: Create Helm Chart

Now, let’s create a Helm chart for our multi-container application.

helm create multi-container-chart

This command creates a basic Helm chart structure. Navigate into the multi-container-chart directory.

Step 4: Customize the values.yaml

Edit values.yaml to define the necessary configurations for our services:

# multi-container-chart/values.yaml
replicaCount: 1

frontend:
  image:
    repository: yourusername/frontend
    tag: latest
  service:
    type: NodePort
    port: 80

backend:
  image:
    repository: yourusername/backend
    tag: latest
  service:
    type: NodePort
    port: 3000

db:
  image:
    repository: postgres
    tag: latest
  service:
    type: ClusterIP
    port: 5432

Step 5: Define Kubernetes Resources

Edit the templates/deployment.yaml to define Deployments for each of your services:

# multi-container-chart/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.frontend.service.name }}
spec:
  replicas: {{ .Values.replicaCount }}
  template:
    metadata:
      labels:
        app: {{ .Values.frontend.service.name }}
    spec:
      containers:
        - name: frontend
          image: "{{ .Values.frontend.image.repository }}:{{ .Values.frontend.image.tag }}"
          ports:
            - containerPort: 80

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.backend.service.name }}
spec:
  replicas: {{ .Values.replicaCount }}
  template:
    metadata:
      labels:
        app: {{ .Values.backend.service.name }}
    spec:
      containers:
        - name: backend
          image: "{{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag }}"
          ports:
            - containerPort: 3000

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.db.image.repository }}
spec:
  replicas: {{ .Values.replicaCount }}
  template:
    metadata:
      labels:
        app: {{ .Values.db.image.repository }}
    spec:
      containers:
        - name: db
          image: "{{ .Values.db.image.repository }}:{{ .Values.db.image.tag }}"
          ports:
            - containerPort: 5432
          env:
            - name: POSTGRES_DB
              value: "mydb"
            - name: POSTGRES_USER
              value: "user"
            - name: POSTGRES_PASSWORD
              value: "password"

Step 6: Create Services

Define services for each component in templates/service.yaml:

# multi-container-chart/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: {{ .Values.frontend.service.name }}
spec:
  type: {{ .Values.frontend.service.type }}
  ports:
    - port: {{ .Values.frontend.service.port }}
  selector:
    app: {{ .Values.frontend.service.name }}

---

apiVersion: v1
kind: Service
metadata:
  name: {{ .Values.backend.service.name }}
spec:
  type: {{ .Values.backend.service.type }}
  ports:
    - port: {{ .Values.backend.service.port }}
  selector:
    app: {{ .Values.backend.service.name }}

---

apiVersion: v1
kind: Service
metadata:
  name: {{ .Values.db.image.repository }}
spec:
  type: {{ .Values.db.service.type }}
  ports:
    - port: {{ .Values.db.service.port }}
  selector:
    app: {{ .Values.db.image.repository }}

Step 7: Deploy the Application

With everything in place, it’s time to deploy the application using Helm. Run the following command from the root of your Helm chart:

helm install my-multi-container-app ./multi-container-chart

Step 8: Verify the Deployment

Check the status of your deployment with:

kubectl get pods
kubectl get services

Troubleshooting Tips

  • Check Container Logs: If a pod fails to start, check the logs with: bash kubectl logs <pod-name>
  • Describe Pods: Get more detailed information about a pod: bash kubectl describe pod <pod-name>
  • Helm Rollback: If something goes wrong, you can roll back to a previous release with: bash helm rollback my-multi-container-app <revision-number>

Conclusion

Deploying a multi-container application using Kubernetes and Helm significantly simplifies the management of complex applications. By leveraging the power of Kubernetes for orchestration and Helm for package management, you can efficiently manage deployments, scale services, and troubleshoot issues. As you explore this setup further, you'll find that it not only enhances your development workflow but also boosts your application's reliability and performance. Happy coding!

SR
Syed
Rizwan

About the Author

Syed Rizwan is a Machine Learning Engineer with 5 years of experience in AI, IoT, and Industrial Automation.