382 lines
18 KiB
Markdown
382 lines
18 KiB
Markdown
# 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: http://<NODE_IP>:30080
|
||
# Adminer: http://<NODE_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://<NODE_IP>:30080`
|
||
- For Minikube: `http://$(minikube ip):30080`
|
||
- Features: Create, complete, delete, and filter tasks
|
||
|
||
### Adminer (Database Management)
|
||
- **URL:** `http://<NODE_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://192.168.49.2:30080
|
||
🗄️ Adminer (DB): http://192.168.49.2: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.
|