Upload files to "z2"
This commit is contained in:
parent
4a165cc49b
commit
2fb6820938
BIN
z2/.service.yaml.swp
Normal file
BIN
z2/.service.yaml.swp
Normal file
Binary file not shown.
153
z2/README.md
Normal file
153
z2/README.md
Normal 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
34
z2/configmap.yaml
Normal 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
118
z2/deployment.yaml
Normal 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
7
z2/namespace.yaml
Normal 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
14
z2/prepare-app.sh
Normal 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
13
z2/secret.yaml
Normal 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
55
z2/service.yaml
Normal 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
22
z2/start-app.sh
Normal 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
99
z2/statefulset.yaml
Normal 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
13
z2/stop-app.sh
Normal 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'"
|
||||
Loading…
Reference in New Issue
Block a user