z2 - Kubernetes
This commit is contained in:
parent
12643a8a97
commit
56e08dbe7b
45
z2/README.md
Normal file
45
z2/README.md
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# ZKT Zadanie 2 - Kubernetes
|
||||||
|
|
||||||
|
## Opis aplikacie
|
||||||
|
Aplikacia predstavuje TODO list, kde pouzivatel moze pridavat, upravovat a mazat ulohy. Data su ukladane do databazy
|
||||||
|
MongoDB.
|
||||||
|
|
||||||
|
Aplikacia pozostava z backendu (Node.js) a databazy (MongoDB), ktore su nasadene v Kubernetes klastri.
|
||||||
|
|
||||||
|
## Pouzite kontajnery
|
||||||
|
- Node.js (backend + frontend)
|
||||||
|
- MongoDB (databaza)
|
||||||
|
|
||||||
|
## Kubernetes objekty
|
||||||
|
- Namespace – izolacia aplikacie
|
||||||
|
- Deployment – backend aplikacia
|
||||||
|
- StatefulSet – MongoDB databaza
|
||||||
|
- Service – komunikacia medzi komponentami
|
||||||
|
- PersistentVolume – trvale ulozisko
|
||||||
|
- PersistentVolumeClaim – poziadavka na ulozisko
|
||||||
|
|
||||||
|
## Virtualne siete a zvazky
|
||||||
|
- Service "web-service" spristupnuje aplikaciu
|
||||||
|
- Service "mongo" umoznuje komunikaciu s databazou
|
||||||
|
- MongoDB pouziva volume /data/db
|
||||||
|
|
||||||
|
## Konfiguracia kontajnerov
|
||||||
|
- backend bezi na porte 3000
|
||||||
|
- MongoDB bezi na porte 27017
|
||||||
|
- komunikacia prebieha cez nazvy sluzieb (mongo)
|
||||||
|
|
||||||
|
## Spustenie
|
||||||
|
|
||||||
|
./prepare-app.sh
|
||||||
|
./start-app.sh
|
||||||
|
|
||||||
|
## Zastavenie
|
||||||
|
|
||||||
|
./stop-app.sh
|
||||||
|
|
||||||
|
## Pristup k aplikacii
|
||||||
|
|
||||||
|
http://localhost:30007
|
||||||
|
|
||||||
|
## Poznamka
|
||||||
|
Aplikacia bola upravena zo zadania c.1 pre nasadenie do Kubernetes.
|
||||||
11
z2/app/Dockerfile
Normal file
11
z2/app/Dockerfile
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
FROM node:18
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN npm init -y && npm install express mongoose cors
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
CMD ["node", "server.js"]
|
||||||
83
z2/app/index.html
Normal file
83
z2/app/index.html
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>TODO LIST</title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>TODO List - Kubernetes</h1>
|
||||||
|
|
||||||
|
<input id="task" placeholder="napis ulohu">
|
||||||
|
<input id="due" type="datetime-local">
|
||||||
|
<button onclick="addTask()">Pridaj</button>
|
||||||
|
|
||||||
|
<div id="list"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
async function loadTasks() {
|
||||||
|
const res = await fetch('http://localhost:30007/tasks');
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
const list = document.getElementById('list');
|
||||||
|
list.innerHTML = "";
|
||||||
|
|
||||||
|
data.forEach(t => {
|
||||||
|
const li = document.createElement('div');
|
||||||
|
li.className = "task";
|
||||||
|
|
||||||
|
li.innerHTML = `
|
||||||
|
<span>
|
||||||
|
${t.text} |
|
||||||
|
${t.due ? t.due.split('T')[0] : ""} |
|
||||||
|
${t.due ? t.due.split('T')[1] : ""}
|
||||||
|
</span>
|
||||||
|
<div class="buttons">
|
||||||
|
<button class="delete" onclick="deleteTask('${t._id}')">X</button>
|
||||||
|
<button class="edit" onclick="editTask('${t._id}', '${t.text}', '${t.due}')">Edit</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
list.appendChild(li);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addTask() {
|
||||||
|
const text = document.getElementById('task').value;
|
||||||
|
const due = document.getElementById('due').value;
|
||||||
|
|
||||||
|
await fetch('http://localhost:30007/tasks', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: JSON.stringify({text, due})
|
||||||
|
});
|
||||||
|
|
||||||
|
loadTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteTask(id) {
|
||||||
|
await fetch(`http://localhost:30007/tasks/${id}`, {
|
||||||
|
method: 'DELETE'
|
||||||
|
});
|
||||||
|
loadTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function editTask(id, oldText, oldDue) {
|
||||||
|
const text = prompt("novy text:", oldText);
|
||||||
|
const due = prompt("novy datum:", oldDue);
|
||||||
|
|
||||||
|
await fetch(`http://localhost:30007/tasks/${id}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: JSON.stringify({text, due})
|
||||||
|
});
|
||||||
|
|
||||||
|
loadTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadTasks();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
59
z2/app/server.js
Normal file
59
z2/app/server.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
const cors = require('cors');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(cors());
|
||||||
|
|
||||||
|
|
||||||
|
app.use(express.static(path.join(__dirname)));
|
||||||
|
|
||||||
|
mongoose.connect('mongodb://mongo:27017/todo');
|
||||||
|
|
||||||
|
const Task = mongoose.model('Task', {
|
||||||
|
text: String,
|
||||||
|
due: String
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
app.get('/', (req, res) => {
|
||||||
|
res.sendFile(path.join(__dirname, 'index.html'));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
app.get('/tasks', async (req, res) => {
|
||||||
|
const tasks = await Task.find();
|
||||||
|
res.json(tasks);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
app.post('/tasks', async (req, res) => {
|
||||||
|
const task = new Task({
|
||||||
|
text: req.body.text,
|
||||||
|
due: req.body.due
|
||||||
|
});
|
||||||
|
await task.save();
|
||||||
|
res.send("ok");
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
app.delete('/tasks/:id', async (req, res) => {
|
||||||
|
await Task.findByIdAndDelete(req.params.id);
|
||||||
|
res.send("deleted");
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
app.put('/tasks/:id', async (req, res) => {
|
||||||
|
await Task.findByIdAndUpdate(req.params.id, {
|
||||||
|
text: req.body.text,
|
||||||
|
due: req.body.due
|
||||||
|
});
|
||||||
|
res.send("updated");
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(3000, () => {
|
||||||
|
console.log("backend bezi na porte 3000");
|
||||||
|
});
|
||||||
65
z2/app/style.css
Normal file
65
z2/app/style.css
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
body {
|
||||||
|
font-family: Arial;
|
||||||
|
background: #f4f6f8;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
background: white;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
width: 400px;
|
||||||
|
margin: 50px auto; /* vycentrovanie */
|
||||||
|
box-shadow: 0 0 10px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
text-align: center; /* nadpis na stred */
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
padding: 8px;
|
||||||
|
margin: 5px 0;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 8px;
|
||||||
|
margin-top: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#list {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
background: #eee;
|
||||||
|
padding: 8px;
|
||||||
|
margin-top: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task span {
|
||||||
|
text-align: left; /* zarovnanie dolava */
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons button {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete {
|
||||||
|
background: red;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit {
|
||||||
|
background: orange;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
21
z2/k8s/deployment.yaml
Normal file
21
z2/k8s/deployment.yaml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: web-deployment
|
||||||
|
namespace: my-app-ns
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: web
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: web
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: web
|
||||||
|
image: my-web-app
|
||||||
|
imagePullPolicy: Never
|
||||||
|
ports:
|
||||||
|
- containerPort: 3000
|
||||||
4
z2/k8s/namespace.yaml
Normal file
4
z2/k8s/namespace.yaml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: my-app-ns
|
||||||
13
z2/k8s/service.yaml
Normal file
13
z2/k8s/service.yaml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: web-service
|
||||||
|
namespace: my-app-ns
|
||||||
|
spec:
|
||||||
|
type: NodePort
|
||||||
|
selector:
|
||||||
|
app: web
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
targetPort: 3000
|
||||||
|
nodePort: 30007
|
||||||
72
z2/k8s/statefulset.yaml
Normal file
72
z2/k8s/statefulset.yaml
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolume
|
||||||
|
metadata:
|
||||||
|
name: mongo-pv
|
||||||
|
spec:
|
||||||
|
capacity:
|
||||||
|
storage: 1Gi
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
hostPath:
|
||||||
|
path: /data/mongo
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: mongo-pvc
|
||||||
|
namespace: my-app-ns
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 1Gi
|
||||||
|
|
||||||
|
---
|
||||||
|
# 🔥 TOTO JE TEN DÔLEŽITÝ SERVICE
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: mongo
|
||||||
|
namespace: my-app-ns
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: mongo
|
||||||
|
ports:
|
||||||
|
- port: 27017
|
||||||
|
targetPort: 27017
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: mongo
|
||||||
|
namespace: my-app-ns
|
||||||
|
spec:
|
||||||
|
serviceName: "mongo"
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: mongo
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: mongo
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: mongo
|
||||||
|
image: mongo
|
||||||
|
ports:
|
||||||
|
- containerPort: 27017
|
||||||
|
volumeMounts:
|
||||||
|
- name: mongo-storage
|
||||||
|
mountPath: /data/db
|
||||||
|
volumeClaimTemplates:
|
||||||
|
- metadata:
|
||||||
|
name: mongo-storage
|
||||||
|
spec:
|
||||||
|
accessModes: ["ReadWriteOnce"]
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 1Gi
|
||||||
7
z2/prepare-app.sh
Executable file
7
z2/prepare-app.sh
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "building docker image..."
|
||||||
|
|
||||||
|
docker build -t my-web-app ./app
|
||||||
|
|
||||||
|
echo "done"
|
||||||
10
z2/start-app.sh
Executable file
10
z2/start-app.sh
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "starting app..."
|
||||||
|
|
||||||
|
kubectl apply -f k8s/namespace.yaml
|
||||||
|
kubectl apply -f k8s/statefulset.yaml
|
||||||
|
kubectl apply -f k8s/deployment.yaml
|
||||||
|
kubectl apply -f k8s/service.yaml
|
||||||
|
|
||||||
|
echo "app started"
|
||||||
10
z2/stop-app.sh
Executable file
10
z2/stop-app.sh
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "stopping app..."
|
||||||
|
|
||||||
|
kubectl delete -f k8s/service.yaml
|
||||||
|
kubectl delete -f k8s/deployment.yaml
|
||||||
|
kubectl delete -f k8s/statefulset.yaml
|
||||||
|
kubectl delete -f k8s/namespace.yaml
|
||||||
|
|
||||||
|
echo "app stopped"
|
||||||
Loading…
Reference in New Issue
Block a user