Téléverser les fichiers vers "z2"
This commit is contained in:
		
							parent
							
								
									16aa94ff55
								
							
						
					
					
						commit
						b91eb63711
					
				
							
								
								
									
										12
									
								
								z2/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								z2/Dockerfile
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
FROM python:3.9-slim
 | 
			
		||||
 | 
			
		||||
WORKDIR /app
 | 
			
		||||
 | 
			
		||||
COPY requirements.txt .
 | 
			
		||||
RUN pip install --no-cache-dir -r requirements.txt
 | 
			
		||||
 | 
			
		||||
COPY app.py .
 | 
			
		||||
 | 
			
		||||
EXPOSE 5000
 | 
			
		||||
 | 
			
		||||
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]
 | 
			
		||||
							
								
								
									
										107
									
								
								z2/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								z2/README.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,107 @@
 | 
			
		||||
# Shopping List Web Application on Kubernetes
 | 
			
		||||
 | 
			
		||||
## Overview
 | 
			
		||||
This project deploys a Python shopping list web application using Flask and PostgreSQL on a Kubernetes cluster. The application allows users to create, track, and manage their shopping items in a persistent way. The deployment includes a Namespace, Deployments for both the web application and PostgreSQL database, a StatefulSet (with PersistentVolume and PersistentVolumeClaim), and Service.
 | 
			
		||||
 | 
			
		||||
## Application Description
 | 
			
		||||
- Simple shopping list application that allows users to add, mark as purchased, and delete items
 | 
			
		||||
- Each item can have a quantity associated with it
 | 
			
		||||
- All data is stored in PostgreSQL for persistence between application restarts
 | 
			
		||||
- The application uses a Flask web framework with a minimalist interface
 | 
			
		||||
 | 
			
		||||
## Containers
 | 
			
		||||
- **simple-web-app**: Runs the Python Flask application on port 5000
 | 
			
		||||
- **postgres**: Runs PostgreSQL database to store shopping list items
 | 
			
		||||
 | 
			
		||||
## Kubernetes Objects
 | 
			
		||||
- **Namespace**: Isolates all the resources under `my-app`
 | 
			
		||||
- **Deployment (Web App)**: Manages the stateless web application pods with 2 replicas for high availability
 | 
			
		||||
- **Deployment (PostgreSQL)**: Manages the PostgreSQL database with persistent storage
 | 
			
		||||
- **StatefulSet**: Manages stateful application pods that require persistent storage
 | 
			
		||||
- **PersistentVolume (PV)**: Provides persistent storage from the host (1GB)
 | 
			
		||||
- **PersistentVolumeClaim (PVC)**: Claims the PV for storage
 | 
			
		||||
- **Service (Web App)**: Exposes the web application externally via LoadBalancer
 | 
			
		||||
- **Service (PostgreSQL)**: Headless service for internal database access
 | 
			
		||||
 | 
			
		||||
## Networking and Storage
 | 
			
		||||
- Internal service discovery allows the web application to connect to PostgreSQL
 | 
			
		||||
- PostgreSQL uses persistent storage to maintain shopping list data even if pods are restarted
 | 
			
		||||
- The web application is exposed externally using a LoadBalancer service
 | 
			
		||||
 | 
			
		||||
## Container Configuration
 | 
			
		||||
- The web app container is based on Python and includes Flask and psycopg2
 | 
			
		||||
- PostgreSQL container uses the official PostgreSQL image
 | 
			
		||||
- Resource limits and readiness probes are configured for better stability
 | 
			
		||||
 | 
			
		||||
## How to Prepare, Run, Pause, and Delete the Application
 | 
			
		||||
 | 
			
		||||
1. **Prepare the application:**
 | 
			
		||||
   ```bash
 | 
			
		||||
   ./prepare-app.sh
 | 
			
		||||
   ```
 | 
			
		||||
   This script builds the Docker image and creates the directory for persistent volume.
 | 
			
		||||
 | 
			
		||||
2. **Start the application:**
 | 
			
		||||
   ```bash
 | 
			
		||||
   ./start-app.sh
 | 
			
		||||
   ```
 | 
			
		||||
   This script creates all necessary Kubernetes objects in the correct order, including PostgreSQL.
 | 
			
		||||
 | 
			
		||||
3. **Pause or delete the application:**
 | 
			
		||||
   ```bash
 | 
			
		||||
   ./stop-app.sh
 | 
			
		||||
   ```
 | 
			
		||||
   This script removes all Kubernetes objects created by `start-app.sh`.
 | 
			
		||||
 | 
			
		||||
## Accessing the Application
 | 
			
		||||
To access the application:
 | 
			
		||||
 | 
			
		||||
1. Find the IP address of your Kubernetes node:
 | 
			
		||||
   ```bash
 | 
			
		||||
   kubectl get nodes -o wide
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
2. Access the application in your browser at:
 | 
			
		||||
   ```
 | 
			
		||||
   http://<NODE_IP>:80
 | 
			
		||||
   ```
 | 
			
		||||
   Where `<NODE_IP>` is the IP address of any of your Kubernetes nodes.
 | 
			
		||||
 | 
			
		||||
## Application Features
 | 
			
		||||
- Add items with quantity
 | 
			
		||||
- Mark items as purchased/unpurchased
 | 
			
		||||
- Delete items
 | 
			
		||||
- Items list is separated into "to buy" and "purchased" sections
 | 
			
		||||
- Data persists between sessions and application restarts
 | 
			
		||||
 | 
			
		||||
## Database Schema
 | 
			
		||||
The application uses a simple PostgreSQL schema:
 | 
			
		||||
- Table: `shopping_items`
 | 
			
		||||
- Fields:
 | 
			
		||||
  - `id`: Serial primary key
 | 
			
		||||
  - `item`: Text (item name)
 | 
			
		||||
  - `quantity`: Integer (defaults to 1)
 | 
			
		||||
  - `purchased`: Boolean flag (defaults to false)
 | 
			
		||||
 | 
			
		||||
## Troubleshooting
 | 
			
		||||
If you encounter issues:
 | 
			
		||||
 | 
			
		||||
1. Check pod status:
 | 
			
		||||
   ```bash
 | 
			
		||||
   kubectl get pods -n my-app
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
2. View pod logs:
 | 
			
		||||
   ```bash
 | 
			
		||||
   kubectl logs <pod-name> -n my-app
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
3. Check service status:
 | 
			
		||||
   ```bash
 | 
			
		||||
   kubectl get svc -n my-app
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
4. Check database connectivity:
 | 
			
		||||
   ```bash
 | 
			
		||||
   kubectl exec -it <web-app-pod-name> -n my-app -- python -c "import psycopg2; conn = psycopg2.connect(host='postgres', dbname='postgres', user='postgres', password='postgres'); print('Connection successful')"
 | 
			
		||||
   ```
 | 
			
		||||
							
								
								
									
										173
									
								
								z2/app.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								z2/app.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,173 @@
 | 
			
		||||
from flask import Flask, request, redirect, url_for, render_template_string
 | 
			
		||||
import os
 | 
			
		||||
import psycopg2
 | 
			
		||||
from psycopg2 import pool
 | 
			
		||||
 | 
			
		||||
app = Flask(__name__)
 | 
			
		||||
 | 
			
		||||
DB_HOST = os.environ.get('DB_HOST', 'localhost')
 | 
			
		||||
DB_PORT = os.environ.get('DB_PORT', '5432')
 | 
			
		||||
DB_NAME = os.environ.get('DB_NAME', 'postgres')
 | 
			
		||||
DB_USER = os.environ.get('DB_USER', 'postgres')
 | 
			
		||||
DB_PASS = os.environ.get('DB_PASS', 'postgres')
 | 
			
		||||
 | 
			
		||||
connection_pool = psycopg2.pool.SimpleConnectionPool(
 | 
			
		||||
    1, 10,
 | 
			
		||||
    host=DB_HOST,
 | 
			
		||||
    port=DB_PORT,
 | 
			
		||||
    database=DB_NAME,
 | 
			
		||||
    user=DB_USER,
 | 
			
		||||
    password=DB_PASS
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
def init_db():
 | 
			
		||||
    conn = connection_pool.getconn()
 | 
			
		||||
    try:
 | 
			
		||||
        with conn.cursor() as cur:
 | 
			
		||||
            cur.execute('''
 | 
			
		||||
                CREATE TABLE IF NOT EXISTS shopping_items (
 | 
			
		||||
                    id SERIAL PRIMARY KEY,
 | 
			
		||||
                    item TEXT NOT NULL,
 | 
			
		||||
                    quantity INTEGER DEFAULT 1,
 | 
			
		||||
                    purchased BOOLEAN DEFAULT FALSE
 | 
			
		||||
                );
 | 
			
		||||
            ''')
 | 
			
		||||
            conn.commit()
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        print(f"Erreur lors de l'initialisation de la base de données: {e}")
 | 
			
		||||
        conn.rollback()
 | 
			
		||||
    finally:
 | 
			
		||||
        connection_pool.putconn(conn)
 | 
			
		||||
 | 
			
		||||
HTML_TEMPLATE = """
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
<head>
 | 
			
		||||
    <title>Liste de Courses</title>
 | 
			
		||||
    <meta charset="UTF-8">
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    <h1>Ma Liste de Courses</h1>
 | 
			
		||||
    
 | 
			
		||||
    <form action="/add" method="post">
 | 
			
		||||
        <input type="text" name="item" placeholder="Article" required>
 | 
			
		||||
        <input type="number" name="quantity" value="1" min="1">
 | 
			
		||||
        <button type="submit">Ajouter</button>
 | 
			
		||||
    </form>
 | 
			
		||||
    
 | 
			
		||||
    <h2>Articles à acheter</h2>
 | 
			
		||||
    <ul>
 | 
			
		||||
        {% for item in items %}
 | 
			
		||||
            {% if not item[3] %}
 | 
			
		||||
                <li>
 | 
			
		||||
                    {{ item[1] }} ({{ item[2] }})
 | 
			
		||||
                    <form action="/toggle/{{ item[0] }}" method="post" style="display:inline">
 | 
			
		||||
                        <button type="submit">Acheté</button>
 | 
			
		||||
                    </form>
 | 
			
		||||
                    <form action="/delete/{{ item[0] }}" method="post" style="display:inline">
 | 
			
		||||
                        <button type="submit">Supprimer</button>
 | 
			
		||||
                    </form>
 | 
			
		||||
                </li>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        {% endfor %}
 | 
			
		||||
    </ul>
 | 
			
		||||
    
 | 
			
		||||
    <h2>Articles achetés</h2>
 | 
			
		||||
    <ul>
 | 
			
		||||
        {% for item in items %}
 | 
			
		||||
            {% if item[3] %}
 | 
			
		||||
                <li>
 | 
			
		||||
                    {{ item[1] }} ({{ item[2] }})
 | 
			
		||||
                    <form action="/toggle/{{ item[0] }}" method="post" style="display:inline">
 | 
			
		||||
                        <button type="submit">Non acheté</button>
 | 
			
		||||
                    </form>
 | 
			
		||||
                    <form action="/delete/{{ item[0] }}" method="post" style="display:inline">
 | 
			
		||||
                        <button type="submit">Supprimer</button>
 | 
			
		||||
                    </form>
 | 
			
		||||
                </li>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        {% endfor %}
 | 
			
		||||
    </ul>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
@app.route('/')
 | 
			
		||||
def index():
 | 
			
		||||
    conn = connection_pool.getconn()
 | 
			
		||||
    try:
 | 
			
		||||
        with conn.cursor() as cur:
 | 
			
		||||
            cur.execute("SELECT id, item, quantity, purchased FROM shopping_items ORDER BY purchased, id")
 | 
			
		||||
            items = cur.fetchall()
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        print(f"Erreur lors de la récupération des articles: {e}")
 | 
			
		||||
        items = []
 | 
			
		||||
    finally:
 | 
			
		||||
        connection_pool.putconn(conn)
 | 
			
		||||
    
 | 
			
		||||
    return render_template_string(HTML_TEMPLATE, items=items)
 | 
			
		||||
 | 
			
		||||
@app.route('/add', methods=['POST'])
 | 
			
		||||
def add_item():
 | 
			
		||||
    item = request.form.get('item')
 | 
			
		||||
    quantity = request.form.get('quantity', 1, type=int)
 | 
			
		||||
    
 | 
			
		||||
    conn = connection_pool.getconn()
 | 
			
		||||
    try:
 | 
			
		||||
        with conn.cursor() as cur:
 | 
			
		||||
            cur.execute(
 | 
			
		||||
                "INSERT INTO shopping_items (item, quantity) VALUES (%s, %s)",
 | 
			
		||||
                (item, quantity)
 | 
			
		||||
            )
 | 
			
		||||
            conn.commit()
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        print(f"Erreur lors de l'ajout d'un article: {e}")
 | 
			
		||||
        conn.rollback()
 | 
			
		||||
    finally:
 | 
			
		||||
        connection_pool.putconn(conn)
 | 
			
		||||
    
 | 
			
		||||
    return redirect(url_for('index'))
 | 
			
		||||
 | 
			
		||||
@app.route('/toggle/<int:item_id>', methods=['POST'])
 | 
			
		||||
def toggle_item(item_id):
 | 
			
		||||
    conn = connection_pool.getconn()
 | 
			
		||||
    try:
 | 
			
		||||
        with conn.cursor() as cur:
 | 
			
		||||
            cur.execute(
 | 
			
		||||
                "UPDATE shopping_items SET purchased = NOT purchased WHERE id = %s",
 | 
			
		||||
                (item_id,)
 | 
			
		||||
            )
 | 
			
		||||
            conn.commit()
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        print(f"Erreur lors du basculement du statut: {e}")
 | 
			
		||||
        conn.rollback()
 | 
			
		||||
    finally:
 | 
			
		||||
        connection_pool.putconn(conn)
 | 
			
		||||
    
 | 
			
		||||
    return redirect(url_for('index'))
 | 
			
		||||
 | 
			
		||||
@app.route('/delete/<int:item_id>', methods=['POST'])
 | 
			
		||||
def delete_item(item_id):
 | 
			
		||||
    conn = connection_pool.getconn()
 | 
			
		||||
    try:
 | 
			
		||||
        with conn.cursor() as cur:
 | 
			
		||||
            cur.execute("DELETE FROM shopping_items WHERE id = %s", (item_id,))
 | 
			
		||||
            conn.commit()
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        print(f"Erreur lors de la suppression d'un article: {e}")
 | 
			
		||||
        conn.rollback()
 | 
			
		||||
    finally:
 | 
			
		||||
        connection_pool.putconn(conn)
 | 
			
		||||
    
 | 
			
		||||
    return redirect(url_for('index'))
 | 
			
		||||
 | 
			
		||||
with app.app_context():
 | 
			
		||||
    try:
 | 
			
		||||
        init_db()
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        print(f"Impossible d'initialiser la base de données: {e}")
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    port = int(os.environ.get("PORT", 5000))
 | 
			
		||||
    app.run(host='0.0.0.0', port=port, debug=True)
 | 
			
		||||
							
								
								
									
										51
									
								
								z2/deployment.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								z2/deployment.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
			
		||||
apiVersion: apps/v1
 | 
			
		||||
kind: Deployment
 | 
			
		||||
metadata:
 | 
			
		||||
  name: web-app-deployment
 | 
			
		||||
  namespace: my-app
 | 
			
		||||
spec:
 | 
			
		||||
  replicas: 2  
 | 
			
		||||
  selector:
 | 
			
		||||
    matchLabels:
 | 
			
		||||
      app: web-app
 | 
			
		||||
  template:
 | 
			
		||||
    metadata:
 | 
			
		||||
      labels:
 | 
			
		||||
        app: web-app
 | 
			
		||||
    spec:
 | 
			
		||||
      containers:
 | 
			
		||||
        - name: web-app-container
 | 
			
		||||
          image: antonin193/simple-web-app:latest
 | 
			
		||||
          imagePullPolicy: Always
 | 
			
		||||
          ports:
 | 
			
		||||
            - containerPort: 5000
 | 
			
		||||
          env:
 | 
			
		||||
            - name: DB_HOST
 | 
			
		||||
              value: postgres
 | 
			
		||||
            - name: DB_PORT
 | 
			
		||||
              value: "5432"
 | 
			
		||||
            - name: DB_NAME
 | 
			
		||||
              value: postgres
 | 
			
		||||
            - name: DB_USER
 | 
			
		||||
              value: postgres
 | 
			
		||||
            - name: DB_PASS
 | 
			
		||||
              value: postgres
 | 
			
		||||
          resources:
 | 
			
		||||
            requests:
 | 
			
		||||
              memory: "64Mi"
 | 
			
		||||
              cpu: "100m"
 | 
			
		||||
            limits:
 | 
			
		||||
              memory: "128Mi"
 | 
			
		||||
              cpu: "200m"
 | 
			
		||||
          readinessProbe:
 | 
			
		||||
            httpGet:
 | 
			
		||||
              path: /
 | 
			
		||||
              port: 5000
 | 
			
		||||
            initialDelaySeconds: 15
 | 
			
		||||
            periodSeconds: 10
 | 
			
		||||
          livenessProbe:
 | 
			
		||||
            httpGet:
 | 
			
		||||
              path: /
 | 
			
		||||
              port: 5000
 | 
			
		||||
            initialDelaySeconds: 20
 | 
			
		||||
            periodSeconds: 20
 | 
			
		||||
							
								
								
									
										7
									
								
								z2/namespace.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								z2/namespace.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
			
		||||
apiVersion: v1
 | 
			
		||||
kind: Namespace
 | 
			
		||||
metadata:
 | 
			
		||||
  name: my-app
 | 
			
		||||
  labels:
 | 
			
		||||
    name: my-app
 | 
			
		||||
    environment: development
 | 
			
		||||
							
								
								
									
										29
									
								
								z2/persistent-storage.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								z2/persistent-storage.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
			
		||||
apiVersion: v1
 | 
			
		||||
kind: PersistentVolume
 | 
			
		||||
metadata:
 | 
			
		||||
  name: data-pv
 | 
			
		||||
  namespace: my-app
 | 
			
		||||
  labels:
 | 
			
		||||
    type: local
 | 
			
		||||
spec:
 | 
			
		||||
  capacity:
 | 
			
		||||
    storage: 1Gi
 | 
			
		||||
  accessModes:
 | 
			
		||||
    - ReadWriteOnce
 | 
			
		||||
  persistentVolumeReclaimPolicy: Retain
 | 
			
		||||
  storageClassName: manual
 | 
			
		||||
  hostPath:
 | 
			
		||||
    path: /data/stateful
 | 
			
		||||
---
 | 
			
		||||
apiVersion: v1
 | 
			
		||||
kind: PersistentVolumeClaim
 | 
			
		||||
metadata:
 | 
			
		||||
  name: data-pvc
 | 
			
		||||
  namespace: my-app
 | 
			
		||||
spec:
 | 
			
		||||
  accessModes:
 | 
			
		||||
    - ReadWriteOnce
 | 
			
		||||
  storageClassName: manual
 | 
			
		||||
  resources:
 | 
			
		||||
    requests:
 | 
			
		||||
      storage: 1Gi
 | 
			
		||||
							
								
								
									
										44
									
								
								z2/postgres-deployment.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								z2/postgres-deployment.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,44 @@
 | 
			
		||||
apiVersion: apps/v1
 | 
			
		||||
kind: Deployment
 | 
			
		||||
metadata:
 | 
			
		||||
  name: postgres
 | 
			
		||||
  namespace: my-app
 | 
			
		||||
spec:
 | 
			
		||||
  replicas: 1
 | 
			
		||||
  selector:
 | 
			
		||||
    matchLabels:
 | 
			
		||||
      app: postgres
 | 
			
		||||
  template:
 | 
			
		||||
    metadata:
 | 
			
		||||
      labels:
 | 
			
		||||
        app: postgres
 | 
			
		||||
    spec:
 | 
			
		||||
      containers:
 | 
			
		||||
        - name: postgres
 | 
			
		||||
          image: postgres:14
 | 
			
		||||
          imagePullPolicy: IfNotPresent
 | 
			
		||||
          ports:
 | 
			
		||||
            - containerPort: 5432
 | 
			
		||||
          env:
 | 
			
		||||
            - name: POSTGRES_PASSWORD
 | 
			
		||||
              value: postgres
 | 
			
		||||
            - name: POSTGRES_USER
 | 
			
		||||
              value: postgres
 | 
			
		||||
            - name: POSTGRES_DB
 | 
			
		||||
              value: postgres
 | 
			
		||||
            - name: PGDATA
 | 
			
		||||
              value: /var/lib/postgresql/data/pgdata
 | 
			
		||||
          volumeMounts:
 | 
			
		||||
            - name: postgres-storage
 | 
			
		||||
              mountPath: /var/lib/postgresql/data
 | 
			
		||||
          resources:
 | 
			
		||||
            requests:
 | 
			
		||||
              memory: "256Mi"
 | 
			
		||||
              cpu: "200m"
 | 
			
		||||
            limits:
 | 
			
		||||
              memory: "512Mi"
 | 
			
		||||
              cpu: "500m"
 | 
			
		||||
      volumes:
 | 
			
		||||
        - name: postgres-storage
 | 
			
		||||
          persistentVolumeClaim:
 | 
			
		||||
            claimName: data-pvc
 | 
			
		||||
							
								
								
									
										15
									
								
								z2/postgres-service.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								z2/postgres-service.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
			
		||||
apiVersion: v1
 | 
			
		||||
kind: Service
 | 
			
		||||
metadata:
 | 
			
		||||
  name: postgres
 | 
			
		||||
  namespace: my-app
 | 
			
		||||
  labels:
 | 
			
		||||
    app: postgres
 | 
			
		||||
spec:
 | 
			
		||||
  selector:
 | 
			
		||||
    app: postgres
 | 
			
		||||
  ports:
 | 
			
		||||
    - protocol: TCP
 | 
			
		||||
      port: 5432
 | 
			
		||||
      targetPort: 5432
 | 
			
		||||
  clusterIP: None
 | 
			
		||||
							
								
								
									
										22
									
								
								z2/prepare-app.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								z2/prepare-app.sh
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
# Script to prepare the application environment
 | 
			
		||||
 | 
			
		||||
export REGION="local"
 | 
			
		||||
echo "Preparing deployment for region: $REGION"
 | 
			
		||||
 | 
			
		||||
# Make sure scripts are executable
 | 
			
		||||
chmod +x start-app.sh stop-app.sh
 | 
			
		||||
 | 
			
		||||
# Build and tag Docker image
 | 
			
		||||
docker build -t simple-web-app:latest .
 | 
			
		||||
docker tag simple-web-app:latest antonin193/simple-web-app:latest
 | 
			
		||||
 | 
			
		||||
# Push to Docker Hub if needed
 | 
			
		||||
echo "Pushing image to Docker Hub..."
 | 
			
		||||
docker push antonin193/simple-web-app:latest
 | 
			
		||||
 | 
			
		||||
# Create directory for persistent volume (Docker Desktop supports hostPath)
 | 
			
		||||
sudo mkdir -p /data/stateful
 | 
			
		||||
sudo chmod 777 /data/stateful
 | 
			
		||||
 | 
			
		||||
echo "Preparation complete: Docker image built, tagged, and volume directory created."
 | 
			
		||||
							
								
								
									
										4
									
								
								z2/requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								z2/requirements.txt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
			
		||||
Flask>=2.0.0
 | 
			
		||||
Werkzeug>=2.0.0
 | 
			
		||||
gunicorn>=20.1.0
 | 
			
		||||
psycopg2-binary>=2.9.3
 | 
			
		||||
							
								
								
									
										16
									
								
								z2/service.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								z2/service.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
			
		||||
apiVersion: v1
 | 
			
		||||
kind: Service
 | 
			
		||||
metadata:
 | 
			
		||||
  name: web-app-service
 | 
			
		||||
  namespace: my-app
 | 
			
		||||
  labels:
 | 
			
		||||
    app: web-app
 | 
			
		||||
spec:
 | 
			
		||||
  type: LoadBalancer
 | 
			
		||||
  selector:
 | 
			
		||||
    app: web-app
 | 
			
		||||
  ports:
 | 
			
		||||
    - protocol: TCP
 | 
			
		||||
      port: 80 
 | 
			
		||||
      targetPort: 5000  
 | 
			
		||||
  sessionAffinity: None 
 | 
			
		||||
							
								
								
									
										82
									
								
								z2/start-app.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								z2/start-app.sh
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,82 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
# Script to deploy the app locally using Docker Desktop + Kubernetes
 | 
			
		||||
 | 
			
		||||
# Set default region (optional but kept for consistency)
 | 
			
		||||
if [ -z "$REGION" ]; then
 | 
			
		||||
    export REGION="local"
 | 
			
		||||
    echo "No region specified. Using default region: $REGION"
 | 
			
		||||
else
 | 
			
		||||
    echo "Deploying to region: $REGION"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Build Docker image
 | 
			
		||||
echo "Building Docker image..."
 | 
			
		||||
docker build -t antonin193/simple-web-app:latest .
 | 
			
		||||
 | 
			
		||||
# Push to Docker Hub (optional for local, but kept if needed across machines)
 | 
			
		||||
echo "Pushing image to Docker Hub..."
 | 
			
		||||
docker push antonin193/simple-web-app:latest
 | 
			
		||||
 | 
			
		||||
# Create Kubernetes Namespace
 | 
			
		||||
echo "Creating namespace..."
 | 
			
		||||
kubectl apply -f namespace.yaml
 | 
			
		||||
 | 
			
		||||
# Create PersistentVolume and PersistentVolumeClaim
 | 
			
		||||
echo "Creating persistent storage..."
 | 
			
		||||
kubectl apply -f persistent-storage.yaml
 | 
			
		||||
 | 
			
		||||
# Wait briefly for resources to be established
 | 
			
		||||
sleep 2
 | 
			
		||||
 | 
			
		||||
# Deploy PostgreSQL first
 | 
			
		||||
echo "Creating PostgreSQL Deployment and Service..."
 | 
			
		||||
kubectl apply -f postgres-deployment.yaml
 | 
			
		||||
kubectl apply -f postgres-service.yaml
 | 
			
		||||
 | 
			
		||||
# Wait for PostgreSQL to be ready
 | 
			
		||||
echo "Waiting for PostgreSQL to be ready..."
 | 
			
		||||
kubectl wait --for=condition=ready pod -l app=postgres -n my-app --timeout=120s
 | 
			
		||||
 | 
			
		||||
# Deploy application
 | 
			
		||||
echo "Creating Deployment..."
 | 
			
		||||
kubectl apply -f deployment.yaml
 | 
			
		||||
 | 
			
		||||
echo "Creating StatefulSet..."
 | 
			
		||||
kubectl apply -f statefulset.yaml
 | 
			
		||||
 | 
			
		||||
echo "Creating Service..."
 | 
			
		||||
kubectl apply -f service.yaml
 | 
			
		||||
 | 
			
		||||
# Wait for LoadBalancer IP (Docker Desktop uses host network so it's usually localhost)
 | 
			
		||||
echo "Waiting for LoadBalancer to obtain an external IP (or localhost for Docker Desktop)..."
 | 
			
		||||
external_ip=""
 | 
			
		||||
attempt=0
 | 
			
		||||
max_attempts=4
 | 
			
		||||
 | 
			
		||||
while [ -z "$external_ip" ] && [ $attempt -lt $max_attempts ]; do
 | 
			
		||||
    sleep 10
 | 
			
		||||
    attempt=$((attempt+1))
 | 
			
		||||
    external_ip=$(kubectl get svc web-app-service -n my-app --template="{{range .status.loadBalancer.ingress}}{{.ip}}{{end}}" 2>/dev/null)
 | 
			
		||||
    
 | 
			
		||||
    if [ -z "$external_ip" ]; then
 | 
			
		||||
        echo "Waiting for external IP... Attempt $attempt of $max_attempts"
 | 
			
		||||
    fi
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
# Fallback to localhost if no external IP is found (common in Docker Desktop)
 | 
			
		||||
if [ -z "$external_ip" ]; then
 | 
			
		||||
    external_ip="localhost"
 | 
			
		||||
    echo ""
 | 
			
		||||
    echo "=========================================================="
 | 
			
		||||
    echo "Could not get external IP from LoadBalancer. Defaulting to localhost."
 | 
			
		||||
    echo "You can try accessing your app at: http://localhost:80"
 | 
			
		||||
    echo "Or check service status manually with:"
 | 
			
		||||
    echo "kubectl get svc web-app-service -n my-app"
 | 
			
		||||
    echo "=========================================================="
 | 
			
		||||
else
 | 
			
		||||
    echo ""
 | 
			
		||||
    echo "=========================================================="
 | 
			
		||||
    echo "Application deployed successfully!"
 | 
			
		||||
    echo "You can try accessing your app at: http://localhost:80"
 | 
			
		||||
    echo "=========================================================="
 | 
			
		||||
fi
 | 
			
		||||
							
								
								
									
										59
									
								
								z2/statefulset.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								z2/statefulset.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,59 @@
 | 
			
		||||
apiVersion: apps/v1
 | 
			
		||||
kind: StatefulSet
 | 
			
		||||
metadata:
 | 
			
		||||
  name: stateful-app
 | 
			
		||||
  namespace: my-app
 | 
			
		||||
spec:
 | 
			
		||||
  serviceName: "stateful-app"
 | 
			
		||||
  replicas: 1
 | 
			
		||||
  selector:
 | 
			
		||||
    matchLabels:
 | 
			
		||||
      app: stateful-app
 | 
			
		||||
  template:
 | 
			
		||||
    metadata:
 | 
			
		||||
      labels:
 | 
			
		||||
        app: stateful-app
 | 
			
		||||
    spec:
 | 
			
		||||
      containers:
 | 
			
		||||
        - name: stateful-app-container
 | 
			
		||||
          image: antonin193/simple-web-app:latest
 | 
			
		||||
          imagePullPolicy: Always
 | 
			
		||||
          ports:
 | 
			
		||||
            - containerPort: 5000
 | 
			
		||||
          env:
 | 
			
		||||
            - name: DB_HOST
 | 
			
		||||
              value: postgres
 | 
			
		||||
            - name: DB_PORT
 | 
			
		||||
              value: "5432"
 | 
			
		||||
            - name: DB_NAME
 | 
			
		||||
              value: postgres
 | 
			
		||||
            - name: DB_USER
 | 
			
		||||
              value: postgres
 | 
			
		||||
            - name: DB_PASS
 | 
			
		||||
              value: postgres
 | 
			
		||||
          resources:
 | 
			
		||||
            requests:
 | 
			
		||||
              memory: "64Mi"
 | 
			
		||||
              cpu: "100m"
 | 
			
		||||
            limits:
 | 
			
		||||
              memory: "128Mi"
 | 
			
		||||
              cpu: "200m"
 | 
			
		||||
          volumeMounts:
 | 
			
		||||
            - name: app-storage
 | 
			
		||||
              mountPath: /data  
 | 
			
		||||
          readinessProbe:
 | 
			
		||||
            httpGet:
 | 
			
		||||
              path: /
 | 
			
		||||
              port: 5000
 | 
			
		||||
            initialDelaySeconds: 15
 | 
			
		||||
            periodSeconds: 20
 | 
			
		||||
  volumeClaimTemplates:
 | 
			
		||||
    - metadata:
 | 
			
		||||
        name: app-storage
 | 
			
		||||
      spec:
 | 
			
		||||
        accessModes:
 | 
			
		||||
          - ReadWriteOnce
 | 
			
		||||
        storageClassName: manual
 | 
			
		||||
        resources:
 | 
			
		||||
          requests:
 | 
			
		||||
            storage: 1Gi
 | 
			
		||||
							
								
								
									
										55
									
								
								z2/stop-app.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								z2/stop-app.sh
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,55 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
# Delete Kubernetes objects in reverse order
 | 
			
		||||
 | 
			
		||||
# Check if REGION environment variable is set
 | 
			
		||||
if [ -z "$REGION" ]; then
 | 
			
		||||
    # Default region if not set
 | 
			
		||||
    export REGION="westeurope"
 | 
			
		||||
    echo "No region specified. Using default region: $REGION"
 | 
			
		||||
else
 | 
			
		||||
    echo "Stopping application in region: $REGION"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# For AKS deployments, make sure we're connected to the right cluster
 | 
			
		||||
if command -v az &> /dev/null; then
 | 
			
		||||
    echo "Checking AKS connection for region $REGION..."
 | 
			
		||||
    # You might need to adjust these parameters based on your specific Azure setup
 | 
			
		||||
    RESOURCE_GROUP="flask-rg-$REGION"
 | 
			
		||||
    CLUSTER_NAME="flask-aks-$REGION"
 | 
			
		||||
    
 | 
			
		||||
    # Try to connect to the Azure cluster
 | 
			
		||||
    if ! az aks get-credentials --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME --overwrite-existing 2>/dev/null; then
 | 
			
		||||
        echo "Warning: Could not connect to AKS cluster in $REGION. Continuing with current kubectl context."
 | 
			
		||||
    else
 | 
			
		||||
        echo "Successfully connected to AKS cluster in $REGION"
 | 
			
		||||
    fi
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
echo "Stopping application..."
 | 
			
		||||
 | 
			
		||||
# Delete Service first to stop incoming traffic
 | 
			
		||||
kubectl delete -f service.yaml 2>/dev/null || echo "Service could not be deleted or does not exist."
 | 
			
		||||
 | 
			
		||||
# Delete StatefulSet and wait for pods to terminate
 | 
			
		||||
kubectl delete -f statefulset.yaml 2>/dev/null || echo "StatefulSet could not be deleted or does not exist."
 | 
			
		||||
 | 
			
		||||
# Delete Deployment
 | 
			
		||||
kubectl delete -f deployment.yaml 2>/dev/null || echo "Deployment could not be deleted or does not exist."
 | 
			
		||||
 | 
			
		||||
# Delete PostgreSQL objects
 | 
			
		||||
kubectl delete -f postgres-service.yaml 2>/dev/null || echo "PostgreSQL service could not be deleted or does not exist."
 | 
			
		||||
kubectl delete -f postgres-deployment.yaml 2>/dev/null || echo "PostgreSQL deployment could not be deleted or does not exist."
 | 
			
		||||
 | 
			
		||||
# Wait for pods to terminate
 | 
			
		||||
echo "Waiting for pods to terminate..."
 | 
			
		||||
kubectl wait --for=delete pod --selector=app=web-app -n my-app --timeout=60s 2>/dev/null || true
 | 
			
		||||
kubectl wait --for=delete pod --selector=app=stateful-app -n my-app --timeout=60s 2>/dev/null || true
 | 
			
		||||
kubectl wait --for=delete pod --selector=app=postgres -n my-app --timeout=60s 2>/dev/null || true
 | 
			
		||||
 | 
			
		||||
# Delete PersistentVolume and PersistentVolumeClaim
 | 
			
		||||
kubectl delete -f persistent-storage.yaml 2>/dev/null || echo "PersistentVolume and PersistentVolumeClaim could not be deleted or do not exist."
 | 
			
		||||
 | 
			
		||||
# Delete Namespace (this will delete all resources in the namespace)
 | 
			
		||||
kubectl delete -f namespace.yaml 2>/dev/null || echo "Namespace could not be deleted or does not exist."
 | 
			
		||||
 | 
			
		||||
echo "Application stopped in region $REGION: All Kubernetes objects have been deleted."
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user