Add CI/CD pipeline, architecture documentation, and monitoring application
- Created a GitHub Actions workflow for CI/CD to deploy to Ghyamah, including testing, building, and pushing Docker images. - Added architecture design document for handling 15,000 requests per second, detailing system components, capacity planning, and cold start strategies. - Introduced a Python-based uptime/latency/SSL monitor with a static dashboard, utilizing standard libraries only. - Included Dockerfile and entrypoint script for the monitoring application, ensuring it runs as a non-root user and handles process management. - Added a .dockerignore file to exclude unnecessary files from the Docker build context. - Created an HTML dashboard for visualizing monitoring metrics, including uptime, latency, and SSL certificate status.
هذا الالتزام موجود في:
6
Q1/.dockerignore
Normal file
6
Q1/.dockerignore
Normal file
@@ -0,0 +1,6 @@
|
||||
data/
|
||||
*.pyc
|
||||
__pycache__/
|
||||
.git
|
||||
.gitignore
|
||||
README.md
|
||||
13
Q1/Dockerfile
Normal file
13
Q1/Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
||||
FROM node:20-alpine3.16
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --chown=node:node server.js .
|
||||
|
||||
RUN npm init -y && npm install express cors
|
||||
|
||||
USER node
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["node" , "server.js"]
|
||||
60
Q1/index.html
Normal file
60
Q1/index.html
Normal file
@@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ar" dir="rtl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>لوحة مراقبة الـ API</title>
|
||||
<style>
|
||||
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: #f4f7f6; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; }
|
||||
.dashboard { background: white; padding: 30px; border-radius: 10px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); text-align: center; width: 400px; }
|
||||
h2 { color: #333; margin-bottom: 20px; }
|
||||
.metric { background: #eef2f5; margin: 10px 0; padding: 15px; border-radius: 8px; font-size: 1.2em; display: flex; justify-content: space-between; }
|
||||
.metric span { font-weight: bold; color: #0056b3; }
|
||||
.status-dot { display: inline-block; width: 12px; height: 12px; border-radius: 50%; background: gray; margin-left: 8px; }
|
||||
.online { background: #28a745; }
|
||||
.offline { background: #dc3545; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="dashboard">
|
||||
<h2>مراقبة النظام <span id="statusDot" class="status-dot"></span></h2>
|
||||
|
||||
<div class="metric">
|
||||
الحالة: <span id="statusText">جاري التحميل...</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
زمن الاستجابة: <span id="responseTime">0 ms</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
عدد الطلبات الإجمالي: <span id="requestCount">0</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const API_URL = "";
|
||||
|
||||
async function fetchMetrics() {
|
||||
try {
|
||||
const res = await fetch(API_URL);
|
||||
const data = await res.json();
|
||||
|
||||
document.getElementById('statusText').innerText = data.status;
|
||||
document.getElementById('statusText').style.color = '#28a745';
|
||||
document.getElementById('statusDot').className = 'status-dot online';
|
||||
|
||||
document.getElementById('responseTime').innerText = data.response_time_ms + ' ms';
|
||||
document.getElementById('requestCount').innerText = data.request_count;
|
||||
} catch (error) {
|
||||
document.getElementById('statusText').innerText = 'انقطاع الاتصال (Offline)';
|
||||
document.getElementById('statusText').style.color = '#dc3545';
|
||||
document.getElementById('statusDot').className = 'status-dot offline';
|
||||
document.getElementById('responseTime').innerText = '-';
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(fetchMetrics, 5000);
|
||||
fetchMetrics();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
28
Q1/monitor.sh
Executable file
28
Q1/monitor.sh
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
|
||||
API_URL=$1
|
||||
|
||||
echo "Starting monitor script..."
|
||||
echo "API URL: $API_URL"
|
||||
echo "------------------------------------------------"
|
||||
|
||||
while true; do
|
||||
HTTP_STATUS=$(curl -o /dev/null -s -w "%{http_code}" -m 5 "$API_URL")
|
||||
|
||||
if [ "$HTTP_STATUS" -eq 200 ]; then
|
||||
response=$(curl -s -m 5 "$API_URL")
|
||||
|
||||
status=$(echo "$response" | jq -r '.status' 2>/dev/null)
|
||||
if [ -n "$status" ] && [ "$status" != "null" ]; then
|
||||
echo "Status: $status | Response Time: ${response_time}ms"
|
||||
else
|
||||
echo "HTTP 200 OK, but invalid JSON format received."
|
||||
fi
|
||||
elif [ "$HTTP_STATUS" -eq 000 ]; then
|
||||
echo "Application is Offline or unreachable (Timeout)."
|
||||
else
|
||||
echo "Application is unhealthy. HTTP Status: $HTTP_STATUS"
|
||||
fi
|
||||
|
||||
sleep 30
|
||||
done
|
||||
35
Q1/server.js
Normal file
35
Q1/server.js
Normal file
@@ -0,0 +1,35 @@
|
||||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
const app = express();
|
||||
|
||||
app.use(cors());
|
||||
|
||||
let requestCount = 0;
|
||||
let lastResponseTime = 0;
|
||||
|
||||
app.use((req, res, next) => {
|
||||
requestCount++;
|
||||
const start = Date.now();
|
||||
res.on('finish', () => {
|
||||
lastResponseTime = Date.now() - start;
|
||||
});
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/health', (req, res) => {
|
||||
res.json({
|
||||
status: 'Online',
|
||||
uptime_seconds: process.uptime(),
|
||||
request_count: requestCount,
|
||||
response_time_ms: lastResponseTime
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
res.send('Welcome to the Backend API!');
|
||||
});
|
||||
|
||||
const PORT = process.env.PORT || 3000;
|
||||
app.listen(PORT, () => {
|
||||
console.log(`API is running on port ${PORT}`);
|
||||
});
|
||||
المرجع في مشكلة جديدة
حظر مستخدم