docs: README covering app, K8s objects, networks, volumes, lifecycle

This commit is contained in:
Brazing Technology 2026-04-29 11:31:18 +05:30
parent b9f9b45b2e
commit 7cbbb6a1f9

180
README.md Normal file
View File

@ -0,0 +1,180 @@
# Task Manager on Kubernetes
A web-based task manager (Nginx + Flask + PostgreSQL) deployed to a Kubernetes cluster (minikube). Migrated from the Docker Compose version in the first assignment.
## What the application does
A simple CRUD task manager. Users can:
- Add a new task by typing a title and pressing Enter.
- Mark a task as completed by toggling its checkbox.
- Delete a task by clicking the X button.
- See all tasks sorted by creation date.
The frontend is a static page served by Nginx. The backend is a Flask REST API. Tasks are stored in PostgreSQL and survive pod restarts thanks to a hostPath PersistentVolume.
## Prerequisites
- **minikube** (any recent version, tested with 1.32+)
- **kubectl** (any version compatible with the minikube cluster)
- **Docker** CLI (used by `prepare-app.sh` to build images directly into minikube's docker daemon)
- **bash** (Linux/macOS native; Git Bash on Windows)
## Containers used
| Container | Image | Description |
|-----------|-------|-------------|
| `web` | `taskapp-web:v1` (built locally from `nginx:alpine`) | Serves static HTML/CSS/JS and reverse-proxies `/api/*` to the `api` Service. `nginx.conf` is mounted from a ConfigMap. |
| `api` | `taskapp-api:v1` (built locally from `python:3.12-slim`) | Flask REST API on Gunicorn (2 workers). Exposes `GET/POST /api/tasks`, `PUT /api/tasks/:id`, `DELETE /api/tasks/:id`, `GET /api/health`. |
| `db` | `postgres:15` (Docker Hub) | Stores `tasks` table. Data lives at `/var/lib/postgresql/data` on a PersistentVolume backed by hostPath. |
## Kubernetes objects
| Object | Name | Description |
|--------|------|-------------|
| Namespace | `taskapp` | Isolates every other resource. |
| Secret | `db-credentials` | Holds `POSTGRES_DB`, `POSTGRES_USER`, `POSTGRES_PASSWORD`. Consumed by both Postgres and Flask via `envFrom`. |
| ConfigMap | `nginx-config` | Holds `nginx.conf`. Mounted into the `web` container at `/etc/nginx/nginx.conf` via `subPath`. |
| PersistentVolume | `db-pv` | 1 Gi, `hostPath: /mnt/data/taskapp-db` on the minikube node. ReclaimPolicy `Retain`. StorageClass `manual`. |
| PersistentVolumeClaim | `db-pvc` | 1 Gi, `ReadWriteOnce`, binds to `db-pv` via label selector. Mounted by the StatefulSet. |
| StatefulSet | `db` | 1 replica, `postgres:15`. Uses the PVC. Has liveness and readiness probes (`pg_isready`). |
| Deployment | `api` | 2 replicas, custom Flask image. Probes hit `GET /api/health` (which checks DB reachability). |
| Deployment | `web` | 2 replicas, custom Nginx image. Probes hit `GET /`. |
| Service | `web` | NodePort 30080 → 80. The only externally reachable Service. |
| Service | `api` | ClusterIP, port 5000. Internal only. |
| Service | `db` | Headless (`clusterIP: None`), port 5432. Internal only. Pairs with the StatefulSet for stable DNS. |
## Virtual networks
The cluster's CNI plugin handles all pod-to-pod traffic. Service discovery is via Kubernetes DNS (`kube-dns` / `coredns`):
- Inside the namespace, every Service is reachable by short name: `web`, `api`, `db`.
- The `web` Service is the only one exposed outside the cluster (NodePort 30080).
- The `db` Service is **headless** — it returns the pod IPs directly instead of load-balancing. This is the canonical pattern for StatefulSets and gives `db-0` a stable DNS identity (`db-0.db.taskapp.svc.cluster.local`).
## Named volumes
| Volume | Mount | Backed by | Reclaim policy |
|--------|-------|-----------|----------------|
| `db-pv` (PVC `db-pvc`) | `/var/lib/postgresql/data` (in `db-0`) | hostPath `/mnt/data/taskapp-db` on the minikube node | Retain |
`Retain` means deleting the PVC does not automatically wipe the underlying directory — the data survives even a full `stop-app.sh` and is reattached on the next `prepare-app.sh + start-app.sh`. To wipe it manually:
```bash
minikube ssh -- sudo rm -rf /mnt/data/taskapp-db
```
## Container configuration performed
- **`web`** — built from `nginx:alpine` plus the static frontend files. The `nginx.conf` is supplied at runtime via the `nginx-config` ConfigMap, mounted with `subPath: nginx.conf` so only that single file is overlaid (not the whole config directory). This means the proxy rule (`proxy_pass http://api_backend;`) and `large_client_header_buffers` setting can be edited by changing the ConfigMap and reloading, without rebuilding the image.
- **`api`** — built from `python:3.12-slim`, installs `libpq-dev` and `gcc` for the `psycopg2` build, then `pip install` of `requirements.txt` (Flask + Gunicorn + psycopg2). Runs Gunicorn with 2 workers on port 5000. Reads DB credentials from env vars sourced from the `db-credentials` Secret.
- **`db`** — official `postgres:15` image, unmodified. Env (DB name, user, password) sourced from the same Secret. `PGDATA` is set to `/var/lib/postgresql/data/pgdata` (a subdir) because Postgres refuses to initialize a non-empty data directory and hostPath dirs occasionally have stray entries.
Resource requests/limits are set on every container so the cluster scheduler can place pods predictably; values are conservative and tested against a 4 GiB minikube VM.
## Usage
### Prepare the application
Builds images directly into minikube's docker daemon, creates the hostPath directory on the node, and applies Namespace + Secret + ConfigMap + PV + PVC + StatefulSet.
```bash
./prepare-app.sh
```
### Start the application
Applies the Deployments and Services. (Re-applies prepare-stage resources idempotently, so it's safe to run on its own too.)
```bash
./start-app.sh
```
The script then opens your default browser at `http://<minikube-ip>:30080`.
### Pause / stop the application
`stop-app.sh` removes every Kubernetes object (Deployments, Services, StatefulSet, PVC, PV, Namespace, Secret, ConfigMap):
```bash
./stop-app.sh
```
The hostPath data on the minikube node is **retained** (PV ReclaimPolicy = `Retain`). To resume from where you left off, run `./prepare-app.sh && ./start-app.sh` again.
### Delete everything (including data)
```bash
./stop-app.sh
minikube ssh -- sudo rm -rf /mnt/data/taskapp-db
```
## Viewing the application in a web browser
After `./start-app.sh` finishes, run:
```bash
minikube service web -n taskapp
```
This opens the browser at the right URL automatically. Alternatively, navigate manually:
```bash
echo "http://$(minikube ip):30080"
```
You'll see the Task Manager interface where you can add, complete, and delete tasks.
## API endpoints
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/health` | Health check (200 if DB reachable, 503 otherwise). Used by readiness probe. |
| GET | `/api/tasks` | List all tasks. |
| POST | `/api/tasks` | Create a task. Body: `{"title": "..."}`. |
| PUT | `/api/tasks/:id` | Toggle completion. |
| DELETE | `/api/tasks/:id` | Delete a task. |
## Project structure
```
qubernetees/
├── README.md # this file
├── prepare-app.sh # build images, create PV, apply prepare-stage manifests
├── start-app.sh # apply Deployments + Services, open browser
├── stop-app.sh # full teardown
├── namespace.yaml # Namespace + Secret + ConfigMap
├── statefulset.yaml # PV + PVC + StatefulSet
├── deployment.yaml # api + web Deployments
├── service.yaml # api + web + db Services
├── nginx.conf # source of the ConfigMap (kept for readability)
├── backend/ # Flask REST API
│ ├── Dockerfile
│ ├── requirements.txt
│ └── app.py # + /api/health endpoint
└── frontend/ # static HTML/CSS/JS
├── Dockerfile
├── index.html
├── style.css
└── app.js
```
## Sources
- First assignment (Docker version of the same app) — code copied from `cloud assiagment/`
- [Kubernetes documentation](https://kubernetes.io/docs/)
- [Kubernetes StatefulSet docs](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/)
- [Postgres official Docker image](https://hub.docker.com/_/postgres)
- [minikube documentation](https://minikube.sigs.k8s.io/docs/)
## Use of Artificial Intelligence
This Kubernetes deployment was designed and implemented with the assistance of **Claude** (Anthropic), an AI assistant. Claude was used for:
- Designing the K8s object topology (Namespace, Deployments, StatefulSet, PV, PVC, Services).
- Authoring the manifest files, lifecycle scripts, and this documentation.
- Reviewing the design against the assignment requirements.
**AI agent used:** Claude Opus 4.7 (Anthropic) via Claude Code CLI.
The application logic itself (Flask backend, frontend) was carried over from the first assignment.