Complete Task 1: Deploy and Monitoring
هذا الالتزام موجود في:
21
.gitignore
مباع
Normal file
21
.gitignore
مباع
Normal file
@@ -0,0 +1,21 @@
|
||||
# Python virtual environments
|
||||
**/venv/
|
||||
**/.venv/
|
||||
|
||||
# Python cache
|
||||
**/__pycache__/
|
||||
*.py[cod]
|
||||
|
||||
# Environment
|
||||
.env
|
||||
.env.*
|
||||
|
||||
# VS Code
|
||||
.vscode/
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
5
q1-deploy-monitor/.dockerignore
Normal file
5
q1-deploy-monitor/.dockerignore
Normal file
@@ -0,0 +1,5 @@
|
||||
venv/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.git
|
||||
.gitignore
|
||||
7
q1-deploy-monitor/Dockerfile
Normal file
7
q1-deploy-monitor/Dockerfile
Normal file
@@ -0,0 +1,7 @@
|
||||
FROM python:3.12-slim
|
||||
WORKDIR /app
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
COPY . .
|
||||
EXPOSE 5000
|
||||
CMD ["python", "app.py"]
|
||||
54
q1-deploy-monitor/app.py
Normal file
54
q1-deploy-monitor/app.py
Normal file
@@ -0,0 +1,54 @@
|
||||
from flask import Flask, g, render_template
|
||||
from flask_cors import CORS
|
||||
from time import time
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app)
|
||||
|
||||
request_count = 0
|
||||
start_time = time()
|
||||
last_response_time = 0
|
||||
|
||||
@app.before_request
|
||||
def count_requests():
|
||||
global request_count
|
||||
request_count += 1
|
||||
|
||||
@app.before_request
|
||||
def before_request():
|
||||
g.start = time()
|
||||
|
||||
@app.after_request
|
||||
def after_request(response):
|
||||
global last_response_time
|
||||
|
||||
last_response_time = round((time() - g.start) * 1000, 2)
|
||||
|
||||
return response
|
||||
|
||||
@app.route("/")
|
||||
def home():
|
||||
return render_template("index.html")
|
||||
|
||||
@app.route("/health")
|
||||
def health():
|
||||
return {
|
||||
"status": "healthy"
|
||||
}
|
||||
|
||||
@app.route("/stats")
|
||||
def stats():
|
||||
uptime = int(time() - start_time)
|
||||
|
||||
return {
|
||||
"application": "Ghaymah SRE Exam",
|
||||
"status": "healthy",
|
||||
"version": "1.0.0",
|
||||
"requests": request_count,
|
||||
"uptime_seconds": uptime,
|
||||
"response_time_ms": last_response_time
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=5000)
|
||||
22
q1-deploy-monitor/monitor.py
Normal file
22
q1-deploy-monitor/monitor.py
Normal file
@@ -0,0 +1,22 @@
|
||||
import time
|
||||
import requests
|
||||
|
||||
URL = "http://localhost:5000/health"
|
||||
|
||||
while True:
|
||||
start = time.time()
|
||||
|
||||
try:
|
||||
response = requests.get(URL, timeout=5)
|
||||
|
||||
response_time = (time.time() - start) * 1000
|
||||
|
||||
print(
|
||||
f"[OK] Status: {response.status_code} | "
|
||||
f"Response Time: {response_time:.2f} ms"
|
||||
)
|
||||
|
||||
except requests.exceptions.RequestException:
|
||||
print("[DOWN] Service is unavailable")
|
||||
|
||||
time.sleep(30)
|
||||
3
q1-deploy-monitor/requirements.txt
Normal file
3
q1-deploy-monitor/requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
Flask==3.1.1
|
||||
requests==2.32.4
|
||||
flask-cors==6.0.1
|
||||
50
q1-deploy-monitor/static/script.js
Normal file
50
q1-deploy-monitor/static/script.js
Normal file
@@ -0,0 +1,50 @@
|
||||
async function loadStats() {
|
||||
try {
|
||||
const response = await fetch("/stats");
|
||||
const data = await response.json();
|
||||
|
||||
// Service Status
|
||||
const statusElement = document.getElementById("status");
|
||||
|
||||
if (data.status === "healthy") {
|
||||
statusElement.textContent = "🟢 Healthy";
|
||||
statusElement.style.color = "green";
|
||||
} else {
|
||||
statusElement.textContent = "🔴 Down";
|
||||
statusElement.style.color = "red";
|
||||
}
|
||||
|
||||
// Response Time
|
||||
const responseElement = document.getElementById("response-time");
|
||||
responseElement.textContent = data.response_time_ms + " ms";
|
||||
|
||||
if (data.response_time_ms < 100) {
|
||||
responseElement.style.color = "green";
|
||||
} else {
|
||||
responseElement.style.color = "orange";
|
||||
}
|
||||
|
||||
// Request Count
|
||||
document.getElementById("requests").textContent = data.requests;
|
||||
|
||||
// Uptime
|
||||
const minutes = Math.floor(data.uptime_seconds / 60);
|
||||
const seconds = data.uptime_seconds % 60;
|
||||
|
||||
document.getElementById("uptime").textContent =
|
||||
`${minutes} min ${seconds} sec`;
|
||||
|
||||
} catch (error) {
|
||||
|
||||
const statusElement = document.getElementById("status");
|
||||
statusElement.textContent = "🔴 Down";
|
||||
statusElement.style.color = "red";
|
||||
|
||||
document.getElementById("response-time").textContent = "--";
|
||||
document.getElementById("requests").textContent = "--";
|
||||
document.getElementById("uptime").textContent = "--";
|
||||
}
|
||||
}
|
||||
|
||||
loadStats();
|
||||
setInterval(loadStats, 5000);
|
||||
52
q1-deploy-monitor/static/style.css
Normal file
52
q1-deploy-monitor/static/style.css
Normal file
@@ -0,0 +1,52 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
background-color: #f5f7fa;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 90%;
|
||||
max-width: 700px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-bottom: 30px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: white;
|
||||
margin: 15px 0;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
border-left: 5px solid #007bff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.card h2 {
|
||||
color: #555;
|
||||
margin-bottom: 10px;
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
.card p {
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.refresh {
|
||||
margin-top: 20px;
|
||||
color: #777;
|
||||
font-size: 15px;
|
||||
}
|
||||
45
q1-deploy-monitor/templates/index.html
Normal file
45
q1-deploy-monitor/templates/index.html
Normal file
@@ -0,0 +1,45 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Ghaymah Monitoring Dashboard</title>
|
||||
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<h1>🚀 Ghaymah Monitoring Dashboard</h1>
|
||||
|
||||
<div class="card">
|
||||
<h2>Service Status</h2>
|
||||
<p id="status">Loading...</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>Response Time</h2>
|
||||
<p id="response-time">Loading...</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>Request Count</h2>
|
||||
<p id="requests">Loading...</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>Uptime</h2>
|
||||
<p id="uptime">Loading...</p>
|
||||
</div>
|
||||
|
||||
<p class="refresh">
|
||||
🔄 Auto Refresh Every 5 Seconds
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="{{ url_for('static', filename='script.js') }}"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
المرجع في مشكلة جديدة
حظر مستخدم