From 9b52815bce8aaacd70a04eaecf58395489e231f4 Mon Sep 17 00:00:00 2001 From: Sabareesan Date: Wed, 1 Apr 2026 01:06:29 +0200 Subject: [PATCH] Docker web app project --- README.md | 61 ++++++++++++++++ Z1/backend/Dockerfile | 12 ++++ Z1/backend/package.json | 9 +++ Z1/backend/server.js | 31 ++++++++ Z1/docker-compose.yml | 36 ++++++++++ Z1/frontend/index.html | 155 ++++++++++++++++++++++++++++++++++++++++ Z1/prepare-app.sh | 11 +++ Z1/remove-app.sh | 9 +++ Z1/start-app.sh | 10 +++ Z1/stop-app.sh | 7 ++ Z1/๐Ÿ“˜ Docker.docx | Bin 0 -> 19382 bytes 11 files changed, 341 insertions(+) create mode 100644 README.md create mode 100644 Z1/backend/Dockerfile create mode 100644 Z1/backend/package.json create mode 100644 Z1/backend/server.js create mode 100644 Z1/docker-compose.yml create mode 100644 Z1/frontend/index.html create mode 100644 Z1/prepare-app.sh create mode 100644 Z1/remove-app.sh create mode 100644 Z1/start-app.sh create mode 100644 Z1/stop-app.sh create mode 100644 Z1/๐Ÿ“˜ Docker.docx diff --git a/README.md b/README.md new file mode 100644 index 0000000..ded6492 --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +# ๐Ÿš€ Docker Multi-Container Web Application + +## ๐Ÿ“Œ Project Overview +This project demonstrates the deployment of a multi-container web application using Docker. + +It includes: +- Frontend (Nginx) +- Backend (Node.js) +- Database (MongoDB) +- Mongo Express (DB UI) + +--- + +## ๐Ÿ—๏ธ Architecture +Browser โ†’ Frontend โ†’ Backend โ†’ MongoDB + +--- + +## โš™๏ธ Technologies Used +- Docker +- Docker Compose +- Node.js +- MongoDB +- Nginx + +--- + +## ๐Ÿ“ฆ Services and Ports + +| Service | Port | +|----------------|------| +| Frontend | 8080 | +| Backend | 3000 | +| MongoDB | 27017 | +| Mongo Express | 8081 | + +--- + +## ๐Ÿ› ๏ธ Setup + +### Prepare +./prepare-app.sh + +### Start +./start-app.sh + +### Stop +./stop-app.sh + +### Remove +./remove-app.sh + +--- + +## ๐Ÿ’พ Persistence +MongoDB uses Docker volume (mongo-data), so data is ู…ุญููˆุธ after restart. + +--- + +## ๐Ÿ‘จโ€๐Ÿ’ป Author +Sabareesan diff --git a/Z1/backend/Dockerfile b/Z1/backend/Dockerfile new file mode 100644 index 0000000..e8d1758 --- /dev/null +++ b/Z1/backend/Dockerfile @@ -0,0 +1,12 @@ +FROM node:18 + +WORKDIR /app + +COPY package.json . +RUN npm install + +COPY . . + +EXPOSE 3000 + +CMD ["node", "server.js"] \ No newline at end of file diff --git a/Z1/backend/package.json b/Z1/backend/package.json new file mode 100644 index 0000000..aa079c4 --- /dev/null +++ b/Z1/backend/package.json @@ -0,0 +1,9 @@ +{ + "name": "backend", + "version": "1.0.0", + "dependencies": { + "express": "^4.18.2", + "mongoose": "^7.0.0", + "cors": "^2.8.5" +} +} \ No newline at end of file diff --git a/Z1/backend/server.js b/Z1/backend/server.js new file mode 100644 index 0000000..38b43fd --- /dev/null +++ b/Z1/backend/server.js @@ -0,0 +1,31 @@ +const express = require("express"); +const mongoose = require("mongoose"); + +const app = express(); +app.use(express.json()); + +const cors = require("cors"); +app.use(cors()); + +mongoose.connect("mongodb://mongo:27017/mydb") + .then(() => console.log("MongoDB connected")) + .catch(err => console.log(err)); + +const Item = mongoose.model("Item", { name: String }); + +app.get("/", (req, res) => { + res.send("Backend running ๐Ÿš€"); +}); + +app.post("/add", async (req, res) => { + const item = new Item({ name: req.body.name }); + await item.save(); + res.send(item); +}); + +app.get("/items", async (req, res) => { + const items = await Item.find(); + res.send(items); +}); + +app.listen(3000, () => console.log("Server running on port 3000")); \ No newline at end of file diff --git a/Z1/docker-compose.yml b/Z1/docker-compose.yml new file mode 100644 index 0000000..ed1414d --- /dev/null +++ b/Z1/docker-compose.yml @@ -0,0 +1,36 @@ +version: "3.9" + +services: + + frontend: + image: nginx:latest + ports: + - "8080:80" + volumes: + - ./frontend:/usr/share/nginx/html + depends_on: + - backend + + backend: + build: ./backend + ports: + - "3000:3000" + depends_on: + - mongo + + mongo: + image: mongo:6 + volumes: + - mongo-data:/data/db + ports: + - "27017:27017" + + mongo-express: + image: mongo-express + ports: + - "8081:8081" + depends_on: + - mongo + +volumes: + mongo-data: \ No newline at end of file diff --git a/Z1/frontend/index.html b/Z1/frontend/index.html new file mode 100644 index 0000000..5992882 --- /dev/null +++ b/Z1/frontend/index.html @@ -0,0 +1,155 @@ + + + + +Docker Web App + + + + + + +
+

๐Ÿš€ Docker App

+ + + +
+ + +
+ + + +
+
+ + + + + \ No newline at end of file diff --git a/Z1/prepare-app.sh b/Z1/prepare-app.sh new file mode 100644 index 0000000..8cd1b19 --- /dev/null +++ b/Z1/prepare-app.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +echo "Preparing application..." + +docker network create app-network 2>/dev/null + +docker volume create mongo-data + +docker-compose build + +echo "Preparation done โœ…" \ No newline at end of file diff --git a/Z1/remove-app.sh b/Z1/remove-app.sh new file mode 100644 index 0000000..2b3cb1c --- /dev/null +++ b/Z1/remove-app.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +echo "Removing application..." + +docker-compose down -v +docker volume rm mongo-data 2>/dev/null +docker network rm app-network 2>/dev/null + +echo "All removed ๐Ÿงน" \ No newline at end of file diff --git a/Z1/start-app.sh b/Z1/start-app.sh new file mode 100644 index 0000000..aa5791f --- /dev/null +++ b/Z1/start-app.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +echo "Starting application..." + +docker-compose up -d + +echo "Application started ๐Ÿš€" +echo "Frontend: http://localhost:8080" +echo "Backend: http://localhost:3000" +echo "Mongo Express: http://localhost:8081" \ No newline at end of file diff --git a/Z1/stop-app.sh b/Z1/stop-app.sh new file mode 100644 index 0000000..8bd39f1 --- /dev/null +++ b/Z1/stop-app.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +echo "Stopping application..." + +docker-compose down + +echo "Application stopped โœ…" \ No newline at end of file diff --git a/Z1/๐Ÿ“˜ Docker.docx b/Z1/๐Ÿ“˜ Docker.docx new file mode 100644 index 0000000000000000000000000000000000000000..a2b8c276bc03a3b83e1bf5b6138b0a806c92cfbf GIT binary patch literal 19382 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 literal 0 HcmV?d00001