From 8ed4acfd15d697f01becc55b6af8f0a24e79bd92 Mon Sep 17 00:00:00 2001 From: Yan Kasabutski Date: Thu, 17 Apr 2025 01:56:18 +0000 Subject: [PATCH] =?UTF-8?q?=D0=97=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D1=84=D0=B0=D0=B9=D0=BB=D1=8B=20=D0=B2=20=C2=AB?= =?UTF-8?q?z2=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- z2/Dockerfile | 15 + z2/README.md | 120 ++++++++ z2/YAN To-Do-List.zip | Bin 0 -> 18693 bytes z2/namespace.yaml | 4 + z2/prepare-app.sh | 684 ++++++++++++++++++++++++++++++++++++++++++ z2/service.yaml | 12 + z2/start-app.sh | 27 ++ z2/stop-app.sh | 18 ++ 8 files changed, 880 insertions(+) create mode 100644 z2/Dockerfile create mode 100644 z2/README.md create mode 100644 z2/YAN To-Do-List.zip create mode 100644 z2/namespace.yaml create mode 100644 z2/prepare-app.sh 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..2e708c4 --- /dev/null +++ b/z2/Dockerfile @@ -0,0 +1,15 @@ +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;"] 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/YAN To-Do-List.zip b/z2/YAN To-Do-List.zip new file mode 100644 index 0000000000000000000000000000000000000000..c71ab6b0cdfe0befb309edb6b9243df381166c12 GIT binary patch literal 18693 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 ./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/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/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!"