- Posted on
- • Artificial Intelligence
Artificial Intelligence SAN Monitoring
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Artificial Intelligence SAN Monitoring on Linux: Detect Problems Before They Become Outages
If your business depends on shared storage, a small SAN hiccup can snowball into a full-blown outage. Traditional threshold-based alerts often miss subtle signals: rising latency at 2 a.m., a queue depth creeping up during backups, or a flapping path that “self-heals” until it doesn’t. This post shows how to bring lightweight AI to your Linux/Bash toolkit to spot storage anomalies early—using tools you already trust.
You’ll learn why AI is a good fit for SAN telemetry and how to build a practical, vendor-agnostic monitoring pipeline on any Linux host that uses your SAN, complete with install commands for apt, dnf, and zypper.
Why AI for SAN monitoring?
SANs are complex and noisy: latency, throughput, IOPS, queue depth, path health, and workload mix all move simultaneously.
Fixed thresholds are brittle: what’s “normal” at noon on Monday is not normal at midnight Sunday.
AI can baseline your environment and flag deviations automatically, reducing false positives and surfacing real issues sooner.
It’s vendor-agnostic: you can start with host-side signals (iostat, multipath status) without proprietary array APIs.
What we’ll build
A minimal, production-friendly flow:
A Bash collector that logs per-device extended IO metrics and multipath health every minute.
A small Python script using Isolation Forest (unsupervised anomaly detection) to flag unusual patterns.
Systemd units/timers to run continuously and alert via logs.
Distro-agnostic installation steps.
Note: Commands that query devices or multipath typically need root.
Prerequisites: Install the tools
We’ll use:
sysstat (iostat) for extended device stats
multipath-tools (or device-mapper-multipath) for path health
jq for robust JSON parsing from iostat
Python 3 + pip (and venv) for the anomaly detector
On Debian/Ubuntu (apt):
sudo apt update
sudo apt install -y sysstat multipath-tools jq python3 python3-venv python3-pip
On RHEL/Fedora/CentOS/Alma/Rocky (dnf):
sudo dnf install -y sysstat device-mapper-multipath jq python3 python3-pip
On SUSE/openSUSE (zypper):
sudo zypper refresh
sudo zypper install -y sysstat multipath-tools jq python3 python3-pip
Enable multipath if applicable:
sudo systemctl enable --now multipathd
Tip (Debian/Ubuntu): iostat works immediately after install. If you also plan to use sar, enable “ENABLED=true” in /etc/default/sysstat and restart the service.
Step 1: Collect SAN metrics with Bash + iostat JSON
We’ll run iostat in JSON mode for parsing stability and include multipath path-failure counts in each record.
Create /usr/local/sbin/collect_san_metrics.sh:
#!/usr/bin/env bash
set -euo pipefail
OUT_DIR="/var/log/san-metrics"
FILE="$OUT_DIR/metrics.csv"
mkdir -p "$OUT_DIR"
chmod 750 "$OUT_DIR"
# Initialize CSV header if missing
if [ ! -s "$FILE" ]; then
echo "ts,device,r_s,w_s,rkB_s,wkB_s,await,aqu_sz,util,mp_failed" > "$FILE"
fi
# Continuous collector: one 60s interval per loop
while true; do
TS="$(date -Is)"
# Count any lines hinting a failed/faulty/offline path (best-effort)
MP_FAILED="$(multipath -ll 2>/dev/null | awk 'BEGIN{IGNORECASE=1} /failed|faulty|offline/ {c++} END{print c+0}')"
# Capture one 60s extended interval as JSON, then parse
if iostat -x -y -o JSON 60 1 > /tmp/iostat.json 2>/dev/null; then
jq -r --arg ts "$TS" --arg mp "$MP_FAILED" '
.sysstat.hosts[0].statistics[-1].disk[]
| select(.disk_device|test("^(sd|nvme|dm-)"))
| [
$ts,
.disk_device,
(."r/s"//0),
(."w/s"//0),
(."rkB/s"//0),
(."wkB/s"//0),
(.await//0),
(."aqu-sz"//0),
(."%util"//0),
$mp
]
| @csv
' /tmp/iostat.json >> "$FILE"
else
# Fallback note if JSON not supported (sysstat < 12)
echo "iostat JSON not available; please upgrade sysstat >= 12" | systemd-cat -t san-metrics || true
sleep 60
fi
done
Make it executable:
sudo install -m 0750 /usr/local/sbin/collect_san_metrics.sh /usr/local/sbin/collect_san_metrics.sh
Create a systemd service to run it continuously:
sudo tee /etc/systemd/system/san-metrics.service >/dev/null <<'UNIT'
[Unit]
Description=SAN metrics collector (iostat + multipath)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/sbin/collect_san_metrics.sh
User=root
Restart=always
RestartSec=5s
Nice=10
IOSchedulingClass=idle
[Install]
WantedBy=multi-user.target
UNIT
Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable --now san-metrics.service
sudo journalctl -u san-metrics.service -f
You should see /var/log/san-metrics/metrics.csv growing with lines like:
2026-07-11T10:12:00+00:00,dm-2,120.3,45.1,8192.0,6144.0,5.2,0.8,73.5,0
Step 2: Add a lightweight AI anomaly detector (Isolation Forest)
Create a Python virtual environment and install dependencies.
On Debian/Ubuntu (apt used above for python3-venv):
sudo python3 -m venv /opt/san-ai/venv
sudo /opt/san-ai/venv/bin/pip install --upgrade pip
sudo /opt/san-ai/venv/bin/pip install pandas numpy scikit-learn
On RHEL/Fedora/CentOS/Alma/Rocky (dnf):
sudo python3 -m venv /opt/san-ai/venv
sudo /opt/san-ai/venv/bin/pip install --upgrade pip
sudo /opt/san-ai/venv/bin/pip install pandas numpy scikit-learn
On SUSE/openSUSE (zypper):
sudo python3 -m venv /opt/san-ai/venv
sudo /opt/san-ai/venv/bin/pip install --upgrade pip
sudo /opt/san-ai/venv/bin/pip install pandas numpy scikit-learn
Create /usr/local/sbin/san_detect.py:
#!/usr/bin/env /opt/san-ai/venv/bin/python
import os
import sys
import time
import pandas as pd
import numpy as np
from sklearn.ensemble import IsolationForest
METRICS = "/var/log/san-metrics/metrics.csv"
MIN_SAMPLES = 200 # minimum rows per device to build a baseline
CONTAMINATION = 0.02 # expected fraction of anomalies
LOOKBACK_MINUTES = 1440 # train on ~1 day by default
TEST_WINDOW = 5 # number of most recent points to score
def log(msg):
print(f"[san-ai] {time.strftime('%Y-%m-%d %H:%M:%S')} {msg}")
def load_data(path):
if not os.path.exists(path):
log(f"missing metrics file: {path}")
sys.exit(0)
# Read efficiently: we can tail a lot of rows if file is large
df = pd.read_csv(path,
names=["ts","device","r_s","w_s","rkB_s","wkB_s","await","aqu_sz","util","mp_failed"],
header=0,
parse_dates=["ts"])
# Keep only recent data
cutoff = pd.Timestamp.utcnow() - pd.Timedelta(minutes=LOOKBACK_MINUTES)
df = df[df["ts"] >= cutoff]
# Numeric cleanup
for c in ["r_s","w_s","rkB_s","wkB_s","await","aqu_sz","util","mp_failed"]:
df[c] = pd.to_numeric(df[c], errors="coerce")
df = df.replace([np.inf, -np.inf], np.nan).dropna()
return df
def detect_multipath(df):
recent = df.sort_values("ts").tail(1)
if not recent.empty and int(recent["mp_failed"].iloc[0]) > 0:
log(f"ALERT: multipath shows {int(recent['mp_failed'].iloc[0])} failed/faulty/offline paths")
def detect_per_device(df):
alerts = 0
features = ["r_s","w_s","rkB_s","wkB_s","await","aqu_sz","util"]
for dev, g in df[df["device"]!="__multipath__"].groupby("device"):
g = g.sort_values("ts")
if len(g) < MIN_SAMPLES:
continue
X = g[features].values
# Fit on historical, score recent window
train_X = X[:-TEST_WINDOW] if len(X) > TEST_WINDOW else X
test_X = X[-TEST_WINDOW:]
if len(train_X) < MIN_SAMPLES:
continue
model = IsolationForest(
n_estimators=200,
contamination=CONTAMINATION,
random_state=42,
n_jobs=1,
warm_start=False
)
model.fit(train_X)
preds = model.predict(test_X) # -1 == anomaly
scores = model.decision_function(test_X)
for i, p in enumerate(preds):
if p == -1:
ts = g["ts"].iloc[len(g)-TEST_WINDOW+i]
row = g.iloc[len(g)-TEST_WINDOW+i]
alerts += 1
log(f"ANOMALY device={dev} ts={ts} util={row['util']:.1f}% await={row['await']:.2f} r_s={row['r_s']:.1f} w_s={row['w_s']:.1f} score={scores[i]:.4f}")
if alerts == 0:
log("OK: no anomalies in last window")
def main():
df = load_data(METRICS)
if df.empty:
log("no recent data to analyze")
return
detect_multipath(df)
detect_per_device(df)
if __name__ == "__main__":
main()
Make it executable:
sudo install -m 0750 /usr/local/sbin/san_detect.py /usr/local/sbin/san_detect.py
Create a systemd unit and timer to run it every 15 minutes:
sudo tee /etc/systemd/system/san-ai.service >/dev/null <<'UNIT'
[Unit]
Description=SAN AI anomaly detector
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/san_detect.py
User=root
Nice=5
UNIT
sudo tee /etc/systemd/system/san-ai.timer >/dev/null <<'TIMER'
[Unit]
Description=Run SAN AI anomaly detector every 15 minutes
[Timer]
OnBootSec=2m
OnUnitActiveSec=15m
AccuracySec=1m
Persistent=true
[Install]
WantedBy=timers.target
TIMER
Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable --now san-ai.timer
sudo systemctl list-timers --all | grep san-ai
sudo journalctl -u san-ai.service -f
You’ll see journal lines with anomalies like:
[san-ai] 2026-07-11 10:30:02 ANOMALY device=dm-2 ts=2026-07-11 10:28:00 util=99.7% await=42.13 r_s=15.0 w_s=320.4 score=-0.1312
Step 3: Tune and harden
Choose the right features: We used r/s, w/s, rkB/s, wkB/s, await, aqu-sz, util. Add or remove based on your environment.
Warm-up/baseline: Set MIN_SAMPLES high enough (e.g., a day’s worth) before trusting alerts. Reduce to accelerate initial learning in labs.
Contamination: Lower CONTAMINATION (e.g., 0.01) to reduce alert volume; raise to catch more edge cases.
Exclude maintenance windows: You can skip writing samples during known backup windows (e.g., if date +%H is 01–03) to avoid polluting the baseline.
Alerting: Pipe san-ai output to your aggregator (journalbeat/rsyslog) or add a simple notifier (e.g., send to a webhook via curl within san_detect.py when an anomaly is found).
Real-world patterns this catches
Silent saturation: %util ~100% with rising await across multiple dm-* devices during business hours—often the first sign of underprovisioned IOPS or a hot volume.
Path flapping: mp_failed jumps above 0 with intermittent await spikes; usually a cable, HBA, or switch port issue.
Noisy neighbor: A backup or misconfigured job drives sustained small random writes; model flags deviations even if absolute throughput doesn’t breach thresholds.
Queue buildup: aqu-sz increasing while IOPS stays flat, suggesting backend contention or firmware/controller throttling.
Optional: Quick visualization
Even without a full TSDB, you can peek at trends:
awk -F, '$2=="dm-2"{print $1","$9}' /var/log/san-metrics/metrics.csv | tail -n 200
Feed that into gnuplot or your favorite quick charting tool. For a full dashboard later, consider pushing metrics to Prometheus via node_exporter’s textfile collector and chart in Grafana.
Conclusion and Call to Action
You don’t need a heavy AIOps platform to get real value from AI on your SAN. With a few standard Linux tools and a small Python script, you can baseline normal behavior, flag anomalies early, and investigate issues before users feel them.
Your next steps: 1. Install the collector and AI detector on one SAN-connected host. 2. Let it learn for a day, then begin triaging anomalies in the journal. 3. Tune contamination and features, then roll out to more hosts. 4. Integrate alerts into your existing pipeline (syslog/Slack/email) and add dashboards.
If you found this useful, try extending it with per-volume tags, window-aware models (weekday/weekend), or array vendor APIs for deeper context—while keeping Bash at the heart of your Linux-first workflow.