diff --git a/Z2/Dockerfile b/Z2/Dockerfile new file mode 100644 index 0000000..24f0109 --- /dev/null +++ b/Z2/Dockerfile @@ -0,0 +1,18 @@ +# Используем легкий образ Python +FROM python:3.10-slim + +# Устанавливаем рабочую директорию +WORKDIR /app + +# Копируем зависимости и устанавливаем их +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Копируем код приложения +COPY . . + +# Открываем порт (по умолчанию Flask — 5000) +EXPOSE 5000 + +# Команда запуска +CMD ["python", "app.py"] diff --git a/Z2/README.md b/Z2/README.md new file mode 100644 index 0000000..51fe746 --- /dev/null +++ b/Z2/README.md @@ -0,0 +1,120 @@ +# To-Do List Application on Kubernetes + +## Application Description + +This is a simple To-Do List web application that allows users to: +- Add new tasks +- Mark tasks as completed +- Delete tasks +- Filter tasks by their status (All, Active, Completed) +- Reorder tasks using drag-and-drop functionality + +The application uses local storage to persist tasks and allows users to manage their daily tasks efficiently. + +## Container Images Used + +1. **todo-web-app:latest**: + - Custom image based on nginx:alpine + - Contains the static web files (HTML, CSS, JavaScript) for the To-Do List application + - Serves the web interface on port 80 + +2. **redis:alpine**: + - Official Redis image based on Alpine Linux + - Used in the StatefulSet for data persistence + - Stores task data on a persistent volume + - Runs on port 6379 + +## Kubernetes Objects + +1. **Namespace** (`todo-app-ns`): + - Isolates all the application resources in a dedicated namespace + +2. **Deployment** (`todo-web-app`): + - Manages the web application containers + - Maintains 2 replicas for high availability + - Defines resource limits and requests + +3. **StatefulSet** (`todo-data-manager`): + - Manages Redis instances for data persistence + - Uses persistent storage to maintain task data + - Ensures data consistency during pod restarts + +4. **PersistentVolume** (`todo-app-pv`): + - Provides storage resources for the application + - Uses local storage on the host at `/mnt/data/todo-app` + - 1GB capacity for storing task data + +5. **PersistentVolumeClaim** (`todo-app-pvc`): + - Claims storage from the PersistentVolume + - Used by the StatefulSet for data persistence + +6. **Services**: + - `todo-web-service`: NodePort service exposing the web application + - `todo-data-service`: Headless service for the StatefulSet + +## Network and Volume Configuration + +### Networks +- The application components communicate within the Kubernetes cluster using services +- `todo-web-service` exposes the web interface externally using NodePort +- `todo-data-service` provides internal DNS-based service discovery for the StatefulSet + +### Volumes +- The application uses a persistent volume mounted at `/mnt/data/todo-app` on the host +- This volume is mounted into the Redis container at `/data` to persist task data +- The volume uses the `manual` storage class with ReadWriteOnce access mode + +## Container Configuration + +### Web Application Container +- Based on nginx:alpine +- Configured to serve static content from `/usr/share/nginx/html` +- Resource limits: 500m CPU, 256Mi memory +- Resource requests: 100m CPU, 128Mi memory + +### Redis Container +- Based on redis:alpine +- Data directory mounted to persistent storage +- Resource limits: 300m CPU, 256Mi memory +- Resource requests: 100m CPU, 128Mi memory + +## How to Use the Application + +### Prerequisites +- Kubernetes cluster (Minikube or similar) +- kubectl configured to communicate with your cluster +- Docker for building the application image + +### Prepare the Application +1. Clone this repository +2. Run the preparation script: + ``` + chmod +x prepare-app.sh + ./prepare-app.sh + ``` + +### Start the Application +1. Run the start script: + ``` + chmod +x start-app.sh + ./start-app.sh + ``` +2. The script will display the URL to access the application + +### Stop the Application +1. Run the stop script: + ``` + chmod +x stop-app.sh + ./stop-app.sh + ``` + +### Accessing the Web Interface +1. After starting the application, note the NodePort displayed in the terminal +2. Open your web browser and navigate to `http://localhost:` +3. You should see the To-Do List application interface +4. Start adding tasks, marking them as completed, or deleting them as needed + +## Troubleshooting +- If pods aren't starting, check the pod status: `kubectl get pods -n todo-app-ns` +- For detailed pod issues: `kubectl describe pod -n todo-app-ns` +- To view logs: `kubectl logs -n todo-app-ns` diff --git a/Z2/app.py b/Z2/app.py new file mode 100644 index 0000000..e8dd825 --- /dev/null +++ b/Z2/app.py @@ -0,0 +1,32 @@ +from flask import Flask, request, jsonify +import json +import os + +app = Flask(__name__) +DATA_FILE = "/data/tasks.json" + +def read_tasks(): + if os.path.exists(DATA_FILE): + with open(DATA_FILE, "r") as f: + return json.load(f) + return [] + +def write_tasks(tasks): + with open(DATA_FILE, "w") as f: + json.dump(tasks, f) + +@app.route("/tasks", methods=["GET"]) +def get_tasks(): + return jsonify(read_tasks()) + +@app.route("/tasks", methods=["POST"]) +def add_task(): + tasks = read_tasks() + new_task = request.json + tasks.append(new_task) + write_tasks(tasks) + return jsonify(new_task), 201 + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=5000) + diff --git a/Z2/backend-deployment.yaml b/Z2/backend-deployment.yaml new file mode 100644 index 0000000..b288cd7 --- /dev/null +++ b/Z2/backend-deployment.yaml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: backend-deployment + namespace: todo-app-ns-v2 # Указываем пространство имен +spec: + replicas: 1 + selector: + matchLabels: + app: backend + template: + metadata: + labels: + app: backend + spec: + containers: + - name: backend + image: jank8000/backend-image:latest + ports: + - containerPort: 8081 diff --git a/Z2/backend-service.yaml b/Z2/backend-service.yaml new file mode 100644 index 0000000..c3aadec --- /dev/null +++ b/Z2/backend-service.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: backend-service + namespace: todo-app-ns-v2 # Указываем пространство имен +spec: + ports: + - port: 8081 + targetPort: 8081 + selector: + app: backend diff --git a/Z2/deployment.yaml b/Z2/deployment.yaml new file mode 100644 index 0000000..580357a --- /dev/null +++ b/Z2/deployment.yaml @@ -0,0 +1,30 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: todo-web-app + namespace: todo-app-ns-v2 + labels: + app: todo-web-app +spec: + replicas: 2 + selector: + matchLabels: + app: todo-web-app + template: + metadata: + labels: + app: todo-web-app + spec: + containers: + - name: todo-web-app + image: todo-web-app:latest + imagePullPolicy: IfNotPresent + ports: + - containerPort: 80 + resources: + limits: + cpu: "500m" + memory: "256Mi" + requests: + cpu: "100m" + memory: "128Mi" diff --git a/Z2/frontend-deployment.yaml b/Z2/frontend-deployment.yaml new file mode 100644 index 0000000..e3272de --- /dev/null +++ b/Z2/frontend-deployment.yaml @@ -0,0 +1,21 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: frontend-deployment + namespace: todo-app-ns-v2 +spec: + replicas: 1 + selector: + matchLabels: + app: todo-web + template: + metadata: + labels: + app: todo-web + spec: + containers: + - name: todo-web + image: jank8000/todo-web:latest # Указываем Docker-образ фронтенда, который вы загрузили + ports: + - containerPort: 80 + diff --git a/Z2/frontend-service.yaml b/Z2/frontend-service.yaml new file mode 100644 index 0000000..035e08e --- /dev/null +++ b/Z2/frontend-service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: frontend-service + namespace: todo-app-ns-v2 +spec: + selector: + app: todo-web + ports: + - protocol: TCP + port: 80 + type: ClusterIP # Сервис будет доступен внутри кластера diff --git a/Z2/index.html b/Z2/index.html new file mode 100644 index 0000000..f98e0d6 --- /dev/null +++ b/Z2/index.html @@ -0,0 +1,30 @@ + + + + + + To-Do List + + + +
+

What should i do list (Reminder)

+

What needs to be done today

+ +
+ + +
+ +
+ + + +
+ +
    +
    + + + + diff --git a/Z2/ingress.yaml b/Z2/ingress.yaml new file mode 100644 index 0000000..f1a803c --- /dev/null +++ b/Z2/ingress.yaml @@ -0,0 +1,18 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: todo-ingress + namespace: todo-app-ns-v2 +spec: + rules: + - host: 147.232.185.30 # Указан ваш внешний IP + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: frontend-service + port: + number: 80 + diff --git a/Z2/namespace.yaml b/Z2/namespace.yaml new file mode 100644 index 0000000..1666873 --- /dev/null +++ b/Z2/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: todo-app-ns-v2 diff --git a/Z2/prepare-app.sh b/Z2/prepare-app.sh new file mode 100644 index 0000000..95ef310 --- /dev/null +++ b/Z2/prepare-app.sh @@ -0,0 +1,684 @@ +#!/bin/bash +set -e + +echo "Preparing To-Do List Application for Kubernetes Deployment" + +# Create directory for application files +mkdir -p ./app + +# Create index.html file +cat > ./app/index.html << 'EOF' + + + + + + To-Do List + + + +
    +

    What should i do list (Reminder)

    +

    What needs to be done today

    + +
    + + +
    + +
    + + + +
    + +
      +
      + + + + +EOF + +# Create style.css file +cat > ./app/style.css << 'EOF' +body { + font-family: "Helvetica Neue", sans-serif; + background: linear-gradient(to bottom, #f7f7f7, #e1e1e1); + display: flex; + align-items: center; + justify-content: center; + min-height: 100vh; +} + +.app-container { + background-color: #ffffff; + padding: 40px; + border-radius: 20px; + width: 100%; + max-width: 500px; + box-shadow: 0px 15px 20px rgba(0, 0, 0, 0.3); + border: 1px solid #d9d9d9; +} + +h1 { + font-family: "Helvetica", sans-serif; + text-align: center; + margin-bottom: 20px; + font-size: 30px; + color: #4a4a4a; + text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); +} + +.subtitle { + font-size: 16px; + color: #6d6d72; + text-align: center; + margin-bottom: 20px; + font-weight: 300; +} + +form { + display: flex; + gap: 10px; + margin-bottom: 20px; +} + +#task-input { + flex-grow: 1; + padding: 15px; + border-radius: 8px; + border: 1px solid #c7c7c7; + background-color: #f0f0f0; + font-size: 18px; + box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.1); +} + +form button { + padding: 15px 25px; + border: none; + background: linear-gradient(to bottom, #4da6ff, #0066cc); + color: #ffffff; + border-radius: 12px; + cursor: pointer; + font-weight: bold; + text-shadow: 0px -1px 1px rgba(0, 0, 0, 0.3); + box-shadow: 0px 5px 8px rgba(0, 0, 0, 0.2); + transition: all 0.2s ease; + font-size: 16px; +} + +form button:active { + background: linear-gradient(to bottom, #0066cc, #4da6ff); + box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2); + transform: translateY(2px); +} + +.filter-buttons { + display: flex; + justify-content: space-around; + margin: 20px 0; +} + +.filter { + background: linear-gradient(to bottom, #e6e6e6, #d1d1d1); + border: 1px solid #c3c3c3; + padding: 10px 20px; + color: #4a4a4a; + border-radius: 12px; + cursor: pointer; + font-weight: bold; + text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.6); + box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.15); + transition: all 0.2s ease; + font-size: 16px; +} + +.filter.active, .filter:hover { + background: linear-gradient(to bottom, #4da6ff, #0066cc); + color: #ffffff; +} + +#task-list { + list-style: none; + max-height: 400px; + overflow-y: auto; + padding: 10px 0; + background-color: #ffffff; + border-radius: 8px; + border: 1px solid #e2e2e2; +} + +.task-item { + padding: 15px; + border-radius: 10px; + background-color: #ffffff; + margin-bottom: 10px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + transition: background 0.3s ease; + display: flex; + justify-content: space-between; + align-items: center; +} + +.task-item:hover { + background: linear-gradient(to bottom, #4da6ff, #0066cc); + color: #ffffff; +} + +.task-item.completed { + text-decoration: line-through; + color: #a09b8d; +} + +.task-actions { + display: flex; + gap: 15px; +} + +.task-actions button { + background: none; + border: none; + cursor: pointer; + font-size: 1.5em; +} + +.task-actions button.complete-btn { + color: #60a36e; + text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.2); +} + +.task-actions button.delete-btn { + color: #d9534f; + text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.2); +} +EOF + +# Create script.js file +cat > ./app/script.js << 'EOF' +// Получение элементов из HTML +const taskForm = document.getElementById("task-form"); +const taskInput = document.getElementById("task-input"); +const taskList = document.getElementById("task-list"); +const filterButtons = document.querySelectorAll(".filter"); + +// Загрузка задач из LocalStorage +let tasks = JSON.parse(localStorage.getItem("tasks")) || []; + +// Переменная для отслеживания перетаскиваемой задачи +let draggedTaskIndex = null; + +// Функция для отображения задач +function displayTasks(filter = "all") { + taskList.innerHTML = ""; + const filteredTasks = tasks.filter(task => + filter === "all" || (filter === "active" && !task.completed) || (filter === "completed" && task.completed) + ); + + filteredTasks.forEach((task, index) => { + const taskItem = document.createElement("li"); + taskItem.classList.add("task-item"); + taskItem.draggable = true; // Сделать элемент перетаскиваемым + + if (task.completed) taskItem.classList.add("completed"); + + taskItem.innerHTML = ` + ${task.text} +
      + + +
      + `; + + // Добавление событий для drag-and-drop + taskItem.addEventListener("dragstart", () => handleDragStart(index)); + taskItem.addEventListener("dragover", (e) => handleDragOver(e)); + taskItem.addEventListener("drop", () => handleDrop(index)); + taskItem.addEventListener("dragend", handleDragEnd); + + taskList.appendChild(taskItem); + }); +} + +// Обработчик начала перетаскивания +function handleDragStart(index) { + draggedTaskIndex = index; +} + +// Обработчик, предотвращающий действия по умолчанию (нужен для drop) +function handleDragOver(e) { + e.preventDefault(); +} + +// Обработчик окончания перетаскивания +function handleDrop(index) { + if (draggedTaskIndex !== null && draggedTaskIndex !== index) { + const draggedTask = tasks[draggedTaskIndex]; + tasks.splice(draggedTaskIndex, 1); + tasks.splice(index, 0, draggedTask); + saveTasks(); + displayTasks(); + } + draggedTaskIndex = null; +} + +// Обработчик окончания перетаскивания +function handleDragEnd() { + draggedTaskIndex = null; +} + +// Добавление новой задачи +taskForm.addEventListener("submit", (e) => { + e.preventDefault(); + const taskText = taskInput.value.trim(); + if (!taskText) return; + + const newTask = { + id: Date.now().toString(), + text: taskText, + completed: false + }; + tasks.push(newTask); + saveTasks(); + displayTasks(); + taskInput.value = ""; +}); + +// Переключение статуса задачи на выполнено +function toggleComplete(taskId) { + tasks = tasks.map(task => + task.id === taskId ? { ...task, completed: !task.completed } : task + ); + saveTasks(); + displayTasks(); +} + +// Удаление задачи +function deleteTask(taskId) { + tasks = tasks.filter(task => task.id !== taskId); + saveTasks(); + displayTasks(); +} + +// Сохранение задач в LocalStorage +function saveTasks() { + localStorage.setItem("tasks", JSON.stringify(tasks)); + console.log("Tasks saved:", tasks); +} + +// Добавление события для фильтров +filterButtons.forEach(button => { + button.addEventListener("click", () => { + filterButtons.forEach(btn => btn.classList.remove("active")); + button.classList.add("active"); + displayTasks(button.dataset.filter); + }); +}); + +// Инициализация +displayTasks(); +EOF + +# Create Dockerfile +cat > ./Dockerfile << 'EOF' +FROM nginx:alpine + +# Create app directory +WORKDIR /usr/share/nginx/html + +# Copy application files +COPY ./app/index.html . +COPY ./app/style.css . +COPY ./app/script.js . + +# Expose port +EXPOSE 80 + +# Start nginx server +CMD ["nginx", "-g", "daemon off;"] +EOF + +# Create Kubernetes namespace file +cat > ./namespace.yaml << 'EOF' +apiVersion: v1 +kind: Namespace +metadata: + name: todo-app-ns + labels: + app: todo-app +EOF + +# Create deployment file +cat > ./deployment.yaml << 'EOF' +apiVersion: apps/v1 +kind: Deployment +metadata: + name: todo-web-app + namespace: todo-app-ns + labels: + app: todo-web-app +spec: + replicas: 2 + selector: + matchLabels: + app: todo-web-app + template: + metadata: + labels: + app: todo-web-app + spec: + containers: + - name: todo-web-app + image: todo-web-app:latest + imagePullPolicy: IfNotPresent + ports: + - containerPort: 80 + resources: + limits: + cpu: "500m" + memory: "256Mi" + requests: + cpu: "100m" + memory: "128Mi" +EOF + +# Create service file +cat > ./service.yaml << 'EOF' +apiVersion: v1 +kind: Service +metadata: + name: todo-web-service + namespace: todo-app-ns +spec: + selector: + app: todo-web-app + ports: + - port: 80 + targetPort: 80 + type: NodePort +EOF + +# Create statefulset file +cat > ./statefulset.yaml << 'EOF' +# PersistentVolume for storing tasks data +apiVersion: v1 +kind: PersistentVolume +metadata: + name: todo-app-pv + labels: + type: local +spec: + storageClassName: manual + capacity: + storage: 1Gi + accessModes: + - ReadWriteOnce + hostPath: + path: "/mnt/data/todo-app" +--- +# PersistentVolumeClaim for the StatefulSet +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: todo-app-pvc + namespace: todo-app-ns +spec: + storageClassName: manual + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +--- +# StatefulSet for task data persistence +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: todo-data-manager + namespace: todo-app-ns +spec: + serviceName: "todo-data-service" + replicas: 1 + selector: + matchLabels: + app: todo-data-manager + template: + metadata: + labels: + app: todo-data-manager + spec: + containers: + - name: todo-data-manager + image: redis:alpine + ports: + - containerPort: 6379 + name: redis + volumeMounts: + - name: todo-data + mountPath: /data + resources: + limits: + cpu: "300m" + memory: "256Mi" + requests: + cpu: "100m" + memory: "128Mi" + volumeClaimTemplates: + - metadata: + name: todo-data + spec: + accessModes: [ "ReadWriteOnce" ] + storageClassName: manual + resources: + requests: + storage: 1Gi +--- +# Service for StatefulSet +apiVersion: v1 +kind: Service +metadata: + name: todo-data-service + namespace: todo-app-ns + labels: + app: todo-data-manager +spec: + ports: + - port: 6379 + name: redis + clusterIP: None + selector: + app: todo-data-manager +EOF + +# Create start app script +cat > ./start-app.sh << 'EOF' +#!/bin/bash +set -e + +echo "Starting To-Do List Application in Kubernetes" + +# Apply namespace +kubectl apply -f namespace.yaml + +# Apply PV, PVC, and StatefulSet +kubectl apply -f statefulset.yaml + +# Apply deployment +kubectl apply -f deployment.yaml + +# Apply service +kubectl apply -f service.yaml + +echo "Waiting for the application to start..." +sleep 5 + +# Get NodePort information +NODE_PORT=$(kubectl get svc todo-web-service -n todo-app-ns -o jsonpath='{.spec.ports[0].nodePort}') +echo "The To-Do List application is available at: http://localhost:$NODE_PORT" + +echo "Application started successfully!" +EOF + +# Create stop app script +cat > ./stop-app.sh << 'EOF' +#!/bin/bash +set -e + +echo "Stopping To-Do List Application in Kubernetes" + +# Delete service +kubectl delete -f service.yaml + +# Delete deployment +kubectl delete -f deployment.yaml + +# Delete StatefulSet, PVC, and PV +kubectl delete -f statefulset.yaml + +# Delete namespace +kubectl delete -f namespace.yaml + +echo "Application stopped successfully!" +EOF + +# Create README.md +cat > ./README.md << 'EOF' +# To-Do List Application on Kubernetes + +## Application Description + +This is a simple To-Do List web application that allows users to: +- Add new tasks +- Mark tasks as completed +- Delete tasks +- Filter tasks by their status (All, Active, Completed) +- Reorder tasks using drag-and-drop functionality + +The application uses local storage to persist tasks and allows users to manage their daily tasks efficiently. + +## Container Images Used + +1. **todo-web-app:latest**: + - Custom image based on nginx:alpine + - Contains the static web files (HTML, CSS, JavaScript) for the To-Do List application + - Serves the web interface on port 80 + +2. **redis:alpine**: + - Official Redis image based on Alpine Linux + - Used in the StatefulSet for data persistence + - Stores task data on a persistent volume + - Runs on port 6379 + +## Kubernetes Objects + +1. **Namespace** (`todo-app-ns`): + - Isolates all the application resources in a dedicated namespace + +2. **Deployment** (`todo-web-app`): + - Manages the web application containers + - Maintains 2 replicas for high availability + - Defines resource limits and requests + +3. **StatefulSet** (`todo-data-manager`): + - Manages Redis instances for data persistence + - Uses persistent storage to maintain task data + - Ensures data consistency during pod restarts + +4. **PersistentVolume** (`todo-app-pv`): + - Provides storage resources for the application + - Uses local storage on the host at `/mnt/data/todo-app` + - 1GB capacity for storing task data + +5. **PersistentVolumeClaim** (`todo-app-pvc`): + - Claims storage from the PersistentVolume + - Used by the StatefulSet for data persistence + +6. **Services**: + - `todo-web-service`: NodePort service exposing the web application + - `todo-data-service`: Headless service for the StatefulSet + +## Network and Volume Configuration + +### Networks +- The application components communicate within the Kubernetes cluster using services +- `todo-web-service` exposes the web interface externally using NodePort +- `todo-data-service` provides internal DNS-based service discovery for the StatefulSet + +### Volumes +- The application uses a persistent volume mounted at `/mnt/data/todo-app` on the host +- This volume is mounted into the Redis container at `/data` to persist task data +- The volume uses the `manual` storage class with ReadWriteOnce access mode + +## Container Configuration + +### Web Application Container +- Based on nginx:alpine +- Configured to serve static content from `/usr/share/nginx/html` +- Resource limits: 500m CPU, 256Mi memory +- Resource requests: 100m CPU, 128Mi memory + +### Redis Container +- Based on redis:alpine +- Data directory mounted to persistent storage +- Resource limits: 300m CPU, 256Mi memory +- Resource requests: 100m CPU, 128Mi memory + +## How to Use the Application + +### Prerequisites +- Kubernetes cluster (Minikube or similar) +- kubectl configured to communicate with your cluster +- Docker for building the application image + +### Prepare the Application +1. Clone this repository +2. Run the preparation script: + ``` + chmod +x prepare-app.sh + ./prepare-app.sh + ``` + +### Start the Application +1. Run the start script: + ``` + chmod +x start-app.sh + ./start-app.sh + ``` +2. The script will display the URL to access the application + +### Stop the Application +1. Run the stop script: + ``` + chmod +x stop-app.sh + ./stop-app.sh + ``` + +### Accessing the Web Interface +1. After starting the application, note the NodePort displayed in the terminal +2. Open your web browser and navigate to `http://localhost:` +3. You should see the To-Do List application interface +4. Start adding tasks, marking them as completed, or deleting them as needed + +## Troubleshooting +- If pods aren't starting, check the pod status: `kubectl get pods -n todo-app-ns` +- For detailed pod issues: `kubectl describe pod -n todo-app-ns` +- To view logs: `kubectl logs -n todo-app-ns` +EOF + +# Build Docker image +echo "Building Docker image for the web application..." +docker build -t todo-web-app:latest . + +# Create the host directory for PersistentVolume +echo "Creating directory for persistent data..." +# Use mkdir without sudo for compatibility with GitBash/Windows +mkdir -p /mnt/data/todo-app 2>/dev/null || { + echo "Note: Could not create /mnt/data/todo-app directory directly." + echo "If you're using Minikube, you may need to create this directory inside the Minikube VM." + echo "You can do this by running: minikube ssh 'sudo mkdir -p /mnt/data/todo-app && sudo chmod 777 /mnt/data/todo-app'" +} + +# Make scripts executable +chmod +x start-app.sh stop-app.sh + +echo "Application preparation completed successfully!" \ No newline at end of file diff --git a/Z2/requirements.txt b/Z2/requirements.txt new file mode 100644 index 0000000..7ff8906 --- /dev/null +++ b/Z2/requirements.txt @@ -0,0 +1,2 @@ +Flask + diff --git a/Z2/script.js b/Z2/script.js new file mode 100644 index 0000000..18955a5 --- /dev/null +++ b/Z2/script.js @@ -0,0 +1,36 @@ +document.addEventListener("DOMContentLoaded", () => { + const taskForm = document.getElementById("task-form"); + const taskInput = document.getElementById("task-input"); + const taskList = document.getElementById("task-list"); + + function loadTasks() { + fetch("/api/tasks") + .then(res => res.json()) + .then(tasks => { + taskList.innerHTML = ""; + tasks.forEach(t => { + const li = document.createElement("li"); + li.textContent = t.task; + taskList.appendChild(li); + }); + }); + } + + taskForm.addEventListener("submit", e => { + e.preventDefault(); + const task = taskInput.value.trim(); + if (!task) return; + + fetch("/api/tasks", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ task }) + }).then(() => { + taskInput.value = ""; + loadTasks(); + }); + }); + + loadTasks(); +}); + diff --git a/Z2/service.yaml b/Z2/service.yaml new file mode 100644 index 0000000..d8f10a4 --- /dev/null +++ b/Z2/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: todo-web-service + namespace: todo-app-ns-v2 +spec: + selector: + app: todo-web-app + ports: + - port: 80 + targetPort: 80 + type: NodePort diff --git a/Z2/start-app.sh b/Z2/start-app.sh new file mode 100644 index 0000000..a597bb8 --- /dev/null +++ b/Z2/start-app.sh @@ -0,0 +1,27 @@ +#!/bin/bash +set -e + +echo "Starting To-Do List Application in Kubernetes" + +# Apply namespace +kubectl apply -f k8s/namespace.yaml + +# Apply PV, PVC, and StatefulSet +kubectl apply -f k8s/statefulset.yaml + +# Apply deployment +kubectl apply -f k8s/deployment.yaml + +# Apply service +kubectl apply -f k8s/service.yaml + +echo "Waiting for the application to start..." +sleep 5 + +# Get NodePort information +NODE_PORT=$(kubectl get svc todo-web-service -n todo-app-ns-v2 -o jsonpath='{.spec.ports[0].nodePort}') +echo "The To-Do List application is available at: http://localhost:$NODE_PORT" + +echo "Application started successfully!" + + diff --git a/Z2/statefulset.yaml b/Z2/statefulset.yaml new file mode 100644 index 0000000..ce62de2 --- /dev/null +++ b/Z2/statefulset.yaml @@ -0,0 +1,37 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: todo-data-manager # Имя StatefulSet + namespace: todo-app-ns-v2 # Пространство имен +spec: + serviceName: "todo-data-service" # Сервис для StatefulSet + replicas: 1 # Количество реплик + selector: + matchLabels: + app: todo-data # Лейбл для выборки подов + template: + metadata: + labels: + app: todo-data # Лейблы для подов + spec: + imagePullSecrets: + - name: my-docker-secret # Секрет для загрузки образа Docker + containers: + - name: todo-data # Имя контейнера + image: jank8000/todo-data-image:latest # Docker образ + ports: + - containerPort: 8080 # Порт контейнера + volumeMounts: + - name: todo-app-pvc # PVC, которое будет монтироваться + mountPath: /data # Место, где данные будут храниться в контейнере + volumeClaimTemplates: + - metadata: + name: todo-app-pvc # PVC для хранения данных + namespace: todo-app-ns-v2 # Пространство имен для PVC + spec: + accessModes: + - ReadWriteOnce # Режим доступа к PVC + resources: + requests: + storage: 1Gi # Запрос на 1Gi хранилища + volumeMode: Filesystem # Монтирование как файловая система diff --git a/Z2/stop-app.sh b/Z2/stop-app.sh new file mode 100644 index 0000000..8d6d13a --- /dev/null +++ b/Z2/stop-app.sh @@ -0,0 +1,18 @@ +#!/bin/bash +set -e + +echo "Stopping To-Do List Application in Kubernetes" + +# Delete service +kubectl delete -f service.yaml + +# Delete deployment +kubectl delete -f deployment.yaml + +# Delete StatefulSet, PVC, and PV +kubectl delete -f statefulset.yaml + +# Delete namespace +kubectl delete -f namespace.yaml + +echo "Application stopped successfully!" diff --git a/Z2/style.css b/Z2/style.css new file mode 100644 index 0000000..e2cf587 --- /dev/null +++ b/Z2/style.css @@ -0,0 +1,150 @@ +body { + font-family: "Helvetica Neue", sans-serif; + background: linear-gradient(to bottom, #f7f7f7, #e1e1e1); + display: flex; + align-items: center; + justify-content: center; + min-height: 100vh; +} + +.app-container { + background-color: #ffffff; + padding: 40px; + border-radius: 20px; + width: 100%; + max-width: 500px; + box-shadow: 0px 15px 20px rgba(0, 0, 0, 0.3); + border: 1px solid #d9d9d9; +} + +h1 { + font-family: "Helvetica", sans-serif; + text-align: center; + margin-bottom: 20px; + font-size: 30px; + color: #4a4a4a; + text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); +} + +.subtitle { + font-size: 16px; + color: #6d6d72; + text-align: center; + margin-bottom: 20px; + font-weight: 300; +} + +form { + display: flex; + gap: 10px; + margin-bottom: 20px; +} + +#task-input { + flex-grow: 1; + padding: 15px; + border-radius: 8px; + border: 1px solid #c7c7c7; + background-color: #f0f0f0; + font-size: 18px; + box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.1); +} + +form button { + padding: 15px 25px; + border: none; + background: linear-gradient(to bottom, #4da6ff, #0066cc); + color: #ffffff; + border-radius: 12px; + cursor: pointer; + font-weight: bold; + text-shadow: 0px -1px 1px rgba(0, 0, 0, 0.3); + box-shadow: 0px 5px 8px rgba(0, 0, 0, 0.2); + transition: all 0.2s ease; + font-size: 16px; +} + +form button:active { + background: linear-gradient(to bottom, #0066cc, #4da6ff); + box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2); + transform: translateY(2px); +} + +.filter-buttons { + display: flex; + justify-content: space-around; + margin: 20px 0; +} + +.filter { + background: linear-gradient(to bottom, #e6e6e6, #d1d1d1); + border: 1px solid #c3c3c3; + padding: 10px 20px; + color: #4a4a4a; + border-radius: 12px; + cursor: pointer; + font-weight: bold; + text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.6); + box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.15); + transition: all 0.2s ease; + font-size: 16px; +} + +.filter.active, .filter:hover { + background: linear-gradient(to bottom, #4da6ff, #0066cc); + color: #ffffff; +} + +#task-list { + list-style: none; + max-height: 400px; + overflow-y: auto; + padding: 10px 0; + background-color: #ffffff; + border-radius: 8px; + border: 1px solid #e2e2e2; +} + +.task-item { + padding: 15px; + border-radius: 10px; + background-color: #ffffff; + margin-bottom: 10px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + transition: background 0.3s ease; + display: flex; + justify-content: space-between; + align-items: center; +} + +.task-item:hover { + background: linear-gradient(to bottom, #4da6ff, #0066cc); + color: #ffffff; +} + +.task-item.completed { + text-decoration: line-through; + color: #a09b8d; +} + +.task-actions { + display: flex; + gap: 15px; +} + +.task-actions button { + background: none; + border: none; + cursor: pointer; + font-size: 1.5em; +} + +.task-actions button.complete-btn { + color: #60a36e; + text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.2); +} + +.task-actions button.delete-btn { + color: #d9534f; + text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.2); +} diff --git a/Z2/tasks.json b/Z2/tasks.json new file mode 100644 index 0000000..60b0742 --- /dev/null +++ b/Z2/tasks.json @@ -0,0 +1 @@ +[] diff --git a/Z2/todo-app-deployment.yaml b/Z2/todo-app-deployment.yaml new file mode 100644 index 0000000..76b5c25 --- /dev/null +++ b/Z2/todo-app-deployment.yaml @@ -0,0 +1,87 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: todo-app-ns-v2 +--- +apiVersion: v1 +kind: Service +metadata: + name: todo-web-service + namespace: todo-app-ns-v2 +spec: + selector: + app: todo-web + ports: + - protocol: TCP + port: 80 + targetPort: 8080 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: frontend-deployment + namespace: todo-app-ns-v2 +spec: + replicas: 1 + selector: + matchLabels: + app: todo-web + template: + metadata: + labels: + app: todo-web + spec: + containers: + - name: frontend + image: your-frontend-image:latest # Укажи свой образ для фронтенда + ports: + - containerPort: 8080 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: backend-deployment + namespace: todo-app-ns-v2 +spec: + replicas: 1 + selector: + matchLabels: + app: todo-backend + template: + metadata: + labels: + app: todo-backend + spec: + containers: + - name: backend + image: your-backend-image:latest # Укажи свой образ для бэкенда + ports: + - containerPort: 8080 +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: todo-app-pv + namespace: todo-app-ns-v2 +spec: + capacity: + storage: 1Gi + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + hostPath: + path: /mnt/data +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: todo-app-pvc + namespace: todo-app-ns-v2 +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi + volumeName: todo-app-pv diff --git a/Z2/todo-app-ns-v2.yaml b/Z2/todo-app-ns-v2.yaml new file mode 100644 index 0000000..a631544 --- /dev/null +++ b/Z2/todo-app-ns-v2.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: todo-app-ns-v2 diff --git a/Z2/todo-app-pv.yaml b/Z2/todo-app-pv.yaml new file mode 100644 index 0000000..07824b4 --- /dev/null +++ b/Z2/todo-app-pv.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: todo-app-pv + namespace: todo-app-ns-v2 # Указываем пространство имен +spec: + capacity: + storage: 1Gi + volumeMode: Filesystem + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + hostPath: + path: "/mnt/data/todo-app" diff --git a/Z2/todo-app-pvc.yaml b/Z2/todo-app-pvc.yaml new file mode 100644 index 0000000..39d9cbd --- /dev/null +++ b/Z2/todo-app-pvc.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: todo-app-pvc + namespace: todo-app-ns-v2 # Указываем пространство имен +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi + volumeMode: Filesystem diff --git a/Z2/todo-data-service.yaml b/Z2/todo-data-service.yaml new file mode 100644 index 0000000..9a091e0 --- /dev/null +++ b/Z2/todo-data-service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: todo-data-service + namespace: todo-app-ns-v2 # Указываем пространство имен +spec: + ports: + - port: 8080 + targetPort: 8080 + selector: + app: todo-data + clusterIP: None # Указывает на использование StatefulSet diff --git a/Z2/todo-data-statefulset.yaml b/Z2/todo-data-statefulset.yaml new file mode 100644 index 0000000..abf474c --- /dev/null +++ b/Z2/todo-data-statefulset.yaml @@ -0,0 +1,23 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: todo-data-manager + namespace: todo-app-ns-v2 # Указываем пространство имен +spec: + serviceName: "todo-data-service" + replicas: 1 + selector: + matchLabels: + app: todo-data + template: + metadata: + labels: + app: todo-data + spec: + imagePullSecrets: + - name: my-docker-secret # Имя секрета для DockerHub + containers: + - name: todo-data + image: jank8000/todo-data-image:latest + ports: + - containerPort: 8080