Загрузить файлы в «z2»
This commit is contained in:
parent
17fe85d77c
commit
ba9904a5e3
7
z2/Dockerfile
Normal file
7
z2/Dockerfile
Normal file
@ -0,0 +1,7 @@
|
||||
FROM python:3.9
|
||||
WORKDIR /app
|
||||
COPY requirements.txt .
|
||||
RUN pip install -r requirements.txt
|
||||
COPY . .
|
||||
CMD ["python", "app.py"]
|
||||
|
89
z2/README.md
Normal file
89
z2/README.md
Normal file
@ -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.
|
58
z2/app.py
Normal file
58
z2/app.py
Normal file
@ -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/<int:user_id>", 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)
|
24
z2/deployment.yaml
Normal file
24
z2/deployment.yaml
Normal file
@ -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"
|
52
z2/index.html
Normal file
52
z2/index.html
Normal file
@ -0,0 +1,52 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="sk">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Správa používateľov</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Zoznam používateľov</h1>
|
||||
<ul id="user-list">
|
||||
{% for user in users %}
|
||||
<li>
|
||||
{{ user[1] }}
|
||||
<button class="delete-btn" onclick="deleteUser('{{ user[0] }}')">Odstrániť</button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
<h2>Pridať používateľa</h2>
|
||||
<input type="text" id="username" placeholder="Zadajte meno">
|
||||
<button onclick="addUser()">Pridať</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function addUser() {
|
||||
const name = document.getElementById("username").value;
|
||||
if (!name) {
|
||||
alert("Zadajte meno!");
|
||||
return;
|
||||
}
|
||||
fetch("/add_user", {
|
||||
method: "POST",
|
||||
body: new URLSearchParams({ name }),
|
||||
headers: { "Content-Type": "application/x-www-form-urlencoded" }
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(() => location.reload());
|
||||
}
|
||||
|
||||
function deleteUser(id) {
|
||||
fetch(`/delete_user/${id}`, { method: "POST" })
|
||||
.then(response => response.json())
|
||||
.then(() => location.reload());
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
4
z2/namespace.yaml
Normal file
4
z2/namespace.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: userapp
|
21
z2/prepare-app.sh
Normal file
21
z2/prepare-app.sh
Normal file
@ -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á."
|
3
z2/requirements.txt
Normal file
3
z2/requirements.txt
Normal file
@ -0,0 +1,3 @@
|
||||
flask
|
||||
psycopg2-binary
|
||||
|
42
z2/service.yaml
Normal file
42
z2/service.yaml
Normal file
@ -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
|
13
z2/start-app.sh
Normal file
13
z2/start-app.sh
Normal file
@ -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"
|
62
z2/statefulset.yaml
Normal file
62
z2/statefulset.yaml
Normal file
@ -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
|
9
z2/stop-app.sh
Normal file
9
z2/stop-app.sh
Normal file
@ -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á."
|
46
z2/styles.css
Normal file
46
z2/styles.css
Normal file
@ -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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user