From 0ed027d8f65484a765f57dd5aee85bffab8531c8 Mon Sep 17 00:00:00 2001 From: Antonin Filippi Date: Wed, 23 Apr 2025 07:19:30 +0000 Subject: [PATCH] =?UTF-8?q?T=C3=A9l=C3=A9verser=20les=20fichiers=20vers=20?= =?UTF-8?q?"z2"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- z2/Dockerfile | 12 ++++ z2/README.md | 31 ++++++++++ z2/app.py | 112 ++++++++++++++++++++++++++++++++++++ z2/deployment.yaml | 50 ++++++++++++++++ z2/namespace.yaml | 4 ++ z2/persistent-storage.yaml | 12 ++++ z2/postgres-deployment.yaml | 33 +++++++++++ z2/postgres-service.yaml | 12 ++++ z2/prepare-app.sh | 11 ++++ z2/requirements.txt | 4 ++ z2/service.yaml | 13 +++++ z2/start-app.sh | 29 ++++++++++ z2/stop-app.sh | 24 ++++++++ 13 files changed, 347 insertions(+) create mode 100644 z2/Dockerfile create mode 100644 z2/README.md create mode 100644 z2/app.py create mode 100644 z2/deployment.yaml create mode 100644 z2/namespace.yaml create mode 100644 z2/persistent-storage.yaml create mode 100644 z2/postgres-deployment.yaml create mode 100644 z2/postgres-service.yaml create mode 100644 z2/prepare-app.sh create mode 100644 z2/requirements.txt create mode 100644 z2/service.yaml create mode 100644 z2/start-app.sh create mode 100644 z2/stop-app.sh diff --git a/z2/Dockerfile b/z2/Dockerfile new file mode 100644 index 0000000..3578b8f --- /dev/null +++ b/z2/Dockerfile @@ -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"] \ No newline at end of file diff --git a/z2/README.md b/z2/README.md new file mode 100644 index 0000000..98530e0 --- /dev/null +++ b/z2/README.md @@ -0,0 +1,31 @@ +# Shopping List Web App on Kubernetes (Local) + +This project deploys a Flask-based shopping list app with PostgreSQL on a local Kubernetes cluster (Docker Desktop). + +## Prerequisites +- Docker Desktop with Kubernetes enabled +- kubectl configured for `docker-desktop` context + +## Files +- `namespace.yaml`: Kubernetes namespace +- `persistent-storage.yaml`: PVC for Postgres data +- `postgres-deployment.yaml` / `postgres-service.yaml`: Postgres setup +- `deployment.yaml` / `service.yaml`: Web app setup +- `prepare-app.sh`: build Docker image +- `start-app.sh`: apply Kubernetes resources +- `stop-app.sh`: delete all resources + +## Usage +1. Build the image: + ```bash + ./prepare-app.sh + +2. Deploy to Kubernetes: + ```bash + ./start-app.sh + +3. Access the app: +Open http://localhost:80 in your browser + +4. Clean up : + ./stop-app.sh \ No newline at end of file diff --git a/z2/app.py b/z2/app.py new file mode 100644 index 0000000..3b9df90 --- /dev/null +++ b/z2/app.py @@ -0,0 +1,112 @@ +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.getenv('DB_HOST', 'localhost') +DB_PORT = os.getenv('DB_PORT', '5432') +DB_NAME = os.getenv('DB_NAME', 'postgres') +DB_USER = os.getenv('DB_USER', 'postgres') +DB_PASS = os.getenv('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 +) + +# Initialize database schema +with connection_pool.getconn() as conn: + 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() + +HTML = """ + + + + Shopping List + + + +

Shopping List

+
+ + + +
+ + + +""" + +@app.route('/') +def index(): + conn = connection_pool.getconn() + with conn.cursor() as cur: + cur.execute("SELECT id, item, quantity, purchased FROM shopping_items ORDER BY purchased, id") + items = cur.fetchall() + connection_pool.putconn(conn) + return render_template_string(HTML, items=items) + +@app.route('/add', methods=['POST']) +def add_item(): + item = request.form['item'] + qty = int(request.form.get('quantity', 1)) + conn = connection_pool.getconn() + with conn.cursor() as cur: + cur.execute("INSERT INTO shopping_items (item, quantity) VALUES (%s, %s)", (item, qty)) + conn.commit() + connection_pool.putconn(conn) + return redirect(url_for('index')) + +@app.route('/toggle/', methods=['POST']) +def toggle_item(item_id): + conn = connection_pool.getconn() + with conn.cursor() as cur: + cur.execute("UPDATE shopping_items SET purchased=NOT purchased WHERE id=%s", (item_id,)) + conn.commit() + connection_pool.putconn(conn) + return redirect(url_for('index')) + +@app.route('/delete/', methods=['POST']) +def delete_item(item_id): + conn = connection_pool.getconn() + with conn.cursor() as cur: + cur.execute("DELETE FROM shopping_items WHERE id=%s", (item_id,)) + conn.commit() + connection_pool.putconn(conn) + return redirect(url_for('index')) + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5000) \ No newline at end of file diff --git a/z2/deployment.yaml b/z2/deployment.yaml new file mode 100644 index 0000000..991b621 --- /dev/null +++ b/z2/deployment.yaml @@ -0,0 +1,50 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: web + namespace: my-app +spec: + replicas: 2 + selector: + matchLabels: + app: web + template: + metadata: + labels: + app: web + spec: + initContainers: + - name: wait-for-postgres + image: busybox + command: + - sh + - -c + - | + until nc -z postgres 5432; do + echo "Waiting for PostgreSQL..." + sleep 2 + done + containers: + - name: web + # use the LOCAL image you just built, not the one on Docker Hub + image: simple-web-app:latest + imagePullPolicy: IfNotPresent + 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 + readinessProbe: + httpGet: + path: / + port: 5000 + initialDelaySeconds: 15 + periodSeconds: 10 diff --git a/z2/namespace.yaml b/z2/namespace.yaml new file mode 100644 index 0000000..ea5c7ee --- /dev/null +++ b/z2/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: my-app \ No newline at end of file diff --git a/z2/persistent-storage.yaml b/z2/persistent-storage.yaml new file mode 100644 index 0000000..e670f82 --- /dev/null +++ b/z2/persistent-storage.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: data-pvc + namespace: my-app +spec: + accessModes: + - ReadWriteOnce + storageClassName: docker-desktop + resources: + requests: + storage: 1Gi \ No newline at end of file diff --git a/z2/postgres-deployment.yaml b/z2/postgres-deployment.yaml new file mode 100644 index 0000000..7cefcf7 --- /dev/null +++ b/z2/postgres-deployment.yaml @@ -0,0 +1,33 @@ +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 + ports: + - containerPort: 5432 + env: + - name: POSTGRES_USER + value: postgres + - name: POSTGRES_PASSWORD + value: postgres + - name: POSTGRES_DB + value: postgres + volumeMounts: + - name: postgres-storage + mountPath: /var/lib/postgresql/data + volumes: + - name: postgres-storage + emptyDir: {} \ No newline at end of file diff --git a/z2/postgres-service.yaml b/z2/postgres-service.yaml new file mode 100644 index 0000000..38d775a --- /dev/null +++ b/z2/postgres-service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: postgres + namespace: my-app +spec: + selector: + app: postgres + ports: + - port: 5432 + targetPort: 5432 + clusterIP: None \ No newline at end of file diff --git a/z2/prepare-app.sh b/z2/prepare-app.sh new file mode 100644 index 0000000..fc2742b --- /dev/null +++ b/z2/prepare-app.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# Prepare the environment: build Docker image + +set -e + +IMAGE_NAME="simple-web-app:latest" + +echo "Building Docker image ${IMAGE_NAME}..." +docker build -t ${IMAGE_NAME} . + +echo "Preparation complete." \ No newline at end of file diff --git a/z2/requirements.txt b/z2/requirements.txt new file mode 100644 index 0000000..9856f32 --- /dev/null +++ b/z2/requirements.txt @@ -0,0 +1,4 @@ +Flask>=2.0.0 +Werkzeug>=2.0.0 +gunicorn>=20.1.0 +psycopg2-binary>=2.9.3 \ No newline at end of file diff --git a/z2/service.yaml b/z2/service.yaml new file mode 100644 index 0000000..c2dd6db --- /dev/null +++ b/z2/service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: web + namespace: my-app +spec: + type: NodePort + selector: + app: web + ports: + - port: 80 + targetPort: 5000 + nodePort: 30080 diff --git a/z2/start-app.sh b/z2/start-app.sh new file mode 100644 index 0000000..2c23a1f --- /dev/null +++ b/z2/start-app.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# Deploy the application to local Kubernetes + +set -e + +# Ensure kubectl uses the local Docker Desktop cluster +kubectl config use-context docker-desktop + +NAMESPACE="my-app" + +echo "Creating namespace..." +kubectl apply -f namespace.yaml + +# (optional) if you’ve switched to emptyDir for Postgres storage, skip PVC +# echo "Creating PersistentVolumeClaim..." +# kubectl apply -f persistent-storage.yaml + +echo "Deploying PostgreSQL..." +kubectl apply -f postgres-deployment.yaml +kubectl apply -f postgres-service.yaml + +echo "Waiting for PostgreSQL pod(s) to be ready..." +kubectl wait --for=condition=ready pod -l app=postgres -n ${NAMESPACE} --timeout=120s + +echo "Deploying web application..." +kubectl apply -f deployment.yaml +kubectl apply -f service.yaml + +echo "Deployment complete. Access the app at http://localhost:30080" diff --git a/z2/stop-app.sh b/z2/stop-app.sh new file mode 100644 index 0000000..efcb4df --- /dev/null +++ b/z2/stop-app.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# Remove the application from local Kubernetes + +set -e + +kubectl config use-context docker-desktop + +NAMESPACE="my-app" + +echo "Deleting web application..." +kubectl delete -f service.yaml -n ${NAMESPACE} --ignore-not-found +kubectl delete -f deployment.yaml -n ${NAMESPACE} --ignore-not-found + +echo "Deleting PostgreSQL..." +kubectl delete -f postgres-service.yaml -n ${NAMESPACE} --ignore-not-found +kubectl delete -f postgres-deployment.yaml -n ${NAMESPACE} --ignore-not-found + +echo "Deleting storage..." +kubectl delete -f persistent-storage.yaml -n ${NAMESPACE} --ignore-not-found + +echo "Deleting namespace..." +kubectl delete -f namespace.yaml --ignore-not-found + +echo "Cleanup complete." \ No newline at end of file