├── .dockerignore
├── package.json
├── k8s
├── basic
│ ├── svc-db.yaml
│ ├── svc-web.yaml
│ ├── pod-db.yaml
│ ├── pod-web.yaml
│ └── ingress.yaml
└── deployments
│ ├── deploy-db.yaml
│ └── deploy-web.yaml
├── eslint.config.js
├── Dockerfile
├── compose.yaml
├── .github
└── workflows
│ └── docker-image.yml
├── LICENSE
├── views
└── index.ejs
├── .gitignore
├── public
└── styles.css
├── app.js
└── README.md
/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | Dockerfile
4 | docker-compose.yml
5 | .dockerignore
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "body-parser": "^1.20.3",
4 | "ejs": "^3.1.10",
5 | "express": "^4.21.1",
6 | "mongoose": "^8.7.1",
7 | "pg": "^8.13.0"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/k8s/basic/svc-db.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: db
5 | spec:
6 | selector:
7 | app: db
8 | ports:
9 | - protocol: TCP
10 | port: 5432
11 | targetPort: 5432
12 |
--------------------------------------------------------------------------------
/k8s/basic/svc-web.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: web
5 | spec:
6 | selector:
7 | app: web
8 | ports:
9 | - protocol: TCP
10 | port: 80
11 | targetPort: 3000
12 | type: ClusterIP
13 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | files: ['**/*.js'], // Target all JS files
4 | ignores: ['views/**/*.js'], // Ignore all files in the views folder
5 | rules: {
6 | 'semi': ['error', 'always'],
7 | 'quotes': ['error', 'single'],
8 | },
9 | },
10 | ];
11 |
--------------------------------------------------------------------------------
/k8s/basic/pod-db.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: db
5 | labels:
6 | app: db
7 | spec:
8 | containers:
9 | - name: postgresql
10 | image: postgres:16.0-bullseye
11 | ports:
12 | - containerPort: 5432
13 | env:
14 | - name: POSTGRES_PASSWORD
15 | value: masterclass100
16 | - name: POSTGRES_USER
17 | value: masterclass
18 | - name: POSTGRES_DB
19 | value: sampledb
20 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Use Node.js 16 or later
2 | FROM node:20
3 |
4 | # Create app directory
5 | WORKDIR /usr/src/app
6 |
7 | # Copy package.json and package-lock.json first for dependency installation
8 | COPY package*.json ./
9 |
10 | # Install dependencies
11 | RUN npm install
12 |
13 | # Copy the rest of the application code
14 | COPY . .
15 |
16 | # Expose port
17 | EXPOSE 3000
18 |
19 | # Start the app using nodemon for auto-reload
20 | CMD ["npx", "nodemon", "app.js"]
21 |
--------------------------------------------------------------------------------
/k8s/basic/pod-web.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: web
5 | labels:
6 | app: web
7 | spec:
8 | containers:
9 | - name: sample-app
10 | image: snkshukla/sample_app:19033dd
11 | ports:
12 | - containerPort: 3000
13 | env:
14 | - name: DB_USER
15 | value: masterclass
16 | - name: DB_PASSWORD
17 | value: masterclass100
18 | - name: DB_NAME
19 | value: sampledb
20 | - name: DB_HOST
21 | value: db # Service name for PostgreSQL
22 |
--------------------------------------------------------------------------------
/k8s/deployments/deploy-db.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: db-deployment
5 | spec:
6 | replicas: 1
7 | selector:
8 | matchLabels:
9 | app: db
10 | template:
11 | metadata:
12 | labels:
13 | app: db
14 | spec:
15 | containers:
16 | - name: postgresql
17 | image: postgres:16.0-bullseye
18 | ports:
19 | - containerPort: 5432
20 | env:
21 | - name: POSTGRES_PASSWORD
22 | value: "masterclass100"
23 | - name: POSTGRES_USER
24 | value: "masterclass"
25 | - name: POSTGRES_DB
26 | value: "sampledb"
27 |
--------------------------------------------------------------------------------
/k8s/basic/ingress.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.k8s.io/v1
2 | kind: Ingress
3 | metadata:
4 | name: masterclass-ingress
5 | annotations:
6 | cert-manager.io/cluster-issuer: letsencrypt-production
7 | nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
8 | spec:
9 | ingressClassName: nginx
10 | rules:
11 | - host: masterclass.getdevops.services
12 | http:
13 | paths:
14 | - path: /
15 | pathType: Prefix
16 | backend:
17 | service:
18 | name: web
19 | port:
20 | number: 80
21 | tls:
22 | - hosts:
23 | - masterclass.getdevops.services
24 | secretName: web-tls
25 |
--------------------------------------------------------------------------------
/compose.yaml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | app:
4 | build: .
5 | ports:
6 | - "3000:3000"
7 | volumes:
8 | - .:/usr/src/app
9 | environment:
10 | - DB_USER=masterclass
11 | - DB_PASSWORD=masterclass100
12 | - DB_NAME=sampledb
13 | - DB_HOST=postgres # Service name for PostgreSQL
14 | depends_on:
15 | - postgres
16 |
17 | postgres:
18 | image: postgres:16.0-bullseye # Use PostgreSQL version 13
19 | environment:
20 | POSTGRES_USER: masterclass
21 | POSTGRES_PASSWORD: masterclass100
22 | POSTGRES_DB: sampledb
23 | ports:
24 | - "5432:5432"
25 | volumes:
26 | - pgdata:/var/lib/postgresql/data
27 |
28 | volumes:
29 | pgdata:
30 |
--------------------------------------------------------------------------------
/k8s/deployments/deploy-web.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: web
5 | labels:
6 | app: web
7 | spec:
8 | replicas: 3 # Adjust the number of replicas as needed
9 | selector:
10 | matchLabels:
11 | app: web
12 | template:
13 | metadata:
14 | labels:
15 | app: web
16 | spec:
17 | containers:
18 | - name: sample-app
19 | image: snkshukla/sample_app:19033dd
20 | ports:
21 | - containerPort: 3000
22 | env:
23 | - name: DB_USER
24 | value: masterclass
25 | - name: DB_PASSWORD
26 | value: masterclass100
27 | - name: DB_NAME
28 | value: sampledb
29 | - name: DB_HOST
30 | value: db # Service name for PostgreSQL
31 |
--------------------------------------------------------------------------------
/.github/workflows/docker-image.yml:
--------------------------------------------------------------------------------
1 | name: Docker Image CI
2 |
3 | on:
4 | push:
5 | branches: [ "main" ]
6 | pull_request:
7 | branches: [ "main" ]
8 |
9 | jobs:
10 |
11 | build:
12 |
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - uses: actions/checkout@v3
17 | - name: Login to Docker Hub
18 | with:
19 | username: ${{ secrets.DOCKER_USERNAME }}
20 | password: ${{ secrets.DOCKER_PASSWORD }}
21 | uses: docker/login-action@v1
22 | - name: Add SHORT_SHA env property with commit short sha
23 | run: echo "SHORT_SHA=`git rev-parse --short HEAD`" >> $GITHUB_ENV
24 | - name: Build the Docker image
25 | run: docker build --file Dockerfile --tag snkshukla/sample_app:${SHORT_SHA} .
26 | - name: Push the docker image
27 | run: docker push snkshukla/sample_app:${SHORT_SHA}
28 | - name: Print published image tag
29 | run: 'echo "Published image tag is: snkshukla/sample_app:$SHORT_SHA"'
30 |
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 [Shubham Shukla]
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/views/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Fancy App
7 |
8 |
9 |
10 |
11 |
12 |
Welcome to Scaler's Masterclass
13 |
14 |
Today's topics
15 |
24 |
25 |
26 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Dependency directories
9 | node_modules/
10 | jspm_packages/
11 |
12 | # Optional npm cache directory
13 | .npm
14 |
15 | # Optional eslint cache
16 | .eslintcache
17 |
18 | # Optional REPL history
19 | .node_repl_history
20 |
21 | # dotenv environment variable files
22 | .env
23 |
24 | # Coverage directory for tests
25 | coverage/
26 |
27 | # nyc test coverage
28 | .nyc_output/
29 |
30 | # Parcel cache (if you're using it)
31 | .cache/
32 |
33 | # Dist directory (for build outputs if you have one)
34 | dist/
35 |
36 | # Optional TypeScript cache
37 | *.tsbuildinfo
38 |
39 | # Optional folders for staging/deployment
40 | .out/
41 | .next/
42 |
43 | # Ignore database files
44 | dump.rdb
45 | *.sqlite
46 | *.db
47 |
48 |
49 | # Ignore VSCode and IDE-specific settings
50 | .vscode/
51 | .idea/
52 |
53 | # OS-specific files
54 | .DS_Store
55 | Thumbs.db
56 |
57 | # Ignore any local config or secret files
58 | config/local.js
59 | config/*.local.js
60 | config/*.json
61 | config/*.env
62 |
63 | # Optional PM2 log files
64 | .pids
65 | logs/
66 |
67 | # Sentry files (if you use Sentry)
68 | .sentryclirc
69 |
70 | # Ignore any EJS cached files (if any caching happens)
71 | *.ejs~
72 |
--------------------------------------------------------------------------------
/public/styles.css:
--------------------------------------------------------------------------------
1 | /* Background and container styles */
2 | body {
3 | margin: 0;
4 | padding: 0;
5 | font-family: 'Arial', sans-serif;
6 | background: url('https://source.unsplash.com/featured/?nature') no-repeat center center fixed;
7 | background-size: cover;
8 | color: white;
9 | }
10 |
11 | .container {
12 | text-align: center;
13 | padding: 50px;
14 | background-color: rgba(0, 0, 0, 0.7);
15 | border-radius: 10px;
16 | margin: 50px auto;
17 | max-width: 600px;
18 | }
19 |
20 | /* Title styles */
21 | .title {
22 | font-size: 2.5rem;
23 | margin-bottom: 20px;
24 | }
25 |
26 | .subtitle {
27 | font-size: 1.8rem;
28 | margin: 20px 0;
29 | }
30 |
31 | /* List styling */
32 | ul {
33 | list-style-type: none;
34 | padding: 0;
35 | }
36 |
37 | li {
38 | font-size: 1.5rem;
39 | margin: 10px 0;
40 | padding: 10px;
41 | background-color: rgba(255, 255, 255, 0.2);
42 | border-radius: 5px;
43 | }
44 |
45 | /* Form styling */
46 | .form-container {
47 | margin-top: 30px;
48 | }
49 |
50 | input[type="text"] {
51 | padding: 10px;
52 | font-size: 1rem;
53 | width: 80%;
54 | border: none;
55 | border-radius: 5px;
56 | outline: none;
57 | }
58 |
59 | .submit-btn {
60 | padding: 10px 20px;
61 | font-size: 1rem;
62 | border: none;
63 | background-color: #28a745;
64 | color: white;
65 | border-radius: 5px;
66 | cursor: pointer;
67 | transition: background-color 0.3s ease;
68 | }
69 |
70 | .submit-btn:hover {
71 | background-color: #218838;
72 | }
73 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const { Pool } = require('pg'); // Import PostgreSQL client
3 | const bodyParser = require('body-parser');
4 | const path = require('path');
5 |
6 | const app = express();
7 |
8 | // Middleware
9 | app.use(bodyParser.urlencoded({ extended: true }));
10 | app.set('view engine', 'ejs');
11 | app.use(express.static(path.join(__dirname, 'public')));
12 |
13 | // PostgreSQL connection setup
14 | const pool = new Pool({
15 | user: process.env.DB_USER,
16 | host: process.env.DB_HOST || 'localhost',
17 | database: process.env.DB_NAME,
18 | password: process.env.DB_PASSWORD,
19 | port: 5432, // Default PostgreSQL port
20 | });
21 |
22 | // Example query to create a table (if it doesn't exist)
23 | pool.query(`
24 | CREATE TABLE IF NOT EXISTS items (
25 | id SERIAL PRIMARY KEY,
26 | name VARCHAR(255) NOT NULL
27 | );
28 | `, (err) => {
29 | if (err) {
30 | console.error('Error creating table:', err);
31 | } else {
32 | console.log('Table created successfully or already exists.');
33 | }
34 | });
35 |
36 | // Routes
37 | app.get('/', async (req, res) => {
38 | try {
39 | const result = await pool.query('SELECT * FROM items;');
40 | res.render('index', { items: result.rows });
41 | } catch (err) {
42 | console.error('Error fetching topics:', err);
43 | res.status(500).send('Error fetching items.');
44 | }
45 | });
46 |
47 | app.post('/add', async (req, res) => {
48 | const itemName = req.body.name;
49 | try {
50 | await pool.query('INSERT INTO items (name) VALUES ($1);', [itemName]);
51 | res.redirect('/');
52 | } catch (err) {
53 | console.error('Error adding topic:', err);
54 | res.status(500).send('Error adding topic.');
55 | }
56 | });
57 |
58 | app.listen(3000, () => {
59 | console.log('Server is running on port 3000');
60 | });
61 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Masterclass Sample Application: Docker, Kubernetes, and Microservices
2 |
3 | [](https://github.com/snkshukla/masterclass-sample/stargazers)
4 | [](https://github.com/snkshukla/masterclass-sample/network/members)
5 | [](LICENSE)
6 |
7 | Welcome to the **Masterclass Sample Repository** - This repository contains the sample application and configuration files used in the Docker, Kubernetes, and Microservices masterclass at **Scaler**. It's designed to be a practical, hands-on resource that you can use to follow along with the class and explore the concepts in more detail. It provides a real-world example of how to containerize, deploy, and orchestrate a simple Node.js application with a database.
8 |
9 | ## Table of Contents
10 |
11 | 1. [Overview](#Overview)
12 | 2. [Repository Structure](#repository-structure)
13 | 3. [Getting Started](#getting-started)
14 | - [Prerequisites](#Prerequisites)
15 | - [Running Locally with Docker Compose]()
16 | - [Deploying on Kubernetes]()
17 | 4. [Learn More]()
18 | 5. [Blog Series (Deep Dive)](#blog-series-deep-dive)
19 | 6. [Contributing]()
20 | 7. [License]()
21 |
22 | ---
23 |
24 | ## Overview
25 |
26 | This simple Node.js application (with an accompanying database) demonstrates key DevOps concepts like:
27 |
28 | - **SDLC (Software Development Life Cycle)**: Planning → Development → Testing → Deployment → Maintenance
29 | - **Docker**: Packaging applications with Dockerfiles and understanding container fundamentals
30 | - **Docker Compose**: Setting up multi-container environments for local development
31 | - **Kubernetes**: Deploying applications at scale with basic manifests, advanced deployments, and Helm charts
32 |
33 | We’ll use this project to explore everything from setting up local development to creating production-ready Kubernetes clusters.
34 |
35 | ---
36 |
37 | ## Repository Structure
38 |
39 | Here’s a breakdown of the major files and folders:
40 |
41 | ```
42 | masterclass-sample/
43 | ├── .dockerignore # Specifies files and directories that Docker should ignore
44 | ├── .github
45 | │ └── workflows # GitHub Actions configurations (CI/CD pipelines, etc.)
46 | ├── .gitignore # Git ignore rules
47 | ├── Dockerfile # Defines how to build the application’s Docker image
48 | ├── LICENSE # Open-source license for this repository
49 | ├── README.md # The file you’re reading now
50 | ├── app.js # Main Node.js application entry point
51 | ├── compose.yaml # Docker Compose configuration file
52 | ├── eslint.config.js # ESLint configuration for consistent coding standards
53 | ├── k8s
54 | │ ├── basic # Basic Kubernetes manifests (e.g., Pod, Service)
55 | │ ├── deployments # Additional/advanced deployment manifests
56 | │ └── helm-chart # Helm chart(s) for packaging and deploying on Kubernetes
57 | ├── package-lock.json # NPM lock file (ensures consistent dependency versions)
58 | ├── package.json # NPM metadata (project dependencies, scripts, etc.)
59 | ├── public
60 | │ └── styles.css # Static CSS for styling the application UI
61 | └── views
62 | └── index.ejs # Template file for server-side rendering
63 |
64 | ```
65 |
66 | ### Highlights
67 |
68 | - **`Dockerfile`**: Defines the base image and steps required to run the Node.js application.
69 | - **`compose.yaml`**: Illustrates a multi-container setup, including a database, for local development.
70 | - **`k8s/`**: Contains everything Kubernetes-related, including:
71 | - **basic**: Simple resource manifests (Pods, Services).
72 | - **deployments**: More advanced deployments or stateful sets.
73 | - **helm-chart**: Helm-based packaging for easier versioning and reusable deployment configurations.
74 | - **`app.js`**: Node.js application logic to demonstrate a minimal web service.
75 |
76 | ---
77 |
78 | ## Getting Started
79 |
80 | ### Prerequisites
81 |
82 | You’ll need the following installed on your machine:
83 |
84 | - Node.js (optional if you just want to run via Docker)
85 | - Docker
86 | - Docker Compose
87 | - Kubernetes CLI (kubectl)
88 | - Helm (optional) for using Helm charts
89 |
90 | ### Running Locally with Docker Compose
91 |
92 | 1. **Clone this repository**:
93 |
94 | ```bash
95 | git clone https://github.com/snkshukla/masterclass-sample.git
96 | cd masterclass-sample
97 | ```
98 |
99 | 2. **Build and run the containers**:
100 |
101 | ```bash
102 | docker-compose -f compose.yaml up --build
103 | ```
104 |
105 | 3. **Access your application**:
106 |
107 | Open your browser and go to:
108 |
109 | ```
110 | http://localhost:3000
111 | ```
112 |
113 | You should see the Node.js application up and running, connected to its database (if configured in the Compose file).
114 |
115 |
116 | ### Deploying on Kubernetes
117 |
118 | > Note: Make sure you have a running Kubernetes cluster (either a local tool like minikube, Kind or a cloud provider).
119 | >
120 | 1. **Apply the basic Kubernetes manifests** (in the `k8s/basic` folder):
121 |
122 | ```bash
123 | kubectl apply -f k8s/basic/
124 | ```
125 |
126 | 2. **Check the status**:
127 |
128 | ```bash
129 | kubectl get pods
130 | kubectl get services
131 | ```
132 |
133 | 3. **(Optional) Port Forward** to access the service locally:
134 |
135 | ```bash
136 | kubectl port-forward svc/web 3000:3000
137 | ```
138 |
139 | 4. **Open your browser** to http://localhost:3000 to see the running application.
140 | 5. **Explore advanced Kubernetes** deployments in the `k8s/deployments` folder, or try **Helm charts** in `k8s/helm-chart` to package and deploy your application more efficiently.
141 |
142 | ---
143 |
144 | ## Learn More
145 |
146 | To supplement what we’ve done here, I’ve created a **series of blog posts** that walk you through each concept at a deeper level—from explaining Docker’s layered architecture to Kubernetes best practices in production. Check it out here:
147 |
148 | [Our Tech Blogs - Up And Running With Docker & Kubernetes](https://www.getdevops.services/docker/)
149 |
150 | These blogs serve as a companion guide, providing extended explanations, troubleshooting tips, and real-world usage patterns.
151 |
152 | ---
153 |
154 | ## Blog Series (Deep Dive)
155 |
156 | For a comprehensive, step-by-step explanation of all the concepts covered in this repository, and the masterclass, visit our blog:
157 |
158 | [**The DevOps Blog**](https://tech.hindizubaan.com/learn)
159 |
160 | The blog series will cover:
161 |
162 | 1. **[Docker 101: Why Containers Matter - Live](https://tech.hindizubaan.com/learn/docker-101-why-containers-matter)**
163 | 2. **[Introduction to DevOps: Culture, Practices, and Tools](https://tech.hindizubaan.com/learn/introduction-to-devops)**
164 | 2. **Building Your First Docker Image (Dockerfile Deep Dive)** - Yet to be published
165 | 3. **Local Development with Docker Compose** - Yet to be published
166 | 4. **[Introduction to Kubernetes: Concepts and Architecture](https://tech.hindizubaan.com/learn/getting-started-with-kubernetes)**
167 | 5. **Advanced Kubernetes: Ingress, ConfigMaps, Secrets, and Scaling**
168 | 7. **Microservices Architecture and Best Practices**
169 | 8. ...More to follow
170 |
171 | Not all these blogs are yet live, but stay tuned as we will target to release atleast one blog every Wednesday.
172 |
173 | ## Contributing
174 |
175 | Contributions are welcome! Feel free to:
176 |
177 | - Open issues for suggestions and bug reports
178 | - Submit pull requests to improve documentation or add features
179 |
180 | Your feedback helps make this repository a better learning resource.
181 |
182 | ---
183 |
184 | ## License
185 |
186 | This project is licensed under the MIT License.
187 |
188 | Feel free to use and modify this code as you see fit, and don’t forget to share your learnings with the community!
189 |
190 | ---
191 |
192 | Happy containerizing, and see you in the next masterclass!
193 |
--------------------------------------------------------------------------------