diff --git a/z2/Dockerfile b/z2/Dockerfile new file mode 100644 index 0000000..de09ef6 --- /dev/null +++ b/z2/Dockerfile @@ -0,0 +1,7 @@ +FROM python:3.9 +WORKDIR /app +COPY requirements.txt . +RUN pip install -r requirements.txt +COPY . . +CMD ["python", "app.py"] + diff --git a/z2/README.md b/z2/README.md new file mode 100644 index 0000000..f271575 --- /dev/null +++ b/z2/README.md @@ -0,0 +1,89 @@ +# **Správa Používateľov - Kubernetes Aplikácia** + +## **1. Podmienky na nasadenie a spustenie aplikácie** +Aby ste mohli aplikáciu spustiť, musíte mať nainštalované a nakonfigurované: +- **Linux (napr. WSL)** alebo iný terminálový prístup k súborom +- **Docker Desktop s povoleným Kubernetes** ([návod](https://docs.docker.com/desktop/kubernetes/)) +- **kubectl** (súčasť Docker Desktop) +- **Docker CLI** na build obrazu pre Flask aplikáciu + +## **2. Opis aplikácie** +Aplikácia poskytuje jednoduché webové rozhranie na správu používateľov. Používatelia sú uložení v databáze PostgreSQL a aplikácia ich dokáže pridávať alebo mazať cez jednoduché API a frontend napísaný vo Flasku. + +## **3. Opis Kubernetes infraštruktúry** +- Aplikácia beží v mennom priestore `userapp` +- Flask aplikácia beží ako `Deployment` +- PostgreSQL beží ako `StatefulSet` so zachovaním dát pomocou `PersistentVolume` +- Komunikácia prebieha cez služby (`Service`) +- Aplikácia je dostupná cez prehliadač na porte **30001** + +## **4. Zoznam použitých kontajnerov** +- **Flask (Python 3.9)** – webová aplikácia s REST API a HTML šablónou +- **PostgreSQL (13)** – relačná databáza na ukladanie používateľov + +## **5. Zoznam Kubernetes objektov** +| Objekt | Typ | Popis | +|---------------------|--------------------------|--------| +| `userapp` | Namespace | Logický priestor pre všetky objekty | +| `flask-app` | Deployment | Webová aplikácia | +| `postgres` | StatefulSet | Databáza PostgreSQL | +| `postgres-pv` | PersistentVolume | Trvalý disk na hoste (hostPath) | +| `postgres-pvc` | PersistentVolumeClaim | Prepojenie PV s databázou | +| `flask-service` | Service (NodePort) | Prístup na aplikáciu z prehliadača | +| `postgres-service` | Service (ClusterIP) | Interné spojenie medzi Flask a DB | + +## **6. Priebeh nasadenia aplikácie** + +### **Príprava aplikácie (Namespace + databáza + tabuľka):** +```bash +./prepare-app.sh +``` + +### **Spustenie webovej aplikácie:** +```bash +./start-app.sh +``` + +### **Zastavenie aplikácie:** +```bash +./stop-app.sh +``` + +## **7. Ako si pozrieť aplikáciu v prehliadači** +Po spustení aplikácie otvorte v prehliadači: +```arduino +http://localhost:30001 +``` + +## **8. Príklad práce s aplikáciou** + +### **Pridanie používateľa:** +1. Otvorte aplikáciu v prehliadači +2. Zadajte meno do textového poľa +3. Kliknite na tlačidlo **"Pridať"** +4. Používateľ sa uloží do databázy a zobrazí v zozname + +### **Odstránenie používateľa:** +1. Kliknite na tlačidlo **"Odstrániť"** pri konkrétnom používateľovi +2. Používateľ sa odstráni z databázy + +## **9. Dôležité poznámky** +- PostgreSQL uchováva dáta v `hostPath` priečinku `/data/postgres` +- Flask obraz je buildovaný **lokálne**, preto v `deployment.yaml` je: +```yaml +imagePullPolicy: Never +``` +- Aplikácia **neobsahuje pgAdmin**, ale všetko funguje bez neho + +## **10. Overenie funkčnosti** +Over stav podov: +```bash +kubectl get pods -n userapp +``` +Očakávaný výstup: +``` +postgres-0 Running +flask-app-xxx Running +``` + +Po úspešnom spustení je aplikácia pripravená na používanie a odovzdanie. \ No newline at end of file diff --git a/z2/app.py b/z2/app.py new file mode 100644 index 0000000..abdace5 --- /dev/null +++ b/z2/app.py @@ -0,0 +1,58 @@ +from flask import Flask, render_template, request, jsonify +import psycopg2 +import os + +app = Flask(__name__, template_folder="templates", static_folder="static") + +# Подключение к БД +def get_db_connection(): + conn = psycopg2.connect( + host="postgres-service", + database="mydatabase", + user="postgres", + password=os.getenv("POSTGRES_PASSWORD", "mysecretpassword") + ) + return conn + +# Главная страница с HTML +@app.route("/") +def home(): + conn = get_db_connection() + cur = conn.cursor() + cur.execute("SELECT id, name FROM users;") + users = cur.fetchall() + cur.close() + conn.close() + return render_template("index.html", users=users) + +# API для добавления пользователя +@app.route("/add_user", methods=["POST"]) +def add_user(): + name = request.form.get("name") + if name: + conn = get_db_connection() + cur = conn.cursor() + cur.execute("INSERT INTO users (name) VALUES (%s) RETURNING id;", (name,)) + user_id = cur.fetchone()[0] + conn.commit() + cur.close() + conn.close() + return jsonify({"id": user_id, "name": name}), 201 + return jsonify({"error": "Имя не может быть пустым"}), 400 + +# API для удаления пользователя +@app.route("/delete_user/", methods=["POST"]) +def delete_user(user_id): + conn = get_db_connection() + cur = conn.cursor() + cur.execute("DELETE FROM users WHERE id = %s RETURNING id;", (user_id,)) + deleted = cur.fetchone() + conn.commit() + cur.close() + conn.close() + if deleted: + return jsonify({"message": "Пользователь удален"}) + return jsonify({"error": "Пользователь не найден"}), 404 + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=5000, debug=True) diff --git a/z2/deployment.yaml b/z2/deployment.yaml new file mode 100644 index 0000000..e8e87b7 --- /dev/null +++ b/z2/deployment.yaml @@ -0,0 +1,24 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: flask-app + namespace: userapp +spec: + replicas: 1 + selector: + matchLabels: + app: flask-app + template: + metadata: + labels: + app: flask-app + spec: + containers: + - name: flask-container + image: flask-user-app:latest + imagePullPolicy: Never + ports: + - containerPort: 5000 + env: + - name: POSTGRES_PASSWORD + value: "mysecretpassword" diff --git a/z2/index.html b/z2/index.html new file mode 100644 index 0000000..8252a7e --- /dev/null +++ b/z2/index.html @@ -0,0 +1,52 @@ + + + + + + + Správa používateľov + + + + +
+

Zoznam používateľov

+ + +

Pridať používateľa

+ + +
+ + + + + \ No newline at end of file diff --git a/z2/namespace.yaml b/z2/namespace.yaml new file mode 100644 index 0000000..fca8b44 --- /dev/null +++ b/z2/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: userapp diff --git a/z2/prepare-app.sh b/z2/prepare-app.sh new file mode 100644 index 0000000..c1080e6 --- /dev/null +++ b/z2/prepare-app.sh @@ -0,0 +1,21 @@ +#!/bin/bash +set -e + +echo "📦 Vytvárame namespace a databázu..." + +kubectl apply -f namespace.yaml +kubectl apply -f statefulset.yaml + +echo "⏳ Čakáme na spustenie PostgreSQL..." +sleep 5 + +POSTGRES_POD=postgres-0 + +kubectl exec -n userapp $POSTGRES_POD -- \ + psql -U postgres -d mydatabase -c " + CREATE TABLE IF NOT EXISTS users ( + id SERIAL PRIMARY KEY, + name TEXT NOT NULL + );" + +echo "✅ Databázová tabuľka 'users' bola vytvorená." diff --git a/z2/requirements.txt b/z2/requirements.txt new file mode 100644 index 0000000..73adb22 --- /dev/null +++ b/z2/requirements.txt @@ -0,0 +1,3 @@ +flask +psycopg2-binary + diff --git a/z2/service.yaml b/z2/service.yaml new file mode 100644 index 0000000..40a5a59 --- /dev/null +++ b/z2/service.yaml @@ -0,0 +1,42 @@ +apiVersion: v1 +kind: Service +metadata: + name: flask-service + namespace: userapp +spec: + selector: + app: flask-app + type: NodePort + ports: + - protocol: TCP + port: 5000 + targetPort: 5000 + nodePort: 30001 +--- +apiVersion: v1 +kind: Service +metadata: + name: postgres-service + namespace: userapp +spec: + selector: + app: postgres + ports: + - protocol: TCP + port: 5432 + targetPort: 5432 +--- +apiVersion: v1 +kind: Service +metadata: + name: pgadmin-service + namespace: userapp +spec: + selector: + app: pgadmin + type: NodePort + ports: + - protocol: TCP + port: 80 + targetPort: 80 + nodePort: 30002 diff --git a/z2/start-app.sh b/z2/start-app.sh new file mode 100644 index 0000000..aae6b74 --- /dev/null +++ b/z2/start-app.sh @@ -0,0 +1,13 @@ +#!/bin/bash +set -e + +echo "🚀 Nasadzujeme webovú aplikáciu..." + +kubectl apply -f deployment.yaml +kubectl apply -f service.yaml + +echo "⏳ Čakám 10 sekúnd na spustenie webovej aplikácie..." +sleep 10 + +echo "🌐 Aplikácia beží na:" +echo "http://localhost:30001" diff --git a/z2/statefulset.yaml b/z2/statefulset.yaml new file mode 100644 index 0000000..e563727 --- /dev/null +++ b/z2/statefulset.yaml @@ -0,0 +1,62 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: postgres-pv + namespace: userapp +spec: + accessModes: + - ReadWriteOnce + capacity: + storage: 1Gi + hostPath: + path: /data/postgres +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: postgres-pvc + namespace: userapp +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: postgres + namespace: userapp +spec: + serviceName: "postgres-service" + replicas: 1 + selector: + matchLabels: + app: postgres + template: + metadata: + labels: + app: postgres + spec: + containers: + - name: postgres + image: postgres:13 + ports: + - containerPort: 5432 + env: + - name: POSTGRES_PASSWORD + value: "mysecretpassword" + - name: POSTGRES_DB + value: "mydatabase" + volumeMounts: + - name: postgres-storage + mountPath: /var/lib/postgresql/data + volumeClaimTemplates: + - metadata: + name: postgres-storage + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi diff --git a/z2/stop-app.sh b/z2/stop-app.sh new file mode 100644 index 0000000..d36ba52 --- /dev/null +++ b/z2/stop-app.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -e + +kubectl delete -f service.yaml || true +kubectl delete -f deployment.yaml || true +kubectl delete -f statefulset.yaml || true +kubectl delete -f namespace.yaml || true + +echo "✅ Aplikácia bola zastavená a vymazaná." diff --git a/z2/styles.css b/z2/styles.css new file mode 100644 index 0000000..933a433 --- /dev/null +++ b/z2/styles.css @@ -0,0 +1,46 @@ +body { + font-family: Arial, sans-serif; + background-color: #f4f4f4; + text-align: center; +} + +.container { + width: 50%; + margin: 50px auto; + background: white; + padding: 20px; + border-radius: 10px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); +} + +h1 { + font-size: 24px; +} + +ul { + list-style: none; + padding: 0; +} + +li { + display: flex; + justify-content: space-between; + padding: 10px; + background: #fff; + margin: 5px 0; + border-radius: 5px; + border: 1px solid #ddd; +} + +button { + background-color: #28a745; + color: white; + border: none; + padding: 5px 10px; + cursor: pointer; + border-radius: 5px; +} + +button.delete-btn { + background-color: #dc3545; +} \ No newline at end of file