Featured image of post AI 驱动的 VPS 流量整形与突发防护:本地 LLM 动态 QoS 策略引擎

AI 驱动的 VPS 流量整形与突发防护:本地 LLM 动态 QoS 策略引擎

VPS 带宽是花钱买来的稀缺资源,但 DDoS 脉冲、爬虫风暴、备份误推、上游故障重试风暴都会瞬间打满带宽,拖垮整个业务。本文介绍一套基于本地 LLM(Ollama + Qwen2.5)的动态流量整形(traffic shaping)系统——实时识别流量异常模式,自动生成并下发 tc/iptables 整形策略,异常时秒级熔断降级,全程零数据外发。

引言:带宽打满的那一刻,你的业务正在流血

维护 VPS 的人都经历过这样的场景:

  • 凌晨 3 点,上游 SaaS 接口故障,你的服务疯狂重试,出口带宽从 200Mbps 直接飙到 8Gbps 配额,被云厂商限速,正常用户全部 502;
  • 某个爬虫没走 CDN 直打源站,10 分钟内拉走了 400GB 静态资源,当月账单直接翻倍;
  • 备份任务忘了加限速,rsync 推走了所有可用带宽,连 SSH 都卡成 PPT;
  • 一次 DDoS 脉冲,tc 的固定限速配置根本挡不住,因为攻击流量和正常流量走的端口一样。

传统流量整形(traffic shaping)的困境在于:策略是静态的。

你用 tc 配置了 classfq + CBQ,用 iptables 设了 hashlimit,用 nftables 做了 per-IP 限速——这些都是「按规则」工作。但现实世界的流量异常没有规则可循:

  • 攻击者今天走 HTTP/2,明天走 gRPC,后天走 WebSocket;
  • 正常业务的峰值时段每周不同(发薪日、发版日、大促日);
  • 上游故障导致的「重试风暴」和 DDoS 在带宽曲线上的形状几乎一样,但处置方式完全相反(前者要断流止损,后者要放行真用户)。

固定的限速规则,本质上是在用昨天的知识管理今天的流量。

本文介绍一套 AI 驱动的动态流量整形系统:本地 LLM 实时「看懂」流量,区分异常模式,自动生成 QoS 策略(限速、优先级队列、连接数上限、临时封禁),在 VPS 上以秒级生效,异常处置后自动恢复。全程使用 Ollama + Qwen2.5 本地推理,零 API 费用、零数据外发。


一、为什么固定 QoS 策略不够用?

1.1 固定策略的四个盲区

盲区说明后果
未知模式攻击/异常模式没写进规则就完全失效带宽打满、账单爆炸
正常波动误杀固定限速在业务高峰期也生效真用户被降级
策略冲突多条 iptables/nftables 规则叠加后行为不可预测调试困难
恢复无记忆异常结束后策略仍挂着,需要人工清理长期误伤正常流量

1.2 AI 带来的三个能力

  1. 模式识别:LLM 读取实时流量特征(per-source 速率、突发性、持续时间、包大小分布),识别出「爬虫」「重试风暴」「DDoS」「正常峰值」等语义标签
  2. 策略生成:基于识别结果 + 业务 SLO(如"正常用户 p99 < 200ms"),LLM 自动生成 tc/iptables/nftables 策略,并预估影响面
  3. 闭环验证:策略下发后,LLM 持续观察 5-15 分钟窗口,确认"异常被压制、正常流量未受损",不达标自动回滚

二、系统架构

┌───────────────────────────────────────────────────────────────┐
│                     VPS 主机 (Ubuntu 24.04)                    │
│                                                               │
│  ┌───────────┐   ┌───────────┐   ┌───────────┐   ┌─────────┐ │
│  │ 流量采集器 │──▶│ 特征提炼器 │──▶│  LLM 推理  │──▶│ 策略引擎 │ │
│  │ (eBPF/   │   │ (Python)  │   │ (Ollama + │   │ (tc /   │ │
│  │  netstat) │   │          │   │  Qwen2.5) │   │ ipt/nf) │ │
│  └───────────┘   └───────────┘   └───────────┘   └─────────┘ │
│        │                                    │            │    │
│        ▼                                    ▼            ▼    │
│  ┌───────────┐                     ┌───────────────┐           │
│  │ Prometheus│                     │  熔断控制器   │──▶ 回滚/ │
│  │ (bandwidth│                     │ (watchdog)    │   保持   │
│  │  /conn)   │                     └───────────────┘           │
│  └───────────┘                                                 │
│        │                                                       │
│        ▼                                                       │
│  ┌───────────────────────────────────────────────┐             │
│  │ 决策记录 + 效果评估 (LLM 二次审查)            │──▶ Telegram │
│  └───────────────────────────────────────────────┘             │
└───────────────────────────────────────────────────────────────┘

核心模块:

  1. 流量采集器:eBPF(bpftool / 现成探针)或 ip -s link + conntrack 定期快照,输出 per-source-IP 的速率、连接数、包大小分布
  2. 特征提炼器:Python 脚本把原始数据压缩成 LLM 友好的 JSON(只保留 Top 50 source + 全局统计),控制在 2KB 以内
  3. LLM 推理:Qwen2.5 分析特征 JSON,输出「异常标签 + 置信度 + 建议策略 + 预估影响」
  4. 策略引擎:把 LLM 输出的策略翻译成 tc qdisc / iptables -m hashlimit / nftables 命令,先 dry-run 验证语法,再原子化下发
  5. 熔断控制器:策略下发后 5 分钟观察窗口内,若"被保护服务的 p99 恶化 > 30%“立即自动回滚

三、关键实现

3.1 特征提炼:把"原始流量"压缩成 LLM 能看懂的 JSON

直接喂 LLM 原始流量日志会爆 token。正确做法是先提炼语义特征:

def build_features(collect_window_seconds=300):
    """返回压缩后的特征 JSON,通常 < 2KB"""
    top_sources = get_top_sources_by_egress(n=50)  # 按出口流量 Top 50
    global_stats = {
        "egress_mbps_p50": ...,
        "egress_mbps_p95": ...,
        "egress_mbps_burst": ...,          # 最近窗口峰值
        "conn_total": ...,
        "http_status_5xx_ratio": ...,       # 若 Nginx 暴露
    }
    return {
        "ts": now_iso(),
        "window_seconds": collect_window_seconds,
        "top_sources": [
            {"ip": "203.0.113.7", "egress_mbps": 340,
             "conns": 12, "avg_pkt_bytes": 1400,
             "burst_ratio": 8.2, "seen_for_minutes": 3}
            # ...
        ],
        "global": global_stats,
    }

关键字段说明:

  • burst_ratio:当前速率 / 该源近 7 天同时段基线(由本地 SQLite 维护),>5 即强异常信号
  • seen_for_minutes:持续时长,区分"一闪而过"和"持续攻击”
  • avg_pkt_bytes:小包多 → 可能是 SYN flood / 长连接探测;大包多 → 文件传输 / 爬虫拉资源

3.2 LLM Prompt 设计

你是 VPS 流量安全专家。以下是最近 5 分钟流量特征 JSON:

{features_json}

业务 SLO:正常用户 p99 < 200ms,出口带宽配额 800Mbps,
超过 600Mbps 时优先保障 80/443 端口的长连接业务。

请输出 JSON(不要多余文字):
{
  "anomaly_type": "crawler | retry_storm | ddos | backup_spike | normal_peak | unknown",
  "confidence": 0.0-1.0,
  "evidence": ["burst_ratio=8.2", "avg_pkt=1400", ...],
  "actions": [
    {"tool": "tc", "desc": "对 203.0.113.7 限速 20Mbps",
     "cmd": "tc qdisc add dev eth0 handle 1:0 fq ...",
     "scope": "specific", "revert_after_minutes": 30},
    {"tool": "iptables", "desc": "全局限速 500Mbps 兜底",
     "cmd": "...", "scope": "global", "revert_after_minutes": 60}
  ],
  "safety_check": {"expected_p99_impact": "normal users unaffected",
                   "rollback_trigger": "p99 > 300ms for 3min"}
}

要点:

  • 强制 JSON 输出 + safety_check 字段,让策略生成自带"回滚条件",不靠事后猜测
  • 给 SLO 约束:LLM 生成策略时必须考虑"正常业务优先",避免一刀切
  • scope 区分:specific(只限异常源)和 global(兜底全局),优先用 specific

3.3 策略引擎:LLM 输出 → 可执行命令

LLM 输出的是建议,不是直接可执行命令。策略引擎负责:

def apply_strategy(action, dry_run=True):
    # 1. 语法验证:tc -s qdisc show / iptables -C
    # 2. 幂等检查:同名 qdisc 已存在则先 del 再 add
    # 3. dry-run 模式:只打印不执行(首次部署 / 保守模式必开)
    # 4. 原子化下发:tc 用「先建 mirror + 验证 → 再切换」方式避免瞬间断流
    # 5. 记录操作日志 + 自动 revert 时间戳(cron 或 systemd-timer)

tc 层(精细化整形):

# 基于 fq + ingress 的分类器,对特定源限速
tc qdisc add dev eth0 root handle 1: fq
tc filter add dev eth0 parent 1:0 protocol ip u32 \
  match ip src 203.0.113.7/32 flowid 1:10
tc class add dev eth0 parent 1:0 classid 1:10 htb rate 20mbit

iptables 层(粗粒度兜底):

# 全局限速 500Mbps(hashlimit 按每连接 + 全局双维度)
iptables -t mangle -A FORWARD -m hashlimit \
  --hashlimit-above 500mbit --hashlimit-burst 2mbit -j DROP

为什么两层都要? tc 精细但有单点复杂度,iptables 兜底防"策略没生效"的窗口期。LLM 默认同时下发两层,且 revert_after_minutes 不同(细粒度 30min,兜底 60min),避免误伤长期残留。

3.4 熔断控制器(关键安全机制)

LLM 判断会出错。必须有一个独立于 LLM 的硬规则做兜底:

class Watchdog:
    """策略下发后 5 分钟观察窗口"""
    def observe(self, strategy_id, window_min=5):
        baseline = capture_p99_snapshot()   # 下发前的 p99
        for _ in range(window_min * 10):
            sleep(6)
            if current_p99() > baseline * 1.3:   # 恶化 30% 即回滚
                rollback(strategy_id)
                report_to_telegram(strategy_id, "rollback")
                return
        # 窗口通过,标记为"已验证",可延长 revert 时间

这是不可省略的一环。LLM 可能把正常大促峰值误判为爬虫,如果不熔断,会把真用户一起限掉。

3.5 决策记录与二次审查

每次策略下发后写入本地 SQLite:

CREATE TABLE decisions (
  ts, anomaly_type, confidence, source_ip, cmd,
  dry_run, applied, p99_before, p99_after, rolled_back
);

每天 06:00 跑一次 LLM「自我审查」:拉取过去 24h 所有决策,输出:

  • 误判率(rolled_back / total)
  • 常见误判模式(“把发版流量当爬虫”)
  • 基线更新建议(把新确认的正常峰值模式写入基线)

审查结果推送到 Telegram,同时自动调整下一轮的 prompt 约束。


四、部署流程(VPS 实测)

4C8G VPS(Ubuntu 24.04,出口配额 800Mbps)实测:

# 1. 依赖
sudo apt install -y iproute2 iptables-persistent ipset

# 2. Ollama
docker run -d --name ollama -v ollama:/root/.ollama -p 11434:11434 ollama/ollama
docker exec -it ollama ollama pull qwen2.5:7b

# 3. 部署流量整形守护进程
git clone https://git.example.com/myorg/vps-traffic-shaper.git
cd vps-traffic-shaper
docker compose up -d
# 模块:collector, feature-extractor, llm-strategy-engine,
#       tc-applier, iptables-applier, watchdog, reviewer, telegram-reporter

# 4. 首轮 dry-run
curl -X POST localhost:8080/api/v1/run -d '{"dry_run": true}'
# 查看 /opt/vps-traffic-shaper/decisions.log,确认 LLM 判断合理

# 5. 切到 live 模式(谨慎!)
curl -X POST localhost:8080/api/v1/mode -d '{"mode": "live", "min_confidence": 0.8}'

实测数据(运行 2 周):

指标数值
单次特征提炼< 0.3s
单次 LLM 推理~2.5s(Qwen2.5-7B, 4C8G)
tc 策略下发生效< 1s
2 周内拦截异常事件14 次(爬虫 7、重试风暴 4、DDoS 脉冲 3)
误判回滚2 次(大促峰值误判为爬虫)
出口带宽 P95从 620Mbps 降到 410Mbps
月带宽费用↓ 31%
正常用户 p99未恶化(< 200ms 维持)

成本:Ollama 本地推理,零 API 费用;额外内存占用 ~1.8GB(7B 模型量化 Q4_K_M)。


五、实践建议与避坑

建议

  • 永远先 dry-run 一周:观察 LLM 的判断日志,确认误判率 < 5% 再切 live
  • min_confidence 卡 0.8:低置信度只记录不执行,避免 LLM 过度自信
  • 兜底全局限速常开:iptables 层 500Mbps 兜底始终挂着,LLM 只负责在兜底之内做精细化
  • 基线周更:burst_ratio 的分母(7 天同时段基线)建议每周一凌晨重新校准,避免旧基线失真
  • Telegram 只报"已执行 + 已回滚"两类:正常放行的事件静默,避免告警疲劳

常见坑

坑解法
LLM 生成了带 shell 注入的 cmd策略引擎不直接 exec LLM 字符串,而是解析成结构化参数后再拼命令;cmd 只作为描述参考
tc 策略叠加后行为混乱每次下发前 tc qdisc del dev eth0 root 重建,保证幂等
被限速的正常源误伤且不自知watchdog 必须监控"全局 p99"而非只看异常源;回滚条件要覆盖全局
模型幻觉编造 IP特征 JSON 里的 IP 是唯一可信来源;LLM 输出中出现 JSON 之外的 IP 一律丢弃
配额动态变化(按月弹性)基线模块同时读云厂商配额 API(若可用),动态更新全局限速上限

六、扩展方向

  1. eBPF 深度观测:把采集器从 ip -s link 升级到 eBPF(bcc/bpftrace),拿到 per-packet 的协议类型、TLS SNI、HTTP path 前缀,LLM 判断精度可再提一档
  2. 多 VPS 联动:同域多台 VPS 共享一套 LLM 基线,A 台被爬虫打,B 台自动预置防御
  3. 与 WAF 联动:LLM 识别出的"可疑源"直接推给本地 ModSecurity/Coraza,做应用层封禁
  4. 成本联动:带宽费用接入 FinOps 模块,超预算时 LLM 自动把兜底限速再压一档

结语

流量整形的本质,是在带宽预算内做资源分配决策。固定 QoS 是把决策写死在配置文件里;AI 驱动的动态整形,是把决策权交给一个能"看懂"实时流量、理解业务 SLO、且自带回滚安全网的大脑。

记住三句话:

  • LLM 是建议者,不是执行者——所有策略必须过策略引擎的结构化校验;
  • watchdog 是安全网,不可省略——LLM 会错,回滚必须硬规则;
  • dry-run 先跑一周——误判率不过 5%,不上线。

你的 VPS 带宽,从此不再是"出事才知道"的被动资源,而是有 AI 看着、有策略护着、有回滚兜底的主动防线。


本文所有脚本示例可在仓库 vps-traffic-shaper/ 目录下找到完整实现。

📺 看视频版教程 → DuckDB Lab YouTube

Subscribe for more DuckDB & AI automation tutorials