commit 18a369f5ad90a40cb8d881dd71480422e366d95e Author: Nihal Ahmed Date: Thu May 14 07:35:50 2026 +0000 commit diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..393dff9 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3.10 + +WORKDIR /app + +COPY requirements.txt . +RUN pip install -r requirements.txt + +COPY . . + +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "5000"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..a5a269d --- /dev/null +++ b/README.md @@ -0,0 +1,189 @@ +# Notes Application on Kubernetes + +## πŸ“Œ Description + +This project is a simple full-stack **Notes Application** deployed on Kubernetes. +It allows users to create and view notes through a web interface. The application demonstrates a complete **3-tier architecture**: + +* Frontend (User Interface) +* Backend (API Server) +* Database (PostgreSQL) + +The application is containerized using Docker and deployed using Kubernetes. + +--- + +## 🧱 Application Architecture + +The system consists of three main components: + +### 1. Frontend + +* Built using HTML and JavaScript +* Served via Nginx +* Provides UI for adding and viewing notes +* Communicates with backend using HTTP requests + +### 2. Backend + +* Built using FastAPI (Python) +* Provides REST API endpoints: + + * `GET /notes` + * `POST /notes` +* Handles business logic and database interaction + +### 3. Database + +* PostgreSQL database +* Stores notes persistently +* Managed using Kubernetes StatefulSet + +--- + +## πŸ“¦ Containers Used + +| Container | Description | +| --------- | --------------------------------------------- | +| frontend | Nginx container serving static frontend files | +| backend | FastAPI application handling API requests | +| postgres | PostgreSQL database for storing notes | + +--- + +## ☸️ Kubernetes Objects + +| Object | Description | +| --------------------------- | -------------------------------------------- | +| Namespace | `notes-app` – isolates application resources | +| Deployment | Manages frontend and backend pods | +| StatefulSet | Manages PostgreSQL with persistent storage | +| Service | Enables communication between components | +| PersistentVolume (PV) | Provides storage for database | +| PersistentVolumeClaim (PVC) | Requests storage for PostgreSQL | + +--- + +## 🌐 Networking + +* Communication is handled using Kubernetes **Services** +* Backend is accessible via: + + ``` + http://backend-service + ``` +* PostgreSQL is accessible via: + + ``` + postgres + ``` +* Frontend is exposed externally using **NodePort** + +--- + +## πŸ’Ύ Storage + +* PostgreSQL uses persistent storage via: + + * PersistentVolume (PV) + * PersistentVolumeClaim (PVC) +* Ensures data is retained even if pods restart + +--- + +## βš™οΈ Container Configuration + +### Backend Configuration + +```text +DATABASE_URL=postgresql://user:password@postgres:5432/notesdb +``` + +### Frontend Configuration + +```text +const API_URL = "http://backend-service"; +``` + +--- + +## πŸš€ How to Run the Application + +### 1. Prepare the Application + +```bash +./scripts/prepare-app.sh +``` + +Builds Docker images and prepares environment. + +--- + +### 2. Start the Application + +```bash +./scripts/start-app.sh +``` + +Deploys all Kubernetes resources. + +--- + +### 3. Access the Application + +#### Option 1: NodePort + +``` +http://localhost:30007 +``` + +#### Option 2: Port Forward (recommended for Docker Desktop) + +```bash +kubectl port-forward service/frontend-service 8080:80 -n notes-app +``` + +Then open: + +``` +http://localhost:8080 +``` + +--- + +### 4. Stop the Application + +```bash +./scripts/stop-app.sh +``` + +Deletes all Kubernetes resources. + +--- + +## πŸ§ͺ Functionality + +* Users can add notes via UI +* Notes are stored in PostgreSQL +* Data persists across restarts + +--- + +## πŸ“ Notes + +* Docker images are hosted on Docker Hub +* Kubernetes cluster used: Docker Desktop Kubernetes +* Application demonstrates real-world container orchestration + +--- + +## πŸ“š References + +* OpenAI. (2026). ChatGPT (GPT-5.5). https://chat.openai.com + Used for guidance on Kubernetes deployment, debugging, and structuring the application. + +--- + +## πŸ‘¨β€πŸ’» Author + +Nihal Ahmed diff --git a/configmap.yaml b/configmap.yaml new file mode 100644 index 0000000..06d7405 Binary files /dev/null and b/configmap.yaml differ diff --git a/crud.py b/crud.py new file mode 100644 index 0000000..5a0bd84 --- /dev/null +++ b/crud.py @@ -0,0 +1,12 @@ +from sqlalchemy.orm import Session +import models, schemas + +def create_note(db: Session, note: schemas.NoteCreate): + db_note = models.Note(content=note.content) + db.add(db_note) + db.commit() + db.refresh(db_note) + return db_note + +def get_notes(db: Session): + return db.query(models.Note).all() \ No newline at end of file diff --git a/database.py b/database.py new file mode 100644 index 0000000..85a13b6 --- /dev/null +++ b/database.py @@ -0,0 +1,10 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker, declarative_base +import os + +DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://user:password@postgres:5432/notesdb") + +engine = create_engine(DATABASE_URL) +SessionLocal = sessionmaker(bind=engine) + +Base = declarative_base() \ No newline at end of file diff --git a/deployment.yaml b/deployment.yaml new file mode 100644 index 0000000..72e058f --- /dev/null +++ b/deployment.yaml @@ -0,0 +1,44 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: backend + namespace: notes-app +spec: + replicas: 1 + selector: + matchLabels: + app: backend + template: + metadata: + labels: + app: backend + spec: + containers: + - name: backend + image: nihal0314/backend:latest # βœ… FIXED + env: + - name: DATABASE_URL + vvalue: postgresql://user:password@postgres:5432/notesdb + ports: + - containerPort: 5000 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: frontend + namespace: notes-app +spec: + replicas: 1 + selector: + matchLabels: + app: frontend + template: + metadata: + labels: + app: frontend + spec: + containers: + - name: frontend + image: nihal0314/frontend:latest # βœ… FIXED + ports: + - containerPort: 80 \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..fcd3e3b --- /dev/null +++ b/index.html @@ -0,0 +1,145 @@ + + + + + + Notes App + + + + + + +
+

Notes App

+ +
+ + +
+ +
+ + +
+ + + + + + \ No newline at end of file diff --git a/init.sql b/init.sql new file mode 100644 index 0000000..a6ec2c5 --- /dev/null +++ b/init.sql @@ -0,0 +1,4 @@ +CREATE TABLE IF NOT EXISTS notes ( + id SERIAL PRIMARY KEY, + content TEXT +); \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..1e9703b --- /dev/null +++ b/main.py @@ -0,0 +1,36 @@ +from fastapi import FastAPI, Depends +from sqlalchemy.orm import Session +import models +from database import engine, SessionLocal +import crud, schemas +from fastapi.middleware.cors import CORSMiddleware +models.Base.metadata.create_all(bind=engine) + +app = FastAPI() + +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + +@app.get("/") +def root(): + return {"message": "API is running"} + +@app.post("/notes") +def create(note: schemas.NoteCreate, db: Session = Depends(get_db)): + return crud.create_note(db, note) + +@app.get("/notes") +def read(db: Session = Depends(get_db)): + return crud.get_notes(db) \ No newline at end of file diff --git a/models.py b/models.py new file mode 100644 index 0000000..cb87f78 --- /dev/null +++ b/models.py @@ -0,0 +1,8 @@ +from sqlalchemy import Column, Integer, String +from database import Base + +class Note(Base): + __tablename__ = "notes" + + id = Column(Integer, primary_key=True, index=True) + content = Column(String, index=True) \ No newline at end of file diff --git a/namespace.yaml b/namespace.yaml new file mode 100644 index 0000000..b75e884 --- /dev/null +++ b/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: notes-app \ No newline at end of file diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..6c704d8 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,8 @@ +server { + listen 80; + + location / { + root /usr/share/nginx/html; + index index.html; + } +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..78c99df --- /dev/null +++ b/package.json @@ -0,0 +1,4 @@ +{ + "name": "notes-frontend", + "version": "1.0.0" +} \ No newline at end of file diff --git a/prepare-app.sh b/prepare-app.sh new file mode 100644 index 0000000..169a51d --- /dev/null +++ b/prepare-app.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker-compose build \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..241a744 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +fastapi +uvicorn +psycopg2-binary +sqlalchemy +pydantic + diff --git a/schemas.py b/schemas.py new file mode 100644 index 0000000..2a41a92 --- /dev/null +++ b/schemas.py @@ -0,0 +1,11 @@ +from pydantic import BaseModel + +class NoteCreate(BaseModel): + content: str + +class NoteResponse(BaseModel): + id: int + content: str + + class Config: + from_attributes = True \ No newline at end of file diff --git a/secret.yaml b/secret.yaml new file mode 100644 index 0000000..06d7405 Binary files /dev/null and b/secret.yaml differ diff --git a/service.yaml b/service.yaml new file mode 100644 index 0000000..57c7365 --- /dev/null +++ b/service.yaml @@ -0,0 +1,25 @@ +apiVersion: v1 +kind: Service +metadata: + name: backend-service + namespace: notes-app +spec: + selector: + app: backend + ports: + - port: 80 + targetPort: 5000 +--- +apiVersion: v1 +kind: Service +metadata: + name: frontend-service + namespace: notes-app +spec: + type: NodePort + selector: + app: frontend + ports: + - port: 80 + targetPort: 80 + nodePort: 30007 \ No newline at end of file diff --git a/start-app.sh b/start-app.sh new file mode 100644 index 0000000..80b5727 --- /dev/null +++ b/start-app.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker-compose up -d \ No newline at end of file diff --git a/statefulset.yaml b/statefulset.yaml new file mode 100644 index 0000000..3d431d5 --- /dev/null +++ b/statefulset.yaml @@ -0,0 +1,51 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: postgres + namespace: notes-app +spec: + serviceName: postgres + replicas: 1 + selector: + matchLabels: + app: postgres + template: + metadata: + labels: + app: postgres + spec: + containers: + - name: postgres + image: postgres:14 + env: + - name: POSTGRES_USER + value: user + - name: POSTGRES_PASSWORD + value: password + - name: POSTGRES_DB + value: notesdb + ports: + - containerPort: 5432 + volumeMounts: + - name: postgres-storage + mountPath: /var/lib/postgresql/data + volumeClaimTemplates: + - metadata: + name: postgres-storage + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi + +apiVersion: v1 +kind: Service +metadata: + name: postgres + namespace: notes-app +spec: + selector: + app: postgres + ports: + - port: 5432 + targetPort: 5432 \ No newline at end of file diff --git a/stop-app.sh b/stop-app.sh new file mode 100644 index 0000000..8bff4e7 --- /dev/null +++ b/stop-app.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker-compose down \ No newline at end of file diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..56004c9 --- /dev/null +++ b/vite.config.js @@ -0,0 +1 @@ +export default {} \ No newline at end of file