Initial commit of sk1 project

This commit is contained in:
Your Name 2025-05-06 18:39:31 +02:00
parent 1a77e8d933
commit d57236617c
13 changed files with 475 additions and 0 deletions

9
sk1/Dockerfile Normal file
View File

@ -0,0 +1,9 @@
FROM python:3.9-slim
WORKDIR /app
COPY app/ .
RUN pip install --no-cache-dir -r requirements.txt
CMD ["python", "main.py"]

155
sk1/README.md Normal file
View File

@ -0,0 +1,155 @@
# Flask + PostgreSQL Deployment on Azure Kubernetes Service (AKS)
This README documents all steps taken to deploy a Dockerized Flask + PostgreSQL application on Azure Kubernetes Service (AKS), including persistent storage, external access, and preparing for custom domain and HTTPS support.
---
## ✅ Project Summary
* Flask app with student registration and listing
* PostgreSQL used as backend DB
* Docker-based deployment
* Deployed on AKS with LoadBalancer exposure
---
## 📦 Step 1: Build and Push Docker Image
bash
# From project root with Dockerfile and app/ folder
cd ~/mypro/sk1
docker build -t flask-app:latest .
docker tag flask-app:latest 151204/flask-app:latest
docker login
docker push 151204/flask-app:latest
---
## ☁ Step 2: Create AKS Cluster
bash
az group create --name FlaskAKS --location eastus
az aks create \
--resource-group FlaskAKS \
--name FlaskCluster \
--node-count 1 \
--node-vm-size Standard_B2s \
--generate-ssh-keys
az aks get-credentials --resource-group FlaskAKS --name FlaskCluster
---
## 🧭 Step 3: Prepare Kubernetes Resources
Project structure:
/k8s
- namespace.yaml
- deployment.yaml # Flask app
- postgres-deployment.yaml
- service.yaml # Both flask + postgres services
- migrate-job.yaml # Optional DB initializer
### Apply Kubernetes Configs
bash
cd ~/mypro/sk1/k8s
kubectl apply -f namespace.yaml
kubectl apply -f postgres-deployment.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl apply -f migrate-job.yaml # optional
---
## 🧠 PostgreSQL Crash Fixes
*Issue:* lost+found inside mounted volume caused PostgreSQL to crash.
*Fix:*
* Mount PVC to /var/lib/postgresql/data
* Set PGDATA to /var/lib/postgresql/data/pgdata
*Final snippet:*
yaml
env:
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
volumeMounts:
- name: postgres-storage
mountPath: /var/lib/postgresql/data
Also:
bash
kubectl delete pvc postgres-pvc -n webapp-namespace
kubectl apply -f postgres-deployment.yaml
---
## 🚪 Step 4: Expose Flask App Publicly
bash
kubectl expose deployment flask-app \
--type=LoadBalancer \
--name=flask-loadbalancer \
--port=80 \
--target-port=8000 \
--namespace=webapp-namespace
Then:
bash
kubectl get service flask-loadbalancer -n webapp-namespace
Open the EXTERNAL-IP in your browser: http://<EXTERNAL-IP>/
---
## 🌐 Step 5: Install NGINX Ingress Controller
bash
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.9.4/deploy/static/provider/cloud/deploy.yaml
---
## 🧭 Next Steps (coming soon)
* Set up Ingress resource to route custom domain
* Add DNS A-record for hfzal.aliscloudwork.tech
* Issue HTTPS certificate with cert-manager + Let's Encrypt
---
## ✅ Status
* [x] Flask app running in AKS
* [x] PostgreSQL running with persistent volume
* [x] Public access via LoadBalancer
* [x] NGINX Ingress installed
* [ ] Custom domain mapping
* [ ] HTTPS with TLS cert
---
*Author:* hafzal03
*Platform:* Azure Kubernetes Service (AKS)
*Date:* 2025-05

81
sk1/app/main.py Normal file
View File

@ -0,0 +1,81 @@
from flask import Flask, request, jsonify, render_template_string
import psycopg2
import os
app = Flask(__name__)
def get_db_connection():
return psycopg2.connect(
host=os.environ.get("POSTGRES_HOST"),
database=os.environ.get("POSTGRES_DB"),
user=os.environ.get("POSTGRES_USER"),
password=os.environ.get("POSTGRES_PASSWORD"),
port=os.environ.get("POSTGRES_PORT", 5432),
)
def init_db():
conn = get_db_connection()
cur = conn.cursor()
cur.execute("""
CREATE TABLE IF NOT EXISTS students (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
room_number TEXT NOT NULL,
faculty TEXT NOT NULL
);
""")
conn.commit()
cur.close()
conn.close()
@app.route("/")
def index():
return "<h1>Welcome!</h1><p>Go to /add to add a student, /students to view all.</p>"
# HTML Form + POST Submission
@app.route("/add", methods=["GET", "POST"])
def add_student():
if request.method == "POST":
name = request.form.get("name")
room_number = request.form.get("room_number")
faculty = request.form.get("faculty")
if not name or not room_number or not faculty:
return "All fields are required!", 400
conn = get_db_connection()
cur = conn.cursor()
cur.execute("INSERT INTO students (name, room_number, faculty) VALUES (%s, %s, %s)", (name, room_number, faculty))
conn.commit()
cur.close()
conn.close()
return "<p>Student added successfully!</p><a href='/add'>Add another</a>"
# HTML form
return render_template_string("""
<h2>Add Student</h2>
<form method="post">
Name: <input name="name"><br>
Room Number: <input name="room_number"><br>
Faculty: <input name="faculty"><br>
<input type="submit" value="Add Student">
</form>
""")
# View all students
@app.route("/students", methods=["GET"])
def get_students():
conn = get_db_connection()
cur = conn.cursor()
cur.execute("SELECT id, name, room_number, faculty FROM students")
rows = cur.fetchall()
cur.close()
conn.close()
return jsonify([
{"id": row[0], "name": row[1], "room_number": row[2], "faculty": row[3]} for row in rows
])
if __name__ == "__main__":
init_db()
app.run(host="0.0.0.0", port=8000)

5
sk1/app/requirements.txt Normal file
View File

@ -0,0 +1,5 @@
Flask==2.0.1
Werkzeug==2.0.3
psycopg2-binary==2.9.1
python-dotenv==0.19.0
Jinja2==3.0.3

32
sk1/k8s/deployment.yaml Normal file
View File

@ -0,0 +1,32 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: flask-app
namespace: webapp-namespace
spec:
replicas: 1
selector:
matchLabels:
app: flask
template:
metadata:
labels:
app: flask
spec:
containers:
- name: flask
image: 151204/flask-app:latest
imagePullPolicy: Always
ports:
- containerPort: 8000
env:
- name: POSTGRES_HOST
value: postgres
- name: POSTGRES_DB
value: mydatabase
- name: POSTGRES_USER
value: myuser
- name: POSTGRES_PASSWORD
value: mypassword
- name: POSTGRES_PORT
value: "5432"

12
sk1/k8s/migrate-job.yaml Normal file
View File

@ -0,0 +1,12 @@
apiVersion: batch/v1
kind: Job
metadata:
name: migrate
spec:
template:
spec:
containers:
- name: migrator
image: myapp:latest
command: ["python", "manage.py", "migrate"]
restartPolicy: Never

4
sk1/k8s/namespace.yaml Normal file
View File

@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: webapp-namespace

View File

@ -0,0 +1,59 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
namespace: webapp-namespace
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
namespace: webapp-namespace
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:13
env:
- name: POSTGRES_DB
value: mydatabase
- name: POSTGRES_USER
value: myuser
- name: POSTGRES_PASSWORD
value: mypassword
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
ports:
- containerPort: 5432
volumeMounts:
- name: postgres-storage
mountPath: /var/lib/postgresql/data
volumes:
- name: postgres-storage
persistentVolumeClaim:
claimName: postgres-pvc
---
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: webapp-namespace
spec:
selector:
app: postgres
ports:
- port: 5432

View File

@ -0,0 +1,59 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
namespace: webapp-namespace
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
namespace: webapp-namespace
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:13
env:
- name: POSTGRES_DB
value: mydatabase
- name: POSTGRES_USER
value: myuser
- name: POSTGRES_PASSWORD
value: mypassword
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
ports:
- containerPort: 5432
volumeMounts:
- name: postgres-storage
mountPath: /var/lib/postgresql/data
volumes:
- name: postgres-storage
persistentVolumeClaim:
claimName: postgres-pvc
---
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: webapp-namespace
spec:
selector:
app: postgres
ports:
- port: 5432

13
sk1/k8s/service.yaml Normal file
View File

@ -0,0 +1,13 @@
apiVersion: v1
kind: Service
metadata:
name: flask-service
namespace: webapp-namespace
spec:
selector:
app: flask
type: NodePort
ports:
- port: 8000
targetPort: 8000
nodePort: 30080 # <-- Changed this

10
sk1/prepare-app.sh Executable file
View File

@ -0,0 +1,10 @@
#!/bin/bash
echo "Building Docker image for Flask app..."
docker build -t flask-app:latest .
echo "Creating Minikube image cache (if using Minikube)..."
eval $(minikube docker-env)
docker build -t flask-app:latest .
echo "Preparation complete."

18
sk1/start-app.sh Executable file
View File

@ -0,0 +1,18 @@
#!/bin/bash
echo "Creating Namespace..."
kubectl apply -f k8s/namespace.yaml
echo "Creating Persistent Volumes and StatefulSet..."
kubectl apply -f k8s/statefulset.yaml -n webapp-namespace
echo "Deploying PostgreSQL..."
kubectl apply -f k8s/postgres-deployment.yaml -n webapp-namespace
echo "Deploying Flask App..."
kubectl apply -f k8s/deployment.yaml -n webapp-namespace
echo "Creating Service..."
kubectl apply -f k8s/service.yaml -n webapp-namespace
echo "All resources have been applied."

18
sk1/stop-app.sh Executable file
View File

@ -0,0 +1,18 @@
#!/bin/bash
echo "Deleting Flask App Deployment..."
kubectl delete -f k8s/deployment.yaml -n webapp-namespace
echo "Deleting Flask Service..."
kubectl delete -f k8s/service.yaml -n webapp-namespace
echo "Deleting PostgreSQL Deployment and PVC..."
kubectl delete -f k8s/postgres-deployment.yaml -n webapp-namespace
echo "Deleting StatefulSet and PVs..."
kubectl delete -f k8s/statefulset.yaml -n webapp-namespace
echo "Deleting Namespace..."
kubectl delete -f k8s/namespace.yaml
echo "All resources have been deleted."