资讯动态

从Docker部署到漏报复盘:AI-Infra-Guard技能扫描实践

发布时间:2026/9/29 5:33:56 来源:尧图企业网站定制
前阵子公司AI平台组的GPU服务器从两台扩到十几台模型服务也从一个变成七八个运维靠人肉巡检已经明显忙不过来了。我当时的想法很简单能不能找一个能自动盘点AI基础设施、定期做体检的工具把所有集群节点、推理服务、向量库、依赖库版本一次性扫清楚并且能出带风险等级的报表这就是我接触AI-Infra-Guard的初衷。AI-Infra-Guard本身是个面向AI基础设施的扫描与守护工具核心功能是技能扫描——它不扫漏洞特征库而是对AI底座的能力项做体检比如GPU驱动与CUDA是否匹配、推理框架版本是否合规、模型服务是否真的能响应推理请求、向量数据库连接池是否健康、API密钥权限是否越界等等。部署方式走Docker一键起配置都在一个YAML里拉完镜像起来就能用。这篇就从头到尾讲一遍部署过程再复盘一次让我印象深刻的漏报事故。1. 我为什么需要“技能扫描”而不是继续写Shell脚本1.1 AI基础设施体检和传统安全扫描不是一回事先说说我遇到的具体场景。每周都要维护GPU集群的依赖清单包括NVIDIA驱动、CUDA版本、cuDNN、PyTorch、vLLM、DeepSpeed、向量数据库实例、Ray集群状态等。过去我用Shell脚本加定时任务每个节点放一个脚本结果发现三个问题第一个问题是脚本碎片化。GPU的脚本只查nvidia-smi模型服务的脚本只测TCP端口两个脚本各扫各的没人把它们汇总成一份全局风险列表。出了事你得一个个脚本去看排查链路特别长。第二个问题是覆盖滞后。新增的模型服务不会自动进扫描范围得手动改脚本参数漏掉是常态。尤其现在AI服务迭代这么快今天上一个推理服务明天换一个向量库脚本根本追不上资产变更的速度。第三个问题是判定太浅。很多检查停留在“进程活着”“端口能通”但进程活着不代表功能可用。比如Triton Inference Server的端口能通但模型可能还在加载中这时候接流量必炸再比如Redis容器起来了但主从同步断了表面看端口通、进程在实际已经失去高可用能力。AI-Infra-Guard解决的正是这三个问题。它的技能扫描思路是先把资产发现出来节点、容器、服务再对每个资产生成一份“技能清单”这个节点该具备哪些能力然后逐项探测能力是否真的可用。这跟传统漏洞扫描的“特征库匹配”思路是两条路线前者关注基础设施的底座能力后者关注已知漏洞两者互补但不可互相替代。1.2 为什么最后选了Docker方式部署其实一开始考虑过裸机安装直接pip install一份Python写的Agent。但第一批要管理的节点就有Linux、Windows混合环境还涉及不同的Python版本和依赖冲突。Docker在这个场景下的优势非常明显镜像把Python解释器、依赖库版本、探针脚本全封装好宿主机只需要有Docker引擎和GPU驱动如果是GPU节点。我在云服务器、物理机、GPU卡机上都用同一个镜像跑通了完全不用管宿主机上是Python 3.8还是3.10这是裸机部署比不了的。另外一个关键点是扫描任务需要定时执行和结果持久化数据要落库、出报表。我把PostgreSQL和Redis作为依赖服务一起放进docker-compose里一次起三个容器编排、重启、日志查看都比手动管理进程省心太多。成本也很低一台2C4G的轻量云服务器就能跑管理端扫描任务分发到各节点所在的网络去执行控制面和数据面分离得很干净。1.3 技能扫描的数据模型资产清单与技能基线部署之前有必要理解AI-Infra-Guard内部怎么组织数据因为后面配规则、看漏报都跟这个模型直接相关。工具维护了两类核心数据资产清单扫描代码自动发现的节点、Docker容器、模型服务、数据库实例每条资产有唯一ID。它也可以对接静态文件里的IP列表做补充发现。技能基线每种资产对应一组“技能项”比如GPU节点必须具备“驱动可加载”“CUDA上下文可创建”“显存余量充足”三项模型服务必须具备“HTTP接口可访问”“推理请求200返回”“模型加载完成”三项。扫描引擎做的就是“资产清单 × 技能基线”的匹配逐项执行探针。这一步在配规则的时候怎么强调都不过分漏报往往不是探针写错了而是资产清单漏了或者基线技能项定义得太浅。我在后续复盘漏报时两个坑都踩到了。2. Docker部署实操从准备环境到跑通首次扫描2.1 环境准备与前置检查清单我部署的这台机器是Ubuntu 22.04 LTS内存32GB磁盘200GB没装GPU所以这台只负责调度和扫描GPU节点通过SSH远程探测的方式纳管。先确认Docker环境docker --version docker compose version如果机器上Docker没装好工具起不来后续都是空谈。我建议先跑一遍hello-world确认引擎正常docker run --rm hello-world再确认两个端口没被占用8890Web控制台、8891API。我把整个工具的数据目录规划成这样/opt/ai-infra-guard/ ├── config/ │ ├── guard.yaml # 主配置文件 │ ├── skills/ │ │ ├── gpu-basics.yaml │ │ ├── model-service.yaml │ │ └── vector-db.yaml │ └── assets/ │ └── static-assets.txt ├── data/ # PostgreSQL数据持久化 └── logs/ # 扫描日志、审计日志数据目录必须挂载到宿主机否则容器一重建历史扫描结果就没了这个坑我踩过一次后面详说。2.2 docker-compose配置的三个关键点我用docker-compose管理三个服务ai-infra-guard主程序、PostgreSQL结果库、Redis任务队列。下面是我最终跑通的完整配置version: 3.8 services: postgres: image: postgres:15-alpine container_name: aig-postgres environment: POSTGRES_USER: aig POSTGRES_PASSWORD: change-me POSTGRES_DB: aig volumes: - /opt/ai-infra-guard/data/pg:/var/lib/postgresql/data networks: - aig-net restart: unless-stopped redis: image: redis:7-alpine container_name: aig-redis command: [redis-server, --appendonly, yes] volumes: - /opt/ai-infra-guard/data/redis:/data networks: - aig-net restart: unless-stopped guard: image: ai-infra-guard:2.1.0 container_name: ai-infra-guard ports: - 8890:8890 - 8891:8891 volumes: - /opt/ai-infra-guard/config:/app/config:ro - /opt/ai-infra-guard/logs:/app/logs - /var/run/docker.sock:/var/run/docker.sock:ro environment: DB_HOST: postgres DB_PORT: 5432 DB_USER: aig DB_PASSWORD: change-me DB_NAME: aig REDIS_HOST: redis REDIS_PORT: 6379 SCAN_INTERVAL_MINUTES: 360 ASSET_SCAN_INTERVAL_MINUTES: 1440 SKILL_SCAN_INTERVAL_MINUTES: 60 depends_on: - postgres - redis networks: - aig-net restart: unless-stopped三个关键点第一PostgreSQL密码我用了环境变量注入不要写死在镜像里。生产上建议用Docker Secrets或者至少改一个随机强密码默认密码在公网环境分分钟被扫。第二挂载/var/run/docker.sock是让工具能自动发现宿主机上的Docker容器资产。这个权限比较大如果你对安全要求高可以让工具只走SSH探活不挂sock。我为了省事选择了挂载但网络出口要限制好。第三三个扫描间隔要按场景分开资产发现一天一次就够了新服务上线不会每天发生技能扫描一小时一次资源水位变化敏感而安全基线的技能项可以更频繁。间隔分开的好处是避免无谓的扫描对生产服务造成打扰。2.3 启动、验证与首次扫描启动cd /opt/ai-infra-guard docker compose up -d启动后先看日志确认三个服务都正常docker compose logs -f guard正常情况应该能看到类似这样的输出[INFO] Connected to database: aigpostgres:5432 [INFO] Connected to redis: redis:6379 [INFO] Asset discovery worker started (interval: 1440m) [INFO] Skill scan worker started (interval: 60m) [INFO] REST API listening on 0.0.0.0:8891 [INFO] Web console listening on 0.0.0.0:8890访问 http://宿主机IP:8890首次登录会要求初始化管理员账号。我建议一开始把静态资产清单配好再触发一次手动扫描验证链路。静态资产文件 /opt/ai-infra-guard/config/assets/static-assets.txt 长这样# 每行一个资产格式ip|端口|资产类型 192.168.10.11|22|gpu-node 192.168.10.12|22|gpu-node 192.168.10.20|8000|model-service:vllm 192.168.10.21|19530|vector-db:milvus手动触发全量扫描curl -X POST http://127.0.0.1:8891/api/v1/scans -d {type: full}等待几十秒后查任务状态curl http://127.0.0.1:8891/api/v1/scans/latest这一步能跑通部署就完成了一大半。接下来才是重头戏看它到底扫出了什么。3. 技能扫描规则解析探针设计决定了扫描的深度3.1 四类探针与我的自定义技能清单第一轮测试我只用了内置的默认规则结果发现内置规则偏向于“存在性检查”进程在不在、端口通不通、版本能不能匹配上。这些规则跑起来很快但信息量一般。后面我陆续补了自定义技能项才真正体会到这个工具的价值。AI-Infra-Guard的探针大致分四类列个表格看得更清楚探针类型检测方式典型技能项失败代表什么命令探针远程执行命令SSH/容器execGPU驱动版本、CUDA路径、显存剩余量组件缺失或版本错配HTTP探针发起GET/POST请求检查状态码与响应体模型服务健康、接口延迟、模型是否加载完成服务不可用或功能异常配置探针读取YAML/JSON配置文件校验字段Ray集群Scaling配置、Milvus索引参数、API密钥权限配置不符合基线依赖探针对比版本清单Python包版本、CUDA Toolkit版本、cuDNN版本依赖不满足最低要求举个例子我写的GPU显存水位技能项是这样的skills: - id: gpu-mem-watermark asset_type: gpu-node name: 显存水位检查 probe_type: command command: nvidia-smi --query-gpumemory.used,memory.total --formatcsv,noheader,nounits parse: columns: [memory.used_mb, memory.total_mb] expression: memory.used_mb / memory.total_mb 0.90 severity: warning这个技能项的逻辑是远程在GPU节点上执行nvidia-smi解析出已用显存和总显存比值超过90%就报警。阈值设90%是因为Triton和vLLM这类推理服务往往会预留显存过高的水位会显著增加显存分配失败的概率。低于这个水位一般不用干预超过就得看看是哪个Worker在吃显存了。3.2 资产发现方式动态发现与静态清单配合AI-Infra-Guard支持两种资产发现静态清单和动态发现。动态发现通过挂载的docker.sock枚举宿主机的容器再通过SSH探测远程节点。我混合使用效果如下本机容器docker.sock直接发现包括PostgreSQL、Redis、vLLM容器。远程GPU节点通过静态清单维护IP、SSH端口和密钥文件。模型服务通过HTTP探活识别到8000端口上有vLLM的响应头自动标记为model-service:vllm。有一个细节特别值得注意动态发现的资产必须等下一轮资产扫描我设的24小时才会更新。也就是说你新起一个模型服务最长要等24小时才会出现在扫描范围里。这就是我后来漏报的一个重要伏笔。3.3 报告输出与分级处理扫描完成后结果会写到PostgreSQL同时汇总成页面报表。分级标准按严重程度分为critical、warning、info三级。critical代表服务不可用warning代表存在性能或配置隐患info是不计入风险的信息项。我一般最关注warning以上的项目。第一次全量扫描下来十几台节点扫出了一堆warning大多是“显存水位超过80%”和“模型服务响应时间P99超过500ms”还有一条critical某台GPU节点的GPU ECC错误计数非零。当时没太在意这条critical结果它就是后面那个雷的引子。4. 漏报复盘扫描全绿的那一天生产服务正在“带病运行”4.1 现象全绿报告与生产告警同时出现事情发生在上线AI-Infra-Guard后的第三周。周五中午监控平台的告警响了生产环境里一个vLLM推理服务的成功率从99.9%掉到94%持续了大概十分钟。我打开AI-Infra-Guard看当天凌晨的扫描结果发现对这台GPU节点的技能扫描全部通过模型服务的探针也显示HTTP 200、响应正常。当时的第一反应是工具扫偏了。因为它扫的时点是凌晨而问题是中午出现的一个是历史快照一个是实时异常时间对不上。我承认这一点但这并不能解释“为什么凌晨扫描对显存水位的判断是健康的而中午就爆了”。4.2 排查过程从日志、指标到规则逐层拆我先把vLLM的日志翻出来发现推理进程反复出现NCCL超时相关的日志而且伴随着显存分配失败的错误。手动执行nvidia-smi查了一下发现GPU显存占用已经到97%缓存调度器的显存碎片在持续累积请求结束后没有正常回收。这时候我回头翻AI-Infra-Guard的规则发现内置规则的检查项只有“进程存在版本匹配”这类逻辑压根没有显存水位探针。也就是说AI-Infra-Guard认为“GPU这块没毛病”因为驱动、CUDA、vLLM版本全都在基线里而它没有看显存还剩多少。第二个漏报更隐蔽。生产环境里的模型服务走的是K8s部署对外入口IP是固定的但某次服务重建后入口IP变了我没有同步到静态资产清单里。技能扫描探针对着旧IP发HTTP请求探针“成功”是因为旧IP上还跑着一个忘了下线的旧版本Triton实例端口照常返回200。换句话说探针扫的根本不是生产实例而是一个影子服务。4.3 根因分析两个漏报点叠加复盘下来那次漏报是两层问题叠加第一技能项覆盖不足。内置规则对GPU节点只查“驱动可加载、CUDA版本、推理框架版本”没有覆盖“显存水位”“ECC错误增量”“CUDA上下文可创建”这类功能级指标。探针停留在“存在性”没上升到“可用性”。第二资产清单漂移。动态发现的周期是24小时静态清单又是手工维护的模型服务入口IP的变化发生在两次资产扫描之间等于扫描范围里根本没有生产实例。这两个漏报点单独看都不是大问题叠加起来就很危险扫描报告显示全绿实际上生产服务正在带病运行。4.4 修复与规则补强修复分三步走。第一步给GPU节点技能项补充两条功能级探针skills: - id: gpu-memory-watermark asset_type: gpu-node name: 显存水位检查 probe_type: command command: nvidia-smi --query-gpumemory.used,memory.total --formatcsv,noheader,nounits parse: columns: [memory.used_mb, memory.total_mb] expression: memory.used_mb / memory.total_mb 0.90 severity: warning - id: gpu-cuda-context asset_type: gpu-node name: CUDA上下文创建检测 probe_type: command command: python3 -c \import torch; assert torch.cuda.is_available(); torch.zeros(1, devicecuda)\ severity: critical显存水位是warning级别因为高水位未必立刻出问题CUDA上下文创建失败直接是critical因为这意味着GPU对上层服务来说已经不可用了。我还加了一条ECC错误增量统计用两次采集的nvidia-smi输出算差值增量超过阈值就告警。第二步把资产发现的间隔从24小时改小并且允许扫描前动态刷新资产列表。配置里把ASSET_SCAN_INTERVAL_MINUTES改成360同时在guard.yaml里打开dynamic_refresh开关。第三步给模型服务的探针从“TCP端口可达”升级到“HTTP推理请求可用”。新技能项会发起一个最小推理请求比如输入一个简单请求要求服务返回正常响应、状态码200且延迟低于阈值skills: - id: model-infer-available asset_type: model-service name: 推理可用性检测 probe_type: http method: POST path: /v1/chat/completions body: {model: default, messages: [{role: user, content: ping}]} expect_status: 200 max_latency_ms: 2000 severity: critical这样改造之后再遇到“进程活着但功能已废”的情况扫描就能抓得住。5. 避坑清单与实操心得5.1 部署阶段最常踩的五个坑写这篇复盘之前我特意把部署和第一次跑通扫描过程中遇到的坑整理了一下五个最常见数据目录没挂载好容器重建后历史扫描结果全丢。这个真踩过重启一下容器上个月的报表全没了当时心态是崩的。建目录时就把三个volumes全部规划好不要图省事。docker-compose里忘记给PostgreSQL配volume导致同样的问题。建议部署前用docker volume ls确认一下持久化卷都创建成功。宿主机Docker版本太旧compose v2语法不识别。Ubuntu 22.04自带的Docker如果太久不升级可能报compose语法错误。部署前先把docker和docker compose plugin升到最新稳定版。挂载docker.sock后工具虽然能发现容器但探针有触碰宿主机Docker API的能力。生产上要限制工具服务容器的权限至少不要给privileged。扫描SSH远程节点时用密码而不是密钥导致定时任务挂在交互式输入上。远程探活一定要用SSH密钥而且要用专用账号不要用root直接登录。5.2 探针开发的三个原则经历了漏报复盘之后我给自己定下了探针开发的三条原则现在写规则几乎不踩坑第一探针必须能区分“存在”和“可用”。进程在、端口通、版本匹配都只是存在不代表可用。判断可用要做功能级验证哪怕是发一个最小推理请求。第二规则不要一次写太复杂。先写一条能跑的最小探针确认能正常采集再叠加解析逻辑和告警阈值。否则排查问题的时候你分不清是探针写错了还是资产真有问题。第三阈值要和业务侧对齐。显存水位80%还是90%才算告警不能拍脑袋定要跟算法团队沟通。他们知道模型在什么水位会OOM知道了再定阈值告警才有意义。5.3 扫描可信度评估三条自检问题最后分享一个自检方法每次看扫描报告之前先问自己三个问题第一资产清单是最新的吗有没有新增、下线或变更IP的服务被漏掉 第二技能项覆盖的是“存在性”还是“可用性”有没有哪个关键能力只测了端口没测功能 第三上一次漏报的复盘结论有没有真正落成新的探针或规则这三个问题都回答“是”这份扫描报告才值得信赖。否则你看到的全绿跟我那次一样只是“看起来安全”。我个人在实际操作中的体会是AI基础设施的巡检最大的敌人不是扫描工具的缺陷而是运维侧对扫描结果的盲目信任。工具能帮你把重复劳动自动化但资产清单的维护、技能项的持续补充、阈值和业务的对齐这些活儿永远得有人盯。现在我每周都会看一眼AI-Infra-Guard的规则列表像维护代码一样维护它新增服务上线那天顺手把资产清单和技能基线更新掉。这条路没有终点但至少比人肉巡检睡得安稳。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑