Complete Task 5: Mithal monitoring dashboard

هذا الالتزام موجود في:
2026-07-28 18:57:31 +03:00
الأصل fbcb4e997c
التزام 3a5d1dece0
15 ملفات معدلة مع 800 إضافات و0 حذوفات

ثنائية
q4-scalability/architecture-diagram.png Executable file

ملف ثنائي غير معروض.

بعد

العرض:  |  الارتفاع:  |  الحجم: 1.2 MiB

عرض الملف

@@ -0,0 +1,49 @@
# Scalable Architecture Design
## Overview
The application is designed to handle up to **15,000 requests per second (req/s)** on Ghaymah using multiple application containers behind a Load Balancer.
The architecture distributes incoming traffic evenly across multiple container instances to ensure high availability, scalability, and fault tolerance.
---
## Architecture Components
- Internet Users
- Ghaymah Load Balancer
- Multiple Application Containers
- Ghaymah Block Storage
- Database
---
## Traffic Flow
1. Users send requests to the application.
2. The Load Balancer receives all incoming traffic.
3. Requests are distributed evenly across the application containers.
4. Each container processes the request.
5. Stateful data is stored in the database using Ghaymah Block Storage.
---
## High Availability
If one container becomes unavailable, the Load Balancer automatically routes traffic to healthy containers without affecting users.
Internet Users
Ghaymah Load Balancer
┌───────────────┼───────────────┐
▼ ▼ ▼
App Container App Container App Container
│ │ │
└───────────────┼───────────────┘
Database
Ghaymah Block Storage

عرض الملف

@@ -0,0 +1,49 @@
# Ghaymah Block Storage for Stateful Workloads
## Overview
Stateful applications store persistent data that must remain available even if a container is restarted, replaced, or moved to another node.
Ghaymah Block Storage provides persistent storage that can be attached to stateful workloads such as databases.
---
## Common Stateful Workloads
- MySQL
- PostgreSQL
- MongoDB
- Redis (Persistent Mode)
---
## How It Works
1. The database container stores its data on a Ghaymah Block Storage volume.
2. If the container fails, a new container can be started.
3. The same Block Storage volume is reattached.
4. The application continues using the existing data.
---
## Benefits
- Persistent data storage
- High durability
- Faster recovery after failures
- Data remains available after container restarts
- Suitable for production database workloads
---
## Example Architecture
```text
Application Containers
Database Container
Ghaymah Block Storage
```

عرض الملف

@@ -0,0 +1,38 @@
# Cold Start Strategy
## Overview
A cold start occurs when a new application container is created to handle increased traffic. During startup, the container needs time to initialize before it can receive requests.
---
## Strategy
### 1. Keep Warm Containers
Maintain a small number of standby containers that are already running and ready to receive traffic immediately.
### 2. Auto Scaling
Automatically create additional containers when CPU usage, memory usage, or request rate exceeds predefined thresholds.
### 3. Lightweight Container Images
Use small Docker images to reduce image download and startup time.
### 4. Health Checks
Only send traffic to containers after they successfully pass health checks.
### 5. Container Image Caching
Cache frequently used container images on the compute nodes to avoid downloading them repeatedly.
---
## Benefits
- Faster application startup
- Lower request latency
- Better user experience during traffic spikes
- Improved application availability

عرض الملف

@@ -0,0 +1,41 @@
# Container Capacity Calculation
## Given
- Expected traffic: **15,000 requests/second**
- Capacity per container: **500 requests/second**
- Safety margin: **30%**
---
## Step 1: Calculate Minimum Containers
Minimum Containers = Total Requests ÷ Capacity per Container
```
15000 ÷ 500 = 30 Containers
```
---
## Step 2: Add Safety Margin
```
30 × 30% = 9 Containers
```
---
## Total Required Containers
```
30 + 9 = 39 Containers
```
---
## Final Result
To safely handle **15,000 requests per second**, the application should run **39 application containers**.
The additional containers provide extra capacity during traffic spikes and improve overall reliability.

عرض الملف

@@ -0,0 +1,13 @@
FROM python:3.12-slim
WORKDIR /app
COPY . .
RUN pip install --no-cache-dir -r requirements.txt
RUN chmod +x start.sh
EXPOSE 8000
CMD ["./start.sh"]

عرض الملف

عرض الملف

@@ -0,0 +1,120 @@
async function loadData() {
try {
const response = await fetch("data.json?t=" + Date.now());
const data = await response.json();
if (data.length === 0) {
return;
}
const latest = data[data.length - 1];
const upCount = data.filter(item => item.uptime === "UP").length;
const uptimePercentage = ((upCount / data.length) * 100).toFixed(1);
document.getElementById("uptime").innerText =
uptimePercentage + "%";
document.getElementById("latency").innerText =
(latest.latency_ms ?? "-") + " ms";
document.getElementById("dns").innerText =
(latest.dns_ms ?? "-") + " ms";
if (latest.ssl && latest.ssl.valid) {
document.getElementById("ssl").innerHTML = `
<strong style="color:green;">Valid</strong><br>
<small>${latest.ssl.expires}</small>
`;
} else {
document.getElementById("ssl").innerHTML = `
<strong style="color:red;">Invalid</strong>
`;
}
const table = document.getElementById("historyTable");
table.innerHTML = "";
data
.slice()
.reverse()
.forEach(item => {
table.innerHTML += `
<tr>
<td>${item.timestamp}</td>
<td>${item.uptime}</td>
<td>${item.latency_ms ?? "-"}</td>
<td>${item.dns_ms ?? "-"}</td>
<td>${item.ssl && item.ssl.valid ? "Valid" : "Invalid"}</td>
</tr>
`;
});
const labels = data.map(item => item.timestamp);
const latency = data.map(item => item.latency_ms);
if (!window.latencyChart) {
window.latencyChart = new Chart(
document.getElementById("latencyChart"),
{
type: "line",
data: {
labels: labels,
datasets: [{
label: "Latency (ms)",
data: latency,
borderColor: "#0b7dda",
backgroundColor: "rgba(11,125,218,0.15)",
fill: true,
tension: 0.3
}]
},
options: {
responsive: true,
plugins: {
legend: {
display: true
}
},
scales: {
y: {
beginAtZero: false
}
}
}
}
);
} else {
window.latencyChart.data.labels = labels;
window.latencyChart.data.datasets[0].data = latency;
window.latencyChart.update();
}
}
catch (error) {
console.error(error);
}
}
loadData();
setInterval(loadData, 60000);

عرض الملف

@@ -0,0 +1,62 @@
[
{
"timestamp": "2026-07-28 17:50:54",
"status_code": 200,
"uptime": "UP",
"latency_ms": 994.55,
"dns_ms": 39.1,
"search_response_ms": 1170.81,
"ssl": {
"valid": true,
"expires": "Sep 15 13:10:47 2026 GMT"
}
},
{
"timestamp": "2026-07-28 17:50:58",
"status_code": 200,
"uptime": "UP",
"latency_ms": 986.42,
"dns_ms": 40.91,
"search_response_ms": 1130.27,
"ssl": {
"valid": true,
"expires": "Sep 15 13:10:47 2026 GMT"
}
},
{
"timestamp": "2026-07-28 17:51:01",
"status_code": 200,
"uptime": "UP",
"latency_ms": 980.57,
"dns_ms": 51.6,
"search_response_ms": 1168.75,
"ssl": {
"valid": true,
"expires": "Sep 15 13:10:47 2026 GMT"
}
},
{
"timestamp": "2026-07-28 17:51:04",
"status_code": 200,
"uptime": "UP",
"latency_ms": 969.79,
"dns_ms": 40.75,
"search_response_ms": 1209.84,
"ssl": {
"valid": true,
"expires": "Sep 15 13:10:47 2026 GMT"
}
},
{
"timestamp": "2026-07-28 17:51:07",
"status_code": 200,
"uptime": "UP",
"latency_ms": 976.47,
"dns_ms": 42.13,
"search_response_ms": 1147.52,
"ssl": {
"valid": true,
"expires": "Sep 15 13:10:47 2026 GMT"
}
}
]

عرض الملف

@@ -0,0 +1,94 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mithal Monitoring Dashboard</title>
<link rel="stylesheet" href="style.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<div class="container">
<h1>Mithal Monitoring Dashboard</h1>
<p class="subtitle">
Website Monitoring Dashboard for mithal.space
</p>
<div class="cards">
<div class="card">
<h2>Uptime</h2>
<p id="uptime">--</p>
</div>
<div class="card">
<h2>Latency</h2>
<p id="latency">--</p>
</div>
<div class="card">
<h2>DNS Time</h2>
<p id="dns">--</p>
</div>
<div class="card">
<h2>SSL</h2>
<p id="ssl">--</p>
</div>
</div>
<div class="chart-card">
<h2>Latency History</h2>
<canvas id="latencyChart"></canvas>
</div>
<div class="table-card">
<h2>Last 10 Checks</h2>
<table>
<thead>
<tr>
<th>Time</th>
<th>Status</th>
<th>Latency (ms)</th>
<th>DNS (ms)</th>
<th>SSL</th>
</tr>
</thead>
<tbody id="historyTable">
</tbody>
</table>
</div>
</div>
<script src="app.js"></script>
</body>
</html>

عرض الملف

@@ -0,0 +1,122 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, Helvetica, sans-serif;
background: #f4f7fb;
color: #333;
}
.container {
width: 95%;
max-width: 1300px;
margin: 40px auto;
}
h1 {
text-align: center;
margin-bottom: 10px;
color: #2c3e50;
}
.subtitle {
text-align: center;
margin-bottom: 35px;
color: gray;
}
.cards {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
margin-bottom: 35px;
}
.card {
background: white;
border-radius: 10px;
padding: 25px;
text-align: center;
box-shadow: 0 2px 8px rgba(0,0,0,.08);
}
.card h2 {
margin-bottom: 15px;
color: #555;
font-size: 20px;
}
.card p {
font-size: 30px;
font-weight: bold;
color: #0b7dda;
}
.chart-card,
.table-card {
background: white;
border-radius: 10px;
padding: 25px;
margin-top: 30px;
box-shadow: 0 2px 8px rgba(0,0,0,.08);
}
.chart-card h2,
.table-card h2 {
margin-bottom: 20px;
}
table {
width: 100%;
border-collapse: collapse;
}
thead {
background: #0b7dda;
color: white;
}
th,
td {
padding: 12px;
text-align: center;
border-bottom: 1px solid #ddd;
}
tbody tr:hover {
background: #f5f5f5;
}
canvas {
width: 100% !important;
max-height: 350px;
}
@media (max-width:900px){
.cards{
grid-template-columns:repeat(2,1fr);
}
}
@media (max-width:600px){
.cards{
grid-template-columns:1fr;
}
table{
font-size:12px;
}
}

عرض الملف

@@ -0,0 +1,62 @@
[
{
"timestamp": "2026-07-28 17:50:54",
"status_code": 200,
"uptime": "UP",
"latency_ms": 994.55,
"dns_ms": 39.1,
"search_response_ms": 1170.81,
"ssl": {
"valid": true,
"expires": "Sep 15 13:10:47 2026 GMT"
}
},
{
"timestamp": "2026-07-28 17:50:58",
"status_code": 200,
"uptime": "UP",
"latency_ms": 986.42,
"dns_ms": 40.91,
"search_response_ms": 1130.27,
"ssl": {
"valid": true,
"expires": "Sep 15 13:10:47 2026 GMT"
}
},
{
"timestamp": "2026-07-28 17:51:01",
"status_code": 200,
"uptime": "UP",
"latency_ms": 980.57,
"dns_ms": 51.6,
"search_response_ms": 1168.75,
"ssl": {
"valid": true,
"expires": "Sep 15 13:10:47 2026 GMT"
}
},
{
"timestamp": "2026-07-28 17:51:04",
"status_code": 200,
"uptime": "UP",
"latency_ms": 969.79,
"dns_ms": 40.75,
"search_response_ms": 1209.84,
"ssl": {
"valid": true,
"expires": "Sep 15 13:10:47 2026 GMT"
}
},
{
"timestamp": "2026-07-28 17:51:07",
"status_code": 200,
"uptime": "UP",
"latency_ms": 976.47,
"dns_ms": 42.13,
"search_response_ms": 1147.52,
"ssl": {
"valid": true,
"expires": "Sep 15 13:10:47 2026 GMT"
}
}
]

عرض الملف

@@ -0,0 +1,135 @@
import json
import os
import socket
import ssl
import time
from datetime import datetime
import dns.resolver
import requests
URL = "https://mithal.space"
DATA_FILE = "data.json"
def get_dns_time():
try:
start = time.time()
dns.resolver.resolve("mithal.space", "A")
end = time.time()
return round((end - start) * 1000, 2)
except Exception:
return None
def get_ssl_info():
try:
hostname = "mithal.space"
context = ssl.create_default_context()
with context.wrap_socket(
socket.socket(),
server_hostname=hostname
) as sock:
sock.settimeout(10)
sock.connect((hostname, 443))
cert = sock.getpeercert()
return {
"valid": True,
"expires": cert["notAfter"]
}
except Exception:
return {
"valid": False,
"expires": None
}
def get_search_response():
try:
start = time.time()
requests.get(URL, timeout=10)
end = time.time()
return round((end - start) * 1000, 2)
except Exception:
return None
def check_website():
try:
response = requests.get(URL, timeout=10)
result = {
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"status_code": response.status_code,
"uptime": "UP" if response.status_code == 200 else "DOWN",
"latency_ms": round(response.elapsed.total_seconds() * 1000, 2),
"dns_ms": get_dns_time(),
"search_response_ms": get_search_response(),
"ssl": get_ssl_info()
}
except Exception as e:
result = {
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"status_code": None,
"uptime": "DOWN",
"latency_ms": None,
"dns_ms": None,
"search_response_ms": None,
"ssl": {
"valid": False,
"expires": None
},
"error": str(e)
}
return result
def load_data():
if not os.path.exists(DATA_FILE):
return []
with open(DATA_FILE, "r") as file:
try:
return json.load(file)
except json.JSONDecodeError:
return []
def save_data(data):
with open(DATA_FILE, "w") as file:
json.dump(data, file, indent=4)
def main():
data = load_data()
result = check_website()
data.append(result)
data = data[-10:]
save_data(data)
print(result)
if __name__ == "__main__":
main()

عرض الملف

@@ -0,0 +1,2 @@
requests
dnspython

عرض الملف

@@ -0,0 +1,13 @@
#!/bin/sh
while true
do
python monitor.py
cp data.json dashboard/data.json
sleep 60
done &
cd dashboard
python -m http.server 8000