new z2 and old z2-failed

This commit is contained in:
Muhammed Tariq Ali Razvi 2025-04-12 21:53:56 +02:00
parent f16847f1fd
commit 52444ad27d
33 changed files with 502 additions and 110 deletions

69
z2-failed/README.md Normal file
View File

@ -0,0 +1,69 @@
# Visitor Counter Application
## Description of the Application
This application is designed to count the number of visits to a webpage. It utilizes Flask for the backend, Apache for serving static web content, and Redis for storing visitor counts.
## List of Containers Used
- **Backend Container**: Built using Flask, this container handles API requests and updates visitor counts in Redis.
- **Web Container**: Built using Apache, this container serves static HTML content.
- **Redis Container**: Uses the official Redis image to store visitor counts persistently.
## List of Kubernetes Objects
- **Deployments**:
- **Backend Deployment**: Manages the backend Flask application.
- **Web Deployment**: Manages the web Apache server.
- **Services**:
- **Backend Service**: Exposes the backend application on a specific port.
- **Web Service**: Exposes the web server on a specific port.
- **Redis Service**: Provides access to the Redis database.
- **StatefulSet**:
- **Redis StatefulSet**: Ensures persistent storage for Redis data.
- **PersistentVolumeClaim (PVC)** and **PersistentVolume (PV)**:
- Used for providing persistent storage to Redis.
## Virtual Networks and Named Volumes
- **Virtual Networks**: Kubernetes automatically manages pod-to-pod communication within a cluster.
- **Named Volumes**: Used for persistent storage in Redis, ensuring data is retained across pod restarts.
## Container Configuration
- **Backend Container**: Configured to listen on port 5000 and connect to Redis for storing visitor counts.
- **Web Container**: Configured to serve static HTML content on port 80.
- **Redis Container**: Configured to store data persistently using a PersistentVolumeClaim.
## Instructions to Prepare, Run, Pause, and Delete the Application
1. **Prepare the Application**:
Run the `prepare-app.sh` script to build Docker images and load them into Minikube.
./prepare-app.sh
2. **Run the Application**:
Run the `start-app.sh` script to create all necessary Kubernetes objects and start the application.
./start-app.sh
3. **Pause the Application**:
You can pause the application by scaling down deployments to zero replicas:
kubectl scale deployment backend-deployment --replicas=0
kubectl scale deployment web-deployment --replicas=0
4. **Delete the Application**:
Run the `stop-app.sh` script to delete all Kubernetes objects and stop the application.
./stop-app.sh
## Instructions to View the Application on the Web
1. **Access the Web Service**:
Use Minikube to access the web service:
minikube service web-service --url
This will output a URL that you can use to view the application in your web browser by ctrl+clicking on the link.

22
z2-failed/backend/app.py Normal file
View File

@ -0,0 +1,22 @@
from flask import Flask, jsonify
from flask_cors import CORS
import redis
app = Flask(__name__)
CORS(app)
# Connect to Redis (running in a separate container and foldr)
redis_client = redis.StrictRedis(host='redis-service', port=6379, decode_responses=True)
@app.route('/counter', methods=['GET'])
def counter():
try:
visits = redis_client.incr('visits')
print(f"[INFO] Visit count: {visits}")
return jsonify({"visits": visits})
except Exception as e:
print(f"[ERROR] Redis connection failed: {e}")
return jsonify({"error": "Could not connect to Redis"}), 500
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=80)

View File

@ -2,6 +2,7 @@ apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-deployment
namespace: visitor-app
spec:
replicas: 1
selector:

4
z2-failed/namespace.yaml Normal file
View File

@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: visitor-app

9
z2-failed/prepare-app.sh Executable file
View File

@ -0,0 +1,9 @@
#!/bin/bash
# build Docker images for backend and web
docker build -t backend:latest ./backend
docker build -t web:latest ./web
# load images into minikube
minikube image load backend:latest
minikube image load web:latest

View File

@ -2,6 +2,7 @@ apiVersion: v1
kind: Service
metadata:
name: redis-service
namespace: visitor-app
spec:
selector:
app: redis-app # This must match the pod label in StatefulSet.

View File

@ -2,6 +2,7 @@ apiVersion: v1
kind: Service
metadata:
name: backend-service
namespace: visitor-app
spec:
selector:
app: backend-app

18
z2-failed/start-app.sh Executable file
View File

@ -0,0 +1,18 @@
#!/bin/bash
# start minikube
minikube start
#create namespace
kubectl apply -f namespace.yaml
# apply all Kubernetes YAML files
kubectl apply -f deployment.yaml -n visitor-app
kubectl apply -f web-deployment.yaml -n visitor-app
kubectl apply -f statefulset.yaml -n visitor-app
kubectl apply -f service.yaml -n visitor-app
kubectl apply -f web-service.yaml -n visitor-app
kubectl apply -f redis-service.yaml -n visitor-app
# Output web service URL
#minikube service web-service --url

View File

@ -2,6 +2,7 @@ apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis-statefulset
namespace: visitor-app
spec:
serviceName: "redis-service"
replicas: 1

7
z2-failed/stop-app.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
# delete the whole namespace since it will deleat all resoruces inside it
kubectl delete namespace visitor-app
# stop minikube
minikube stop

View File

@ -2,6 +2,7 @@ apiVersion: apps/v1
kind: Deployment
metadata:
name: web-deployment
namespace: visitor-app
spec:
replicas: 1
selector:

View File

@ -2,6 +2,7 @@ apiVersion: v1
kind: Service
metadata:
name: web-service
namespace: visitor-app
spec:
selector:
app: web-app

20
z2-failed/web/Dockerfile Normal file
View File

@ -0,0 +1,20 @@
FROM httpd:2.4
# Enable mod_proxy and mod_proxy_http
RUN sed -i '/^#LoadModule proxy_module/s/^#//' /usr/local/apache2/conf/httpd.conf && \
sed -i '/^#LoadModule proxy_http_module/s/^#//' /usr/local/apache2/conf/httpd.conf
# Copy the HTML file
COPY index.html /usr/local/apache2/htdocs/
# Add a proxy configuration for /counter
#RUN echo "ProxyPass /counter http://backend-service:5000/counter" >> /usr/local/apache2/conf/httpd.conf && \
# echo "ProxyPassReverse /counter http://backend-service:5000/counter" >> /usr/local/apache2/conf/httpd.conf
#Add a proxy configuration for /counter
RUN echo "ProxyPass /counter http://backend-service.visitor-app.svc.cluster.local:5000/counter" >> /usr/local/apache2/conf/httpd.conf && \
echo "ProxyPassReverse /counter http://backend-service.visitor-app.svc.cluster.local:5000/counter" >> /usr/local/apache2/conf/httpd.conf
EXPOSE 80

View File

@ -8,7 +8,7 @@
<body>
<h1>Welcome to the Visitor Counter App!</h1>
<h2>Made by ALI</h2>
<h3>Also made for assinment 1</h3>
<h3>Also copied from assinemnt 1 to make assinment 2</h3>
<p>This page has been visited <span id="counter">0</span> times.</p>
<script>

View File

@ -1,69 +1,105 @@
# Visitor Counter Application
# Web Form Application with PostgreSQL on Kubernetes
## Description of the Application
This application is designed to count the number of visits to a webpage. It utilizes Flask for the backend, Apache for serving static web content, and Redis for storing visitor counts.
## 📌 Application Description
## List of Containers Used
This project is a simple web application built with Flask that provides a form for user input. The form collects the following data:
- Name
- Middle name
- Surname
- Age
- **Backend Container**: Built using Flask, this container handles API requests and updates visitor counts in Redis.
- **Web Container**: Built using Apache, this container serves static HTML content.
- **Redis Container**: Uses the official Redis image to store visitor counts persistently.
Upon submission, the data is stored in a PostgreSQL database. A confirmation message is displayed once the data is successfully inserted.
## List of Kubernetes Objects
---
- **Deployments**:
- **Backend Deployment**: Manages the backend Flask application.
- **Web Deployment**: Manages the web Apache server.
- **Services**:
- **Backend Service**: Exposes the backend application on a specific port.
- **Web Service**: Exposes the web server on a specific port.
- **Redis Service**: Provides access to the Redis database.
- **StatefulSet**:
- **Redis StatefulSet**: Ensures persistent storage for Redis data.
- **PersistentVolumeClaim (PVC)** and **PersistentVolume (PV)**:
- Used for providing persistent storage to Redis.
## 🐳 Containers Used
## Virtual Networks and Named Volumes
1. **web-app**
- A Python Flask-based container serving the HTML form and handling form submission.
- Connects to the PostgreSQL container using environment variables.
- **Virtual Networks**: Kubernetes automatically manages pod-to-pod communication within a cluster.
- **Named Volumes**: Used for persistent storage in Redis, ensuring data is retained across pod restarts.
2. **postgres**
- Official PostgreSQL container used as the backend database.
- Uses a named volume to persist data.
- Initializes the database and the `users` table using a custom `init.sql` script.
## Container Configuration
---
- **Backend Container**: Configured to listen on port 5000 and connect to Redis for storing visitor counts.
- **Web Container**: Configured to serve static HTML content on port 80.
- **Redis Container**: Configured to store data persistently using a PersistentVolumeClaim.
## 🕸 Kubernetes Objects
## Instructions to Prepare, Run, Pause, and Delete the Application
| Object | Description |
|--------------------|-----------------------------------------------------------------------------|
| `Namespace` | Isolates the application under `webform-app`. |
| `Deployment` | Manages the Flask web app, ensuring high availability. |
| `StatefulSet` | Manages the PostgreSQL database pod with persistent identity and storage. |
| `Service` | Exposes both the web app and PostgreSQL internally via DNS. |
| `PersistentVolume` | Provides physical storage for PostgreSQL data. |
| `PersistentVolumeClaim` | Requests storage for the StatefulSet. |
1. **Prepare the Application**:
Run the `prepare-app.sh` script to build Docker images and load them into Minikube.
---
./prepare-app.sh
## 🌐 Virtual Networks & Volumes
- **Virtual Networking**: Kubernetes Services provide internal DNS-based networking. The Flask app communicates with PostgreSQL using the service name `postgres` within the same namespace.
- **Named Volumes**:
- A named volume is mounted at `/var/lib/postgresql/data` in the PostgreSQL container to persist database data across pod restarts.
---
## ⚙️ Container Configuration
- Flask container:
- Dockerized using `python:3.11-slim` as the base image.
- `flask` and `psycopg2-binary` installed.
- `app.py` and `index.html` are copied into the container.
- Uses port `5000`.
- PostgreSQL container:
- Uses `docker-entrypoint-initdb.d/init.sql` to initialize the database (`userdb`) and create the `users` table on first run.
- Uses environment variables for default credentials and database name.
---
2. **Run the Application**:
Run the `start-app.sh` script to create all necessary Kubernetes objects and start the application.
./start-app.sh
## 🛠️ Instructions
### ✅ Prepare the Application
```bash
# Create namespace
kubectl apply -f kubi/namespace.yaml
# Create persistent volume and claim
kubectl apply -f kubi/pv.yaml
kubectl apply -f kubi/pvc.yaml
# Deploy PostgreSQL StatefulSet and Service
kubectl apply -f kubi/postgres-statefulset.yaml
kubectl apply -f kubi/postgres-service.yaml
# Deploy Flask web app and Service
kubectl apply -f kubi/webapp-deployment.yaml
kubectl apply -f kubi/webapp-service.yaml
```
3. **Pause the Application**:
You can pause the application by scaling down deployments to zero replicas:
# Run the Application
kubectl get pods -n webform-app
kubectl scale deployment backend-deployment --replicas=0
kubectl scale deployment web-deployment --replicas=0
# You can pause the app using:
kubectl scale deployment web-app --replicas=0 -n webform-app
# Delete the app
kubectl delete -f kubi/ --recursive
# to view the app in browser
# find the NodePort exposed by the web app
kubectl get svc -n webform-app
# Then open broswerr and go to
http://<your-node-ip>:<node-port>
# Since its minikube use
minikube service web-app-service -n webform-app
4. **Delete the Application**:
Run the `stop-app.sh` script to delete all Kubernetes objects and stop the application.
./stop-app.sh
## Instructions to View the Application on the Web
1. **Access the Web Service**:
Use Minikube to access the web service:
minikube service web-service --url
This will output a URL that you can use to view the application in your web browser by ctrl+clicking on the link.

15
z2/app/Dockerfile Normal file
View File

@ -0,0 +1,15 @@
FROM python:3.11-slim
WORKDIR /app
# Install dependencies directly
RUN pip install flask psycopg2-binary
#checking flask version
RUN python -c "import flask; print(flask.__version__)"
#copy app files
COPY app.py .
COPY index.html .
# start app
CMD ["python3", "app.py"]

60
z2/app/app.py Normal file
View File

@ -0,0 +1,60 @@
from flask import Flask, request, render_template
import psycopg2
import os
app = Flask(__name__, template_folder=".")
# This function runs before the first request to create the users table if it doesn't exist
#@app.before_first_request
def init_db():
conn = psycopg2.connect(
host=os.environ.get("DB_HOST", "db-service"),
database=os.environ.get("DB_NAME", "userdb"),
user=os.environ.get("DB_USER", "user"),
password=os.environ.get("DB_PASS", "pass")
)
cur = conn.cursor()
# Ensure the users table exists
cur.execute("""
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name TEXT,
surname TEXT,
middlename TEXT,
age INT
);
""")
conn.commit()
cur.close()
conn.close()
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
name = request.form['name']
surname = request.form['surname']
middlename = request.form['middlename']
age = request.form['age']
conn = psycopg2.connect(
host=os.environ.get("DB_HOST", "db-service"),
database=os.environ.get("DB_NAME", "userdb"),
user=os.environ.get("DB_USER", "user"),
password=os.environ.get("DB_PASS", "pass")
)
cur = conn.cursor()
cur.execute("INSERT INTO users (name, surname, middlename, age) VALUES (%s, %s, %s, %s)",
(name, surname, middlename, age))
conn.commit()
cur.close()
conn.close()
return f"Hi I am {name}, my middle name is {middlename}, and surname is {surname}, who's {age} years old."
return render_template("index.html")
if __name__ == "__main__":
app.run(host='0.0.0.0', port=5000)

18
z2/app/index.html Normal file
View File

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<title>User Form</title>
</head>
<body>
<h2>Enter Your Details</h2>
<form method="POST">
Name: <input name="name"><br>
Surname: <input name="surname"><br>
middle Name: <input name="middlename"><br>
Age: <input name="age"><br>
<input type="submit" value="Submit">
</form>
</body>
</html>

View File

@ -1,18 +0,0 @@
from flask import Flask, jsonify
from flask_cors import CORS
import redis
app = Flask(__name__)
CORS(app)
# Connect to Redis (running in a separate container and foldr)
redis_client = redis.StrictRedis(host='redis', port=6379, decode_responses=True)
@app.route('/counter', methods=['GET'])
def counter():
# Increment visitor count in Redis
visits = redis_client.incr('visits')
return jsonify({"visits": visits})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)

9
z2/db/init.sql Normal file
View File

@ -0,0 +1,9 @@
-- Create the users tabl
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name TEXT,
surname TEXT,
middlename TEXT,
age INT
);

10
z2/init.sql Normal file
View File

@ -0,0 +1,10 @@
CREATE DATABASE userdb;
\c userdb;
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT,
surname TEXT,
middlename TEXT,
age INT
);

31
z2/kubi/deployment.yaml Normal file
View File

@ -0,0 +1,31 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
namespace: webform-app
spec:
replicas: 1
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: web-app
image: web-app:latest
imagePullPolicy: Never
ports:
- containerPort: 5000
env:
- name: DB_HOST
value: db-service
- name: DB_NAME
value: userdb
- name: DB_USER
value: user
- name: DB_PASS
value: pass

4
z2/kubi/namespace.yaml Normal file
View File

@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: webform-app

25
z2/kubi/service.yaml Normal file
View File

@ -0,0 +1,25 @@
apiVersion: v1
kind: Service
metadata:
name: web-app
namespace: webform-app
spec:
selector:
app: web-app
type: NodePort
ports:
- port: 5000
targetPort: 5000
nodePort: 30008
---
apiVersion: v1
kind: Service
metadata:
name: db-service
namespace: webform-app
spec:
selector:
app: db
ports:
- port: 5432
targetPort: 5432

74
z2/kubi/statefulset.yaml Normal file
View File

@ -0,0 +1,74 @@
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: db-pv
namespace: webform-app
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/data/db"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: db-pvc
namespace: webform-app
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
namespace: webform-app
spec:
selector:
matchLabels:
app: db
serviceName: "db-service"
replicas: 1
template:
metadata:
labels:
app: db
spec:
containers:
- name: postgres
image: postgres:14
ports:
- containerPort: 5432
env:
- name: POSTGRES_DB
value: userdb
- name: POSTGRES_USER
value: user
- name: POSTGRES_PASSWORD
value: pass
volumeMounts:
- name: db-storage
mountPath: /var/lib/postgresql/data
- name: db-init
mountPath: /docker-entrypoint-initdb.d
volumes:
- name: db-init
configMap:
name: db-init-script
volumeClaimTemplates:
- metadata:
name: db-storage
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi

View File

@ -1,9 +1,2 @@
#!/bin/bash
# build Docker images for backend and web
docker build -t backend:latest ./backend
docker build -t web:latest ./web
# load images into minikube
minikube image load backend:latest
minikube image load web:latest
docker build -t web-app:latest ./app

View File

@ -1,15 +1,9 @@
#!/bin/bash
kubectl apply -f kubi/namespace.yaml
kubectl apply -f kubi/statefulset.yaml
kubectl create configmap db-init-script --from-file=db/init.sql -n webform-app
kubectl apply -f kubi/deployment.yaml
kubectl apply -f kubi/service.yaml
# start minikube
minikube start
# apply all Kubernetes YAML files
kubectl apply -f backend-deployment.yaml
kubectl apply -f web-deployment.yaml
kubectl apply -f statefulset.yaml
kubectl apply -f backend-service.yaml
kubectl apply -f web-service.yaml
kubectl apply -f redis-service.yaml
# Output web service URL
minikube service web-service --url
# Get url
minikube service web-app -n webform-app --url

View File

@ -1,12 +1,9 @@
#!/bin/bash
kubectl delete -f kubi/service.yaml
kubectl delete -f kubi/deployment.yaml
kubectl delete -f kubi/statefulset.yaml
kubectl delete configmap db-init-script -n webform-app
kubectl delete -f kubi/namespace.yaml
# delete all Kubernetes objects
kubectl delete -f backend-deployment.yaml
kubectl delete -f web-deployment.yaml
kubectl delete -f statefulset.yaml
kubectl delete -f backend-service.yaml
kubectl delete -f web-service.yaml
kubectl delete -f redis-service.yaml
# stop minikube
# stop the minikube
minikube stop

View File

@ -1,11 +0,0 @@
FROM httpd:2.4
# Copies your static HTML file into the Apache document root
COPY index.html /usr/local/apache2/htdocs/
# Add a proxy configuration for /counter
RUN echo "ProxyPass /counter http://backend-service:5000/counter" >> /usr/local/apache2/conf/httpd.conf
RUN echo "ProxyPassReverse /counter http://backend-service:5000/counter" >> /usr/local/apache2/conf/httpd.conf
EXPOSE 80