diff --git a/sk1/Dockerfile b/sk1/Dockerfile
new file mode 100644
index 0000000..a62458e
--- /dev/null
+++ b/sk1/Dockerfile
@@ -0,0 +1,16 @@
+FROM python:3.9-slim
+
+WORKDIR /app
+
+COPY requirements.txt .
+RUN pip install --no-cache-dir -r requirements.txt
+
+COPY app.py .
+
+ENV FLASK_APP=app.py
+ENV FLASK_RUN_HOST=0.0.0.0
+ENV FLASK_RUN_PORT=5000
+
+EXPOSE 5000
+
+CMD ["flask", "run"]
\ No newline at end of file
diff --git a/sk1/README.md b/sk1/README.md
new file mode 100644
index 0000000..14302b2
--- /dev/null
+++ b/sk1/README.md
@@ -0,0 +1,60 @@
+
+Blog Application - Deployment on Azure AKS
+==========================================
+
+Application Description
+------------------------
+A simple Flask blog app allowing users to create, view, and delete articles. Articles are stored persistently in PostgreSQL.
+
+Cloud & Technologies Used
+--------------------------
+- Cloud: Microsoft Azure (AKS for orchestration, ACR for Docker images)
+- Stack: Flask, PostgreSQL, Docker, Kubernetes
+- Kubernetes Objects:
+ - Namespace, Deployment (Flask), StatefulSet (PostgreSQL), PVC, Services
+
+File Overview
+-------------
+| File | Description |
+|--------------------|------------------------------------------------------------|
+| app.py | Flask app logic |
+| Dockerfile | Defines Flask app image |
+| requirements.txt | Python dependencies |
+| namespace.yaml | Creates a K8s namespace |
+| deployment.yaml | Deploys the Flask app |
+| statefulset.yaml | Deploys PostgreSQL with persistent volume |
+| service.yaml | Exposes app and DB services |
+| aks-cluster.sh | Creates AKS cluster and sets up infra |
+| prepare-app.sh | Builds image, deploys app and services |
+| remove-app.sh | Tears down all K8s components |
+| README.md | This documentation |
+
+Deployment Instructions
+------------------------
+1. Set up infrastructure
+ ./aks-cluster.sh
+
+2. Deploy the app
+ ./prepare-app.sh
+
+3. Access the app
+ Open the external IP in your browser (output by the script)
+
+4. Remove everything
+ ./remove-app.sh
+
+Requirements
+------------
+- Azure account, Azure CLI
+- kubectl & Docker installed locally
+
+Notes
+-----
+- PostgreSQL uses a persistent volume (data survives restarts)
+- The Flask app restarts automatically on failure
+- All deployments are fully scripted via shell & YAML
+
+Sources & Tools
+---------------
+- Flask documentation: https://flask.palletsprojects.com/
+- Kubernetes documentation: https://kubernetes.io/
\ No newline at end of file
diff --git a/sk1/aks-cluster.sh b/sk1/aks-cluster.sh
new file mode 100644
index 0000000..3afa1c9
--- /dev/null
+++ b/sk1/aks-cluster.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+# Variables
+RESOURCE_GROUP="blog-app-rg"
+CLUSTER_NAME="blog-app-aks"
+LOCATION="westeurope" # Choose an appropriate Azure region
+NODE_COUNT=1
+NODE_VM_SIZE="Standard_B2s" # Economic VM size for demo projects
+
+# Create resource group
+echo "Creating resource group..."
+az group create --name $RESOURCE_GROUP --location $LOCATION
+
+# Create AKS cluster
+echo "Creating AKS cluster (this may take several minutes)..."
+az aks create \
+ --resource-group $RESOURCE_GROUP \
+ --name $CLUSTER_NAME \
+ --node-count $NODE_COUNT \
+ --node-vm-size $NODE_VM_SIZE \
+ --enable-managed-identity \
+ --generate-ssh-keys
+
+# Get credentials
+echo "Configuring kubectl..."
+az aks get-credentials --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME
+
+echo "AKS cluster configuration completed!"
+echo "You can now run ./prepare-app.sh to deploy the application"
\ No newline at end of file
diff --git a/sk1/app.py b/sk1/app.py
new file mode 100644
index 0000000..df8a3e3
--- /dev/null
+++ b/sk1/app.py
@@ -0,0 +1,136 @@
+from flask import Flask, request, jsonify
+import os
+import psycopg2
+import time
+
+app = Flask(__name__)
+
+# Database Configuration
+db_host = os.environ.get("DB_HOST", "blog-db-service")
+db_name = os.environ.get("POSTGRES_DB", "blogdb")
+db_user = os.environ.get("POSTGRES_USER", "admin")
+db_password = os.environ.get("POSTGRES_PASSWORD", "password")
+
+def get_db_connection():
+ return psycopg2.connect(host=db_host, database=db_name, user=db_user, password=db_password)
+
+def create_table():
+ max_retries = 10
+ retry_delay = 5 # seconds
+
+ for attempt in range(max_retries):
+ try:
+ print(f"Attempt {attempt+1}/{max_retries} to connect to database...")
+ conn = get_db_connection()
+ cur = conn.cursor()
+ cur.execute('''
+ CREATE TABLE IF NOT EXISTS articles (
+ id SERIAL PRIMARY KEY,
+ title TEXT NOT NULL,
+ content TEXT NOT NULL
+ )
+ ''')
+ conn.commit()
+ cur.close()
+ conn.close()
+ print("Table 'articles' created successfully")
+ return True
+ except Exception as e:
+ print(f"Database connection attempt {attempt+1}/{max_retries} failed: {e}")
+ if attempt < max_retries - 1:
+ print(f"Retrying in {retry_delay} seconds...")
+ time.sleep(retry_delay)
+
+ print("Failed to create table after multiple attempts")
+ return False
+
+# Initialize the variable at startup
+table_created = False
+
+@app.route("/", methods=["GET", "POST"])
+def index():
+ global table_created
+
+ # If the table is not yet created, try to create it
+ if not table_created:
+ table_created = create_table()
+
+ # Check if the table has been created before continuing
+ if not table_created:
+ return "Database connection failed. Please try again later.", 500
+
+ try:
+ conn = get_db_connection()
+ cur = conn.cursor()
+
+ if request.method == "POST":
+ title = request.form.get("title")
+ content = request.form.get("content")
+ if title and content:
+ cur.execute("INSERT INTO articles (title, content) VALUES (%s, %s)", (title, content))
+ conn.commit()
+
+ cur.execute("SELECT * FROM articles ORDER BY id DESC")
+ articles = cur.fetchall()
+ cur.close()
+ conn.close()
+
+ return f"""
+
+
+ My Blog
+
+
+
+
+
+
My Blog
+
+
+
+
Articles
+ {''.join(f'
' for a in articles)}
+
+
+
+
+ """
+ except Exception as e:
+ return f"An error occurred: {str(e)}", 500
+
+@app.route("/delete/", methods=["POST"])
+def delete_article(article_id):
+ try:
+ conn = get_db_connection()
+ cur = conn.cursor()
+ cur.execute("DELETE FROM articles WHERE id = %s", (article_id,))
+ conn.commit()
+ cur.close()
+ conn.close()
+ return ""
+ except Exception as e:
+ return f"An error occurred while deleting: {str(e)}", 500
+
+if __name__ == "__main__":
+ create_table()
+ app.run(host="0.0.0.0", port=5000)
\ No newline at end of file
diff --git a/sk1/deployment.yaml b/sk1/deployment.yaml
new file mode 100644
index 0000000..2c80e7b
--- /dev/null
+++ b/sk1/deployment.yaml
@@ -0,0 +1,32 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: blog-app
+ namespace: myapp-namespace
+ labels:
+ app: blog
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: blog
+ template:
+ metadata:
+ labels:
+ app: blog
+ spec:
+ containers:
+ - name: blog-app
+ image: blogappacr.azurecr.io/blog-app:latest
+ imagePullPolicy: Always
+ ports:
+ - containerPort: 5000
+ env:
+ - name: DB_HOST
+ value: "blog-db-service"
+ - name: POSTGRES_DB
+ value: "blogdb"
+ - name: POSTGRES_USER
+ value: "admin"
+ - name: POSTGRES_PASSWORD
+ value: "password"
\ No newline at end of file
diff --git a/sk1/ingress.yaml b/sk1/ingress.yaml
new file mode 100644
index 0000000..01a416d
--- /dev/null
+++ b/sk1/ingress.yaml
@@ -0,0 +1,19 @@
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: blog-app-ingress
+ namespace: myapp-namespace
+ annotations:
+ kubernetes.io/ingress.class: "nginx"
+spec:
+ rules:
+ - host: blog-app.example.com
+ http:
+ paths:
+ - path: /
+ pathType: Prefix
+ backend:
+ service:
+ name: blog-app-service
+ port:
+ number: 80
\ No newline at end of file
diff --git a/sk1/namespace.yaml b/sk1/namespace.yaml
new file mode 100644
index 0000000..e14643a
--- /dev/null
+++ b/sk1/namespace.yaml
@@ -0,0 +1,4 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: myapp-namespace
\ No newline at end of file
diff --git a/sk1/prepare-app.sh b/sk1/prepare-app.sh
new file mode 100644
index 0000000..e20c737
--- /dev/null
+++ b/sk1/prepare-app.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+# 1. Check required tools
+echo "Checking required tools..."
+command -v kubectl >/dev/null 2>&1 || { echo "Error: kubectl is not installed"; exit 1; }
+command -v docker >/dev/null 2>&1 || { echo "Error: Docker is not installed"; exit 1; }
+
+# 2. Set Azure Container Registry name
+ACR_NAME="blogappacr"
+RESOURCE_GROUP="blog-app-rg"
+
+# 3. Log in to Azure Container Registry
+echo "Logging in to Azure Container Registry..."
+az acr login --name $ACR_NAME
+
+# 4. Build and push Docker image
+echo "Building and pushing Docker image..."
+docker build -t ${ACR_NAME}.azurecr.io/blog-app:latest .
+docker push ${ACR_NAME}.azurecr.io/blog-app:latest
+
+# 5. Update deployment file with correct image name
+echo "Updating deployment file..."
+sed -i "s|image: .*|image: ${ACR_NAME}.azurecr.io/blog-app:latest|g" deployment.yaml
+
+# 6. Deploy to Kubernetes
+echo "Deploy to Kubernetes..."
+kubectl apply -f namespace.yaml
+kubectl apply -f statefulset.yaml
+kubectl apply -f deployment.yaml
+kubectl apply -f service.yaml
+
+# 7. Wait for services to get external IP
+echo "Waiting for external IP"
+EXTERNAL_IP=""
+while [ -z "$EXTERNAL_IP" ]; do
+ EXTERNAL_IP=$(kubectl get service blog-app-service -n myapp-namespace -o jsonpath='{.status.loadBalancer.ingress[0].ip}' 2>/dev/null)
+ [ -z "$EXTERNAL_IP" ] && sleep 10
+done
+
+# 8. Display success message
+echo ""
+echo "The blog application is now running! You can access it at: http://$EXTERNAL_IP"
+echo ""
\ No newline at end of file
diff --git a/sk1/remove-app.sh b/sk1/remove-app.sh
new file mode 100644
index 0000000..c1bef6d
--- /dev/null
+++ b/sk1/remove-app.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+echo "Removing all ressources..."
+kubectl delete -f service.yaml
+
+kubectl delete -f deployment.yaml
+
+kubectl delete -f statefulset.yaml
+
+kubectl delete -f namespace.yaml
+
+echo "All resources have been successfully removed."
\ No newline at end of file
diff --git a/sk1/requirements.txt b/sk1/requirements.txt
new file mode 100644
index 0000000..8860e6a
--- /dev/null
+++ b/sk1/requirements.txt
@@ -0,0 +1,3 @@
+Flask
+psycopg2-binary
+requests
\ No newline at end of file
diff --git a/sk1/service.yaml b/sk1/service.yaml
new file mode 100644
index 0000000..b3b262c
--- /dev/null
+++ b/sk1/service.yaml
@@ -0,0 +1,29 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: blog-app-service
+ namespace: myapp-namespace
+spec:
+ selector:
+ app: blog
+ ports:
+ - protocol: TCP
+ port: 80
+ targetPort: 5000
+ type: LoadBalancer
+
+---
+
+apiVersion: v1
+kind: Service
+metadata:
+ name: blog-db-service
+ namespace: myapp-namespace
+spec:
+ selector:
+ app: blog-db
+ ports:
+ - protocol: TCP
+ port: 5432
+ targetPort: 5432
+ type: ClusterIP
\ No newline at end of file
diff --git a/sk1/statefulset.yaml b/sk1/statefulset.yaml
new file mode 100644
index 0000000..e55aa29
--- /dev/null
+++ b/sk1/statefulset.yaml
@@ -0,0 +1,40 @@
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+ name: blog-db
+ namespace: myapp-namespace
+spec:
+ serviceName: "blog-db-service"
+ replicas: 1
+ selector:
+ matchLabels:
+ app: blog-db
+ template:
+ metadata:
+ labels:
+ app: blog-db
+ spec:
+ containers:
+ - name: postgres
+ image: postgres:13
+ ports:
+ - containerPort: 5432
+ env:
+ - name: POSTGRES_DB
+ value: "blogdb"
+ - name: POSTGRES_USER
+ value: "admin"
+ - name: POSTGRES_PASSWORD
+ value: "password"
+ volumeMounts:
+ - name: postgres-storage
+ mountPath: /var/lib/postgresql/data
+ subPath: pgdata
+ volumeClaimTemplates:
+ - metadata:
+ name: postgres-storage
+ spec:
+ accessModes: ["ReadWriteOnce"]
+ resources:
+ requests:
+ storage: 1Gi
\ No newline at end of file