Initial commit of sk1 project
This commit is contained in:
parent
1a77e8d933
commit
d57236617c
9
sk1/Dockerfile
Normal file
9
sk1/Dockerfile
Normal 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
155
sk1/README.md
Normal 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
81
sk1/app/main.py
Normal 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
5
sk1/app/requirements.txt
Normal 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
32
sk1/k8s/deployment.yaml
Normal 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
12
sk1/k8s/migrate-job.yaml
Normal 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
4
sk1/k8s/namespace.yaml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: webapp-namespace
|
59
sk1/k8s/postgres-deployment.yaml
Normal file
59
sk1/k8s/postgres-deployment.yaml
Normal 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
|
59
sk1/k8s/postgres-deployment.yaml4
Normal file
59
sk1/k8s/postgres-deployment.yaml4
Normal 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
13
sk1/k8s/service.yaml
Normal 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
10
sk1/prepare-app.sh
Executable 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
18
sk1/start-app.sh
Executable 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
18
sk1/stop-app.sh
Executable 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."
|
Loading…
Reference in New Issue
Block a user