From dddfae165b59fdf9ea96e6f6b05e5cc3056014bc Mon Sep 17 00:00:00 2001 From: Shivam Date: Thu, 8 Oct 2026 20:29:16 +0530 Subject: [PATCH] feat: containerize MEAN stack with Docker Compose --- NOTICE | 9 + README.md | 822 ++++++++++++++++++++++++++++++++++----- client/.dockerignore | 8 + client/Dockerfile | 37 +- client/nginx.conf | 25 +- docker-compose.yml | 73 ++++ docs/architecture.md | 246 ++++++++++++ docs/deployment-guide.md | 231 +++++++++++ docs/troubleshooting.md | 424 ++++++++++++++++++++ server/.dockerignore | 8 + server/Dockerfile | 42 +- 11 files changed, 1800 insertions(+), 125 deletions(-) create mode 100644 NOTICE create mode 100644 client/.dockerignore create mode 100644 docker-compose.yml create mode 100644 docs/architecture.md create mode 100644 docs/deployment-guide.md create mode 100644 docs/troubleshooting.md create mode 100644 server/.dockerignore diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..df5972c --- /dev/null +++ b/NOTICE @@ -0,0 +1,9 @@ +# Attribution / Modification Notice + +This repository is based on the original MongoDB Developer MEAN Stack Example project. + +The original project is licensed under the Apache License, Version 2.0. + +This repository contains modifications and additions made for a personal DevOps learning project, including Docker/Docker Compose deployment configuration, NGINX configuration, containerization, deployment documentation, troubleshooting documentation, and related infrastructure work. + +The original project's copyright and attribution notices remain applicable to the original portions of the work. diff --git a/README.md b/README.md index a689bc3..f6fa561 100644 --- a/README.md +++ b/README.md @@ -1,189 +1,803 @@ -# MEAN Stack Example: Employee Records App (MongoDB, Express, Angular, Node.js) +# MEAN Stack Example: Employee Records App -A full-stack CRUD application built with MongoDB, Express, Angular, and Node.js (MEAN). +A full-stack employee management application built with the **MEAN stack** — MongoDB, Express, Angular, and Node.js. -Companion code for the [MEAN Stack Tutorial](https://www.mongodb.com/languages/mean-stack-tutorial?utm_campaign=devrel&utm_source=github&utm_medium=referral&utm_content=mean.stack.example&utm_term=learning.fuel). +This repository started from the original MEAN Stack Example application and has been extended as a practical **DevOps learning project**. The application is now containerized with Docker Compose, with Kubernetes and AWS/EKS planned as the next stages. -[![CI](https://github.com/mongodb-developer/mean-stack-example/actions/workflows/ci.yml/badge.svg)](https://github.com/mongodb-developer/mean-stack-example/actions/workflows/ci.yml) -[![License: Apache-2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) -[![GitHub stars](https://img.shields.io/github/stars/mongodb-developer/mean-stack-example?style=social)](https://github.com/mongodb-developer/mean-stack-example/stargazers) +> **Current focus:** Phase 1 — Docker Compose deployment + +--- ## Project Overview -This project demonstrates an employee record tracker: +The application provides a simple employee record management system with full CRUD operations: + +- Create employee records +- Read employee records +- Update employee records +- Delete employee records + +The Angular frontend communicates with a Node.js/Express API, while employee data is stored in MongoDB. -- Create records -- Read records from MongoDB -- Update records -- Delete records +The original application architecture was: -The Angular app in `client` calls an Express API in `server`, and data is stored in MongoDB. +```text +Angular Frontend → Express API → MongoDB +``` -## MEAN Stack Architecture +The current Dockerized architecture is: ```text -┌─────────────────────┐ REST (JSON) ┌──────────────────────────┐ -│ Angular (CLI) │ ─────────────────────► │ Express API │ -│ client │ ◄───────────────────── │ server │ -│ :4200 │ │ :5300 │ -└─────────────────────┘ └───────────┬──────────────┘ - │ MongoDB Node.js driver - ▼ - ┌──────────────────────────┐ - │ MongoDB │ - │ database: meanStackExample - │ collection: employees │ - └──────────────────────────┘ + ┌─────────────────────────┐ + │ Browser │ + │ localhost:8080 │ + └────────────┬────────────┘ + │ + ▼ + ┌─────────────────────────┐ + │ Angular + NGINX │ + │ mean-client │ + │ :80 │ + └────────────┬────────────┘ + │ /api/ + ▼ + ┌─────────────────────────┐ + │ Node.js + Express │ + │ mean-server │ + │ :5300 │ + └────────────┬────────────┘ + │ + ▼ + ┌─────────────────────────┐ + │ MongoDB 4.4 │ + │ mean-db │ + │ :27017 │ + └─────────────────────────┘ ``` -Stack: +--- + +## Current Project Status + +| Phase | Status | Description | +|---|---|---| +| Phase 1 | ✅ Completed | Docker + Docker Compose | +| Phase 2 | 🚧 Planned | Kubernetes deployment | +| Phase 3 | ⏳ Planned | Advanced Kubernetes | +| Phase 4 | ⏳ Planned | AWS / EKS deployment | + +--- + +## Tech Stack -- Frontend: Angular 21, Angular Material -- Backend: Node.js, Express 4, TypeScript, MongoDB Node.js Driver 6 -- Database: MongoDB (`meanStackExample.employees` collection) +### Application + +- **Frontend:** Angular +- **Backend:** Node.js + Express + TypeScript +- **Database:** MongoDB +- **API:** REST / JSON + +### DevOps + +- Docker +- Docker Compose +- NGINX +- Multi-stage Docker builds +- Docker healthcheck +- Named Docker volume +- Docker network / service discovery + +### Planned + +- Kubernetes +- Ingress +- ConfigMaps / Secrets +- Probes +- HPA +- RBAC +- NetworkPolicy +- CI/CD +- AWS EKS + +--- ## Project Structure ```text -client/ # Angular frontend -server/ # Express API + MongoDB integration +mean-stack-example/ +│ +├── .github/ +│ ├── CODEOWNERS +│ └── workflows/ +│ └── ci.yml +│ +├── client/ +│ ├── src/ +│ ├── Dockerfile +│ ├── nginx.conf +│ ├── .dockerignore +│ └── ... +│ +├── server/ +│ ├── src/ +│ ├── scripts/ +│ ├── tests/ +│ ├── Dockerfile +│ ├── .dockerignore +│ ├── .env.example +│ └── ... +│ +├── docs/ +│ ├── architecture.md +│ ├── deployment-guide.md +│ └── troubleshooting.md +│ +├── docker-compose.yml +├── README.md +├── LICENSE +├── NOTICE +└── .gitignore + +``` + +--- + +# Docker Deployment + +## Docker Compose Services + +The application is split into three containers: + +| Service | Container | Purpose | Port | +|---|---|---|---| +| `client` | `mean-client` | Angular + NGINX | `8080 → 80` | +| `server` | `mean-server` | Express API | `5300 → 5300` | +| `database` | `mean-db` | MongoDB | internal `27017` | + +MongoDB is intentionally **not published to the host**. The backend reaches it through the Docker Compose service name: + +```text +mongodb://database:27017/ +``` + +Docker's internal DNS resolves `database` to the MongoDB container. + +--- + +## Why NGINX Is Used + +The Angular application is built into static production files and served by NGINX. + +NGINX also acts as a reverse proxy for the backend API. + +For example: + +```text +Browser + │ + ├── / → Angular application + │ + └── /api/employees → NGINX + │ + ▼ + mean-server:5300 + │ + ▼ + MongoDB +``` + +The `/api/` prefix is removed by the NGINX proxy configuration before the request reaches Express. + +Therefore: + +```text +/api/employees +``` + +is forwarded internally as: + +```text +/employees ``` +which matches the backend route. + +--- + +# Quick Start + ## Prerequisites -- Node.js ^24.18.0 -- npm ^11.16.0 -- A local MongoDB instance or a free [MongoDB Atlas](https://www.mongodb.com/atlas?utm_campaign=devrel&utm_source=github&utm_medium=referral&utm_content=mean.stack.example&utm_term=learning.fuel) cluster +Install: -## Quick Start and MongoDB Setup +- Docker +- Docker Compose plugin + +Verify: ```bash -# 1) Clone -git clone https://github.com/mongodb-developer/mean-stack-example.git -cd mean-stack-example +docker --version +docker compose version +``` + +No local Node.js or MongoDB installation is required for the Docker deployment. + +--- -# 2) Create server environment file -cp server/.env.example server/.env +## 1. Clone the Repository + +```bash +git clone https://github.com/Shivam-Infra-Labs/mean-stack-example +cd mean-stack-example ``` -Update `server/.env` with one of the following `DATABASE_URI` values: +--- + +## 2. Validate the Compose Configuration -Local MongoDB: +Before starting the application: -```env -DATABASE_URI=mongodb://localhost:27017/ -PORT=5300 +```bash +docker compose config ``` -Atlas cluster: +If the configuration is valid, Docker Compose will render the final configuration without errors. + +--- + +## 3. Build the Images -```env -DATABASE_URI=mongodb+srv://:@.mongodb.net/ -PORT=5300 +```bash +docker compose build ``` -If you are new to Atlas, use the [Atlas quick start guide](https://www.mongodb.com/docs/atlas/getting-started/?utm_campaign=devrel&utm_source=github&utm_medium=referral&utm_content=mean.stack.example&utm_term=learning.fuel) and then paste your connection string into `DATABASE_URI`. +This builds: + +- Angular + NGINX image +- Node.js + Express image -Optional: seed sample data: +MongoDB uses the official `mongo:4.4` image. + +--- + +## 4. Start the Application ```bash -(cd server && npm install && npm run seed) +docker compose up -d ``` -Start the backend API: +Check the containers: ```bash -cd server -npm start +docker compose ps +``` + +Expected services: + +```text +mean-client +mean-server +mean-db ``` -Start the frontend in a second terminal: +--- + +## 5. Open the Application + +Open: + +```text +http://localhost:8080 +``` + +The Employee Management application should be available. + +--- + +# Health Check + +The backend exposes: + +```text +GET /healthcheck +``` + +From the host: ```bash -cd client -npm install -npm start +curl http://localhost:5300/healthcheck ``` -Open `http://localhost:4200`. +Expected response: -## GitHub Codespaces and Dev Containers +```json +{"status":"ok"} +``` -GitHub Codespaces is an easy and fast way to get this project running without installing anything locally. It uses a dev container, which is a Docker environment configured for development. +The MongoDB container also has a Docker healthcheck using: -[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/mongodb-developer/mean-stack-example?quickstart=1) +```bash +mongo --eval "db.adminCommand('ping')" +``` -## REST API Endpoints +The backend waits for MongoDB to become healthy before starting. -Base URL: `http://localhost:5300` +--- + +# REST API + +Backend base URL: + +```text +http://localhost:5300 +``` | Method | Endpoint | Description | |---|---|---| -| `GET` | `/healthcheck` | Check API readiness | +| `GET` | `/healthcheck` | Check API health | | `GET` | `/employees` | Retrieve all employees | -| `GET` | `/employees/:id` | Retrieve one employee by ID | +| `GET` | `/employees/:id` | Retrieve one employee | | `POST` | `/employees` | Create an employee | | `PUT` | `/employees/:id` | Update an employee | | `DELETE` | `/employees/:id` | Delete an employee | -Example request body for create or update: +Through the frontend NGINX proxy, API requests use: + +```text +/api/employees +``` + +Example request body: ```json { - "name": "Jane Smith", - "position": "Developer", - "level": "senior" + "name": "Jane Smith", + "position": "Developer", + "level": "senior" } ``` -## MongoDB Features Demonstrated +Supported levels: + +```text +junior +mid +senior +``` + +--- + +# Database + +The backend connects to MongoDB using the Docker service name: + +```text +mongodb://database:27017/ +``` + +The application uses: + +```text +Database: meanStackExample +Collection: employees +``` + +The database uses a named Docker volume: + +```text +mongo-data +``` + +This means MongoDB data survives normal container recreation. + +For example: + +```bash +docker compose down +docker compose up -d +``` + +does **not** remove the named volume. + +To remove the database volume intentionally: + +```bash +docker compose down -v +``` + +> **Warning:** `docker compose down -v` deletes the Compose-managed database volume and therefore removes the stored MongoDB data. + +--- + +# Docker Architecture + +## Container Communication + +The services communicate over the Docker Compose network. + +```text +mean-client + │ + │ HTTP + ▼ +mean-server:5300 + │ + │ MongoDB protocol + ▼ +database:27017 +``` + +Containers should use Docker service names for internal communication rather than `localhost`. + +For example, inside the backend container: + +```text +database:27017 +``` + +is correct. + +This would be incorrect: + +```text +localhost:27017 +``` + +because `localhost` inside the backend container refers to the backend container itself. + +--- + +# Multi-Stage Builds + +Both application images use multi-stage Docker builds. + +### Client + +```text +Node.js + ↓ +Angular production build + ↓ +NGINX runtime image +``` + +### Server + +```text +Node.js + ↓ +TypeScript build + ↓ +Production Node.js runtime image +``` + +This keeps build dependencies separate from the final runtime image. + +--- + +# Data Persistence + +MongoDB uses: + +```yaml +volumes: + - mongo-data:/data/db +``` + +The volume provides persistent database storage outside the MongoDB container lifecycle. + +A normal restart: + +```bash +docker compose restart +``` + +does not remove the data. + +Recreating the containers with: + +```bash +docker compose down +docker compose up -d +``` + +also preserves the named volume. + +--- + +# Useful Docker Commands + +### View containers + +```bash +docker compose ps +``` + +### View logs + +```bash +docker compose logs +``` + +### Follow logs + +```bash +docker compose logs -f +``` + +### Backend logs + +```bash +docker compose logs -f server +``` + +### Frontend logs + +```bash +docker compose logs -f client +``` + +### MongoDB logs + +```bash +docker compose logs -f database +``` + +### Restart + +```bash +docker compose restart +``` + +### Stop containers + +```bash +docker compose down +``` + +### Rebuild after changing code/configuration + +```bash +docker compose build +docker compose up -d +``` + +### Inspect running containers + +```bash +docker ps +``` + +--- + +# Documentation + +Detailed documentation is available in: + +- [`docs/architecture.md`](docs/architecture.md) +- [`docs/deployment-guide.md`](docs/deployment-guide.md) +- [`docs/troubleshooting.md`](docs/troubleshooting.md) + +--- + +# Troubleshooting + +## Frontend Shows "Welcome to nginx!" + +Check the files inside the NGINX document root: + +```bash +docker exec mean-client ls -lah /usr/share/nginx/html +``` + +The Angular build should be present. + +The current Angular build produces `index.csr.html`, so the client image moves it to: + +```text +/usr/share/nginx/html/index.html +``` + +during the image build. + +--- + +## API Healthcheck Fails + +Check: + +```bash +docker compose ps +``` + +Then inspect backend logs: + +```bash +docker compose logs server +``` + +Also test: + +```bash +curl http://localhost:5300/healthcheck +``` + +--- + +## Employee Data Is Missing + +Check that MongoDB is healthy: + +```bash +docker compose ps +``` + +Then inspect: + +```bash +docker compose logs database +``` + +Also verify that the MongoDB volume still exists. + +Avoid: + +```bash +docker compose down -v +``` + +unless you intentionally want to delete the database volume. + +--- + +# Known Project Note: MongoDB Version + +This learning deployment currently uses: + +```text +mongo:4.4 +``` + +This was intentionally selected for the local learning environment. + +**MongoDB 4.4 is an old/EOL release and should not be considered a production recommendation.** + +For future production-oriented deployment, the database version should be upgraded to a currently supported MongoDB release after compatibility testing. + +--- + +# DevOps Roadmap + +The project is intentionally being developed in stages. + +## Phase 1 — Docker Compose ✅ + +Completed: + +- Dockerized Angular frontend +- NGINX static serving +- NGINX reverse proxy +- Dockerized Node.js backend +- MongoDB container +- Docker Compose orchestration +- MongoDB healthcheck +- Service dependency +- Named persistent volume +- Multi-stage builds +- `.dockerignore` +- CRUD verification +- Restart/recreation persistence verification + +--- + +## Phase 2 — Kubernetes 🚧 + +Planned: + +```text +Namespace +Deployments +Services +ConfigMaps +Secrets +Persistent storage +Ingress +Liveness probes +Readiness probes +``` + +The application will first be deployed locally using a Kubernetes learning cluster. + +--- + +## Phase 3 — Advanced Kubernetes + +Planned: + +- HPA +- RBAC +- NetworkPolicy +- PodDisruptionBudget +- Centralized logging +- Backup/restore +- Security scanning +- Rollback strategy +- Smoke tests +- Failure testing +- Multiple environments + +Later: + +- Helm +- Argo CD +- Service mesh +- Distributed tracing +- Chaos/failure experiments + +--- + +## Phase 4 — AWS / EKS + +After the local Kubernetes implementation is stable, the project can be moved toward AWS: + +```text +AWS + │ + └── EKS + │ + ├── Frontend + ├── Backend + ├── Ingress + └── Supporting infrastructure +``` + +The goal is to progressively transform the original application into a realistic DevOps/cloud portfolio project rather than jumping directly to cloud deployment. + +--- + +# Original Project + +This project is based on the original **MEAN Stack Example** application and its educational purpose. -| Feature | Where | -|---|---| -| [MongoDB Node.js Driver](https://www.mongodb.com/docs/drivers/node/current/?utm_campaign=devrel&utm_source=github&utm_medium=referral&utm_content=mean.stack.example&utm_term=learning.fuel) | `server/src/database.ts` | -| [CRUD operations](https://www.mongodb.com/docs/manual/crud/?utm_campaign=devrel&utm_source=github&utm_medium=referral&utm_content=mean.stack.example&utm_term=learning.fuel) | `server/src/employee.routes.ts` | -| [MongoDB schema validation](https://www.mongodb.com/docs/manual/core/schema-validation/?utm_campaign=devrel&utm_source=github&utm_medium=referral&utm_content=mean.stack.example&utm_term=learning.fuel) | startup validation in `server/src/database.ts` | -| [Environment-based connection setup](https://www.mongodb.com/docs/drivers/node/current/fundamentals/connection/connect/?utm_campaign=devrel&utm_source=github&utm_medium=referral&utm_content=mean.stack.example&utm_term=learning.fuel) | `DATABASE_URI` in `server/.env` | +The original application demonstrates: -## Troubleshooting +- Angular frontend +- Express API +- MongoDB +- CRUD operations +- MongoDB schema validation -### Cannot connect to MongoDB Atlas +The Docker and DevOps work in this repository extends that application into a containerized deployment workflow. -- Verify `DATABASE_URI` in `server/.env` -- Confirm your database user credentials are correct (Atlas) -- Confirm your IP is in [Atlas Network Access](https://www.mongodb.com/docs/atlas/security/ip-access-list/?utm_campaign=devrel&utm_source=github&utm_medium=referral&utm_content=mean.stack.example&utm_term=learning.fuel) +--- -### Backend fails to start +# License -- Check Node version: `node --version` -- Confirm `server/.env` exists -- Reinstall dependencies in `server`: `npm install` +The original project is distributed under the **Apache 2.0** license. -### Frontend shows empty data +See [`LICENSE`](LICENSE) for the license text. -- Confirm backend is running on `:5300` -- Open browser dev tools and check network requests -- Confirm records exist in MongoDB (or run `cd server && npm run seed`) +--- -### Port already in use +## Project Status -- Change `PORT` in `server/.env`, or stop the process using `:5300` +**Current:** Docker Compose deployment completed and verified. -## Community and Support +**Next:** Kubernetes deployment. -- Use [GitHub Issues](https://github.com/mongodb-developer/mean-stack-example/issues) for bugs and feature requests -- Use [MongoDB Community Forums](https://www.mongodb.com/community/forums/) for general MongoDB questions +--- -## Additional Resources +# 👨‍💻 Author -- [MEAN Stack Tutorial](https://www.mongodb.com/languages/mean-stack-tutorial?utm_campaign=devrel&utm_source=github&utm_medium=referral&utm_content=mean.stack.example&utm_term=learning.fuel) -- [MongoDB Atlas Docs](https://www.mongodb.com/docs/atlas?utm_campaign=devrel&utm_source=github&utm_medium=referral&utm_content=mean.stack.example&utm_term=learning.fuel) -- [MongoDB Node.js Driver Docs](https://www.mongodb.com/docs/drivers/node/current/?utm_campaign=devrel&utm_source=github&utm_medium=referral&utm_content=mean.stack.example&utm_term=learning.fuel) +**Shivam Kumar Sinha** +*DevOps | Cloud Computing | Linux | Networking | Docker | Kubernetes* -## License +🌐 **Connect With Me:** +- 💼 **LinkedIn:** [Shivam Kumar Sinha](https://www.linkedin.com/in/shivam-kumar-sinha-0a9248308/) +- 💻 **GitHub:** [Shivam-Infra-Labs](https://github.com/Shivam-Infra-Labs) -[Apache 2.0](LICENSE) +--- +# ⭐ Support -## Disclaimer +If you find this project helpful, please consider giving it a ⭐ **Star** on GitHub! -This repository is for educational use and is not a supported MongoDB product. diff --git a/client/.dockerignore b/client/.dockerignore new file mode 100644 index 0000000..5b7f8f3 --- /dev/null +++ b/client/.dockerignore @@ -0,0 +1,8 @@ +node_modules +dist +.angular +.git +.github +.vscode +.env +*.log diff --git a/client/Dockerfile b/client/Dockerfile index d00a6cf..ba75c6d 100644 --- a/client/Dockerfile +++ b/client/Dockerfile @@ -1,19 +1,34 @@ -FROM node:17-slim AS build +# ============================================ +# Stage 1: Build Angular application +# ============================================ +FROM node:22-alpine AS builder -WORKDIR /usr/src/app -COPY package.json package-lock.json ./ +WORKDIR /app -# Install dependencies and copy them to the container -RUN npm install +# Install dependencies +COPY package*.json ./ +RUN npm ci + +# Copy application source COPY . . -# Build the Angular application for production -RUN npm run build --prod +# Production build +RUN npm run build -- --configuration production + + +# ============================================ +# Stage 2: Serve with NGINX +# ============================================ +FROM nginx:alpine -# Configure the nginx web server -FROM nginx:1.17.1-alpine +# Copy Angular production build +COPY --from=builder /app/dist/client/browser/. /usr/share/nginx/html + +RUN mv /usr/share/nginx/html/index.csr.html /usr/share/nginx/html/index.html + +# Custom NGINX configuration COPY nginx.conf /etc/nginx/nginx.conf -COPY --from=build /usr/src/app/dist/client /usr/share/nginx/html -# Run the web service on container startup. +EXPOSE 80 + CMD ["nginx", "-g", "daemon off;"] diff --git a/client/nginx.conf b/client/nginx.conf index 9b57ec7..b4519a3 100644 --- a/client/nginx.conf +++ b/client/nginx.conf @@ -1,15 +1,32 @@ -events{} +events {} http { - include /etc/nginx/mime.types; server { - listen 8080; - server_name 0.0.0.0; + listen 80; + server_name _; + root /usr/share/nginx/html; index index.html; + # ============================================ + # Backend API + # ============================================ + location /api/ { + proxy_pass http://server:5300/; + + proxy_http_version 1.1; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # ============================================ + # Angular Frontend + # ============================================ location / { try_files $uri $uri/ /index.html; } diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..3a1dc3c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,73 @@ +services: + + # ============================================ + # MongoDB + # ============================================ + database: + image: mongo:4.4 + container_name: mean-db + restart: unless-stopped + + volumes: + - mongo-data:/data/db + + healthcheck: + test: + [ + "CMD", + "mongo", + "--eval", + "db.adminCommand('ping')" + ] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + + + # ============================================ + # Node.js + Express Backend + # ============================================ + server: + build: + context: ./server + dockerfile: Dockerfile + + container_name: mean-server + restart: unless-stopped + + ports: + - "5300:5300" + + environment: + DATABASE_URI: mongodb://database:27017/ + PORT: 5300 + + depends_on: + database: + condition: service_healthy + + + # ============================================ + # Angular + NGINX Frontend + # ============================================ + client: + build: + context: ./client + dockerfile: Dockerfile + + container_name: mean-client + restart: unless-stopped + + ports: + - "8080:80" + + depends_on: + - server + + +# ============================================ +# Persistent MongoDB storage +# ============================================ +volumes: + mongo-data: diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..594df58 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,246 @@ +# Architecture + +## 1. Current Docker Architecture + +```text + Host Machine + │ + │ :8080 + ▼ + ┌───────────────────────┐ + │ mean-client │ + │ Angular + NGINX │ + │ container :80 │ + └───────────┬───────────┘ + │ + /api/│ + ▼ + ┌───────────────────────┐ + │ mean-server │ + │ Node + Express │ + │ container :5300 │ + └───────────┬───────────┘ + │ + MongoDB + ▼ + ┌───────────────────────┐ + │ mean-db │ + │ MongoDB 4.4 │ + │ container :27017 │ + └───────────┬───────────┘ + │ + ▼ + mongo-data volume +``` + +## 2. Service Responsibilities + +### Client + +The client container: + +- contains the Angular production build +- serves static files through NGINX +- handles browser requests +- proxies `/api/` requests to the backend + +### Server + +The server container: + +- runs the Node.js/Express application +- exposes port `5300` +- provides employee CRUD endpoints +- connects to MongoDB +- exposes `/healthcheck` + +### Database + +The database container: + +- runs MongoDB 4.4 +- stores the `meanStackExample` database +- stores the `employees` collection +- persists data using the `mongo-data` named volume + +## 3. Request Flow + +A browser request for the application: + +```text +GET / + ↓ +localhost:8080 + ↓ +NGINX + ↓ +Angular static files +``` + +An API request: + +```text +GET /api/employees + ↓ +localhost:8080 + ↓ +NGINX + ↓ +server:5300/employees + ↓ +Express + ↓ +MongoDB +``` + +The trailing `/` in the NGINX `proxy_pass` configuration causes the `/api/` prefix to be removed when forwarding the request. + +## 4. Docker DNS + +Docker Compose creates an internal network for the services. + +The backend connects to MongoDB using: + +```text +database:27017 +``` + +not: + +```text +localhost:27017 +``` + +Inside the server container, `localhost` means the server container itself. + +## 5. Startup Dependency + +MongoDB has a healthcheck. + +The server is configured to wait for: + +```text +database → healthy +``` + +before starting. + +This reduces startup race conditions between MongoDB and the backend. + +The client starts after the server service has started. + +## 6. Storage + +MongoDB stores its data in: + +```text +/data/db +``` + +which is backed by: + +```text +mongo-data +``` + +The volume survives: + +```bash +docker compose restart +``` + +and: + +```bash +docker compose down +docker compose up -d +``` + +It is removed only when the volume is explicitly deleted, such as: + +```bash +docker compose down -v +``` + +## 7. Network Exposure + +The host exposes: + +```text +8080 → client +5300 → server +``` + +MongoDB is not published to the host. + +The database is therefore accessible to the backend through the internal Docker network without requiring a host port mapping. + +## 8. Build Flow + +### Client + +```text +client source + ↓ +Node.js build stage + ↓ +Angular production build + ↓ +NGINX runtime image +``` + +### Server + +```text +server source + ↓ +Node.js build stage + ↓ +TypeScript compilation + ↓ +production Node.js runtime +``` + +This separates build tooling from runtime containers. + +## 9. Application Data Model + +The backend uses: + +```text +Database: meanStackExample +Collection: employees +``` + +Employee records contain: + +```text +name +position +level +_id +``` + +The accepted `level` values are: + +```text +junior +mid +senior +``` + +## 10. Current Limitations + +The current Docker Compose deployment is intentionally a learning/portfolio stage. + +It does not yet provide: + +- Kubernetes orchestration +- high availability +- horizontal scaling +- production-grade secret management +- centralized observability +- automated CI/CD +- cloud infrastructure + +These are planned for later project phases. diff --git a/docs/deployment-guide.md b/docs/deployment-guide.md new file mode 100644 index 0000000..7e0d20e --- /dev/null +++ b/docs/deployment-guide.md @@ -0,0 +1,231 @@ +# Deployment Guide + +## Prerequisites + +Install: + +```bash +docker --version +docker compose version +``` + +The Docker deployment does not require a separately installed MongoDB or Node.js runtime on the host. + +## 1. Clone + +```bash +git clone +cd mean-stack-example +``` + +## 2. Validate Configuration + +```bash +docker compose config +``` + +This is a useful first check because it catches malformed Compose YAML before containers are started. + +## 3. Build + +```bash +docker compose build +``` + +## 4. Start + +```bash +docker compose up -d +``` + +## 5. Verify + +```bash +docker compose ps +``` + +The expected services are: + +```text +mean-client +mean-server +mean-db +``` + +MongoDB should report a healthy status. + +## 6. Test the Frontend + +Open: + +```text +http://localhost:8080 +``` + +The Employee Management application should load. + +## 7. Test the API + +```bash +curl http://localhost:5300/healthcheck +``` + +Expected: + +```json +{"status":"ok"} +``` + +## 8. Test CRUD + +Use the application UI to: + +1. Create an employee. +2. Verify the employee appears. +3. Edit the employee. +4. Verify the updated values. +5. Delete the employee. +6. Verify the employee is removed. + +## 9. Test Persistence + +Create or keep an employee record and run: + +```bash +docker compose restart +``` + +Then refresh the application. + +The record should remain. + +You can also test container recreation: + +```bash +docker compose down +docker compose up -d +``` + +The named `mongo-data` volume should preserve the database. + +## 10. Inspect Volumes + +```bash +docker volume ls +``` + +To inspect the Compose volume: + +```bash +docker volume inspect _mongo-data +``` + +The exact project prefix depends on the Compose project name. + +## 11. Inspect the Network + +```bash +docker network ls +``` + +Then: + +```bash +docker network inspect _default +``` + +Again, the exact name depends on the Compose project name. + +## 12. View Logs + +All services: + +```bash +docker compose logs +``` + +Follow logs: + +```bash +docker compose logs -f +``` + +Specific service: + +```bash +docker compose logs -f server +docker compose logs -f client +docker compose logs -f database +``` + +## 13. Rebuild After Configuration Changes + +When Dockerfiles, NGINX configuration, or other image contents change: + +```bash +docker compose build +docker compose up -d +``` + +For a clean rebuild: + +```bash +docker compose build --no-cache +docker compose up -d +``` + +## 14. Stop the Application + +```bash +docker compose down +``` + +This removes containers and the Compose network but keeps the named database volume. + +## 15. Full Reset + +If you intentionally want to delete the database volume: + +```bash +docker compose down -v +``` + +Then start again: + +```bash +docker compose up -d +``` + +**Warning:** this removes persisted MongoDB data. + +## Deployment Checklist + +```text +[ ] Docker installed +[ ] docker compose config succeeds +[ ] Images build successfully +[ ] MongoDB becomes healthy +[ ] Server container starts +[ ] Client container starts +[ ] Frontend loads on :8080 +[ ] /healthcheck returns status ok +[ ] CRUD works +[ ] Data survives restart +[ ] Data survives compose down/up +``` + +## Current Deployment Model + +```text +Local machine + │ + └── Docker Compose + │ + ├── Angular + NGINX + ├── Node + Express + └── MongoDB 4.4 +``` + +This is the completed Phase 1 deployment. + +The next stage is to reproduce the same application architecture with Kubernetes. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 0000000..6a56fab --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,424 @@ +# Troubleshooting + +## 1. General Diagnostic Sequence + +When something fails, start with: + +```bash +docker compose ps +docker compose logs +``` + +Then inspect the specific service: + +```bash +docker compose logs client +docker compose logs server +docker compose logs database +``` + +Check the API: + +```bash +curl http://localhost:5300/healthcheck +``` + +Check the frontend: + +```text +http://localhost:8080 +``` + +--- + +## 2. Frontend Shows "Welcome to nginx!" + +This means NGINX is running but the expected Angular `index.html` is not being served. + +Inspect the document root: + +```bash +docker exec mean-client ls -lah /usr/share/nginx/html +``` + +The Angular build files should be present. + +The current Angular build produces: + +```text +index.csr.html +``` + +The client Dockerfile therefore moves it to: + +```text +index.html +``` + +during the image build. + +After changing the Dockerfile, rebuild: + +```bash +docker compose build client +docker compose up -d client +``` + +--- + +## 3. `/api/healthcheck` Does Not Work + +First test the backend directly: + +```bash +curl http://localhost:5300/healthcheck +``` + +If this fails, inspect: + +```bash +docker compose logs server +``` + +If the direct API works but the frontend API does not, inspect the NGINX configuration. + +The frontend should proxy: + +```text +/api/ +``` + +to: + +```text +server:5300 +``` + +--- + +## 4. Backend Cannot Connect to MongoDB + +Check service status: + +```bash +docker compose ps +``` + +MongoDB should be healthy. + +Check database logs: + +```bash +docker compose logs database +``` + +Check server logs: + +```bash +docker compose logs server +``` + +The backend should use: + +```text +mongodb://database:27017/ +``` + +Do not use: + +```text +mongodb://localhost:27017/ +``` + +inside the backend container. + +--- + +## 5. MongoDB Is Not Healthy + +Inspect: + +```bash +docker compose ps +docker compose logs database +``` + +The configured healthcheck uses: + +```bash +mongo --eval "db.adminCommand('ping')" +``` + +MongoDB 4.4 must be fully started before the healthcheck can succeed. + +--- + +## 6. Employee Data Disappeared + +First check whether the database volume still exists: + +```bash +docker volume ls +``` + +Remember: + +```bash +docker compose restart +``` + +does not delete volumes. + +Also: + +```bash +docker compose down +docker compose up -d +``` + +does not delete named volumes. + +But: + +```bash +docker compose down -v +``` + +does delete the Compose-managed volumes. + +If `down -v` was executed, the previous MongoDB data is expected to be gone. + +--- + +## 7. Port 8080 Already in Use + +Check which process is using the port. + +On Linux: + +```bash +sudo ss -ltnp | grep :8080 +``` + +You can either stop the conflicting process or change the host-side Compose mapping. + +For example: + +```yaml +ports: + - "8081:80" +``` + +Then open: + +```text +http://localhost:8081 +``` + +--- + +## 8. Port 5300 Already in Use + +Check: + +```bash +sudo ss -ltnp | grep :5300 +``` + +Stop the conflicting process or change the host-side port mapping. + +The container can continue listening on: + +```text +5300 +``` + +while a different host port is published. + +--- + +## 9. Docker Build Fails + +Start with: + +```bash +docker compose build +``` + +Read the first meaningful error rather than only the final error line. + +For the client: + +```bash +docker compose build client +``` + +For the server: + +```bash +docker compose build server +``` + +For a completely fresh build: + +```bash +docker compose build --no-cache +``` + +--- + +## 10. Inspect Container Files + +Client: + +```bash +docker exec -it mean-client sh +``` + +Then: + +```bash +ls -lah /usr/share/nginx/html +``` + +Server: + +```bash +docker exec -it mean-server sh +``` + +Then: + +```bash +ls -lah /app +``` + +--- + +## 11. Check NGINX Configuration + +Enter the client container: + +```bash +docker exec -it mean-client sh +``` + +Then: + +```bash +nginx -t +``` + +A successful configuration test should report that the syntax is valid. + +--- + +## 12. Angular Route Returns 404 + +The NGINX configuration uses: + +```nginx +try_files $uri $uri/ /index.html; +``` + +This allows Angular client-side routes to fall back to the main application entry point. + +If Angular routes return NGINX 404 errors, verify that the custom NGINX configuration is actually being copied into: + +```text +/etc/nginx/nginx.conf +``` + +--- + +## 13. API Requests Return 404 + +Verify the backend route: + +```text +/employees +``` + +The frontend-facing route is: + +```text +/api/employees +``` + +NGINX removes the `/api/` prefix before forwarding. + +Therefore: + +```text +/api/employees +``` + +becomes: + +```text +/employees +``` + +inside the backend. + +--- + +## 14. Useful One-Line Checks + +Container status: + +```bash +docker compose ps +``` + +API: + +```bash +curl http://localhost:5300/healthcheck +``` + +All logs: + +```bash +docker compose logs --tail=100 +``` + +Database logs: + +```bash +docker compose logs --tail=100 database +``` + +Server logs: + +```bash +docker compose logs --tail=100 server +``` + +Client logs: + +```bash +docker compose logs --tail=100 client +``` + +--- + +## Troubleshooting Principle + +Diagnose the system layer by layer: + +```text +Docker + ↓ +Container + ↓ +Service + ↓ +Network + ↓ +Application + ↓ +Database +``` + +Do not immediately rebuild everything. First identify which layer is actually failing. diff --git a/server/.dockerignore b/server/.dockerignore new file mode 100644 index 0000000..02085d9 --- /dev/null +++ b/server/.dockerignore @@ -0,0 +1,8 @@ +node_modules +dist +.git +.github +.vscode +.env +*.log +coverage diff --git a/server/Dockerfile b/server/Dockerfile index 41ae27a..4e914f9 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -1,11 +1,41 @@ -FROM node:17-slim -WORKDIR /usr/app -# Install dependencies and build the project. -COPY package.json package-lock.json ./ -RUN npm install +# ============================================ +# Stage 1: Build +# ============================================ +FROM node:22-alpine AS builder + +WORKDIR /app + +# Install dependencies +COPY package*.json ./ +RUN npm ci + +# Copy source code COPY . . + +# Build TypeScript application RUN npm run build -# Run the web service on container startup. + +# ============================================ +# Stage 2: Production runtime +# ============================================ +FROM node:22-alpine + +WORKDIR /app + +# Install production dependencies only +COPY package*.json ./ +RUN npm ci --omit=dev + +# Copy compiled application +COPY --from=builder /app/dist ./dist + +# Copy required scripts +COPY --from=builder /app/scripts ./scripts + +EXPOSE 5300 + +ENV PORT=5300 + CMD ["node", "dist/server.js"]