Complete interview solution

هذا الالتزام موجود في:
2026-07-27 04:45:59 +03:00
الأصل bc5489289e
التزام 0b269a7618
8 ملفات معدلة مع 1262 إضافات و0 حذوفات

عرض الملف

@@ -0,0 +1,213 @@
# Incident Postmortem Report
## Application Information
| Item | Value |
|------|-------|
| **Application** | API Service on Ghaymah Platform |
| **Incident Date** | 2026-07-27 |
| **Incident Duration** | 45 Minutes (10:15 11:00) |
| **Severity** | High |
| **Impact** | Complete service outage. Users were unable to access the API during the incident. |
---
# 1. Executive Summary
On **2026-07-27**, the API service experienced a **45-minute outage** due to repeated **OOMKilled** events.
The application gradually consumed more memory because of a memory leak while processing large requests. Once the container exceeded its allocated memory limit, the platform terminated and restarted it automatically. This restart cycle repeated continuously, resulting in complete service unavailability.
The immediate mitigation was increasing the container memory allocation and restarting the service. Long-term corrective actions include fixing the memory leak, improving monitoring, and implementing an auto-scaling policy based on resource utilization.
---
# 2. Incident Timeline
| Time | Event |
|------|-------|
| **10:15** | Memory usage started increasing on one of the application containers. |
| **10:22** | First container was terminated with **OOMKilled** and automatically restarted by the platform. |
| **10:25 10:45** | Multiple containers repeatedly entered restart loops, reducing the application's availability. |
| **10:45** | Monitoring system generated alerts indicating complete service outage. |
| **10:50** | SRE team began investigating the incident and identified abnormal memory usage. |
| **11:00** | Container memory limit increased from **512 MiB** to **1 GiB** and services were restarted successfully. |
| **11:15** | Additional monitoring and temporary mitigation measures were applied. |
---
# 3. Root Cause Analysis
## Immediate Cause
The application exhausted the available memory, causing the platform to terminate the container with an **OOMKilled** event.
## Root Cause
A memory leak inside the request processing logic (or an external library) continuously retained memory instead of releasing it after requests were completed.
## Contributing Factors
- Sudden increase in traffic (approximately **3× normal load**).
- Insufficient memory allocation.
- No automatic scaling based on memory utilization.
- Lack of early warning alerts before reaching the memory limit.
---
# 4. Recommendations
## Short-Term Actions
- Increase container memory limits.
- Monitor memory consumption continuously.
- Restart unhealthy containers automatically.
## Long-Term Improvements
- Fix the memory leak in the application.
- Configure appropriate container memory **requests** and **limits**.
- Enable memory-based auto-scaling.
- Configure proactive alerting.
- Perform regular load and stress testing.
- Maintain an incident response runbook for OOMKilled events.
---
# 5. Auto-Scaling Policy
To prevent similar incidents, the following auto-scaling policy is recommended.
| Configuration | Value |
|--------------|-------|
| **Metric** | Average Container Memory Usage |
| **Scale-Out Threshold** | Memory usage > **70%** for **2 minutes** |
| **Scale-Out Action** | Add one container instance |
| **Minimum Replicas** | 2 |
| **Maximum Replicas** | 10 |
| **Cooldown Period** | 5 Minutes |
| **Scale-In Threshold** | Memory usage < **40%** for **10 minutes** |
| **Scale-In Action** | Remove one container instance |
### Example Auto-Scaling Configuration
```yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-hpa
spec:
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 70
```
> **Note:** The YAML above is an example of a memory-based auto-scaling policy and illustrates the desired behavior conceptually.
---
# 6. Early Detection Using Ghaymah Monitoring
The following monitoring capabilities should be configured to detect similar incidents before they impact users.
## Resource Monitoring
Track real-time metrics including:
- Container Memory Usage
- CPU Utilization
- Container Restart Count
- Container Health Status
---
## Alerting
Configure alerts such as:
| Condition | Action |
|-----------|--------|
| Memory usage > 60% for 3 minutes | Send Email / Slack / Webhook notification |
| Multiple container restarts | Trigger High-Priority Alert |
| Service unavailable | Immediate Critical Alert |
---
## Event Monitoring
Monitor platform events including:
- OOMKilled
- Container Restart
- CrashLoop
- Failed Deployments
---
## Dashboard
Create a monitoring dashboard displaying:
- Current Container Status
- Memory Utilization
- CPU Utilization
- Response Time
- Request Count
- Restart Count
- OOMKilled Events
- Service Availability (Uptime)
---
## Trend Analysis
Analyze historical metrics to identify:
- Gradual memory growth
- Traffic spikes
- Abnormal restart frequency
- Resource utilization trends
This enables proactive detection before a service outage occurs.
---
# 7. Lessons Learned
The incident highlighted several important operational improvements:
- Proper memory sizing is essential.
- Continuous monitoring should be proactive rather than reactive.
- Memory-based auto-scaling reduces service interruption.
- Early alerts significantly reduce recovery time.
- Load testing helps identify memory leaks before production deployment.
---
# Action Items
| Priority | Action | Status |
|----------|--------|--------|
| High | Fix memory leak | Planned |
| High | Configure memory alerts | Planned |
| High | Enable auto-scaling | Planned |
| Medium | Perform load testing | Planned |
| Medium | Update operational runbook | Planned |
---
# Report Information
| Item | Value |
|------|-------|
| **Prepared By** | SRE Team |
| **Platform** | Ghaymah Systems |
| **Report Date** | 2026-07-27 |
| **Incident Type** | Repeated OOMKilled |
| **Status** | Closed |

235
q3-cicd/README.md Normal file
عرض الملف

@@ -0,0 +1,235 @@
# CI/CD Pipeline Documentation
This document describes the Continuous Integration and Continuous Deployment (CI/CD) pipeline implemented for the Ghaymah SRE exam.
---
# Pipeline Overview
The pipeline automates the application delivery process by:
1. Building a Docker image.
2. Pushing the image to the Ghaymah Container Registry.
3. Deploying automatically to the **Staging** environment.
4. Waiting for **manual approval**.
5. Deploying the same image to **Production**.
```
Developer Push
GitHub Actions
Build Docker Image
Push to Ghaymah Container Registry
Deploy to Staging
Manual Approval
Deploy to Production
```
---
# Manual Approval for Production
Production deployments are protected using **GitHub Environments**.
## Configuration
1. Open the repository.
2. Navigate to:
```
Settings
→ Environments
```
3. Create an environment named:
```
production
```
4. Enable:
- Required reviewers
- (Optional) Wait timer
- (Optional) Deployment branch restrictions
---
## Deployment Flow
After the application is successfully deployed to **Staging**, the workflow pauses before deploying to **Production**.
```
Build
Deploy Staging
Waiting for Approval
Reviewer Approves
Deploy Production
```
Only authorized reviewers can approve the deployment.
---
## Benefits
- Prevents accidental deployments.
- Adds an approval gate before production.
- Provides a complete audit trail.
- Supports controlled production releases.
---
# Staging vs Production
| Feature | Staging | Production |
|----------|----------|------------|
| Purpose | Integration testing and QA | Live customer environment |
| Users | Developers & QA | End users |
| Data | Test or sanitized data | Real production data |
| Replicas | Usually 1 | Usually 3 or more |
| Resources | Lower CPU/Memory | Higher CPU/Memory |
| Secrets | Test credentials | Production credentials |
| Deployment | Automatic | Manual approval required |
| Monitoring | Basic monitoring | Full monitoring and alerting |
| Rollback | Low impact | Carefully managed |
| Domain | staging.example.com | api.example.com |
| Logging | Verbose | Optimized for production |
> **Best Practice**
>
> The **same Docker image** (identified by its commit SHA) should be promoted from **Staging** to **Production**. Rebuilding the image for Production should be avoided to guarantee consistency.
---
# Ghaymah CLI Integration
> **Note**
>
> The official Ghaymah CLI documentation was not provided with the exam. The following commands are **placeholders** and should be replaced with the official syntax when available.
---
## 1. Install CLI
```bash
curl -sSL https://get.ghaymah.systems/cli/install.sh | sh
export PATH="$HOME/.ghaymah/bin:$PATH"
```
---
## 2. Authenticate
```bash
ghaymah login --token $GHAYMAH_TOKEN
```
Authentication credentials should be stored securely using GitHub Secrets.
---
## 3. Deploy Application
```bash
ghaymah deploy \
--image registry.ghaymah.systems/ghaymah-api:latest \
--name api-production \
--port 5000 \
--env FLASK_ENV=production \
--replicas 3 \
--health-check /health
```
---
## 4. Verify Deployment
```bash
ghaymah status --name api-production
ghaymah logs --name api-production
```
---
## 5. Scale Application
```bash
ghaymah scale \
--name api-production \
--replicas 5
```
---
## 6. Rollback
```bash
ghaymah deploy \
--image registry.ghaymah.systems/ghaymah-api:<previous-tag> \
--name api-production \
--port 5000
```
---
# GitHub Secrets
Sensitive information should **never** be stored in the repository.
Configure the following repository secrets:
| Secret | Description |
|---------|-------------|
| `GHAYMAH_USERNAME` | Container Registry username |
| `GHAYMAH_TOKEN` | Access token used for authentication |
These secrets are referenced in the workflow as:
```yaml
${{ secrets.GHAYMAH_USERNAME }}
${{ secrets.GHAYMAH_TOKEN }}
```
---
# Best Practices
- Build the Docker image once and promote the same image between environments.
- Tag every image with the Git commit SHA for traceability.
- Keep secrets in **GitHub Secrets**, never in source code.
- Protect Production using **GitHub Environment approvals**.
- Enable Docker Buildx cache to reduce build time.
- Use health checks before considering a deployment successful.
- Maintain separate configurations for Staging and Production.
- Document deployment and rollback procedures.
- Monitor deployments and configure alerting for failures.
---
# References
- GitHub Actions Documentation
- Docker Buildx Documentation
- GitHub Environments Documentation
> Ghaymah CLI commands in this document are provided as implementation placeholders because the official CLI documentation was not included with the exam.

عرض الملف

@@ -0,0 +1,136 @@
name: Build and Deploy to Ghaymah
on:
# Run automatically when code is pushed to the main branch
push:
branches:
- main
# Allow manual workflow execution from GitHub Actions
workflow_dispatch:
env:
# Replace with the official Ghaymah Container Registry URL
REGISTRY: registry.ghaymah.systems
# Docker image name
IMAGE_NAME: ghaymah-api
jobs:
# -------------------------------------------------------
# Build the Docker image and push it to the registry
# -------------------------------------------------------
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
# Check out the repository source code
- name: Checkout code
uses: actions/checkout@v4
# Enable Docker Buildx for advanced builds and caching
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# Authenticate with the container registry
- name: Log in to Ghaymah Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.GHAYMAH_USERNAME }}
password: ${{ secrets.GHAYMAH_TOKEN }}
# Build the Docker image and push it to the registry
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
# Path containing the Dockerfile
context: ./q1-deploy-monitor
# Push the image after a successful build
push: true
# Publish two image tags:
# - latest
# - commit SHA for traceability
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
# Enable Docker layer caching
cache-from: type=gha
cache-to: type=gha,mode=max
# -------------------------------------------------------
# Deploy the application to the staging environment
# -------------------------------------------------------
deploy-staging:
needs: build-and-push
runs-on: ubuntu-latest
# GitHub Environment
environment: staging
steps:
# Install the Ghaymah CLI
# Replace with the official installation command if different
- name: Install Ghaymah CLI
run: |
curl -sSL https://get.ghaymah.systems/cli/install.sh | sh
echo "$HOME/.ghaymah/bin" >> $GITHUB_PATH
# Deploy the application to staging
# Replace the commands below with the official CLI syntax if needed
- name: Deploy to Staging
env:
GHAYMAH_TOKEN: ${{ secrets.GHAYMAH_TOKEN }}
run: |
ghaymah login --token $GHAYMAH_TOKEN
ghaymah deploy \
--image ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \
--name api-staging \
--port 5000 \
--env FLASK_ENV=staging \
--replicas 1
# -------------------------------------------------------
# Deploy the application to the production environment
# -------------------------------------------------------
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
# Configure this environment with required reviewers
# to enable manual approval before deployment
environment: production
steps:
# Install the Ghaymah CLI
- name: Install Ghaymah CLI
run: |
curl -sSL https://get.ghaymah.systems/cli/install.sh | sh
echo "$HOME/.ghaymah/bin" >> $GITHUB_PATH
# Deploy the application to production
# This job runs only after manual approval
- name: Deploy to Production
env:
GHAYMAH_TOKEN: ${{ secrets.GHAYMAH_TOKEN }}
run: |
ghaymah login --token $GHAYMAH_TOKEN
ghaymah deploy \
--image ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \
--name api-production \
--port 5000 \
--env FLASK_ENV=production \
--replicas 3 \
--health-check /health

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

قبل

العرض:  |  الارتفاع:  |  الحجم: 0 B

بعد

العرض:  |  الارتفاع:  |  الحجم: 146 KiB

عرض الملف

@@ -0,0 +1,167 @@
# Scalability and Load Balancing Design
## Scenario
Design an architecture capable of handling:
- **15,000 requests per second (req/s)**
Each application container can process:
- **500 req/s**
A **30% capacity buffer** is required to handle traffic spikes and unexpected load.
---
# 1. High-Level Architecture
The proposed architecture consists of the following components:
- DNS
- Load Balancer
- Multiple API containers
- Auto Scaling service
- Container Orchestrator
- Database
- Ghaymah Block Storage
- Monitoring and Alerting
## Architecture Diagram
The following diagram illustrates the proposed scalable architecture for handling 15,000 requests per second using load balancing, auto scaling, centralized monitoring, and persistent storage.
![Architecture](architecture.png)
---
## Request Flow
```
Users
DNS
Load Balancer
API Containers
Database
```
Monitoring components (Prometheus, Grafana, and centralized logging) collect metrics and logs from the Load Balancer, API containers, and database.
---
# 2. Container Capacity Calculation
## Step 1: Calculate Required Containers
Traffic:
```
15,000 req/s
```
Container capacity:
```
500 req/s
```
Required containers:
```
15000 / 500 = 30 containers
```
---
## Step 2: Add 30% Safety Buffer
```
30 × 1.30 = 39 containers
```
---
## Recommended Deployment Size
Minimum recommended deployment:
**39 containers**
This provides additional capacity for traffic spikes and reduces the risk of resource saturation during unexpected load increases.
---
# 3. Cold Start Strategy
To minimize startup delays during scaling events, the following strategies are recommended.
## Warm Pool
Maintain 23 idle containers ready to receive traffic immediately.
## Container Image Optimization
- Use lightweight base images.
- Remove unnecessary packages and dependencies.
- Keep image size as small as possible.
## Image Pre-Pulling
Pre-pull container images on worker nodes to reduce deployment time and avoid downloading images during scale-out events.
## Health Checks
New containers must pass readiness and liveness checks before receiving production traffic.
## Gradual Traffic Shift
The Load Balancer should gradually route traffic to newly started containers after successful health verification.
## Predictive Scaling
Scale proactively based on CPU utilization, memory usage, and request-rate trends instead of waiting until the system reaches full capacity.
---
# 4. Using Ghaymah Block Storage for Stateful Workloads
Ghaymah Block Storage provides persistent storage volumes that remain available even if containers are restarted, replaced, or rescheduled.
This makes it suitable for workloads that require durable data storage.
Examples include:
- Databases
- Message queues
- File storage
- Application uploads
- Persistent logs
## Benefits
- Persistent data survives container restarts.
- Volumes can be attached to replacement containers.
- Improves reliability for stateful applications.
- Simplifies backup and disaster recovery.
- Enhances data durability during scaling events.
---
# Best Practices
- Deploy multiple replicas behind a Load Balancer.
- Enable Horizontal Auto Scaling based on CPU, memory, and request rate.
- Configure readiness and liveness probes.
- Use lightweight container images to reduce cold-start time.
- Store persistent data using Ghaymah Block Storage.
- Continuously monitor latency, error rate, CPU, and memory utilization.
- Configure alerts for abnormal response times and infrastructure failures.
- Regularly perform load testing to validate scalability assumptions.

عرض الملف

@@ -0,0 +1,336 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>mithal.space Monitoring Dashboard</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #f0f2f5;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px 30px;
border-radius: 12px;
margin-bottom: 20px;
}
.header h1 {
font-weight: 300;
font-size: 2rem;
}
.header .subtitle {
opacity: 0.9;
margin-top: 5px;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.card {
background: white;
padding: 20px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
transition: transform 0.2s;
}
.card:hover {
transform: translateY(-3px);
box-shadow: 0 4px 16px rgba(0,0,0,0.12);
}
.card-label {
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: 0.5px;
color: #6c757d;
margin-bottom: 8px;
}
.card-value {
font-size: 2rem;
font-weight: 600;
}
.card-value .unit {
font-size: 1rem;
font-weight: 400;
color: #6c757d;
margin-left: 4px;
}
.status-up { color: #28a745; }
.status-down { color: #dc3545; }
.chart-container {
background: white;
padding: 20px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
margin-bottom: 20px;
height: 300px;
}
.table-container {
background: white;
padding: 20px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
overflow-x: auto;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 0.9rem;
}
th {
text-align: left;
padding: 12px 8px;
border-bottom: 2px solid #dee2e6;
color: #495057;
font-weight: 600;
}
td {
padding: 10px 8px;
border-bottom: 1px solid #e9ecef;
}
.badge {
display: inline-block;
padding: 2px 12px;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 600;
}
.badge-up { background: #d4edda; color: #155724; }
.badge-down { background: #f8d7da; color: #721c24; }
.badge-ssl-ok { background: #d4edda; color: #155724; }
.badge-ssl-warning { background: #fff3cd; color: #856404; }
.badge-ssl-danger { background: #f8d7da; color: #721c24; }
.text-muted { color: #6c757d; font-size: 0.85rem; }
.text-center { text-align: center; }
.last-update {
margin-top: 20px;
padding: 12px;
background: white;
border-radius: 8px;
text-align: center;
color: #6c757d;
font-size: 0.9rem;
}
@media (max-width: 600px) {
.grid { grid-template-columns: 1fr 1fr; }
.card-value { font-size: 1.5rem; }
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>📊 mithal.space Monitoring Dashboard</h1>
<div class="subtitle">Real-time performance & availability monitoring</div>
</div>
<div class="grid">
<div class="card">
<div class="card-label">🟢 Uptime (24h)</div>
<div class="card-value" id="uptime">--</div>
</div>
<div class="card">
<div class="card-label">📡 Current Status</div>
<div class="card-value" id="status">--</div>
</div>
<div class="card">
<div class="card-label">⚡ Last Latency</div>
<div class="card-value" id="last-latency">-- <span class="unit">ms</span></div>
</div>
<div class="card">
<div class="card-label">🔒 SSL Certificate</div>
<div class="card-value" id="ssl-status">--</div>
<div class="text-muted" id="ssl-days">-- days remaining</div>
</div>
<div class="card">
<div class="card-label">🌐 DNS Resolution</div>
<div class="card-value" id="dns-latency">-- <span class="unit">ms</span></div>
</div>
<div class="card">
<div class="card-label">🔍 Search Response</div>
<div class="card-value" id="search-latency">-- <span class="unit">ms</span></div>
</div>
</div>
<div class="chart-container">
<canvas id="latencyChart"></canvas>
</div>
<div class="table-container">
<h3 style="margin-bottom: 15px; font-weight: 400;">📋 Recent Checks (last 10)</h3>
<table>
<thead>
<tr>
<th>Timestamp</th>
<th>Status</th>
<th>Latency (ms)</th>
<th>DNS (ms)</th>
<th>SSL</th>
<th>Search (ms)</th>
</tr>
</thead>
<tbody id="checks-body">
<tr><td colspan="6" class="text-muted text-center">Loading data...</td></tr>
</tbody>
</table>
</div>
<div class="last-update" id="last-update">📡 Last updated: Loading...</div>
</div>
<script>
let chart = null;
function computeUptime(data) {
if (!data || data.length === 0) return 0;
const now = new Date();
const last24h = data.filter(entry => {
const ts = new Date(entry.timestamp);
return (now - ts) <= 24 * 60 * 60 * 1000;
});
if (last24h.length === 0) return 0;
const up = last24h.filter(e => e.is_up === true).length;
return (up / last24h.length) * 100;
}
function getSslBadge(days) {
if (days === null || days === undefined)
return '<span class="badge badge-down">❌ Unknown</span>';
if (days > 30)
return '<span class="badge badge-ssl-ok">✅ Valid</span>';
else if (days > 7)
return '<span class="badge badge-ssl-warning">⚠️ Expiring soon</span>';
else
return '<span class="badge badge-ssl-danger">🚨 Expiring!</span>';
}
function updateDashboard(data) {
if (!data || data.length === 0) {
document.getElementById('uptime').textContent = '--';
document.getElementById('status').innerHTML = '--';
document.getElementById('last-latency').innerHTML = '-- <span class="unit">ms</span>';
document.getElementById('ssl-status').textContent = '--';
document.getElementById('ssl-days').textContent = '-- days remaining';
document.getElementById('dns-latency').innerHTML = '-- <span class="unit">ms</span>';
document.getElementById('search-latency').innerHTML = '-- <span class="unit">ms</span>';
document.getElementById('checks-body').innerHTML = '<tr><td colspan="6" class="text-muted text-center">No data</td></tr>';
return;
}
const latest = data[data.length - 1];
const statusEl = document.getElementById('status');
statusEl.innerHTML = latest.is_up ? '<span class="status-up">✅ UP</span>' : '<span class="status-down">❌ DOWN</span>';
document.getElementById('last-latency').innerHTML = `${latest.latency_ms || '—'} <span class="unit">ms</span>`;
const sslDays = latest.ssl_days_remaining;
document.getElementById('ssl-status').innerHTML = getSslBadge(sslDays);
document.getElementById('ssl-days').textContent = sslDays !== null ? `${sslDays} days remaining` : 'Unknown';
document.getElementById('dns-latency').innerHTML = `${latest.dns_ms || '—'} <span class="unit">ms</span>`;
document.getElementById('search-latency').innerHTML = `${latest.search_latency_ms || '—'} <span class="unit">ms</span>`;
const uptime = computeUptime(data);
document.getElementById('uptime').textContent = uptime.toFixed(2) + '%';
// Table (last 10)
const tbody = document.getElementById('checks-body');
tbody.innerHTML = '';
const last10 = data.slice(-10).reverse();
last10.forEach(entry => {
const row = document.createElement('tr');
const statusBadge = entry.is_up ? '<span class="badge badge-up">UP</span>' : '<span class="badge badge-down">DOWN</span>';
const sslBadge = getSslBadge(entry.ssl_days_remaining);
row.innerHTML = `
<td>${new Date(entry.timestamp).toLocaleString()}</td>
<td>${statusBadge}</td>
<td>${entry.latency_ms || '—'}</td>
<td>${entry.dns_ms || '—'}</td>
<td>${sslBadge}</td>
<td>${entry.search_latency_ms || '—'}</td>
`;
tbody.appendChild(row);
});
document.getElementById('last-update').textContent = `📡 Last updated: ${new Date().toLocaleString()}`;
// Chart (last 60)
const chartData = data.slice(-60);
const labels = chartData.map(e => new Date(e.timestamp).toLocaleTimeString());
const latencies = chartData.map(e => e.latency_ms || 0);
const statuses = chartData.map(e => e.is_up ? 1 : 0);
if (chart) {
chart.data.labels = labels;
chart.data.datasets[0].data = latencies;
chart.data.datasets[0].pointBackgroundColor = statuses.map(s => s === 1 ? '#28a745' : '#dc3545');
chart.update();
} else {
const ctx = document.getElementById('latencyChart').getContext('2d');
chart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: 'Latency (ms)',
data: latencies,
borderColor: '#667eea',
backgroundColor: 'rgba(102, 126, 234, 0.1)',
tension: 0.2,
fill: true,
pointRadius: 3,
pointBackgroundColor: statuses.map(s => s === 1 ? '#28a745' : '#dc3545'),
pointBorderColor: '#ffffff',
pointBorderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: { legend: { display: false } },
scales: {
x: { grid: { display: false }, ticks: { maxTicksLimit: 15 } },
y: { beginAtZero: true, grid: { color: '#f0f0f0' }, title: { display: true, text: 'Latency (ms)' } }
}
}
});
}
}
async function fetchData() {
try {
const response = await fetch('monitoring-data.json');
if (!response.ok) throw new Error('Network error');
const data = await response.json();
return Array.isArray(data) ? data : [];
} catch (error) {
console.error('Failed to fetch data:', error);
return [];
}
}
async function refresh() {
const data = await fetchData();
updateDashboard(data);
}
refresh();
setInterval(refresh, 10000);
</script>
</body>
</html>

عرض الملف

@@ -0,0 +1,174 @@
#!/usr/bin/env python3
"""
Monitoring Script for mithal.space
Collects: latency, uptime, SSL, DNS, search response
"""
import requests
import json
import time
import socket
import ssl
import datetime
import os
from urllib.parse import urlparse
# Configuration
TARGET_URL = "https://mithal.space"
SEARCH_QUERY = "?q=test" # adjust if search endpoint differs
DATA_FILE = "monitoring-data.json"
MAX_ENTRIES = 1440 # 24 hours * 60 minutes
TIMEOUT = 10
def check_ssl_certificate(hostname, port=443):
"""Check SSL certificate expiry date."""
try:
context = ssl.create_default_context()
with socket.create_connection((hostname, port), timeout=TIMEOUT) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
cert = ssock.getpeercert()
expiry_date = datetime.datetime.strptime(cert['notAfter'], '%b %d %H:%M:%S %Y %Z')
days_remaining = (expiry_date - datetime.datetime.utcnow()).days
return {
"expiry_date": expiry_date.isoformat(),
"days_remaining": days_remaining,
"valid": days_remaining > 0
}
except Exception as e:
return {
"expiry_date": None,
"days_remaining": None,
"valid": False,
"error": str(e)
}
def resolve_dns(hostname):
"""Measure DNS resolution time."""
try:
start = time.time()
socket.gethostbyname(hostname)
dns_time = round((time.time() - start) * 1000, 2)
return dns_time
except Exception:
return None
def check_latency_and_uptime(url):
"""Check HTTP response time and status code."""
try:
start = time.time()
response = requests.get(url, timeout=TIMEOUT)
latency = round((time.time() - start) * 1000, 2)
status_code = response.status_code
is_up = 200 <= status_code < 400
return {
"latency": latency,
"status_code": status_code,
"is_up": is_up,
"error": None
}
except requests.RequestException as e:
return {
"latency": None,
"status_code": None,
"is_up": False,
"error": str(e)
}
def check_search_response(url, query="?q=test"):
"""Measure search endpoint response time."""
try:
search_url = f"{url}{query}"
start = time.time()
response = requests.get(search_url, timeout=TIMEOUT)
search_latency = round((time.time() - start) * 1000, 2)
return {
"search_latency": search_latency,
"search_status": response.status_code,
"search_error": None
}
except requests.RequestException as e:
return {
"search_latency": None,
"search_status": None,
"search_error": str(e)
}
def collect_metrics():
"""Collect all metrics for mithal.space."""
parsed = urlparse(TARGET_URL)
hostname = parsed.hostname or "mithal.space"
dns_time = resolve_dns(hostname)
ssl_info = check_ssl_certificate(hostname)
health = check_latency_and_uptime(TARGET_URL)
search = check_search_response(TARGET_URL, SEARCH_QUERY)
metrics = {
"timestamp": datetime.datetime.utcnow().isoformat() + "Z",
"url": TARGET_URL,
"dns_ms": dns_time,
"ssl_valid": ssl_info.get("valid", False),
"ssl_days_remaining": ssl_info.get("days_remaining"),
"ssl_expiry_date": ssl_info.get("expiry_date"),
"latency_ms": health.get("latency"),
"status_code": health.get("status_code"),
"is_up": health.get("is_up", False),
"error": health.get("error"),
"search_latency_ms": search.get("search_latency"),
"search_status_code": search.get("search_status"),
"search_error": search.get("search_error")
}
return metrics
def save_metrics(metrics, filename=DATA_FILE, max_entries=MAX_ENTRIES):
"""Save metrics to JSON file with size limit."""
try:
if os.path.exists(filename):
with open(filename, 'r') as f:
data = json.load(f)
else:
data = []
data.append(metrics)
if len(data) > max_entries:
data = data[-max_entries:]
with open(filename, 'w') as f:
json.dump(data, f, indent=2)
return True
except Exception as e:
print(f"Error saving data: {e}")
return False
def run_monitor():
"""Main monitoring loop."""
print(f"🚀 Starting monitoring for {TARGET_URL}")
print(f"📊 Data will be saved to {DATA_FILE}")
print("=" * 50)
while True:
try:
print(f"[{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Collecting metrics...")
metrics = collect_metrics()
status = "✅ UP" if metrics['is_up'] else "❌ DOWN"
print(f" Status: {status}")
print(f" Latency: {metrics['latency_ms']} ms")
print(f" DNS: {metrics['dns_ms']} ms")
print(f" SSL: {metrics['ssl_days_remaining']} days remaining")
print(f" Search: {metrics['search_latency_ms']} ms")
if save_metrics(metrics):
print(" ✅ Data saved")
else:
print(" ❌ Failed to save data")
print("-" * 50)
except KeyboardInterrupt:
print("\n🛑 Monitoring stopped by user")
break
except Exception as e:
print(f"❌ Error in monitoring loop: {e}")
time.sleep(60)
if __name__ == "__main__":
run_monitor()

عرض الملف

@@ -0,0 +1 @@
requests==2.31.0