# Task Manager — Kubernetes Deployment A full-stack task management web application deployed on **Kubernetes**, extending the Docker Compose version (z1) with Kubernetes-native orchestration, scaling, and persistent storage. ## Table of Contents - [Description](#description) - [Prerequisites](#prerequisites) - [Architecture](#architecture) - [Kubernetes Objects](#kubernetes-objects) - [Containers Used](#containers-used) - [Networking](#networking) - [Persistent Volumes](#persistent-volumes) - [Container Configuration](#container-configuration) - [Quick Start](#quick-start) - [Usage Instructions](#usage-instructions) - [Viewing the Application](#viewing-the-application) - [Example Workflow](#example-workflow) - [File Structure](#file-structure) - [Sources](#sources) - [Use of Artificial Intelligence](#use-of-artificial-intelligence) --- ## Description **Task Manager** is a full-stack web application for creating, managing, and tracking tasks. Users can: - **Create tasks** with a title and optional description - **Mark tasks** as completed or reopen them - **Delete tasks** they no longer need - **Filter tasks** by status (All / Active / Completed) - **View statistics** including total, active, and completed task counts - **Manage the database** via Adminer web interface The application consists of a dark-themed Nginx frontend, a Node.js/Express REST API backend, PostgreSQL for persistent task storage, and Redis for API response caching — all orchestrated by Kubernetes. --- ## Prerequisites | Software | Minimum Version | Purpose | |----------|----------------|---------| | **Linux** | Any modern distribution | Host OS | | **Docker** | 20.10+ | Build container images | | **kubectl** | 1.25+ | Kubernetes CLI | | **Minikube** or **kind** | Latest | Local Kubernetes cluster | | **bash** | 4.0+ | Running management scripts | ### Verify installation: ```bash docker --version kubectl version --client minikube version # or: kind version ``` ### Start a local cluster (if not already running): ```bash minikube start # or kind create cluster ``` --- ## Architecture ``` ┌─────────────────────────────────────────────────────────────┐ │ Namespace: taskmanager │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Kubernetes Cluster DNS │ │ │ │ │ │ │ │ NodePort :30080 NodePort :30081 │ │ │ │ │ │ │ │ │ │ ┌────▼──────────┐ ┌────────▼──────────┐ │ │ │ │ │ Service │ │ Service │ │ │ │ │ │ (frontend) │ │ (adminer) │ │ │ │ │ └────┬──────────┘ └────────┬──────────┘ │ │ │ │ │ │ │ │ │ │ ┌────▼──────────┐ ┌────────▼──────────┐ │ │ │ │ │ Deployment │ │ Deployment │ │ │ │ │ │ (Nginx ×1) │ │ (Adminer ×1) │ │ │ │ │ └────┬──────────┘ └────────┬──────────┘ │ │ │ │ │ /api/ proxy │ │ │ │ │ ┌────▼──────────┐ │ │ │ │ │ │ Service │ │ │ │ │ │ │ (api:3000) │ │ │ │ │ │ └────┬──────────┘ │ │ │ │ │ │ │ │ │ │ │ ┌────▼──────────┐ ┌────────▼──────────┐ │ │ │ │ │ Deployment │ │ StatefulSet │ │ │ │ │ │ (API ×2) │ │ (Postgres ×1) │ │ │ │ │ └───┬───────────┘ └────────┬──────────┘ │ │ │ │ │ │ PVC → PV │ │ │ │ ┌───▼──────────┐ │ /mnt/.../postgres │ │ │ │ │ StatefulSet │ │ │ │ │ │ │ (Redis ×1) │─────────────►│ │ │ │ │ └──────────────┘ │ │ │ │ │ PVC → PV │ │ │ │ │ /mnt/.../redis │ │ │ │ └─────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` --- ## Kubernetes Objects | Object | Name | File | Description | |--------|------|------|-------------| | **Namespace** | `taskmanager` | `namespace.yaml` | Isolates all objects from other cluster workloads | | **Secret** | `postgres-secret` | `configmap-secret.yaml` | Stores base64-encoded DB username and password | | **ConfigMap** | `postgres-init` | `configmap-secret.yaml` | Contains `init.sql` to create the tasks table on first run | | **PersistentVolume** | `postgres-pv` | `statefulset.yaml` | 1 Gi host-path volume for PostgreSQL data | | **PersistentVolumeClaim** | `postgres-pvc` | `statefulset.yaml` | Claims `postgres-pv` for the postgres StatefulSet | | **PersistentVolume** | `redis-pv` | `statefulset.yaml` | 500 Mi host-path volume for Redis AOF data | | **PersistentVolumeClaim** | `redis-pvc` | `statefulset.yaml` | Claims `redis-pv` for the redis StatefulSet | | **StatefulSet** | `postgres` | `statefulset.yaml` | Manages the PostgreSQL 16 database pod with stable identity | | **StatefulSet** | `redis` | `statefulset.yaml` | Manages the Redis 7 cache pod with stable identity | | **Deployment** | `taskmanager-api` | `deployment.yaml` | Runs 2 replicas of the Node.js REST API | | **Deployment** | `taskmanager-frontend` | `deployment.yaml` | Runs 1 replica of the Nginx frontend | | **Deployment** | `taskmanager-adminer` | `deployment.yaml` | Runs 1 replica of Adminer (DB web UI) | | **Service** | `postgres` | `service.yaml` | ClusterIP — internal access to PostgreSQL on port 5432 | | **Service** | `redis` | `service.yaml` | ClusterIP — internal access to Redis on port 6379 | | **Service** | `taskmanager-api` | `service.yaml` | ClusterIP — internal access to API on port 3000 | | **Service** | `taskmanager-frontend` | `service.yaml` | NodePort — exposes UI at port 30080 | | **Service** | `taskmanager-adminer` | `service.yaml` | NodePort — exposes Adminer at port 30081 | --- ## Containers Used ### 1. `taskmanager-frontend` (Custom — Nginx) - **Image:** Built from `frontend/Dockerfile` using `nginx:alpine` - **Role:** Serves the static HTML/CSS/JS UI and reverse-proxies `/api/` requests to `taskmanager-api:3000` using Kubernetes DNS resolution - **Port:** 80 (internal), exposed at NodePort 30080 ### 2. `taskmanager-api` (Custom — Node.js) - **Image:** Built from `api/Dockerfile` using `node:20-alpine` - **Role:** REST API providing full CRUD operations for tasks. Uses PostgreSQL for data and Redis for caching (30 s TTL). 2 replicas for availability - **Port:** 3000 (ClusterIP) ### 3. `postgres` (Official — `postgres:16-alpine`) - **Role:** Primary relational database. The `init.sql` ConfigMap is mounted into `/docker-entrypoint-initdb.d/` so the schema and sample data are created automatically the first time - **Port:** 5432 (ClusterIP) ### 4. `redis` (Official — `redis:7-alpine`) - **Role:** In-memory cache for task list API responses. Configured with append-only persistence (`--appendonly yes`) so the cache survives pod restarts - **Port:** 6379 (ClusterIP) ### 5. `adminer` (Official — `adminer:latest`) - **Role:** Web-based database management interface for browsing, querying, and managing the PostgreSQL database directly in a browser - **Port:** 8080 (internal), exposed at NodePort 30081 --- ## Networking Kubernetes uses a **flat cluster network** — every pod can reach every other pod by its Service DNS name inside the same namespace. | Service Name | Type | Port | Accessible From | |-------------|------|------|----------------| | `postgres` | ClusterIP | 5432 | API pods only (internal) | | `redis` | ClusterIP | 6379 | API pods only (internal) | | `taskmanager-api` | ClusterIP | 3000 | Frontend pods (Nginx proxy) | | `taskmanager-frontend` | NodePort | 80 → 30080 | External (browser) | | `taskmanager-adminer` | NodePort | 8080 → 30081 | External (browser) | **DNS resolution example:** Inside the cluster, Nginx resolves `taskmanager-api` to the API Service IP automatically through Kubernetes DNS (`kube-dns`). The database and cache are not reachable from outside the cluster. --- ## Persistent Volumes | Volume Name | Type | Capacity | Mount Path | Purpose | |------------|------|----------|-----------|---------| | `postgres-pv` | hostPath | 1 Gi | `/mnt/taskmanager/postgres` | PostgreSQL data directory | | `redis-pv` | hostPath | 500 Mi | `/mnt/taskmanager/redis` | Redis append-only file | **Data persistence:** Stopping the application by scaling to zero (`./stop-app.sh`) or restarting pods does **not** delete the host directories or PVs. Only `./remove-app.sh` deletes them. --- ## Container Configuration All containers are configured using **environment variables**, **Secrets**, and **ConfigMaps**: - **Secrets:** `postgres-secret` provides the database username and password to both the `postgres` StatefulSet and `taskmanager-api` Deployment, avoiding plain-text credentials in YAML files - **ConfigMap:** `postgres-init` provides the `init.sql` script mounted as a file into the postgres container - **Resource limits:** Every container has `requests` and `limits` defined to prevent resource starvation - **Readiness/liveness probes:** All containers have health checks so Kubernetes only routes traffic to healthy pods and automatically restarts crashed pods - **Restart policy:** Kubernetes restarts failed pods by default (managed by the Deployment/StatefulSet controllers) - **StatefulSets** are used for PostgreSQL and Redis because they need a stable network identity and persistent storage. The API and frontend use Deployments because they are stateless and benefit from rolling updates --- ## Quick Start ```bash # 1. Start a local Kubernetes cluster (if not already running) minikube start # 2. Prepare — build Docker images and create host directories ./prepare-app.sh # 3. Deploy to Kubernetes ./start-app.sh # 4. Open the app in your browser # Task Manager UI: http://$(minikube ip):30080 # Adminer (DB UI): http://$(minikube ip):30081 ``` For Minikube, get the node IP with: ```bash minikube ip ``` --- ## Usage Instructions ### Preparing the application ```bash ./prepare-app.sh ``` Builds the custom Docker images (`taskmanager-api:latest`, `taskmanager-frontend:latest`), creates the host-path directories used by the PersistentVolumes, and **automatically loads the images into Minikube** if it detects a running Minikube cluster (preventing `ImagePullBackOff` errors). ### Starting the application ```bash ./start-app.sh ``` Applies all Kubernetes manifests in the correct dependency order and waits for StatefulSets and Deployments to be ready. Prints the access URLs at the end. ### Stopping and removing the application ```bash ./stop-app.sh ``` Deletes **all** Kubernetes objects (Deployments, StatefulSets, Services, PVCs, Secrets, ConfigMap, Namespace) and the PersistentVolumes and host directories. ⚠️ **All task data will be lost.** Run `./prepare-app.sh` then `./start-app.sh` to start fresh. ### Pausing without data loss ```bash # Scale all workloads to 0 — objects and PVs remain, data is preserved kubectl scale deployment --all --replicas=0 -n taskmanager kubectl scale statefulset --all --replicas=0 -n taskmanager # Resume ./start-app.sh ``` --- ## Viewing the Application ### Task Manager (Main UI) - **URL:** `http://$(minikube ip):30080` - Features: Create, complete, delete, and filter tasks ### Adminer (Database Management) - **URL:** `http://$(minikube ip):30081` - **Login credentials:** - System: `PostgreSQL` - Server: `postgres` - Username: `taskuser` - Password: `taskpass` - Database: `taskmanager` --- ## Example Workflow ```bash # Start a Minikube cluster $ minikube start # Prepare images and host directories $ ./prepare-app.sh ============================================= Preparing Task Manager for Kubernetes... ============================================= [1/2] Building Docker images... ✓ taskmanager-api:latest built ✓ taskmanager-frontend:latest built [2/2] Creating host directories for PersistentVolumes... ✓ /mnt/taskmanager/postgres created ✓ /mnt/taskmanager/redis created ============================================= ✓ Preparation complete! Run ./start-app.sh to deploy to Kubernetes ============================================= # Deploy the application $ ./start-app.sh ============================================= Starting Task Manager on Kubernetes... ============================================= [1/6] Creating Namespace... [2/6] Applying Secrets and ConfigMaps... [3/6] Applying PersistentVolumes, PVCs and StatefulSets... [4/6] Waiting for postgres StatefulSet to be Ready... Waiting for redis StatefulSet to be Ready... [5/6] Applying Deployments... [6/6] Applying Services... Waiting for API deployment to be ready... ============================================= ✓ Task Manager is running! 🌐 Task Manager: http://$(minikube ip):30080 🗄️ Adminer (DB): http://$(minikube ip):30081 ============================================= # Stop (data preserved) $ ./stop-app.sh ✓ Application stopped. Data is preserved in PersistentVolumes. # Start again — all tasks still there $ ./start-app.sh # Remove everything $ ./remove-app.sh ✓ Application completely removed. ``` --- ## File Structure ``` z2/ ├── namespace.yaml # Namespace: taskmanager ├── configmap-secret.yaml # Secret (DB credentials) + ConfigMap (init.sql) ├── statefulset.yaml # PV, PVC, StatefulSet for postgres and redis ├── deployment.yaml # Deployments for API, frontend, adminer ├── service.yaml # Services (ClusterIP + NodePort) ├── prepare-app.sh # Build images, create host directories ├── start-app.sh # Deploy all Kubernetes objects ├── stop-app.sh # Scale to zero (pause) ├── remove-app.sh # Delete everything ├── README.md # This documentation ├── api/ │ ├── Dockerfile # Node.js 20 Alpine image │ ├── package.json # Node dependencies │ ├── server.js # Express REST API │ └── db.js # PostgreSQL connection pool ├── frontend/ │ ├── Dockerfile # Nginx Alpine image │ ├── nginx.conf # Nginx config (proxies /api/ → taskmanager-api) │ └── public/ │ ├── index.html # Task Manager SPA │ ├── style.css # Dark theme CSS │ └── app.js # Frontend JavaScript └── db/ └── init.sql # Database schema + sample data ``` --- ## Sources 1. **Kubernetes Documentation** — [https://kubernetes.io/docs/](https://kubernetes.io/docs/) 2. **kubectl Reference** — [https://kubernetes.io/docs/reference/kubectl/](https://kubernetes.io/docs/reference/kubectl/) 3. **Kubernetes: StatefulSets** — [https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/) 4. **Kubernetes: Persistent Volumes** — [https://kubernetes.io/docs/concepts/storage/persistent-volumes/](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) 5. **Kubernetes: Secrets** — [https://kubernetes.io/docs/concepts/configuration/secret/](https://kubernetes.io/docs/concepts/configuration/secret/) 6. **Minikube Documentation** — [https://minikube.sigs.k8s.io/docs/](https://minikube.sigs.k8s.io/docs/) 7. **PostgreSQL Docker Image** — [https://hub.docker.com/_/postgres](https://hub.docker.com/_/postgres) 8. **Redis Docker Image** — [https://hub.docker.com/_/redis](https://hub.docker.com/_/redis) 9. **Adminer Docker Image** — [https://hub.docker.com/_/adminer](https://hub.docker.com/_/adminer) 10. **Node.js Docker Image** — [https://hub.docker.com/_/node](https://hub.docker.com/_/node) 11. **Nginx Docker Image** — [https://hub.docker.com/_/nginx](https://hub.docker.com/_/nginx) 12. **Express.js Documentation** — [https://expressjs.com/](https://expressjs.com/) --- ## Use of Artificial Intelligence Artificial intelligence tools such as ChatGPT and Claude were used as a support tool during development for understanding Kubernetes concepts, writing YAML manifests, and debugging configuration issues. All implementation, testing, and integration were performed independently.