Upload files to "z2"

This commit is contained in:
Mohammed Niaz Khaleel Jameel 2026-04-29 08:46:13 +00:00
parent 4a165cc49b
commit 2fb6820938
11 changed files with 528 additions and 0 deletions

BIN
z2/.service.yaml.swp Normal file

Binary file not shown.

153
z2/README.md Normal file
View File

@ -0,0 +1,153 @@
# TaskFlow — Kubernetes Assignment (Z2)
> A task management web application deployed on Kubernetes.
---
## 1. What the Application Does
TaskFlow is a simple web-based task manager. Users can:
- **Create** tasks with a title, description, and status
- **Advance** tasks through stages: `Todo → In Progress → Done`
- **Delete** tasks they no longer need
All data is stored in PostgreSQL and persists across pod restarts.
---
## 2. Containers Used
| Container | Image | Description |
|---|---|---|
| `taskflow-web` | `taskflow-web:latest` | Python 3.11 / Flask app served by Gunicorn on port 5000. Provides the HTML UI and REST API. |
| `postgres` | `postgres:15-alpine` | PostgreSQL 15 database. Stores all tasks in the `taskdb` database. |
| `wait-for-db` (init) | `busybox:1.36` | Init container that waits until PostgreSQL is ready before the web app starts. |
---
## 3. Kubernetes Objects
| Object | Kind | Description |
|---|---|---|
| `taskflow` | Namespace | Isolates all TaskFlow resources from other apps on the cluster. |
| `postgres-pv` | PersistentVolume | 1 GiB hostPath volume — real folder on the node's disk. |
| `postgres-pvc` | PersistentVolumeClaim | Requests the PV above. Referenced by the StatefulSet. |
| `postgres` | StatefulSet | Manages the PostgreSQL pod with stable name (`postgres-0`) and persistent storage. |
| `taskflow-web` | Deployment | Runs 2 replicas of the Flask web app with liveness and readiness probes. |
| `taskflow-web-service` | Service (NodePort) | Exposes the website on port `30080`. Entry point for the browser. |
| `postgres-service` | Service (ClusterIP) | Internal DNS name for PostgreSQL. Not reachable from outside the cluster. |
| `postgres-secret` | Secret | Stores the database username and password as base64-encoded values. |
| `postgres-init` | ConfigMap | Contains the SQL init script that creates the `tasks` table on first run. |
---
## 4. How Storage Works
Three objects work together:
**PersistentVolume (PV)**
A real directory `/mnt/data/taskflow-postgres` on the node's filesystem. Cluster-scoped, 1 GiB, `Retain` policy — data stays on disk even if the app is deleted.
**PersistentVolumeClaim (PVC)**
A request for that storage. Kubernetes matches it to the PV via `storageClassName: manual`.
**StatefulSet Volume Mount**
The PostgreSQL container mounts the PVC at `/var/lib/postgresql/data`. Every write goes:
```
PostgreSQL container
→ /var/lib/postgresql/data
→ postgres-pvc
→ postgres-pv
→ /mnt/data/taskflow-postgres (real folder on disk)
```
Data survives pod restarts, crashes, and redeployments because it lives on disk.
---
## 5. Virtual Networks
| Service | Type | Purpose |
|---|---|---|
| `taskflow-web-service` | NodePort `:30080` | Exposes the website to external browsers. |
| `postgres-service` | ClusterIP `:5432` | Internal DNS for PostgreSQL. Only pods inside the cluster can reach it. |
The web pods connect to PostgreSQL using the DNS name `postgres-service:5432` — Kubernetes resolves this automatically. No IP address is needed.
---
## 6. How to Run the Application
### Prepare (run once)
```bash
chmod +x setup.sh
./setup.sh
```
Writes all files, builds the Docker image, creates the PV directory, and deploys every Kubernetes object in order.
### Start
```bash
./start-app.sh
```
### Stop / Delete
```bash
./stop-app.sh
```
### View in Browser
```bash
minikube service taskflow-web-service -n taskflow
# or manually:
echo "http://$(minikube ip):30080"
```
### Useful Commands
```bash
# Check running pods
kubectl get pods -n taskflow
# View web app logs
kubectl logs -f deployment/taskflow-web -n taskflow
# View database logs
kubectl logs -f statefulset/postgres -n taskflow
# Open a psql shell
kubectl exec -it postgres-0 -n taskflow -- psql -U taskuser -d taskdb
```
---
## 7. File Structure
```
z2/
├── app/
│ ├── app.py # Flask web application
│ ├── Dockerfile # Container image definition
│ └── requirements.txt # Python dependencies
├── db/
│ └── init.sql # Reference SQL init script
├── namespace.yaml # Namespace: taskflow
├── deployment.yaml # Deployment: taskflow-web (2 replicas)
├── statefulset.yaml # PV + PVC + StatefulSet: postgres
├── service.yaml # NodePort (web) + ClusterIP (postgres)
├── secret.yaml # Database credentials
├── configmap.yaml # SQL init script
├── setup.sh # All-in-one setup script
├── start-app.sh # Deploy all objects
├── stop-app.sh # Remove all objects
├── prepare-app.sh # Build image + create PV directory
└── README.md # This file
```
---
## 8. Declaration of AI Usage
AI tools (Claude by Anthropic) were used to assist with generating boilerplate code, Kubernetes YAML manifests, and shell scripts. All generated content was reviewed, tested, and understood by the student. The application structure, use case, and deployment decisions were directed by the student. AI was used as a productivity tool, not as a replacement for understanding.

34
z2/configmap.yaml Normal file
View File

@ -0,0 +1,34 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: icebay-init
namespace: icebay
labels:
app: icebay
component: database
data:
init.sql: |
CREATE TABLE IF NOT EXISTS orders (
id SERIAL PRIMARY KEY,
name VARCHAR(200) NOT NULL,
address TEXT NOT NULL,
items TEXT NOT NULL,
status VARCHAR(50) DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS contacts (
id SERIAL PRIMARY KEY,
name VARCHAR(200) NOT NULL,
email VARCHAR(200) NOT NULL,
subject VARCHAR(300),
message TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name VARCHAR(200) NOT NULL,
email VARCHAR(200) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO orders (name, address, items, status) VALUES
('Demo Customer','SaiBaba Colony, Coimbatore','Arabian Fantasy x5, Vanilla x3','confirmed');

118
z2/deployment.yaml Normal file
View File

@ -0,0 +1,118 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: icebay-web
namespace: icebay
labels:
app: icebay
component: web
spec:
replicas: 2
selector:
matchLabels:
app: icebay
component: web
template:
metadata:
labels:
app: icebay
component: web
spec:
initContainers:
- name: wait-for-db
image: busybox:1.36
command: [sh,-c,"until nc -z postgres-service 5432; do sleep 2; done"]
- name: wait-for-redis
image: busybox:1.36
command: [sh,-c,"until nc -z redis-service 6379; do sleep 2; done"]
containers:
- name: icebay-web
image: icebay-web:latest
imagePullPolicy: Never
ports:
- containerPort: 5000
env:
- name: DB_HOST
value: "postgres-service"
- name: DB_PORT
value: "5432"
- name: DB_NAME
value: "icebaydb"
- name: DB_USER
valueFrom:
secretKeyRef:
name: icebay-secret
key: POSTGRES_USER
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: icebay-secret
key: POSTGRES_PASSWORD
- name: REDIS_HOST
value: "redis-service"
- name: REDIS_PORT
value: "6379"
- name: SECRET_KEY
valueFrom:
secretKeyRef:
name: icebay-secret
key: SECRET_KEY
readinessProbe:
httpGet:
path: /health
port: 5000
initialDelaySeconds: 10
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 5000
initialDelaySeconds: 20
periodSeconds: 10
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "300m"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
namespace: icebay
labels:
app: icebay
component: cache
spec:
replicas: 1
selector:
matchLabels:
app: icebay
component: cache
template:
metadata:
labels:
app: icebay
component: cache
spec:
containers:
- name: redis
image: redis:7-alpine
ports:
- containerPort: 6379
command: ["redis-server","--maxmemory","64mb","--maxmemory-policy","allkeys-lru"]
readinessProbe:
exec:
command: ["redis-cli","ping"]
initialDelaySeconds: 5
periodSeconds: 5
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "128Mi"
cpu: "100m"

7
z2/namespace.yaml Normal file
View File

@ -0,0 +1,7 @@
apiVersion: v1
kind: Namespace
metadata:
name: icebay
labels:
app: icebay
managed-by: kubectl

14
z2/prepare-app.sh Normal file
View File

@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo "=== Ice Bay — Prepare ==="
if command -v minikube &>/dev/null && minikube status 2>/dev/null | grep -q "Running"; then
eval "$(minikube docker-env)"
fi
docker build -t icebay-web:latest "${SCRIPT_DIR}/app"
if command -v minikube &>/dev/null && minikube status 2>/dev/null | grep -q "Running"; then
minikube ssh "sudo mkdir -p /mnt/data/icebay-postgres && sudo chmod 777 /mnt/data/icebay-postgres"
else
sudo mkdir -p /mnt/data/icebay-postgres && sudo chmod 777 /mnt/data/icebay-postgres
fi
echo "Done. Run ./start-app.sh"

13
z2/secret.yaml Normal file
View File

@ -0,0 +1,13 @@
apiVersion: v1
kind: Secret
metadata:
name: icebay-secret
namespace: icebay
labels:
app: icebay
type: Opaque
# icebayuser / icebaypass / icebay-secret-2025 (base64-encoded)
data:
POSTGRES_USER: aWNlYmF5dXNlcg==
POSTGRES_PASSWORD: aWNlYmF5cGFzcw==
SECRET_KEY: aWNlYmF5LXNlY3JldC0yMDI1

55
z2/service.yaml Normal file
View File

@ -0,0 +1,55 @@
---
apiVersion: v1
kind: Service
metadata:
name: icebay-web-service
namespace: icebay
labels:
app: icebay
component: web
spec:
type: NodePort
selector:
app: icebay
component: web
ports:
- name: http
port: 80
targetPort: 5000
nodePort: 30080
---
apiVersion: v1
kind: Service
metadata:
name: postgres-service
namespace: icebay
labels:
app: icebay
component: database
spec:
type: ClusterIP
selector:
app: icebay
component: database
ports:
- name: postgres
port: 5432
targetPort: 5432
---
apiVersion: v1
kind: Service
metadata:
name: redis-service
namespace: icebay
labels:
app: icebay
component: cache
spec:
type: ClusterIP
selector:
app: icebay
component: cache
ports:
- name: redis
port: 6379
targetPort: 6379

22
z2/start-app.sh Normal file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
NS="icebay"
echo "=== Ice Bay — Start ==="
kubectl apply -f "${SCRIPT_DIR}/namespace.yaml"
kubectl apply -f "${SCRIPT_DIR}/secret.yaml"
kubectl apply -f "${SCRIPT_DIR}/configmap.yaml"
kubectl delete pv icebay-postgres-pv --ignore-not-found=true
kubectl apply -f "${SCRIPT_DIR}/statefulset.yaml"
kubectl apply -f "${SCRIPT_DIR}/service.yaml"
echo "Waiting for PostgreSQL..."; kubectl rollout status statefulset/postgres -n "${NS}" --timeout=120s
kubectl apply -f "${SCRIPT_DIR}/deployment.yaml"
echo "Waiting for Redis..."; kubectl rollout status deployment/redis -n "${NS}" --timeout=60s
echo "Waiting for web app..."; kubectl rollout status deployment/icebay-web -n "${NS}" --timeout=120s
echo ""; kubectl get pods -n "${NS}"; echo ""; kubectl get services -n "${NS}"
if command -v minikube &>/dev/null && minikube status 2>/dev/null | grep -q "Running"; then
echo "App URL: http://$(minikube ip):30080"
else
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}' 2>/dev/null || echo "<node-ip>")
echo "App URL: http://${NODE_IP}:30080"
fi

99
z2/statefulset.yaml Normal file
View File

@ -0,0 +1,99 @@
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: icebay-postgres-pv
labels:
app: icebay
component: database
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: manual
hostPath:
path: /mnt/data/icebay-postgres
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: icebay-postgres-pvc
namespace: icebay
labels:
app: icebay
component: database
spec:
accessModes:
- ReadWriteOnce
storageClassName: manual
resources:
requests:
storage: 2Gi
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
namespace: icebay
labels:
app: icebay
component: database
spec:
serviceName: "postgres-service"
replicas: 1
selector:
matchLabels:
app: icebay
component: database
template:
metadata:
labels:
app: icebay
component: database
spec:
containers:
- name: postgres
image: postgres:15-alpine
ports:
- containerPort: 5432
env:
- name: POSTGRES_DB
value: "icebaydb"
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: icebay-secret
key: POSTGRES_USER
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: icebay-secret
key: POSTGRES_PASSWORD
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
volumeMounts:
- name: postgres-storage
mountPath: /var/lib/postgresql/data
- name: init-script
mountPath: /docker-entrypoint-initdb.d
readinessProbe:
exec:
command: ["pg_isready","-U","icebayuser","-d","icebaydb"]
initialDelaySeconds: 10
periodSeconds: 5
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
volumes:
- name: postgres-storage
persistentVolumeClaim:
claimName: icebay-postgres-pvc
- name: init-script
configMap:
name: icebay-init

13
z2/stop-app.sh Normal file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo "=== Ice Bay — Stop ==="
kubectl delete -f "${SCRIPT_DIR}/deployment.yaml" --ignore-not-found=true
kubectl delete -f "${SCRIPT_DIR}/service.yaml" --ignore-not-found=true
kubectl delete -f "${SCRIPT_DIR}/statefulset.yaml" --ignore-not-found=true
kubectl delete -f "${SCRIPT_DIR}/configmap.yaml" --ignore-not-found=true
kubectl delete -f "${SCRIPT_DIR}/secret.yaml" --ignore-not-found=true
kubectl delete -f "${SCRIPT_DIR}/namespace.yaml" --ignore-not-found=true
kubectl delete pv icebay-postgres-pv --ignore-not-found=true
echo "All removed. To also delete data:"
echo " minikube ssh 'sudo rm -rf /mnt/data/icebay-postgres'"