Posted on
Artificial Intelligence

Artificial Intelligence Linux Fleet Management

Author
  • User
    linuxbash
    Posts by this author
    Posts by this author

Artificial Intelligence Linux Fleet Management: From Bash to Self-Healing Ops

What if your Linux servers could predict failures, flag weird behavior before it bites production, and even fix the obvious stuff automatically—using tools you already know? That’s the promise of AI-assisted fleet management: make smarter decisions with your telemetry, reduce alert fatigue, and turn routine firefighting into quiet, predictable operations.

This post shows a practical, ops-first path to bring AI into your Linux fleet without a giant platform rewrite. You’ll instrument your nodes with standard packages, feed signals to a lightweight anomaly detector, and hook in safe, auditable remediation. It’s all scriptable, reproducible, and friendly to Bash.

Why AI for Linux fleet management now?

  • Scale and complexity: Fleets span clouds, regions, and hardware generations. “Thresholds” that worked at 50 nodes drown you in noise at 500.

  • Early detection: Models can spot subtle drift and pre-failure patterns (thermal creep, SMART counters, memory leaks) faster than human eyes on dashboards.

  • Consistency: Automated, explainable actions based on the same signals you already collect—no vendor lock-in required.

What we’ll build

  • A minimal telemetry collector using sysstat, lm-sensors, smartmontools, and Bash

  • A per-node anomaly detector using Python (IsolationForest) running from systemd timers

  • A safe remediation hook (Bash) for quick wins

  • An optional Ansible playbook to roll it out at scale

You can start with 1–10 nodes and grow. All examples are distro-agnostic and include apt, dnf, and zypper instructions.


1) Instrument your fleet with standard packages

We’ll collect:

  • Load averages and memory pressure

  • Temperatures

  • Disk SMART health summaries

  • Top CPU consumers

Install prerequisites.

Debian/Ubuntu (apt):

sudo apt update
sudo apt install -y python3 python3-venv python3-pip sysstat smartmontools lm-sensors jq git

RHEL/CentOS/Fedora (dnf):

sudo dnf install -y python3 python3-virtualenv python3-pip sysstat smartmontools lm_sensors jq git

openSUSE (zypper):

sudo zypper refresh
sudo zypper install -y python3 python3-virtualenv python3-pip sysstat smartmontools lm_sensors jq git

Detect hardware sensors (answer prompts or run auto):

sudo sensors-detect --auto

Create a service user and directories:

sudo useradd -r -s /usr/sbin/nologin -M afm
sudo mkdir -p /opt/afm /var/log/afm
sudo chown -R afm:afm /opt/afm /var/log/afm

Create the collector script at /opt/afm/collector.sh:

#!/usr/bin/env bash
set -euo pipefail

log=/var/log/afm/metrics.jsonl
ts=$(date -Is)

# Load averages
read load1 load5 load15 rest < /proc/loadavg

# Memory usage (%)
mem_total_kb=$(awk '/MemTotal/ {print $2}' /proc/meminfo)
mem_avail_kb=$(awk '/MemAvailable/ {print $2}' /proc/meminfo)
mem_used_pct=$(awk -v t="$mem_total_kb" -v a="$mem_avail_kb" 'BEGIN { printf "%.2f", (t-a)/t*100 }')

# Temperatures (max)
temp_max_c=$(sensors -j 2>/dev/null | jq '[.. | objects | .temp1_input?, .Tccd1?, .Tccd2?, .Tctl?, .Package_id_0?.temp1_input?] | flatten | map(select(.!=null)) | max // 0')

# SMART bad sectors (sum across disks)
disk_bad=0
while read -r d; do
  val=$(sudo smartctl -A "/dev/$d" 2>/dev/null | awk '/Reallocated_Sector_Ct/ {print $10}' | head -n1)
  [[ -z "${val:-}" ]] && val=0
  disk_bad=$((disk_bad + val))
done < <(lsblk -dno NAME,TYPE | awk '$2=="disk"{print $1}')

# Top CPU consumers
top_procs=$(ps -eo pid,comm,%cpu,%mem --sort=-%cpu --no-headers | head -5 | awk '{printf("{\"pid\":%s,\"cmd\":\"%s\",\"cpu\":%s,\"mem\":%s},",$1,$2,$3,$4)}' | sed 's/,$//')

# Host identity
host=$(hostname)

# Emit JSON line
printf '{"ts":"%s","host":"%s","load1":%s,"load5":%s,"load15":%s,"mem_used_pct":%s,"temp_max_c":%s,"disk_bad":%s,"top":[%s]}\n' \
  "$ts" "$host" "$load1" "$load5" "$load15" "$mem_used_pct" "$temp_max_c" "$disk_bad" "$top_procs" >> "$log"

Permissions and sudoers for smartctl:

sudo chown afm:afm /opt/afm/collector.sh
sudo chmod 0755 /opt/afm/collector.sh

# Allow afm to run smartctl without password
echo 'afm ALL=(root) NOPASSWD: /usr/sbin/smartctl' | sudo tee /etc/sudoers.d/afm-smartctl >/dev/null
sudo chmod 0440 /etc/sudoers.d/afm-smartctl

Systemd timer to run every minute:

/etc/systemd/system/afm-collector.service

[Unit]
Description=AFM Telemetry Collector

[Service]
Type=oneshot
User=afm
Group=afm
ExecStart=/opt/afm/collector.sh
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/log/afm
CapabilityBoundingSet=

/etc/systemd/system/afm-collector.timer

[Unit]
Description=Run AFM collector every minute

[Timer]
OnBootSec=30s
OnUnitActiveSec=60s
RandomizedDelaySec=10s
Unit=afm-collector.service

[Install]
WantedBy=timers.target

Enable and start:

sudo systemctl daemon-reload
sudo systemctl enable --now afm-collector.timer
sudo systemctl status afm-collector.timer --no-pager

Validation:

tail -n3 /var/log/afm/metrics.jsonl | jq

2) Add a lightweight per-node anomaly detector

We’ll use a tiny Python script with scikit-learn’s IsolationForest. It trains on recent history (e.g., last 500 samples) and flags the newest point if it looks odd.

Create a venv and install packages:

sudo -u afm python3 -m venv /opt/afm/venv
sudo -u afm /opt/afm/venv/bin/pip install --upgrade pip
sudo -u afm /opt/afm/venv/bin/pip install numpy pandas scikit-learn

Create /opt/afm/anomaly.py:

#!/usr/bin/env /opt/afm/venv/bin/python
import json, sys, os
import pandas as pd
from sklearn.ensemble import IsolationForest

LOG="/var/log/afm/metrics.jsonl"
ALERT_OUT="/var/log/afm/anomalies.jsonl"

features = ["load1","mem_used_pct","temp_max_c","disk_bad"]

def read_df(path, n=500):
    rows=[]
    try:
        with open(path, "r") as f:
            for line in f.readlines()[-n:]:
                try:
                    j = json.loads(line)
                    rows.append({k:j.get(k,0) for k in features} | {"ts": j.get("ts"), "host": j.get("host")})
                except Exception:
                    pass
    except FileNotFoundError:
        pass
    if not rows:
        return pd.DataFrame(columns=features+["ts","host"])
    return pd.DataFrame(rows)

def main():
    df = read_df(LOG, 500)
    if len(df) < 30:
        sys.exit(0)  # warmup
    X = df[features].fillna(0)
    model = IsolationForest(n_estimators=200, contamination="auto", random_state=42)
    model.fit(X[:-1])
    score = model.decision_function(X[-1:])[0]  # higher is more normal
    pred = model.predict(X[-1:])[0]            # -1 anomaly, 1 normal
    last = df.iloc[-1].to_dict()
    out = {
        "ts": last["ts"],
        "host": last.get("host",""),
        "score": round(float(score),4),
        "is_anomaly": bool(pred == -1),
        "features": {k: float(last[k]) for k in features}
    }
    os.makedirs(os.path.dirname(ALERT_OUT), exist_ok=True)
    with open(ALERT_OUT, "a") as f:
        f.write(json.dumps(out)+"\n")
    if out["is_anomaly"]:
        os.system(f"/opt/afm/remediate.sh '{json.dumps(out).replace(\"'\",\"'\\''\")}'")
if __name__ == "__main__":
    main()

Make it executable:

sudo chown afm:afm /opt/afm/anomaly.py
sudo chmod 0755 /opt/afm/anomaly.py

Systemd timer:

/etc/systemd/system/afm-anomaly.service

[Unit]
Description=AFM Anomaly Detection

[Service]
Type=oneshot
User=afm
Group=afm
ExecStart=/opt/afm/anomaly.py
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/log/afm

/etc/systemd/system/afm-anomaly.timer

[Unit]
Description=Run AFM anomaly detection every minute

[Timer]
OnBootSec=1m
OnUnitActiveSec=60s
RandomizedDelaySec=15s
Unit=afm-anomaly.service

[Install]
WantedBy=timers.target

Enable and start:

sudo systemctl daemon-reload
sudo systemctl enable --now afm-anomaly.timer
sudo systemctl status afm-anomaly.timer --no-pager

Check output:

tail -n5 /var/log/afm/anomalies.jsonl | jq

3) Hook in safe, auditable remediation

Start with conservative, reversible actions and strong guardrails. Create /opt/afm/remediate.sh:

#!/usr/bin/env bash
set -euo pipefail

event_json="${1:-}"
log=/var/log/afm/actions.log
ts=$(date -Is)

# Parse fields using jq (fallbacks to neutral values)
is_anom=$(jq -r '.is_anomaly' <<<"$event_json" 2>/dev/null || echo "false")
mem=$(jq -r '.features.mem_used_pct' <<<"$event_json" 2>/dev/null || echo "0")
load1=$(jq -r '.features.load1' <<<"$event_json" 2>/dev/null || echo "0")
temp=$(jq -r '.features.temp_max_c' <<<"$event_json" 2>/dev/null || echo "0")

# Example remediation policies (edit to match your env)
action="none"
note=""

# 1) Memory pressure with high load: restart a known leaky service safely
if [[ "$is_anom" == "true" ]] && awk "BEGIN{exit !($mem>90 && $load1>2)}"; then
  svc="your-service.service"   # TODO: change this
  if systemctl is-active --quiet "$svc"; then
    action="systemctl restart $svc"
    note="high mem+load; restarting $svc"
    # Dry-run in first week: comment out next line until confident
    # sudo systemctl restart "$svc"
  fi
fi

# 2) Thermal creep: throttle a workload or raise alert
if [[ "$is_anom" == "true" ]] && awk "BEGIN{exit !($temp>85)}"; then
  action="${action}; echo 'THERMAL ALERT'"
  note="${note}; temp>${temp}C"
fi

# 3) Always log decisions
printf '%s %s | %s | %s\n' "$ts" "$(hostname)" "$action" "$note" >> "$log"

Permissions:

sudo chown afm:afm /opt/afm/remediate.sh
sudo chmod 0755 /opt/afm/remediate.sh

Start with dry-runs (log only). After a week of observation, enable the actual systemctl restart line or add your own handlers. For safety:

  • Keep the remediation script idempotent

  • Add rate limiting (e.g., refuse to restart the same unit >2 times/hour)

  • Require a second signal (e.g., two consecutive anomaly windows) before acting


4) Manage at scale with Ansible

Use Ansible to roll the above to many nodes in minutes.

Install Ansible on your control machine.

Debian/Ubuntu (try ansible, fallback to ansible-core):

sudo apt update
sudo apt install -y ansible || sudo apt install -y ansible-core

RHEL/CentOS/Fedora:

sudo dnf install -y ansible-core

openSUSE:

sudo zypper refresh
sudo zypper install -y ansible

Example inventory (inventory.ini):

[afm]
node1.example.com
node2.example.com

Minimal playbook (deploy-afm.yml):

---

- hosts: afm
  become: yes
  vars:
    afm_user: afm
    afm_dir: /opt/afm
  tasks:
    - name: Ensure packages
      package:
        name:
          - python3
          - python3-venv
          - python3-pip
          - sysstat
          - smartmontools
          - lm-sensors
          - jq
          - git
        state: present

    - name: Create afm user
      user:
        name: "{{ afm_user }}"
        system: yes
        shell: /usr/sbin/nologin
        create_home: no

    - name: Create dirs
      file:
        path: "{{ item }}"
        state: directory
        owner: "{{ afm_user }}"
        group: "{{ afm_user }}"
        mode: '0755'
      loop:
        - "{{ afm_dir }}"
        - /var/log/afm

    - name: Deploy collector
      copy:
        src: files/collector.sh
        dest: "{{ afm_dir }}/collector.sh"
        owner: "{{ afm_user }}"
        group: "{{ afm_user }}"
        mode: '0755'

    - name: Allow smartctl
      copy:
        dest: /etc/sudoers.d/afm-smartctl
        content: "afm ALL=(root) NOPASSWD: /usr/sbin/smartctl\n"
        mode: '0440'

    - name: Deploy anomaly detector
      copy:
        src: files/anomaly.py
        dest: "{{ afm_dir }}/anomaly.py"
        owner: "{{ afm_user }}"
        group: "{{ afm_user }}"
        mode: '0755'

    - name: Deploy remediation
      copy:
        src: files/remediate.sh
        dest: "{{ afm_dir }}/remediate.sh"
        owner: "{{ afm_user }}"
        group: "{{ afm_user }}"
        mode: '0755'

    - name: Create venv and install deps
      become_user: "{{ afm_user }}"
      shell: |
        python3 -m venv {{ afm_dir }}/venv
        {{ afm_dir }}/venv/bin/pip install --upgrade pip
        {{ afm_dir }}/venv/bin/pip install numpy pandas scikit-learn
      args:
        creates: "{{ afm_dir }}/venv/bin/python"

    - name: Systemd units
      copy:
        src: files/{{ item }}
        dest: /etc/systemd/system/{{ item }}
        mode: '0644'
      loop:
        - afm-collector.service
        - afm-collector.timer
        - afm-anomaly.service
        - afm-anomaly.timer

    - name: Reload systemd and enable timers
      shell: |
        systemctl daemon-reload
        systemctl enable --now afm-collector.timer
        systemctl enable --now afm-anomaly.timer

Run it:

ansible-playbook -i inventory.ini deploy-afm.yml

Tip: Store your AFM files/ units under a Git repo and use CI to validate syntax (shellcheck, yamllint) before rollout.


Real-world examples

  • Noisy neighbor turned SLO-killer: The model flagged a pattern of high load with normal CPU idle but spiking iowait—correlating to backup windows. The remediation temporarily lowered I/O priority of the job (ionice -c3) during peak hours, cutting latency alerts by 80%.

  • Catching failing disks early: A gradual rise in SMART reallocated sectors triggered an anomaly 72 hours before kernel I/O errors appeared. Ops scheduled a preemptive replacement, avoiding an after‑hours incident.


Operational tips and guardrails

  • Start with observe-only mode. Log decisions for 1–2 weeks before enabling any restarts.

  • Version everything. Keep scripts and units in Git; deploy with Ansible.

  • Least privilege. Run as a dedicated user; confine with systemd sandboxing.

  • Don’t overfit. Use a small, stable feature set and enough history (>= 200 samples).

  • Document playbooks. When automation acts, it should explain why in logs.


Conclusion and next steps

You don’t need a heavyweight platform to get real value from AI in Linux fleet management. With a few well‑chosen packages, some Bash, and a tiny Python model, you can:

  • See problems earlier

  • Reduce mean time to recovery

  • Automate boring, reversible fixes

Your next steps: 1) Roll this stack to 1–3 non-critical nodes. 2) Let it learn for a week; review anomalies and actions. 3) Enable one safe remediation (with rate limiting). 4) Expand with Ansible; add features that matter in your environment.

If you want a follow-up post with Grafana dashboards, Prometheus exporters, or central aggregation patterns (rsyslog/Loki), let me know which direction helps your team most.