How to Deploy Flask, React & PostgreSQL via Helm

Introduction

This guide walks you through how to deploy a Two-Tier App with Flask, React, PostgreSQL, and Helm on a local Kubernetes cluster. By the end, you’ll have a fully functional setup, leveraging Helm’s simplicity for Kubernetes resource management This deployment will take place on a local Kubernetes cluster created with Kind (Kubernetes in Docker) on an Ubuntu 20.04 desktop. The project demonstrates managing backend and frontend configurations, handling secrets, and deploying and migrating databases.

We’ll cover the environment setup, application workflow, deployment process, and necessary Kubernetes and Helm configurations to ensure seamless functionality.

Environment Setup

  • Ubuntu 20.04 Desktop: Ensure you have a compatible development environment with Docker, Kubernetes (via Kind), and Helm installed.
  • Docker: We’ll use Docker to containerize the application and push images to a registry.
  • Kind (Kubernetes in Docker): Kind will manage the local Kubernetes cluster.
  • Helm: We’ll create Helm charts for easy, reusable Kubernetes deployments.

Prerequisites

  • Docker: Ensure Docker is installed and running.
  • Kind: Install Kind for a local Kubernetes environment.
  • Helm: Install Helm to simplify Kubernetes deployments

Application Workflow

Reactjs frontend Dockerisation

# Dockerfile for Flask Backend
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["flask", "run", "--host=0.0.0.0"]

The backend code is organized in the k8s/flask-backend folder of the repository https://github.com/rjshk013/medium-blog/.

Steps to Clone and Prepare the Backend Code

Clone the Repository: First, let’s clone the repository to access the backend code locally. Open a terminal on your local machine and run:

Dockerize the Backend: Inside the swagger-backend folder, you’ll find a Dockerfile configured to build the Flask application. This Dockerfile will handle dependencies and setup, ready for deployment to Kubernetes.

2. React Frontend Dockerfile

# Dockerfile for React Frontend
FROM node:16-alpine as build
WORKDIR /app
COPY package.json ./
RUN npm install
COPY . .
RUN npm run build
# Serve with NGINX
FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html
COPY ./public/env.template.js /usr/share/nginx/html/env.template.js
COPY ./entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
CMD ["/entrypoint.sh"]

The frontend application code is maintained in the k8s/reactjs-frontend folder of the GitHub repository.
The frontend’s Dockerfile is set up to build the React application and serve it via NGINX. The env.template.js file enables dynamic configuration of the backend URL at runtime.

3. Database Initialization

Initialize the database schema and create required tables and admin user data through a migration command.

Pushing Images to Docker Registry

After building Docker images, push them to a container registry (or use a local registry if preferred):

# Navigate to the backend folder
cd medium-blog/k8s/flask-backend
docker build -t your-username/flask-backend:latest .
docker push your-username/flask-backend:latest

# Navigate to the frontend folder
cd medium-blog/k8s/reactjs-frontend
docker build -t your-username/react-frontend:latest .
docker push your-username/react-frontend:latest
Helm Chart for Application Deployment

The Helm chart for this two-tier application is available in the GitHub repository (https://github.com/rjshk013/medium-blog/) under k8s/helmchart-twotierapp. This Helm chart is specifically configured to deploy both the frontend and backend components. Moreover, it efficiently manages environment variables and ensures the creation of necessary Kubernetes resources for a smooth and seamless deployment.

1. Chart Structure

The Helm chart directory structure:

├── Chart.yaml
├── charts
├── templates
│   ├── NOTES.txt
│   ├── _helpers.tpl
│   ├── deployment.yaml
│   ├── ingress.yaml
│   ├── postgres-deployment.yaml
│   ├── postgres-secrets.yaml
│   ├── postgres-service.yaml
│   ├── service.yaml
│   ├── serviceaccount.yaml
│   └── tests
│       └── test-connection.yaml
├── test-pod.yaml
└── values.yaml
2. values.yaml

This file contains customizable configurations:

replicaCount: 1

image:
  repository: rjshk013/react-frontend-app
  tag: latest
  pullPolicy: IfNotPresent

env:  # Move env to the top level
  REACT_APP_BACKEND_URL: "http://172.18.0.2:32001"

backend:
  repository: rjshk013/swagger-backend
  tag: latest
  env:
    POSTGRES_HOST: "mypostgres-service"
    POSTGRES_PORT: "5432"


postgresql:
  image: postgres:latest  # Official PostgreSQL image
  
# Define secret values for PostgreSQL credentials
secrets:
  postgres:
    POSTGRES_USER: "postgres"
    POSTGRES_PASSWORD: "admin"
    POSTGRES_DB: "postgres"
service:
  type: NodePort
  frontendPort: 80
  backendPort: 5000
  nodePort: 32000
  backendNodePort: 32001
  dbPort: 5432
serviceAccount:
  create: true          # Set to false if you don't need a ServiceAccount created
  name: ""              # Name of the ServiceAccount (if empty, Helm will use the default name)
  annotations: {} 

ingress:
  enabled: false       # Set to true if you want to enable ingress
  annotations: {}      # You can add any annotations required for the ingress
  hosts:
    - host: chart-example.local
      paths:
        - path: /
          pathType: ImplementationSpecific
  tls: []              # TLS configuration (optional)
resources:
  limits:
    memory: "256Mi"
    cpu: "500m"
  requests:
    memory: "128Mi"
    cpu: "250m"

Expose the Backend via NodePort

This exposes the backend service on a specific port of each Kubernetes node. You can access it through the node’s IP and assigned port, which is externally reachable.

3. deployment.yaml Template

The deployment.yaml manages backend and frontend pods:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-frontend
  labels:
    app: {{ .Release.Name }}-frontend
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ .Release.Name }}-frontend
  template:
    metadata:
      labels:
        app: {{ .Release.Name }}-frontend
    spec:   
      containers:
        - name: myfrontend-app
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          ports:
            - containerPort: {{ .Values.service.frontendPort }}
          env:  # Ensure `env` is under the container spec
            - name: REACT_APP_BACKEND_URL
              value: "{{ .Values.env.REACT_APP_BACKEND_URL }}"         
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-backend
  labels:
    app: {{ .Release.Name }}-backend
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ .Release.Name }}-backend
  template:
    metadata:
      labels:
        app: {{ .Release.Name }}-backend
    spec:
      nodeSelector:
        role: backend-db-node      
      containers:
        - name: mybackend-app
          image: "{{ .Values.backend.repository }}:{{ .Values.backend.tag }}"
          ports:
            - containerPort: {{ .Values.service.backendPort }}
          env:
            - name: POSTGRES_USER
              valueFrom:
                secretKeyRef:
                  name: postgres-secrets
                  key: POSTGRES_USER
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: postgres-secrets
                  key: POSTGRES_PASSWORD
            - name: POSTGRES_HOST
              value: "{{ .Values.backend.env.POSTGRES_HOST }}"
            - name: POSTGRES_PORT
              value: "{{ .Values.backend.env.POSTGRES_PORT }}"
            - name: POSTGRES_DB
              valueFrom:
                secretKeyRef:
                  name: postgres-secrets
                  key: POSTGRES_DB
Managing Environment Variables with Kubernetes Secrets

Creating Secrets for PostgreSQL Credentials

apiVersion: v1
kind: Secret
metadata:
name: postgres-secrets
labels:
app: {{ .Release.Name }}-db
type: Opaque
data:
POSTGRES_USER: {{ .Values.secrets.postgres.POSTGRES_USER | b64enc }}
POSTGRES_PASSWORD: {{ .Values.secrets.postgres.POSTGRES_PASSWORD | b64enc }}
POSTGRES_DB: {{ .Values.secrets.postgres.POSTGRES_DB | b64enc }}

Install the Helm Chart: With the values.yaml configured, install the application to your Kubernetes cluster:

helm install twotierapp ./ -n two-tierapp

Check the pods are created & running

Post-Deployment: After deployment, remember to run database migrations to set up tables and insert initial data for the backend.

flask db init<br>flask db migrate -m "Initial migration for User model"<br>flask db upgrade<br>flask insert_admin_user

Additionally, check that the Kubernetes services are properly exposed using NodePort, as this will allow external access to the application components

Accessing the Frontend Application

Open a browser and navigate to the NodePort of the frontend application. If you used a NodePort service for the frontend, you can find it from the kubectl get svc command

For example, if the frontend NodePort is exposed on http://<node-ip>:<nodeport>, enter this URL in your browser.In my case it is http://172.18.0.2:32000

Login with Admin Credentials:

  • On the login page, use the credentials below to log in:
  • Username: admin
  • Password: admin
  • After successful login, you will be directed to the welcome dashboard.

Inspecting Frontend-Backend Communication:

  • To verify the frontend is properly communicating with the backend, open your browser’s Developer Tools (usually accessible with F12 or Ctrl+Shift+I).
  • Go to the Network tab, then refresh the page or perform login actions.
  • Look for requests to the backend URL (e.g., http://<backend-node-ip>:<backend-nodeport>).
  • Confirm that the responses are 200 OK or similar, indicating successful communication with the backend service.

Conclusion

This guide walks you through deploying a two-tier application on a local Kubernetes cluster using Kind and Helm, managing environment variables securely with Kubernetes secrets, and configuring runtime flexibility for frontend applications. With this setup, you have a foundation for scalable, containerized deployments and flexible configurations across environments.

One thought on “How to Deploy Flask, React & PostgreSQL via Helm

Leave a Comment

Your email address will not be published. Required fields are marked *

Stay up to date with our blogs.

Subscribe to receive email notifications for new blog posts.