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.
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!