From Threshold Alerts to Intelligent Awareness
Traditional VPS monitoring relies on fixed thresholds — CPU > 80% triggers an alert, memory > 90% triggers an alert. This approach is simple but has two fatal flaws:
- Alert fatigue: Scheduled tasks and traffic spikes trigger countless false positives, causing operators to desensitize to alerts.
- Silent failures: Slow resource leaks and gradual performance degradation go undetected by static thresholds.
The core idea of AI-powered monitoring is simple: stop asking “did it exceed a threshold?” and start asking “is this normal?” Machine learning models learn historical behavior baselines and identify deviations from normal patterns — regardless of which metric is involved.
Architecture Overview
┌─────────────────────────────────────────────────────┐
│ AI Monitoring Stack │
├──────────┬──────────┬──────────┬────────────────────┤
│ Data │ Storage │ AI │ Execution │
│ Collection│ Layer │ Engine │ Layer │
├──────────┼──────────┼──────────┼────────────────────┤
│ Prometheus│ Timescale│ Isolation│ Ansible / │
│ Node_Exp │ Forest │ Forest │ Shell Scripts │
│ Telegraf │ InfluxDB │ LSTM │ Terraform │
│ cAdvisor │ ClickHouse│ Autoencoder│ Kubernetes │
└──────────┴──────────┴──────────┴────────────────────┘
│ │ │
▼ ▼ ▼
Infrastructure Time-series Prediction &
Metrics Data Decision Making
Step 1: Building the Data Collection Layer
Deploy a unified collector on your VPS:
# docker-compose.monitoring.yml
version: '3.8'
services:
prometheus:
image: prom/prometheus:v2.51.0
volumes:
- ./prometheus:/etc/prometheus
- prom_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=30d'
- '--web.enable-lifecycle'
node-exporter:
image: prom/node-exporter:v1.7.0
pid: host
restart: unless-stopped
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
cadvisor:
image: gcr.io/cadvisor/cadvisor:v0.49.1
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- /:/rootfs:ro
- /sys:/sys:ro
timescaledb:
image: timescale/timescaledb:latest-pg16
environment:
POSTGRES_PASSWORD: ${TS_PASSWORD}
volumes:
- ts_data:/var/lib/postgresql/data
telegraf:
image: telegraf:1.30
volumes:
- ./telegraf.conf:/etc/telegraf/telegraf.conf:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
volumes:
prom_data:
ts_data:
# prometheus/prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "alert_rules.yml"
scrape_configs:
- job_name: 'node'
static_configs:
- targets: ['node-exporter:9100']
- job_name: 'cadvisor'
static_configs:
- targets: ['cadvisor:8080']
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
remote_write:
- url: http://timescaledb:9201/api/v1/write
Step 2: Deploying the AI Anomaly Detection Engine
Here’s a lightweight approach combining Python ML libraries with Prometheus metrics.
Option A: Isolation Forest (Recommended for Getting Started)
# ai_detector/isolation_forest.py
import numpy as np
from sklearn.ensemble import IsolationForest
import joblib
from datetime import datetime, timedelta
class VPSAnomalyDetector:
def __init__(self, contamination=0.05):
self.model = IsolationForest(
n_estimators=200,
contamination=contamination,
max_samples='auto',
random_state=42
)
self.feature_names = [
'cpu_usage', 'memory_usage', 'disk_io_read',
'disk_io_write', 'network_in', 'network_out',
'load_avg_1m', 'open_fds'
]
self.is_trained = False
def extract_features(self, metrics_dict):
"""Extract feature vectors from Prometheus metric dictionaries"""
features = []
for name in self.feature_names:
if name in metrics_dict:
features.append(metrics_dict[name])
else:
features.append(0.0)
return np.array(features).reshape(1, -1)
def train(self, historical_metrics):
"""Train the model with historical data"""
X = np.array(historical_metrics)
self.model.fit(X)
self.is_trained = True
print(f"✅ Model trained successfully on {len(X)} historical samples")
def detect(self, current_metrics):
"""Detect whether current metrics are anomalous"""
if not self.is_trained:
return {"is_anomaly": False, "score": 0.0}
feature_vec = self.extract_features(current_metrics)
prediction = self.model.predict(feature_vec)[0]
score = self.model.score_samples(feature_vec)[0]
is_anomaly = (prediction == -1)
severity = self._calculate_severity(score)
return {
"is_anomaly": bool(is_anomaly),
"anomaly_score": float(-score), # Higher = more anomalous
"severity": severity,
"timestamp": datetime.utcnow().isoformat()
}
def _calculate_severity(self, score):
"""Calculate severity based on anomaly score"""
if score < -0.1:
return "low"
elif score < -0.3:
return "medium"
elif score < -0.5:
return "high"
else:
return "critical"
def save_model(self, path="model_isolation_forest.pkl"):
joblib.dump(self.model, path)
print(f"📦 Model saved to {path}")
def load_model(self, path="model_isolation_forest.pkl"):
self.model = joblib.load(path)
self.is_trained = True
print(f"📂 Model loaded from {path}")
Option B: LSTM Time-Series Prediction
# ai_detector/lstm_predictor.py
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout
from sklearn.preprocessing import MinMaxScaler
class LSTMAnomalyPredictor:
def __init__(self, sequence_length=24, look_ahead=3):
self.sequence_length = sequence_length
self.look_ahead = look_ahead
self.scaler = MinMaxScaler()
self.model = None
self.is_trained = False
def prepare_sequences(self, data):
"""Convert time-series data into LSTM input format"""
scaled = self.scaler.fit_transform(data)
X, y = [], []
for i in range(len(scaled) - self.sequence_length - self.look_ahead):
X.append(scaled[i:i + self.sequence_length])
y.append(scaled[i + self.sequence_length:
i + self.sequence_length + self.look_ahead])
return np.array(X), np.array(y)
def build_model(self, input_shape):
model = Sequential([
LSTM(128, return_sequences=True, input_shape=input_shape),
Dropout(0.2),
LSTM(64, return_sequences=False),
Dropout(0.2),
LSTM(32),
Dense(input_shape[-1])
])
model.compile(optimizer='adam', loss='mse')
return model
def train(self, historical_data):
"""Train the LSTM model"""
X, y = self.prepare_sequences(historical_data)
self.model = self.build_model((X.shape[1], X.shape[2]))
history = self.model.fit(
X, y,
epochs=50,
batch_size=32,
validation_split=0.2,
callbacks=[
tf.keras.callbacks.EarlyStopping(
monitor='val_loss', patience=5, restore_best_weights=True
)
],
verbose=1
)
self.is_trained = True
print("✅ LSTM model training complete")
return history
def predict_and_detect(self, recent_data):
"""Predict next time step and detect anomalies"""
if not self.is_trained:
return {"is_anomaly": False, "predicted_value": None}
scaled_input = self.scaler.transform(recent_data.reshape(1, -1))
sequence = scaled_input.reshape(1, 1, -1)
predicted_scaled = self.model.predict(sequence, verbose=0)
predicted = self.scaler.inverse_transform(predicted_scaled)
actual = recent_data[-1]
error = abs(actual - predicted[0][0])
threshold = self._compute_threshold()
return {
"is_anomaly": bool(error > threshold),
"predicted_value": float(predicted[0][0]),
"actual_value": float(actual),
"prediction_error": float(error),
"threshold": float(threshold)
}
def _compute_threshold(self):
"""Dynamically compute threshold based on training data std dev"""
return 2.0 # Adjust based on actual training data
Step 3: Predictive Alerts
Instead of alerting after a failure, predict failures before they happen:
# ai_detector/predictive_alerts.py
import numpy as np
from scipy import stats
class ResourceTrendPredictor:
"""Resource trend predictor — forecasts when disk/memory will be exhausted"""
def __init__(self, window_size=48):
self.window_size = window_size
def predict_exhaustion_time(self, historical_values, capacity, unit_hours=1):
"""
Linear regression to predict resource exhaustion time
Args:
historical_values: Historical resource usage over N time points
capacity: Total capacity
unit_hours: Sampling interval in hours
Returns:
dict: Predicted exhaustion time and confidence metrics
"""
if len(historical_values) < 10:
return {"error": "Insufficient data points"}
x = np.arange(len(historical_values))
y = np.array(historical_values)
# Linear regression
slope, intercept, r_value, p_value, std_err = stats.linregress(x, y)
# Predict exhaustion time
remaining_capacity = capacity - y[-1]
if slope <= 0:
return {
"trend": "stable_or_decreasing",
"slope_per_hour": float(slope * unit_hours),
"r_squared": float(r_value ** 2),
"current_usage": float(y[-1]),
"capacity": capacity
}
hours_to_exhaust = remaining_capacity / (slope * unit_hours)
# Confidence interval
projected_at_confidence = hours_to_exhaust * (1 - std_err / abs(slope))
return {
"trend": "increasing",
"hours_to_exhaust": float(hours_to_exhaust),
"days_to_exhaust": float(hours_to_exhaust / 24),
"confidence_r_squared": float(r_value ** 2),
"p_value": float(p_value),
"slope_per_hour": float(slope * unit_hours),
"current_usage": float(y[-1]),
"projected_usage_24h": float(y[-1] + slope * 24 * unit_hours),
"projected_usage_7d": float(y[-1] + slope * 168 * unit_hours)
}
def detect_cyclic_pattern(self, values, period_hint=None):
"""Detect cyclic patterns (e.g., daily/weekly rhythms)"""
values = np.array(values)
n = len(values)
if period_hint:
periods_to_check = [period_hint]
else:
periods_to_check = [6, 12, 24, 48, 168] # hours
results = {}
for period in periods_to_check:
if period >= n // 2:
continue
autocorr = np.correlate(values - np.mean(values),
values - np.mean(values), mode='full')
autocorr = autocorr[n - 1:]
if len(autocorr) > period:
corr_at_period = autocorr[period] / (autocorr[0] + 1e-10)
results[f"period_{period}h"] = float(corr_at_period)
best_period = max(results, key=results.get) if results else None
has_cycle = best_period and results[best_period] > 0.5
return {
"has_cyclic_pattern": bool(has_cycle),
"autocorrelations": results,
"best_period_hours": int(best_period.split('_')[1])
if best_period else None
}
Step 4: Automated Remediation Pipeline
Once anomalies are detected, the system should respond autonomously:
# ai_detector/auto_remediation.yaml
remediation_policies:
- name: "high_cpu_process_kill"
condition:
anomaly_type: "cpu_spike"
severity: "critical"
duration_minutes: 5
actions:
- type: "shell"
script: |
#!/bin/bash
TOP_PID=$(ps aux --sort=-%cpu | awk 'NR==2{print $2}')
TOP_PROC=$(ps -p $TOP_PID -o comm=)
echo "$(date) [AUTO] CPU spike detected: PID=$TOP_PID ($TOP_PROC)"
kill -TERM $TOP_PID 2>/dev/null
sleep 10
if kill -0 $TOP_PID 2>/dev/null; then
kill -KILL $TOP_PID 2>/dev/null
fi
- type: "notify"
channel: "slack"
message: "🔥 CPU anomaly: Terminated process {{process_name}} (PID {{pid}})"
- name: "memory_leak_restart"
condition:
anomaly_type: "memory_growth"
trend: "increasing"
projected_exhaust_hours: "< 24"
actions:
- type: "docker"
action: "restart_service"
target: "{{service_name}}"
- type: "notify"
channel: "email"
message: "⚠️ Memory growth detected: {{service}} will exhaust memory in {{hours}}h, auto-restarted"
- name: "disk_cleanup"
condition:
anomaly_type: "disk_full_warning"
disk_usage_percent: "> 85"
actions:
- type: "shell"
script: |
#!/bin/bash
find /var/log -name "*.gz" -mtime +7 -delete
find /var/log -name "*.log" -size +100M -exec truncate -s 0 {} \;
docker system prune -f --filter "until=168h"
rm -rf /tmp/* 2>/dev/null
- type: "notify"
channel: "slack"
message: "🧹 Disk space low: Auto-cleanup executed, freed {{freed_space}} MB"
- name: "security_incident_response"
condition:
anomaly_type: "brute_force_detected"
failed_logins_per_minute: "> 10"
actions:
- type: "shell"
script: |
#!/bin/bash
ATTACKER_IP=$(lastb | head -1 | awk '{print $3}')
iptables -A INPUT -s $ATTACKER_IP -j DROP 2>/dev/null
fail2ban-client set sshd banip $ATTACKER_IP
env:
require_root: true
- type: "notify"
channel: "pagerduty"
priority: "P1"
message: "🛡️ Security incident: Brute force detected, IP blocked {{attacker_ip}}"
Step 5: Visualization & Dashboards
Integrate all data sources into Grafana for AI analysis visualization:
// Grafana Dashboard JSON snippet
{
"dashboard": {
"title": "AI VPS Intelligent Monitoring Panel",
"panels": [
{
"title": "Real-time Anomaly Score",
"type": "gauge",
"targets": [
{
"expr": "ai_anomaly_score{job=\"vps\"}",
"legendFormat": "{{instance}}"
}
]
},
{
"title": "Resource Exhaustion Forecast",
"type": "timeseries",
"targets": [
{
"expr": "resource_projected_exhaust_hours{metric=\"disk\"}",
"legendFormat": "Disk exhaustion forecast (hours)"
},
{
"expr": "resource_projected_exhaust_hours{metric=\"memory\"}",
"legendFormat": "Memory exhaustion forecast (hours)"
}
]
},
{
"title": "Cyclic Pattern Detection",
"type": "table",
"targets": [
{
"expr": "cyclic_pattern_detected_total",
"legendFormat": "{{metric}}"
}
]
},
{
"title": "Auto-Remediation Events",
"type": "stat",
"targets": [
{
"expr": "sum(increase(auto_remediation_events_total[24h]))",
"legendFormat": "Remediations today"
}
]
}
]
}
}
Case Study: From Alert Storm to Precision Intervention
An e-commerce VPS during a major sales event:
| Scenario | Traditional | AI-Powered |
|---|---|---|
| CPU spike | One alert per minute, 24/hour | Single detection with root cause analysis |
| Memory leak | OOM only after 48 hours | Trend prediction warns 12 hours ahead |
| Disk full | Service breaks when full | 72-hour prediction, auto-cleanup triggered |
| SSH brute force | Post-incident audit finding | Real-time detection, instant IP block |
Results:
- Alert volume reduced by 87% (from 300+/day to ~40)
- MTTR (Mean Time To Recovery) dropped from 45 min to 3 min
- Unplanned downtime events reduced by 94%
Implementation Roadmap
Week 1-2: Foundation
├── Deploy Prometheus + Node Exporter + cAdvisor
├── Configure Grafana basic dashboards
└── Collect at least 7 days of historical data
Week 3-4: AI Model Training
├── Build baseline with Isolation Forest
├── Validate detection accuracy (manual labeling)
└── Tune contamination parameter
Week 5-6: Prediction & Automation
├── Deploy LSTM time-series predictor
├── Write remediation playbooks
├── Set up tiered response policies
└── Integrate Slack/email/PagerDuty
Week 7+: Continuous Improvement
├── Retrain models weekly (incremental learning)
├── Evaluate remediation outcomes, refine policies
└── Expand to new services and metrics
Security Considerations
- Least privilege: Auto-remediation scripts should run with minimum permissions, avoiding unnecessary root access.
- Human approval: Critical operations (data deletion, production service restarts) require human confirmation.
- Audit logging: All AI decisions and automated actions must be logged for audit trails.
- Rollback mechanism: Every auto-remediation should be reversible, with pre-change snapshots preserved.
Summary
AI-powered monitoring doesn’t replace Prometheus and Grafana — it makes them smarter. Through anomaly detection, trend prediction, and automated remediation, your VPS operations can shift from “firefighting mode” to “prevention mode.”
The core principle: observe first, detect second, predict third, automate last. Each layer builds on the reliability of the one before it.
中文版本见左侧标签,包含完整代码示例和中文注释。
