# Notes App — Kubernetes Assignment ## What the application does A simple **Notes** web application where users can create, view, and delete short text notes. Notes are stored persistently in a PostgreSQL database. The app is accessible via a web browser. ## Containers used | Container | Image | Description | |---|---|---| | `frontend` | custom (Nginx 1.27-alpine) | Serves the static HTML/JS frontend and proxies `/api/` requests to the backend | | `backend` | custom (Python 3.12-slim / Flask) | REST API for CRUD operations on notes | | `db` | `postgres:16-alpine` | Relational database storing notes persistently | | `adminer` | `adminer:4` | Web UI for browsing and managing the PostgreSQL database | ## Kubernetes objects | File | Object | Kind | Description | |---|---|---|---| | `k8s/namespace.yaml` | `notes-app` | Namespace | Isolates all app objects in one namespace | | `k8s/statefulset.yaml` | `db` | StatefulSet | Runs the PostgreSQL pod with stable identity | | `k8s/statefulset.yaml` | `postgres-pv` | PersistentVolume | Host-path volume providing 1Gi of storage | | `k8s/statefulset.yaml` | `postgres-pvc` | PersistentVolumeClaim | Claims the PV for use by the StatefulSet | | `k8s/deployment.yaml` | `backend` | Deployment | Runs the Flask API pod | | `k8s/deployment.yaml` | `frontend` | Deployment | Runs the Nginx frontend pod | | `k8s/deployment.yaml` | `adminer` | Deployment | Runs the Adminer pod | | `k8s/service.yaml` | `db` | Service (ClusterIP/Headless) | Internal DNS for the database (`db:5432`) | | `k8s/service.yaml` | `backend` | Service (ClusterIP) | Internal DNS for the API (`backend:5000`) | | `k8s/service.yaml` | `frontend` | Service (NodePort 30080) | Exposes the frontend to the host | | `k8s/service.yaml` | `adminer` | Service (NodePort 30081) | Exposes Adminer to the host | | `k8s/configmap.yaml` | `db-init` | ConfigMap | Holds `init.sql` mounted into the DB container | ## Networking and volumes - All objects live in the `notes-app` namespace. - Pods communicate via Kubernetes internal DNS (service names). - The `db` service is headless (`clusterIP: None`) — required for StatefulSets. - The `frontend` and `adminer` services use `NodePort` to be reachable from the host. - `postgres-pv` is a `hostPath` PersistentVolume at `/data/notes-postgres` on the node. - `postgres-pvc` binds to `postgres-pv` and is mounted into the `db` pod. ## Container configuration - **db**: configured via env vars (`POSTGRES_DB`, `POSTGRES_USER`, `POSTGRES_PASSWORD`). The `db-init` ConfigMap is mounted into `/docker-entrypoint-initdb.d/` and runs once on first startup. - **backend**: receives `DATABASE_URL` pointing to the `db` service. - **frontend**: Nginx proxies `/api/` to `http://backend:5000` and serves `index.html` for all other paths. - **adminer**: no extra configuration, exposed on NodePort 30081. ## Instructions ### Prepare (build and load images) ```bash chmod +x prepare-app.sh start-app.sh stop-app.sh remove-app.sh ./prepare-app.sh ``` ### Start ```bash ./start-app.sh ``` ### Stop (data is preserved) ```bash ./stop-app.sh ``` ### Remove everything ```bash ./remove-app.sh ``` ## Viewing the application | URL | Description | |---|---| | http://localhost:30080 | Notes web application | | http://localhost:30081 | Adminer — Server: `db`, User: `appuser`, Password: `apppassword`, Database: `appdb` | > If using minikube, replace `localhost` with the output of `minikube ip`. ## Use of artificial intelligence This project was created with the assistance of Kiro AI (kiro-cli chat agent). The AI generated the Kubernetes manifests, shell scripts, and documentation. All generated files were reviewed and understood by the author.