
1. 先把CMDB和资产台账分清楚很多团队上来就问维易CMDB怎么部署、怎么配采集器我一般会先把这个问题按住你打算拿这个配置管理数据库去解决什么如果答不上来装完也只是多了一个没人看的网页系统。CMDBConfiguration Management Database配置管理数据库真正值钱的地方不在于存了多少台机器而在于它能不能在故障定位、变更评估、自动化执行这三件事上被别人调用。它是底座不是展品。我自己见过太多项目死在这儿上线时轰轰烈烈录了三万条CIConfiguration Item配置项半年后准确率掉到五成以下运维出事还是靠老员工记忆和翻聊天记录。原因不是工具不行而是从第一天起就把它当盘点表来做——只关心有多少台服务器、装在哪个机柜不关心谁依赖谁。资产台账回答的是我有什么CMDB回答的是它跟谁有关系、动了它会怎样。这两个问题的数据模型、更新机制、责任人都完全不一样。这篇内容适合三类人看正在选型或刚接手CMDB的运维负责人、要把CMDB接进监控与自动化流程的一线工程师、以及被要求三个月把CMDB建起来的项目负责人。我会按数据建模、数据来源、治理机制、消费场景、落地节奏这个顺序讲尽量把每一步为什么这么定说清楚能直接抄的部分我会给字段表、判定规则和可跑的脚本片段。1.1 一张表格为什么三年就撑不住了早期用Excel或者共享表格管配置成本几乎为零谁都能改看起来很灵活。问题会在规模过了一个临界点后集中爆发。第一个崩的是唯一性同一台机器网络组记的是IP系统组记的是主机名云平台记的是实例ID三份记录对不上谁也说不清是不是同一台。第二个崩的是关系表格里能写所属系统但写不出这台数据库被哪三个应用连接、连接用的是哪个端口、走的是哪个VIP。第三个崩的是时效机器下线了没人删新机器上线来不及加三个月后这张表就变成了历史文档。更隐蔽的是权限。表格要么全开放要么全锁死前者谁都能改坏后者变成某一个人的私产他一休假整个团队抓瞎。CMDB之所以要用数据库而不是表格核心就是它能做字段级权限、能做变更留痕、能做字段级的数据来源优先级判定。这三件事在表格里靠人肉纪律在系统里靠规则约束。1.2 从消费场景倒推而不是从清单正推我建议的建模顺序是反的先列出谁会来查这个库再决定存什么。常见的消费方大概有这么几类每类的诉求差别很大。消费方典型问题对数据的要求一线值班这个告警的主机属于哪个系统、找谁归属关系准、负责人电话准、响应要快变更评审改这台存储会影响哪些业务依赖关系全、方向正确、深度可查自动化平台这批机器用什么账号、装在哪技术属性全、格式规范、可机器读安全合规哪些系统还没做基线加固状态字段准、可批量筛选成本管理哪些资源闲置可以回收生命周期状态准、利用率有来源把这张表列出来你会发现业务系统这个CI类型必须存在而且它和主机、数据库之间的归属关系是整个库的骨架。很多项目失败就在于只建了服务器这一层业务系统靠一个文本字段填结果永远对不齐。提示建模启动会上把这张消费方清单打印出来贴在墙上每加一个字段都问一句哪个消费方要用它。没人用的字段一律不加这是控制CMDB膨胀最有效的办法。1.3 我的取舍原则宁可少而准不要多而虚CMDB有个很反直觉的规律字段越多准确率越低而且是指数级下降。原因是每个字段都要有人负责维护字段数量一多责任必然稀释。我的做法是给每个CI类型限定字段上限核心类型不超过25个属性非核心类型不超过12个。超出的部分不进CMDB放到专业系统里CMDB只存一个指针。比如主机的磁盘分区明细、进程列表、端口监听详情这些完全交给监控和主机管理工具CMDB只存这台机器有哪几个关键服务端口对外够做防火墙策略评估就行。这个原则听上去是妥协实际上是把有限的维护精力集中到真正影响决策的字段上长期看准确率反而更高。2. 数据模型CI类型、属性和关系怎么落地数据模型是CMDB的地基改起来代价最大所以第一版就要想清楚。我一般把模型拆成三层来看类型层有哪些CI、属性层每个CI记什么、关系层CI之间怎么连。三层里最容易做错的是关系层因为大部分人只想到了属于没想到依赖和冗余。2.1 CI类型清单从十个核心类型起步不要一上来铺几十个类型先覆盖主干。我通常从这十类开始跑顺了再扩。业务系统对外提供服务的逻辑单元是归属关系的顶点应用服务可独立部署的进程单元一个业务系统下可能有多个主机物理机与虚拟机合并为一类用虚拟化类型属性区分容器集群与工作负载集群、命名空间、工作负载三层数据库实例注意是实例不是集群集群用关系表达中间件实例消息队列、缓存、网关等网络设备交换机、路由器、防火墙负载均衡与VIPVIP单独建类方便做入口梳理存储资源存储池与卷机房与机柜位置维度用于空间与供电管理这十类基本能覆盖八成以上的查询需求。像证书、域名、定时任务这类等真有消费方提需求再加别提前囤。2.2 属性字段的四分法同一个CI上的字段来源和维护方式完全不同混在一起管必然乱。我习惯把字段分成四组每组用不同的采集和维护策略。分组包含字段示例主要来源更新方式标识类CI编码、主机名、序列号、IP、云资源ID自动采集全自动人工只读归属类所属业务系统、负责人、责任团队、环境、成本中心组织架构人工确认半自动需审批技术类CPU/内存/磁盘、OS版本、内核版本、端口、部署路径Agent/API采集全自动管理类生命周期状态、上线时间、维保到期、备份策略流程驱动人工流程联动分组的意义在于冲突裁决。当自动采集和人工填写打架时不同分组走不同规则标识类和技术类以自动采集为准人工改不了归属类以人工审批结果为准采集值只做参考管理类以流程单据为准。这条规则写进系统配置里能省掉后面无数的口水仗。2.3 关系类型别只建属于关系建少了拓扑就是断的。我通常至少定义这七种关系并且规定方向方向搞反了影响分析全错。关系名方向语义典型用途belongs_toA→BA属于B主机属于业务系统runs_onA→BA运行在B上应用服务运行在主机上depends_onA→BA依赖B应用依赖数据库connects_toA→BA连接B含端口应用连接数据库实例containsA→BA包含B集群包含工作负载deployed_onA→BA部署于BVIP部署在负载均衡上redundant_withA↔B互为冗余双机互备depends_on和connects_to要分开这是很多人忽略的细节。依赖是逻辑上的没有它我不行连接是物理上的我这条TCP连着它。做变更影响分析用依赖做防火墙策略梳理用连接。混成一个关系两边都不好用。2.4 唯一标识与命名规范去重是CMDB最基础也最容易翻车的能力。我的做法是给每条CI生成一个不可变的CI编码格式为类型码-环境码-流水号比如HOST-PRD-000171。这个编码一旦生成永不修改所有外部系统引用它而不是引用主机名或IP——主机名会改IP会回收编码不会。同时给每个类型定义业务唯一键用于采集入库时做幂等匹配。主机用云资源ID或序列号数据库实例用主机端口实例名VIP用IP端口协议。业务唯一键冲突是重复数据的根源必须在入库层做唯一约束不能靠事后清洗。ALTER TABLE ci_host ADD CONSTRAINT uk_host_biz_key UNIQUE (cloud_resource_id); ALTER TABLE ci_db_instance ADD CONSTRAINT uk_db_biz_key UNIQUE (host_ci_id, port, instance_name);注意不要用主机名做唯一键。改名、大小写、域名后缀不一致随便一个都能造出重复记录。3. 数据从哪来采集、对账与人工兜底的边界CMDB的准确率不是录出来的是对出来的。凡是靠人工录入的库三个月内必然失真。所以第一优先级是把自动采集做扎实人工只处理自动化搞不定的那部分。这里的关键不是采得多而是采进来的数据和人工维护的数据怎么裁决。3.1 采集源分层四路并行各有分工我一般会布四类采集源让它们互相校验而不是互相替代。云平台/虚拟化平台API拿虚拟机、容器集群、存储卷的权威数据标识类字段以它为准主机Agent拿OS版本、内核、CPU内存、监听端口技术类字段以它为准网络扫描拿存活状态、开放端口用于发现漏网设备和验证Agent覆盖流程系统变更单、工单、采购单管生命周期状态和归属审批四路数据进同一个暂存区先做归一化统一主机名大小写、IP格式、时间格式再做匹配按业务唯一键最后做字段级裁决。这个流水线一定要分暂存-匹配-裁决-入库四步不要一步到位直接写主表出了问题根本回溯不了。3.2 冲突裁决给每个字段定优先级裁决规则要写成配置不能写死在代码里。我常用的优先级从高到低大致是流程单据 云平台API Agent采集 网络扫描 人工填写。但这是整体顺序具体到字段要单独定因为不同字段的权威源不一样。字段权威来源说明云资源ID、序列号云平台API唯一性依据不接受其他来源覆盖IP地址云平台API / DHCPAgent上报只做校验冲突时告警主机名云平台API改名需走变更采集值不直接覆盖负责人、责任团队组织架构审批采集值只作参考不覆盖生命周期状态流程单据只在变更/下线流程中变更OS版本、内核版本Agent网络扫描不产生此类数据开放端口Agent / 扫描两者不一致时取并集并标记待核实取并集并标记待核实这条很实用Agent看不到的端口可能是宿主机层面的映射扫描看不到的可能是防火墙拦了。两个都不删标个疑点让人工判断比强行选一个更稳。3.3 人工维护的边界只留三个口子我坚持人工只能在这三个地方介入新建归属关系新系统上线时指定负责人、确认疑点数据采集冲突产生的待核实项、执行生命周期动作上线、闲置、待回收、下线。除此之外一律不允许直接改字段值要改就走变更流程。这个约束听上去很死板但它解决了一个大问题责任可追溯。任何一条数据的变更都有来源、有操作人、有时间出了问题能倒查。反过来说如果允许随便改那准确率就变成了某个人的责任心问题这在团队规模超过十人后必然失效。3.4 入库示例幂等写入与变更留痕下面这段是采集入库的骨架逻辑重点是幂等和留痕不是业务细节你可以按自己平台的SDK改写。import hashlib, json, datetime def build_ci_id(ci_type: str, env: str, biz_key: str) - str: h hashlib.sha1(biz_key.encode(utf-8)).hexdigest()[:8].upper() return f{ci_type}-{env}-{h} def upsert_ci(conn, ci_type, env, biz_key, attrs, source): ci_id build_ci_id(ci_type, env, biz_key) cur conn.cursor() cur.execute(SELECT attrs FROM ci_staging WHERE ci_id%s, (ci_id,)) row cur.fetchone() if row is None: cur.execute( INSERT INTO ci_staging(ci_id, ci_type, env, biz_key, attrs, source, updated_at) VALUES (%s,%s,%s,%s,%s,%s,%s), (ci_id, ci_type, env, biz_key, json.dumps(attrs), source, datetime.datetime.now()) ) else: old row[0] diff {k: (old.get(k), v) for k, v in attrs.items() if old.get(k) ! v} if diff: cur.execute( INSERT INTO ci_change_log(ci_id, source, diff, created_at) VALUES (%s,%s,%s,%s), (ci_id, source, json.dumps(diff, ensure_asciiFalse), datetime.datetime.now()) ) cur.execute( UPDATE ci_staging SET attrs%s, source%s, updated_at%s WHERE ci_id%s, (json.dumps(attrs), source, datetime.datetime.now(), ci_id) ) conn.commit()这段代码有两个我自己很在意的设计点。一是build_ci_id用业务键做哈希保证同一台机器无论从哪个源进来编码都一样天然幂等二是每次写入都把差异记到ci_change_log后面做数据质量分析和追责全靠这张表。差异日志的量会很大建议按月分区保留最近六个月就够了。4. 数据治理怎么让库半年后还能用CMDB项目的真正难点不在建设期在运营期。上线三个月后准确率断崖式下跌是这类系统最常见的死法。原因通常不是技术问题而是没人对数据负责以及错了没有代价。治理机制要解决的就是这两件事。4.1 质量度量四个指标盯住就够了指标不用多多了没人看。我一般固定看四个每周出一次报表推到负责人群里。指标计算口径健康阈值说明完整率必填字段非空的CI数 / 总CI数≥ 95%按CI类型分别统计关联率至少有一条关系的CI数 / 总CI数≥ 90%孤儿CI是重灾区准确率抽检正确的CI数 / 抽检总数≥ 92%每周随机抽50条人工核对时效性最近7天有采集更新的CI数 / 总CI数≥ 85%长期不更新的多为僵尸数据准确率必须靠抽检没法自动算。这个活儿听着笨但效果最好因为抽检结果一发责任人立刻有压力。我一般会做一个简单的抽检页面随机弹出CI让责任人确认对/不对/已下线三十秒能点完一条。4.2 变更闭环把CMDB挂到变更流程上这是我最看重的一条机制。变更单提交时必须选CMDB里的CI变更完成后系统自动触发一次采集把变更前后的差异拉出来对比差异没消除就不能关单。这条规则一旦跑起来数据想不准都难因为每次变更都是一次免费的数据校准。具体做法是变更单里加一个涉及CI字段允许多选变更执行完成后调用采集接口拿最新数据比对如果关键字段IP、端口、部署路径、版本有变化生成一条待确认记录指派给变更执行人确认。这个过程不需要额外的人工成本只是把原来事后没人管的环节变成了关单前必须过的检查点。提示第一版不要追求全自动差异比对容易误判。先做变更后强制跑一次采集差异只告警不拦截跑两个月把规则调准了再上拦截。4.3 责任田划分每个CI都必须有人技术上就一条规则没有负责人的CI不允许入库。负责人是必填字段且在组织架构变更时自动同步。这条规则很硬但它逼着团队在上线时就想清楚归属而不是先录进去以后再说。再往上一层业务系统要有明确的系统责任人和数据责任人这两个角色不一定同一个人。系统责任人管架构和可用性数据责任人管CMDB里这个系统的记录准不准。两个人一起出现在月度报表里压力自然就有了。4.4 定期巡检几条SQL常备着日常巡检我基本靠这几条查询配置成定时任务结果直接进周报。-- 1. 孤儿主机没有任何归属关系 SELECT h.ci_id, h.hostname FROM ci_host h LEFT JOIN ci_relation r ON r.source_id h.ci_id AND r.rel_type belongs_to WHERE r.id IS NULL AND h.lifecycle_state in_use; -- 2. 无主CI负责人为空 SELECT ci_id, ci_type FROM ci_master WHERE owner_id IS NULL OR owner_id ; -- 3. 僵尸CI90天没有采集更新且状态仍为在用 SELECT ci_id, ci_type, last_seen_at FROM ci_master WHERE lifecycle_state in_use AND last_seen_at NOW() - INTERVAL 90 days; -- 4. 疑似重复同名不同ID SELECT hostname, COUNT(*) FROM ci_host WHERE lifecycle_state offline GROUP BY hostname HAVING COUNT(*) 1;第4条特别值得说。主机名重复通常是云上重建时老记录没删导致的这类记录不清理扫描和Agent采集会一直往两条记录上写越写越乱。5. 把CMDB接进运维主流程消费场景落地一个没人调用的CMDB准确率必然衰减因为它不影响任何人的日常工作错了也没人喊。所以要尽早把消费场景接上让CMDB变成别人流程里的必经环节。下面这几个场景是我验证过最容易见效的。5.1 告警降噪与定位加速监控系统发出的告警只带IP或实例ID值班的人拿到手第一件事是查这是哪台机器、属于谁。这个动作如果能从三分钟压到三秒值班体验会完全不同。做法是在告警网关做一次CMDB富化告警进来先查CI把业务系统、负责人、环境、所在机房这些标签补进去再发出去。如果CI查不到就单独打一个未知资产标签这本身就是一个数据漏采信号。def enrich_alert(alert, cmdb_client): ci cmdb_client.get_ci_by_ip(alert[ip]) if not ci: alert[tags][asset_known] false alert[tags][owner] unassigned return alert alert[tags].update({ asset_known: true, ci_id: ci[ci_id], biz_system: ci[biz_system_name], owner: ci[owner], env: ci[env], idc: ci[idc], }) return alert这段代码的价值不在于技术含量而在于它把查CMDB变成了系统的默认动作。跑一个月你会收到一堆asset_knownfalse的告警那就是你的采集盲区清单按着这个清单补采集覆盖率提升非常快。5.2 变更影响分析递归查询下游依赖变更评审会上最常问的一句话是改这个会影响谁。这个问题用CMDB的依赖关系能算出来关键是要限制深度和防环。WITH RECURSIVE impact AS ( SELECT ci_id, ci_name, ci_type, 1 AS depth, ARRAY[ci_id] AS path FROM ci_master WHERE ci_id APP-PRD-000123 UNION ALL SELECT m.ci_id, m.ci_name, m.ci_type, i.depth 1, i.path || m.ci_id FROM ci_relation r JOIN impact i ON r.target_id i.ci_id JOIN ci_master m ON m.ci_id r.source_id WHERE r.rel_type IN (depends_on, connects_to) AND i.depth 5 AND NOT m.ci_id ANY(i.path) ) SELECT ci_id, ci_name, ci_type, MIN(depth) AS depth FROM impact GROUP BY ci_id, ci_name, ci_type ORDER BY depth, ci_type;这里的方向很重要查谁依赖我要从被改对象出发沿depends_on反向找来源。如果关系方向建反了这条查询会返回空而且你不会立刻发现直到某次变更漏掉了下游系统。所以建模阶段就要把方向约定写进文档并在系统里对关系类型做方向校验。深度限制我一般设到5层。超过5层的关系准确率已经很低了返回一堆噪声反而干扰判断。真需要更深的说明依赖关系本身没梳理清楚该去做专项治理。5.3 自动化执行从CMDB取参数而不是硬编码批量运维脚本里硬编码IP列表是最常见的隐患。机器一下线脚本还照跑报一堆错还得人工排查。改成从CMDB按标签取目标这个问题基本消失。def get_targets(cmdb_client, biz_system, env, role): return cmdb_client.query( ci_typehost, filters{ biz_system: biz_system, env: env, app_role: role, lifecycle_state: in_use, }, fields[ci_id, ip, hostname, ssh_port, os_family], )注意lifecycle_state这个过滤条件它保证已下线和待回收的机器不会被选中。这个字段的价值就在这里如果状态字段不准自动化就成了风险源。所以我一直强调生命周期状态必须由流程驱动不能人工随手改。5.4 容量与生命周期让闲置资源自己浮出来资源回收这件事靠人主动申报基本收不回来。用CMDB配合监控数据能自动出清单状态为在用但连续30天CPU和连接数极低的主机状态为在用但90天没有采集更新的实例维保到期前60天的设备。这几张清单每月推一次回收率通常能有两位数百分比。需要说明的是利用率数据不在CMDB里要从监控系统取。CMDB只提供资产清单归属关系监控提供使用情况两边按CI编码关联。这也是为什么前面强调CI编码要稳定——它是所有系统之间的粘合剂。5.5 API设计给别人用的是契约不是接口CMDB对外的API要当成产品来做因为调用方很多改一次接口影响的系统一大片。我的经验是至少定义四个稳定接口按条件查询CI列表、按CI编码取详情、按CI编码查关系、按业务键做幂等写入。分页和字段裁剪一定要做。查询接口默认只返回10个核心字段需要更多字段显式声明否则一个业务系统有五百台机器一次查询能把调用方内存打爆。这个坑我踩过后来强制加上fields参数才好。6. 落地节奏与常见问题排查CMDB这类项目节奏比技术重要。想一次建全的项目我基本没见过成功的能跑起来的都是分期推进、每期都有可见产出。下面这个三段式节奏是我在几个规模不同的环境里都用过的。6.1 三段式推进路线阶段周期目标关键产出第一阶段0-3个月主干类型建模云与主机自动采集主机、业务系统、数据库三类覆盖率≥90%第二阶段3-6个月关系补全变更闭环告警富化关联率≥85%变更后自动校验上线第三阶段6-12个月影响分析自动化取数质量报表变更评审用CMDB出影响清单第一阶段千万不要贪。我见过一个项目一开始就要建三十个CI类型结果三个月过去一个都没做准。先把主机和业务系统这两层打通让告警富化能跑起来团队看到效果后面推进阻力小得多。第二阶段的重心是关系。这一步的工作量比想象中大因为很多依赖关系在系统里根本没有记录需要靠网络流量分析、配置文件解析、人工访谈三条路一起补。我的做法是先补关键路径上的关系也就是核心业务系统到数据库、到中间件这一段边缘系统的关系可以后面慢慢来。第三阶段才开始谈自动化取数。这一步的前提是准确率已经稳定在90%以上否则自动化就是在放风险。6.2 常见问题速查表现象大概率原因处理思路记录数持续上涨但实际机器没变多唯一键选错重建机器生成新记录改用云资源ID或序列号做唯一键清理历史重复关联率上不去大量孤儿CI归属关系靠人工填没人填上线时强制指定业务系统纳入上线检查项告警富化经常查不到采集频率太低或IP复用缩短采集周期到15分钟IP回收时同步更新状态影响分析结果为空关系方向建反或关系类型缺失校验关系方向补 depends_on 关系负责人字段大量为空组织架构变更未同步接组织架构接口每日同步并告警缺失数据被人工改乱未做字段级权限标识类和技术类字段设为采集专写人工只读变更后数据不准没有变更闭环变更关单前强制触发采集比对6.3 我踩过的几个坑第一个坑是执念于全量准确。早期我追求每条CI的每个字段都要对结果团队把大量时间花在补一些根本没人查的字段上。后来改了策略核心字段标识、归属、生命周期要求接近100%技术类字段允许有缺因为技术字段缺失时工程师会自己去机器上查成本很低归属字段缺失时他连找谁都不知道成本极高。这个权重差别想明白精力分配就清楚了。第二个坑是把CMDB做成了IT部门的内部系统。业务方不知道自己有个系统在这个库里出了事还是各找各的。后来我们改成每个季度把业务系统的CI清单和负责人推给业务方确认确认过程本身就是在建立共识。这一步不做CMDB永远只是运维的自娱自乐。第三个坑是采集频率定得太激进。一开始设成5分钟一次全量采集结果数据库压力很大采集任务还经常跑不完。后来改成增量分层主机状态15分钟一次技术属性每天一次关系每天一次。采集压力降下来之后反而更稳定了。第四个坑是忽略了下线清理。机器下线了CI记录还在在用状态自动化脚本会一直把它当目标。后来加了一条规则连续三次采集不到且状态为在用自动转为待确认由责任人确认下线。这个自动降级机制上线后僵尸数据少了七成。6.4 判据什么时候算真的建成了我不看CI总数也不看覆盖率报表。我的判断标准只有一条别的团队在遇到问题时会主动打开CMDB查而不是先来问你。达到这个状态说明数据可信、查询够快、入口够明显剩下的都是持续优化的事。具体到可量化的信号大概是这三个变更评审会上有人主动拿CMDB的影响清单说话值班同事处理告警时会看富化出来的业务系统标签自动化平台的取数配置里能看到CMDB的查询接口。这三个信号同时出现项目基本就站住了。我自己在这个方向上折腾下来最深的体会是CMDB的成败八成在建模和治理规则上两成才在工具本身。工具能提供采集、存储、查询、权限这些能力但哪些字段必须有负责人采集值和人工值谁说了算变更后要不要强制比对这些决定都得靠人拍板而且要写进系统配置里变成硬约束不能停在文档上。规则软了数据必然软这是绕不过去的。