zkt26/z2/README.md

250 lines
9.0 KiB
Markdown

# K8s Todo App
A full-stack **Todo List** web application deployed on **Kubernetes** using **Minikube**.
The app stores tasks in a PostgreSQL database and serves a modern web UI via a Python Flask backend.
---
## What the Application Does
Users can:
- **Add** new tasks via a text input form
- **Mark tasks as done** by clicking the circle toggle button
- **Delete** tasks with the ✕ button
- View a live progress counter (e.g. `2/5 done`)
All data is persisted to a PostgreSQL database backed by a Kubernetes `PersistentVolume`.
---
## Architecture Overview
```
Browser
▼ NodePort :30080
┌──────────────────────────────────────┐
│ Namespace: todo-app │
│ │
│ ┌────────────────────┐ │
│ │ Deployment │ │
│ │ todo-app (x2 pods)│ ──────────► │─── postgres-service:5432
│ │ Flask + Gunicorn │ │ │
│ └────────────────────┘ │ ┌──────────────────┐
│ │ │ StatefulSet │
│ │ │ postgres (x1) │
│ │ │ PostgreSQL 16 │
│ │ │ │ │
│ │ │ PVC ──► PV │
│ │ │ (hostPath) │
│ │ └──────────────────┘
└──────────────────────────────────────┘
```
---
## Container Images Used
| Image | Version | Purpose |
|-------|---------|---------|
| `todo-app` | `latest` (built locally) | Flask web server + Gunicorn WSGI |
| `postgres` | `16-alpine` | Relational database |
| `busybox` | `1.36` | Init container — waits for PostgreSQL before app starts |
### `todo-app` (custom image)
- Built from `app/Dockerfile` using Python 3.12-slim
- Runs a **Flask** application with **Gunicorn** (2 workers)
- Connects to PostgreSQL via environment variables
- Exposes port `5000`
- Runs as a non-root user (`appuser`) for security
### `postgres:16-alpine`
- Official PostgreSQL image (Alpine variant for smaller size)
- Initialises the `tododb` database using a mounted SQL ConfigMap
- Data is persisted via a PersistentVolumeClaim
---
## Kubernetes Objects
| Object | Name | Description |
|--------|------|-------------|
| `Namespace` | `todo-app` | Isolates all app resources from the rest of the cluster |
| `Deployment` | `todo-app` | Runs 2 replicas of the Flask web app with rolling updates |
| `StatefulSet` | `postgres` | Manages the PostgreSQL pod with stable network identity |
| `PersistentVolume` | `postgres-pv` | 2Gi host-path volume on the minikube node |
| `PersistentVolumeClaim` | `postgres-pvc` | Binds to `postgres-pv`, used by the StatefulSet |
| `Service` | `todo-app-service` | NodePort (30080) — exposes the Flask app to the browser |
| `Service` | `postgres-service` | ClusterIP — internal access to PostgreSQL on port 5432 |
| `Service` | `postgres-headless` | Headless — required by StatefulSet for stable DNS |
| `Secret` | `postgres-secret` | Stores DB credentials (username, password, DB name) |
| `ConfigMap` | `postgres-init-sql` | Holds the SQL init script run by PostgreSQL on first boot |
---
## Networks and Volumes
### Virtual Networks (Services)
- **`todo-app-service`** — `NodePort` type. Maps external port `30080` → pod port `5000`. Accessible at `http://<minikube-ip>:30080`.
- **`postgres-service`** — `ClusterIP` type. Only reachable inside the cluster at `postgres-service.todo-app.svc.cluster.local:5432`.
- **`postgres-headless`** — `ClusterIP: None`. A DNS-only headless service required by the StatefulSet so each pod gets a stable DNS entry (`postgres-0.postgres-headless.todo-app.svc.cluster.local`).
### Persistent Volumes
- **`postgres-pv`** — `hostPath` pointing to `/mnt/data/postgres` inside the minikube VM. Survives pod restarts and redeployments.
- **`postgres-pvc`** — Claims 2Gi from `postgres-pv`. Mounted at `/var/lib/postgresql/data` inside the PostgreSQL container.
---
## Container Configuration
### Flask App (`todo-app`)
- `DB_HOST` — hostname of the PostgreSQL service (`postgres-service.todo-app.svc.cluster.local`)
- `DB_NAME` — database name (`tododb`)
- `DB_USER` / `DB_PASS` — injected from the `postgres-secret` Kubernetes Secret
- **Liveness probe**: `GET /health` every 10s — restarts the pod if it hangs
- **Readiness probe**: `GET /health` every 5s — holds traffic until the app is truly ready
- **Init container**: `busybox` runs `nc -z postgres-service 5432` in a loop until PostgreSQL accepts connections before the main container starts
### PostgreSQL (`postgres`)
- `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB` — injected from `postgres-secret`
- `PGDATA=/var/lib/postgresql/data/pgdata` — prevents permission errors on mounted volumes
- SQL init script from ConfigMap is placed at `/docker-entrypoint-initdb.d/init.sql` and runs automatically on first start
- **Liveness probe**: `pg_isready -U postgres`
- **Readiness probe**: `pg_isready -U postgres`
---
## Step-by-Step Instructions
### Prerequisites — Install Everything on Ubuntu
Run the following commands in sequence in your terminal:
```bash
# 1. Update system
sudo apt-get update && sudo apt-get upgrade -y
# 2. Install Docker
sudo apt-get install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
sudo usermod -aG docker $USER
newgrp docker
# 3. Install kubectl
curl -LO "https://dl.k8s.io/release/$(curl -sL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
kubectl version --client
# 4. Install minikube
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
minikube version
```
### Prepare the Application (run once)
```bash
# Navigate to the project directory
cd k8s-todo-app
# Make all scripts executable
chmod +x prepare-app.sh start-app.sh stop-app.sh
# Run the preparation script (starts minikube, builds Docker image, creates host dir)
bash prepare-app.sh
```
### Start the Application
```bash
bash start-app.sh
```
This script creates all Kubernetes objects in order:
1. Namespace
2. ConfigMap
3. PersistentVolume, PVC, Secret, StatefulSet (PostgreSQL)
4. Services
5. Deployment (Flask app)
### View the Application in a Browser
```bash
# Option A — get the URL automatically
minikube service todo-app-service -n todo-app
# Option B — open directly
minikube service todo-app-service -n todo-app --url
# Copy the URL and open it in your browser
# e.g. http://192.168.49.2:30080
```
### Useful Commands While Running
```bash
# See all running pods
kubectl get pods -n todo-app
# See all objects
kubectl get all -n todo-app
# Watch pods in real time
kubectl get pods -n todo-app -w
# View Flask app logs
kubectl logs -l app=todo-app -n todo-app --tail=50
# View PostgreSQL logs
kubectl logs postgres-0 -n todo-app --tail=50
# Connect to PostgreSQL shell
kubectl exec -it postgres-0 -n todo-app -- psql -U postgres -d tododb
# Scale up/down the web app
kubectl scale deployment todo-app -n todo-app --replicas=3
```
### Pause / Delete the Application
```bash
# Delete all Kubernetes objects (data on PV is preserved)
bash stop-app.sh
# To also remove all stored database data
minikube ssh -- "sudo rm -rf /mnt/data/postgres"
# To stop minikube entirely
minikube stop
# To delete the minikube cluster completely
minikube delete
```
### Re-deploy After Stop
```bash
bash start-app.sh # no need to run prepare-app.sh again unless minikube was deleted
```
---
## Troubleshooting
| Problem | Fix |
|---------|-----|
| `ImagePullBackOff` on todo-app | Re-run `bash prepare-app.sh` to rebuild image inside minikube |
| Postgres pod in `Pending` | Run `kubectl describe pv postgres-pv` — check hostPath exists |
| App shows "Cannot connect to database" | Wait 30s, postgres is still initialising; check `kubectl logs postgres-0 -n todo-app` |
| Can't reach browser URL | Run `minikube service todo-app-service -n todo-app` to get the correct URL |
| Permission error on PV | Run `minikube ssh -- 'sudo chmod 777 /mnt/data/postgres'` |