Step-by-Step Guide to Deploying a Flask Application with PostgreSQL on OpenShift

Purpose of the guide: To help developers deploy a simple Flask application with a PostgreSQL database on OpenShift.

Step-by-Step Guide to Deploying a Flask Application with PostgreSQL on OpenShift

Introduction

OpenShift is a robust Kubernetes-based platform for deploying and managing containerized applications. In this guide, we’ll deploy a Python Flask application backed by a PostgreSQL database on OpenShift. By the end of this article, you'll have a fully functional web application running in a cloud-native environment.


Prerequisites

Before you begin, ensure the following:

  • Access to an OpenShift Cluster: Ensure you have the necessary permissions to create projects and deploy applications.

  • OpenShift CLI (oc) Installed and Configured: If you haven't installed it yet, follow the official installation guide.

  • Basic Understanding of Docker and Kubernetes Concepts: Familiarity with containers, images, pods, and services will be beneficial.


Step 1: Setting up the Flask Application

Let's start by setting up a simple Flask application that allows users to submit messages, which are then stored in a PostgreSQL database.

Directory Structure:

Organize your project files as follows:

flask-app/
├── app.py
├── Dockerfile
├── requirements.txt
└── templates/
    └── index.html

app.py:

from flask import Flask, render_template, request
import psycopg2

app = Flask(__name__)

# Database configuration
DB_HOST = "postgresql-service"
DB_NAME = "testdb"
DB_USER = "testuser"
DB_PASSWORD = "testpassword"

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/submit', methods=['POST'])
def submit():
    name = request.form['name']
    message = request.form['message']

    # Insert into the database
    try:
        conn = psycopg2.connect(
            host=DB_HOST, database=DB_NAME, user=DB_USER, password=DB_PASSWORD
        )
        cursor = conn.cursor()
        cursor.execute("INSERT INTO messages (name, message) VALUES (%s, %s)", (name, message))
        conn.commit()
        cursor.close()
        conn.close()
        return "Message submitted successfully!"
    except Exception as e:
        return f"Error: {e}"

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080)

Dockerfile:

FROM python:3.9-slim
WORKDIR /app
COPY . /app
RUN pip install -r requirements.txt
CMD ["python", "app.py"]

requirements.txt:

flask
psycopg2-binary

templates/index.html:

<!DOCTYPE html>
<html>
<head>
    <title>Flask 3-Tier App</title>
</head>
<body>
    <h1>Submit a Message</h1>
    <form action="/submit" method="POST">
        <label for="name">Name:</label><br>
        <input type="text" id="name" name="name" required><br><br>
        <label for="message">Message:</label><br>
        <textarea id="message" name="message" required></textarea><br><br>
        <button type="submit">Submit</button>
    </form>
</body>
</html>

Step 2: setup the Openshift

Login to OpenShift
use the Openshift CLI to log in to your cluster:

oc login --token=<your_token> --server=<your_server>

Create a new project
Organize your deployment into new project:

oc new-project flask-oc-demo

This command creates a new namespace in OpenShift, isolating your application's resources for better management.


Step 3: Building the Flask Application Image

Create an ImageStream
ImageStreams track container images:

oc create imagestream flask-oc-demo

Create a BuildConfig:
A BuildConfig defines how OpenShift builds your application image. We'll use the Docker strategy with a binary source, allowing us to upload local files directly.

Create a file named buildconfig.yml with the following content:

apiVersion: build.openshift.io/v1
kind: BuildConfig
metadata:
  name: flask-oc-demo
spec:
  source:
    type: Binary
  strategy:
    type: Docker
    dockerStrategy: {}
  output:
    to:
      kind: ImageStreamTag
      name: flask-oc-demo:latest

Apply the configuration:

oc apply -f buildconfig.yml

Start the build
Upload the source code and start the build:

oc start-build flask-oc-demo --from-dir=. --follow

This command packages your local source code and uses the specified Dockerfile to build the image within OpenShift.


Step 4: Deploying Postgresql on OpenShift

Deploy PostgreSQL Create a file named postgresql-deployment.yml with the following content:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgresql
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgresql
  template:
    metadata:
      labels:
        app: postgresql
    spec:
      containers:
      - name: postgresql
        image: postgres:latest
        ports:
        - containerPort: 5432
        env:
        - name: POSTGRES_USER
          value: "testuser"
        - name: POSTGRES_PASSWORD
          value: "testpassword"
        - name: POSTGRES_DB
          value: "testdb"
        volumeMounts:
        - name: postgres-storage
          mountPath: /var/lib/postgresql/data
      volumes:
      - name: postgres-storage
        emptyDir: {}

Create a file named postgresql-service.yml with the following content:

apiVersion: v1
kind: Service
metadata:
  name: postgresql-service
spec:
  selector:
    app: postgresql
  ports:
  - protocol: TCP
    port: 5432
    targetPort: 5432

Apply configurations

oc apply -f postgresql-deployment.yml
oc apply -f postgresql-service.yml

Resolving PostgreSQL Permission Issues

When deploying the PostgreSQL pod, you may encounter the following error:

chmod: changing permissions of '/var/lib/postgresql/data': Operation not permitted

This issue occurs because OpenShift enforces a restricted Security Context Constraint (SCC) by default, which prevents the container from running as root or modifying permissions.

Solution: Assign the anyuid SCC

To resolve this, assign the anyuid SCC to the default service account in your project:

oc adm policy add-scc-to-user anyuid -z default -n flask-oc-demo

Restart the PostgreSQL deployment:

oc rollout restart deployment/postgresql

Initialize the PostgreSQL Database

Log in to the PostgreSQL pod:

oc rsh <postgresql-pod-name>

Connect to the database:

psql -U testuser -d testdb

Create the messages table:

CREATE TABLE messages (
    id SERIAL PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    message TEXT NOT NULL
);

Verify the table creation:

\dt

Step 5: Deploying the Flask application

Create a file named flask_web.yml with the following content:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: flask-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: flask-app
  template:
    metadata:
      labels:
        app: flask-app
    spec:
      containers:
      - name: flask-app
        image: <imge stream tag>
        ports:
        - containerPort: 8080
        env:
        - name: DB_HOST
          value: "postgresql-service"
        - name: DB_NAME
          value: "testdb"
        - name: DB_USER
          value: "testuser"
        - name: DB_PASSWORD
          value: "testpassword"

---

apiVersion: v1
kind: Service
metadata:
  name: flask-app
spec:
  selector:
    app: flask-app
  ports:
  - protocol: TCP
    port: 8080
    targetPort: 8080

Deploy the Flask application:

oc apply -f flask_web.yml

Step 6: Expose and Access the Application

Expose the Flask application service to create a route:

oc expose svc/flask-app

check the route:

oc get routes

Access the application by visiting the route URL in your browser. You should see the Flask application running with the ability to submit messages stored in PostgreSQL.


Explanations

What is SCC?

Security Context Constraints (SCC) control the permissions under which pods run in OpenShift. The anyuid SCC allows containers to run as any user, including root, which is necessary for PostgreSQL in this setup. Learn more in the OpenShift SCC documentation.

What is an ImageStream?

ImageStreams in OpenShift allow you to manage and track images over time. This ensures consistency and enables automated updates for deployments when new images are pushed. More details are available here.

What is a BuildConfig?

A BuildConfig defines how OpenShift builds your application image. In this guide, we used:

  • Docker Strategy: Builds the image using a Dockerfile, providing full control over the build process.

  • Binary Build: Allows uploading local application files for the build process. Learn more about BuildConfigs here.


Conclusion

Congratulations! You have successfully deployed a Flask application backed by a PostgreSQL database on OpenShift. This guide demonstrated how to:

  • Set up a Flask application.

  • Build and deploy the application image using OpenShift.

  • Configure and troubleshoot PostgreSQL.

  • Expose and access the application via a route.

For next steps, consider:

  • Scaling the application for production workloads.

  • Automating builds and deployments with CI/CD pipelines.

  • Integrating monitoring tools like Prometheus and Grafana for observability.

Let me know if you have questions or suggestions in the comments below!