Skip to content

Commit

Permalink
SA2 Kubernetes Project completed
Browse files Browse the repository at this point in the history
  • Loading branch information
yavuzoz committed Dec 7, 2023
0 parents commit 45e112d
Show file tree
Hide file tree
Showing 14 changed files with 1,333 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
kube
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
*/public/uploads

*.tar
data/
4 changes: 4 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FROM node:18.0-slim
COPY . .
RUN npm install
CMD [ "node", "index.js" ]
110 changes: 110 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@

SA II Project: Building and Scaling expressjs-mongodb-app on Kubernetes
Project Overview
Welcome to the documentation of the System Administration II course at TSBE. This project focuses on the installation and scaling of the expressjs-mongodb-app on a Kubernetes cluster.

Project Goals
The primary objectives of this project are to set up and configure a Kubernetes cluster with the following key features:

Scalability: Utilizing Kubernetes orchestration for seamless scaling.
Persistent Storage: Implementing containers with persistent storage, specifically for a MongoDB Database.
Container Networking: Establishing effective container networking with port forwarding to enable external access.
Security: Configuring a secure environment to minimize potential security risks.
Used Ports:
3000: expressjs-app
27017: MongoDB

Platform & Limitations
The following virtual platform is hosted by the school to execute the project:

vmKL1
The vmKL1 is the management machine used to set up, administer, and access the Kubernetes cluster.

VM Specs:

OS: Kali GNU/Linux
Kernel: Linux 6.5.0-kali3-amd64
CPU: 2 vCPUs
RAM: 16 GB
Storage: /dev/sda 35 GB
Network: Access to vmLM1 & Internet
IP: 192.168.110.70
Installed Software:

vscode
vmLM1

The vmLM1 is the Kubernetes host itself. It will act as Control Plane (Master Node) & Data Plane (Worker Node).
VM Specs:
OS: Ubuntu 22.04.3 LTS
Kernel: Linux
CPU: 4 vCPUs
RAM: 12 GB
Storage: /dev/sda 16 GB
Network: Access to vmKL1 & Internet
IP: 192.168.110.60
Installed Software:
minikube version: v1.32.0
The container network is set up in a way that only necessary communication is allowed. UFW is active and configured. Open ports to the Kubernetes Node (vmLM1):

22/TCP -> SSH
443/TCP -> Kubernetes API
30703/TCP -> express-mongodb-app
8001/TCP -> Kubernetes Dashboard
31185/TCP -> Argocd (optional)
As far as the container network goes, only pods that need external access are configured with NodePort. All other pods are configured with Cluster IP for Kubernetes internal-only communication.

Manual Setup Guide
vmKL1:
# Update the System
sudo apt update -y
sudo apt upgrade -y

vmLM1:
# Port forward , Enable & open necessary ports on the server firewall
sudo ufw allow 22/tcp
sudo ufw allow 443/tcp
sudo ufw allow 80
sudo ufw allow 8001 # for minikube dashboard
sudo ufw allow 30703 # for express-mongodb-app
sudo ufw allow 31185 # for argocd (optional)
sudo ufw enable

# Enable SSH access by uncommenting the following line in /etc/ssh/sshd_config
Port 22

# Restart sshd
sudo systemctl restart sshd

# Update the System
sudo apt update -y
sudo apt upgrade -y

# Install Docker
sudo apt install docker.io
docker --version # Check Docker version

# Download Minikube
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
minikube version # Check Minikube version

# Install Kubectl
sudo snap install kubectl --classic
kubectl version # Check Kubectl version

# Start Minikube with Docker driver
minikube start --driver=docker
sudo usermod -aG docker $USER && newgrp docker
minikube node add —worker
kubectl label node <node_name> node-role.kubernetes.io/worker=worker
kubectl get nodes

# Update local apt cache
sudo apt update

# Create a namespace for the app
kubectl create namespace nodejs-mongodb-app

# Create a project folder for deployment
mkdir ~/kube
73 changes: 73 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
const path = require('path')
const express = require('express')
const MongoClient = require('mongodb').MongoClient
const multer = require('multer')
const { marked } = require('marked')

const app = express()
const port = process.env.PORT || 3000
const mongoURL = process.env.MONGO_URL || 'mongodb://localhost:27017/dev'

async function initMongo() {
console.log('Initialising MongoDB...')
let success = false
while (!success) {
try {
client = await MongoClient.connect(mongoURL, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
success = true
} catch {
console.log('Error connecting to MongoDB, retrying in 1 second')
await new Promise(resolve => setTimeout(resolve, 1000))
}
}
console.log('MongoDB initialised')
return client.db(client.s.options.dbName).collection('notes')
}

async function start() {
const db = await initMongo()

app.set('view engine', 'pug')
app.set('views', path.join(__dirname, 'views'))
app.use(express.static(path.join(__dirname, 'public')))

app.get('/', async (req, res) => {
res.render('index', { notes: await retrieveNotes(db) })
})

app.post(
'/note',
multer({ dest: path.join(__dirname, 'public/uploads/') }).single('image'),
async (req, res) => {
if (!req.body.upload && req.body.description) {
await saveNote(db, { description: req.body.description })
res.redirect('/')
} else if (req.body.upload && req.file) {
const link = `/uploads/${encodeURIComponent(req.file.filename)}`
res.render('index', {
content: `${req.body.description} ![](${link})`,
notes: await retrieveNotes(db),
})
}
},
)

app.listen(port, () => {
console.log(`App listening on http://localhost:${port}`)
})
}

async function saveNote(db, note) {
await db.insertOne(note)
}

async function retrieveNotes(db) {
const notes = await db.find().toArray()
const sortedNotes = notes.reverse()
return sortedNotes.map(it => ({ ...it, description: marked(it.description) }))
}

start()
6 changes: 6 additions & 0 deletions kube/mongo-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: mongo-config
data:
MONGO_URL: "mongodb://mongo:27017/dev"
12 changes: 12 additions & 0 deletions kube/mongo-hpa.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: mongo-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: mongo
minReplicas: 1
maxReplicas: 5
targetCPUUtilizationPercentage: 50
64 changes: 64 additions & 0 deletions kube/mongo.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mongo-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 256Mi

---
apiVersion: v1
kind: Service
metadata:
name: mongo
spec:
selector:
app: mongo
ports:
- port: 27017
targetPort: 27017

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mongo
spec:
serviceName: "mongo"
replicas: 1
selector:
matchLabels:
app: mongo
template:
metadata:
labels:
app: mongo
spec:
containers:
- name: mongo
image: mongo:6.0.2-focal
ports:
- containerPort: 27017
volumeMounts:
- name: storage
mountPath: /data/db
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"

volumeClaimTemplates:
- metadata:
name: storage
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 256Mi
12 changes: 12 additions & 0 deletions kube/server-hpa.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: server-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: server
minReplicas: 1
maxReplicas: 10
targetCPUUtilizationPercentage: 50
47 changes: 47 additions & 0 deletions kube/server.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: server
spec:
replicas: 1
selector:
matchLabels:
app: server
template:
metadata:
labels:
app: server
spec:
containers:
- name: server
image: yavuzozbay/nodeserver:2.0.0
ports:
- containerPort: 3000
env:
- name: MONGO_URL
valueFrom:
configMapKeyRef:
name: mongo-config
key: MONGO_URL
imagePullPolicy: Always
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "264Mi"
cpu: "250m"

---
apiVersion: v1
kind: Service
metadata:
name: server-service
spec:
selector:
app: server
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: NodePort
Loading

0 comments on commit 45e112d

Please sign in to comment.