0%

新产品线跑了一段时间了,这两天团队复盘了一哈,其中谈到了产品和研发沟通问题。

简单记录一下

梳理了这么一个公式
  • 我做了xx设计(实现)

    1.xx
    2.xx
    3.xx

  • 能达到xx效果

    1.xx
    2.xx
    3.xx


    point来了,我分割一下

  • 它的限制是什么

    1.xx
    2.xx
    3.xx

    导致达不到xx效果
    1.xx
    2.xx

切记

所以说做东西别上来先傲娇吧啦吧啦说一通,报喜不报忧,后面只会任由PO摩擦。

背景

因为团队内部经常会有问题复盘以及技术故事讨论等活动,怎么让这个讨论是有营养且找到根因,保证最终能落到具体的行动项上面。我觉得这是一门很大的学问。

不信你留心观察你参加相关会议或者讨论,你会发现弄了半天问题好像解了又好像没解,过程可能还会伴有撕逼和甩锅…,有句古语说得好:我在旁边坐,锅从天上来。

那咋聊呢

Point 1 描述问题

描述问题,切忌采用下结论的思维和话语,比如:我认为、我觉得…。

我们是为了解问题的,所以你只需要言简意赅的把问题说清楚,这个时候你是一个莫得感情的机器。

怎么言简意赅呢?

站在第三视角陈述:只说现象不说结论,时间、环境、人物、操作过程、发生的现象、造成什么影响…。

如果有必要还可以进行一个动作:过程还原

直接叙述工作过程,有问题的环节或阶段,什么人,做了什么事,当时是怎么考虑的,在这个动作后结果是什么。

Point 2 根因分析

描述之后这个时候可以说说自己的看法了:描述最终定位到的直接原因是什么

有个模板可参考

1.技术根因分析

  • 引入环节:

    产品设计是否有问题?

    需求分析是否有问题?

    设计环节是否有问题?

    代码编写是否有问题?

    其他?

  • 流出环节:

    各评审环节是否有遗漏?

    是否进行研发自测?

    测试场景、测试用例是否覆盖全?

    是否进行了系统测试?

    其他?

  • 确定关键根因是什么:

    如果有多个根因在逻辑层次上相同,则取关键的原因,根因应该是具体的、客观的、在目前组织能力下可被改进的。

2.管理根因分析

  • 流程/制度原因:

  • 组织因素:

  • 执行原因:

    【帮助】流程/制度方面:考虑组织管理上是否有合适的流程、指导书、管理Checklist;

    组织因素方面:考虑人员分配、个人技能、培训、组织环境等原因;

    执行方面:考虑计划、监控、沟通方面的原因。

这个活动一定要有被随便蹂躏的那种奔放和豁达!!!

Point 3 纠正、预防措施

分析完了之后,一定要有落地的行动项。

根本原因 措施类型 措施内容 责任人 预定完成日期
技术根因: 例如,XX特性,在大规格、灵活配置等方面需求设计不充分 纠正措施 例如:对XX特性组织进行重新设计,刷新XX方案 Jack Ma 2020/11/1
预防措施 例如:更新××技术规范、工具、checklist等等 Pony Ma 2020/11/1

背景

前前后后经历了几个公司敏捷的做法,有些些感悟,在此表一表。

开始

首先咱们说一下为啥需要敏捷,因为敏捷的业务目标是更早的交付价值以及更灵活的应对变化。我认为这是之所以那么多公司或者团队想要实践敏捷的最大原因之一。

让我知道敏捷的公司,那是在14年我进了一家主打3D数字建模的公司,我的直接领导是公司敏捷的推崇者,当时两个研发团队也是按照敏捷走的,但是敏捷到底是个啥?当时老大给了我一本书让我自己去悟。书名好像是《Scrum敏捷软件开发》。之后大体有了个了解,至少对站会、用户故事、backlog、sprint、SM、PO等词有个概念。

那时候觉得挺新鲜因为在此之前我只知道瀑布模式。在这家公司时我对敏捷印象是最深的,因为我经历了新旧之间的碰撞,不仅是思想上的还有实操上的。因为公司还有个管评审的年长型主管A大,他刚好更偏向于瀑布模式。

正文

chapter 1 :

先交代一些往事,当时的我,迷茫的一逼,感觉写代码不适合我,我想要转产品,当然我说的产品不是UE性质那种产品哈,我说的是正儿八经的那种产品经理,“那种”指的是:从0到1打造适合用户且用户要买单的那种产品的产品经理。

刚好我的leader就有个类似经历的人,他曾经因为在上海的一个研究所交付的产品令客户不满意,他去呆了3个月,硬生生从0到1重新根据需求设计产品,最终成功交付,当时研究所的人对他的评价是,他比我更懂我得工作。不然为啥他被现在的公司拉去当了副总?你品一品他当时只是一个研发岗。

我虽然面试的是开发岗,刚好当时负责面试的人有事出去了,他刚好遇到了就说先跟他聊一聊,当我告诉他有意向走产品的时候,由于聊的很好,他果断把我留下来当他的助手了。所以我入职是没有经过技术面试的…。

入职的title是产品助理,前期主要干的事就是看书,用Latex写公司宣传文档和摸鱼,后期就让我跟项目,主要是做项目需求。

由于我们的客户多是各种工厂所以通常位置都比较偏僻,想去泡个脚都要摩托转公交再转出租的节奏。

犹记得当时把我一个人丢到工厂的时候,那种无助…工厂里的人通常都比较务实比较直接。被怼的体无完肤到和颜悦色,中间也就是几顿酒的事….故事太多以后写小说再用。

说这些是怎么个意思呢?其实没啥意思,就是说说一些背景,任何事物都是有联系的,你再细品。

chapter 2:

接上,在工厂第一次差不多进一个月,工作内容就是梳理各个环节各个干系人的需求,并整理成文档。

感觉终于告一段落之后,速度赶回北京。然后就是评审,这个时候问题就来了,我老大觉得现有的需求可以开始准备进入研发了,遇到需求问题再澄清就行,但是评审主管就不干了,说你这需求文档需要细化的地方还有挺多,不能进入研发,所以评审没过。会后两位大佬分别拉我谈了一次,一个觉得需求不够完整(瀑布),一个觉得需求已经够了(敏捷)。那为什么双方没有达成一致呢,在我看来当时我老大推崇的敏捷没有玩好,比如A提的一点,研发是敏捷了节奏都挺快,交付周期也缩短了,但是交付结果不太好,老有返工,导致一个项目迟迟不能最终验收,这就是A不赞同的根本原因,因为连客户想要什么都没有完全搞清楚。

对应到敏捷里的内容就是,在检视和调整两块没有足够重视。也就是下面的几个会议没有开好:

  1. Sprint 计划会议
  2. 每日 Scrum 站会
  3. Sprint 评审会议
  4. Sprint 回顾会议

我理解以上四个会议就是每个迭代里防止走偏的措施,但最后还是走偏了。

chapter 3:

现公司一进来就是敏捷,但是大多数时间也是走的不顺,有段时间还专门找了教练带着跑了几个月,效果也是一般般。

当时我想了想可能原因有下面几方面:

  1. 公司是家传统意义上的软件公司,前面都是瀑布模式,虽然是新产品线但是也有很多老员工,所以不管从公司还是从人来说,这个转变是需要时间且需要强有力的刺激,很明显是时间给够了但是刺激不够。我们都知道有刺激才会有反应。

  2. 大家对敏捷的理解参差不齐,很多朋友是随波逐流,且最致命的是这类朋友基本上都认为自己是懂敏捷的,但是真的懂吗,大概率不是的或者只停留在字面上的理解(我其实也是一知半解)。

    敏捷应用其实在我看来是一系列的动作的组合,该套组合需是不断优化且需要组织严格遵守的,组合好之后,把该组合套在软件的开发周期里,然后一遍一遍的重复重复再重复。

    另外很多人的误解是,敏捷就不用写任何文档了,在我看来不是的,必要的文档还是需要的比如设计文档。

前段时间公司大佬给够了刺激,比如重新梳理组织架构、丰富了工具、完善了开发/评审流程(必要的设计文档等)、强制执行双周迭代、双周演示会等等,我的感受是团队的流转效率是有提高的。

小结

我理解的敏捷其实是门大学问,不是只有简单的一些关键词,比如还有“以赋能和信任个人为中心的文化”这类的理念内容,持续学习中。

所以其实我没啥发言权,不过随便说说,也没谁管得着,反正是我自己的博客….。

从我的经历来看,敏捷是个好东西,但是落地也确实不容易,除了敏捷本身就代表一种变革(动作大)以外,它本身也涉及到很多关于人的因素,比如人的思维、习惯、团队文化等,但是如果因为对敏捷应用的不成功,就觉得敏捷不好用,并怀疑它的价值,不可取哈。

另外敏捷只是解决问题的一种方式,也不要把它捧得太高。

任何方法或者工具都有它所适用的上下文。

我认为敏捷是解决问题的一种方式,技术越来越多成为变现的一种手段。那当然你会问谁先行呢?思维观念要变,同时也需要给搭配上不同的手段,谁先谁后并不是主要矛盾。— DXcon

敏捷的价值观

摘自<说透敏捷>

  1. 个体和交互胜过过程和工具。
  2. 可以工作的软件胜过面面俱到的文档。
  3. 客户合作胜过合同谈判。
  4. 响应变化胜过遵循计划。
  5. 虽然右项有价值,但我们更重视左项

一个公司或者团队要应用敏捷,不代表要全套接收,有可能只需要敏捷里的某块就可以,不信谣不传谣。

真正的敏捷应该是Be Agile而不是Do Agile。在我们强调/争执xx方法论,xx实践的时候,我们往往在做敏捷,而不是真正的敏捷了。

多说一句

团队,除了学会传统”规划、组织、领导、控制 “的管理方式之外,还需要”激励、授权、支持、交流 “,这实际上意味着,领导者(特别是CEO)把资源分配采取分布式,而不是集中制(集中在自己手里)。这对团队/组织/公司,在应对复杂、即时、阶层式问题时的灵活度、适应度增加很多。

本文引用的内容,如有侵权请联系我删除,给您带来的不便我很抱歉。

背景

干研发的有个高频词语:抽象,这个词语可应用于各种场景,我今天聊的是代码抽象,在此篇就比较low逼的理解成代码复用吧,不然感觉有点虚。

为啥记录这个呢还是源于近段时间遇到的一些矛盾,重复代码该不该都抽出来,在这之前我会毫不犹豫的说应该,包括现在团队里也几乎是这样的声音,但是是不是就一定对呢?现在我觉得这个观点是不对的,因为我发现有些代码抽出来之后反倒变得越来越不可掌控。

所以我在思考克制抽象是不是也应该提出来。为了验证这个思考,遂搜了搜,别说还真有那么些大佬早就提出了这个观点。

AHA

AHA (读作”Aha!” ):Avoid Hasty Abstractions(避免草率的抽象)

读了几篇文章特别感动,尤其是Sandi Metz的 The Wrong Abstraction,特别有共鸣。

核心观点就是

宁愿复制而不是错误的抽象

具体的支撑克制抽象内容,这几篇文章说的很清楚了,我就不再来一遍了。

我就给个现实的例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/**
* 获取某个CI模型数据 v1.0
* @param {object} code 模型code
* @returns {object} 格式化之后的模型对象
*/
export const getCi = async (code) => {
const meta = await request.post('/api/v1/model/ci/getCi', { code });
// 绑定数据字典
meta.attributes = meta.attributes.map((item) => {
const attr = { ...item };
if (attr.changeValue === 'dict') {
if (!DICT.get(attr.code)) {
rlog.error(`找不到 ${attr.code} 对应的数据字典`);
} else {
attr.dict = DICT.get(attr.code).items;
}
}
return attr;
});
return meta;
};

/**
* 获取某个CI模型数据 v2.0
* @param {object} code 模型code
* @param {boolean} userVisibleFilter 是否按照模型的userVisible过滤,发现页面不过滤
* @returns {object} 格式化之后的模型对象
*/
export const getCi = async (code, userVisibleFilter = true) => {
const meta = await request.post('/api/v1/model/ci/getCi', { code });
// 获取过滤userVisible=true的属性(用户可见)
const { attributes } = meta;
const visibleAttributes = userVisibleFilter
? attributes.filter((item) => item.userVisible)
: attributes;
// 属性绑定数据字典
meta.attributes = visibleAttributes.map((item) => {
const attr = { ...item };
if (attr.changeValue === 'dict') {
if (!DICT.get(attr.code)) {
console.error(`找不到 ${attr.code} 对应的数据字典`);
} else {
attr.dict = DICT.get(attr.code).items;
}
}
return attr;
});
return meta;
};


/**
*
* 获取某个CI模型数据
* @param {object} code 模型code
* @param {boolean} userVisibleFilter 是否按照模型的userVisible过滤,发现页面不过滤
* @param {boolean} dict 是否需要绑定数据字典
* @returns {object} 格式化之后的模型对象
*/
export const getCi = async (code, userVisibleFilter = true, dict = true) => {
const { meta, visibleAttributes } = await formatMeta(code, userVisibleFilter);

if (!dict) {
meta.attributes = visibleAttributes;
return meta;
}

// 属性绑定数据字典
return bindDict(meta, visibleAttributes);
};

/**
*
* 获取某个CI模型数据 v3.0
* @param {object} code 模型code
* @param {boolean} userVisibleFilter 是否按照模型的userVisible过滤,发现页面不过滤
* @param {boolean} dict 是否需要绑定数据字典
* @returns {object} 格式化之后的模型对象
*/
export const getCi = async (code, userVisibleFilter = true, dict = true) => {
const meta = await formatVisibleAttributes(code, userVisibleFilter);
if (!dict) {
// 属性绑定数据字典
return bindDict(meta);
}
return meta;
};

/**
*
* 获取某个CI模型数据 v4.0
* @param {object} code 模型code
* @param {boolean} userVisibleFilter 是否按照模型的userVisible过滤,发现页面不过滤
* @param {boolean} dict 是否需要绑定数据字典
*/
export const getCi = async (code, userVisibleFilter = true, dict = true) => {
const meta = await formatVisibleAttributes(code, userVisibleFilter);
// 属性绑定数据字典
if (dict) {
try {
// 用户自建属性(数据字典)
const userDict = await getUserDicts(code);
return bindDict(meta, userDict);
} catch (error) {
rlog.error(error);
return meta;
}
}
return meta;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 过滤可见属性
* @param {string} code
* @param {boolean} userVisibleFilter
* @returns {object} 只包含可见属性的模型对象
*/
async function formatVisibleAttributes(code, userVisibleFilter) {
const meta = await request.post('/api/v1/model/getCi', { code });
if (meta) {
let { attributes } = meta;
if (!attributes) {
return meta;
}
// 适配后端,使属性正序
attributes = attributes.reverse();
// 获取过滤userVisible=true的属性(用户可见)
if (userVisibleFilter) {
meta.attributes = attributes.filter(
(item) => item.userVisible === 'true'
);
}
return meta;
}
return meta;
}
如上所示

总共经历了至少4次的改动,逻辑变得越来越复杂,因为需要适配多种场景,本来我一开始抽出来,理由很简单,因为该api是一个获取底层数据的api,大多数前端的功能都需要调用该api,且都是需要有数据字典的,因为要正确的展示数据,所以我抽了一个方法。

这个时候还是很美好的,不过后续就像 The Wrong Abstraction里写的一样,各个使用方或找我或自己对该方法进行了扩展,这方法那是叫惨不忍睹啊,就这还是我重构之后的样子,没重构之前更丑。

那有人就问了,为什么就扩展了呢?

  1. 个人风格问题,该方法之前满足我得需求现在不满足了,所以我要改它,这样最简单,我可不管其它模块需不需要这个逻辑。
  2. 我也知道可能在上面加不太合适,因为加的扩展逻辑不是所有模块都需要的,但是也不是我一个人需要的,比如A、B、C、D…,A、B都需要,那为了不重复写代码,在原有方法上扩展我觉得也还行。

后来当我发现的时候,我就在群里发出了一个共识。

  1. 这类公共的api原则上不加个性化的扩展但是可加通用性(不影响整体数据结构且没有业务逻辑,比如:对原始数据进行数据筛选(eg:可见、不可见))的扩展,且加的时候需要与该api的最初作者对齐。
  2. 如果要扩展个性化,请自行copy一份代码再修改。

我的理由是如果再这么搞那我就不维护了爱咋咋滴………………..当然前面是意淫的咱们是一个team,和为贵。

真正的理由是维护成本会越来越高且与当初抽象的意义渐行渐远。

可能我给的例子不够有足够力量的说服力,但是我还是觉得,抽象不一定就一定时好的必须的,有些时候我们得反过来想想,任何事情都有两面性。虽然咱没有能力提出牛逼得理论和观点,但是我们可以基于大佬们提出得理论和观点,做些反思、验证…。

有句话不是说吗:站在巨人的肩膀上。这句话我理解不是说巨人的肩膀才稳,而是说能看得更远。

You Know

Duplication is far cheaper than the wrong abstraction

本文引用的内容,如有侵权请联系我删除,给您带来的不便我很抱歉。

// eslint-disable-next-line no-prototype-builtins

​ if (!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) return 0;

no-prototype-builtins - Rules - ESLint - Pluggable JavaScript linter

Rule Details

This rule disallows calling some Object.prototype methods directly on object instances.

Examples of incorrect code for this rule:

1
2
3
4
5
6
7
/*eslint no-prototype-builtins: "error"*/

var hasBarProperty = foo.hasOwnProperty("bar");

var isPrototypeOfBar = foo.isPrototypeOf(bar);

var barIsEnumerable = foo.propertyIsEnumerable("bar");

Examples of correct code for this rule:

1
2
3
4
5
6
7
/*eslint no-prototype-builtins: "error"*/

var hasBarProperty = Object.prototype.hasOwnProperty.call(foo, "bar");

var isPrototypeOfBar = Object.prototype.isPrototypeOf.call(foo, bar);

var barIsEnumerable = {}.propertyIsEnumerable.call(foo, "bar");

背景

由于事业部专门成立团队做部署平台,由于各种因素迟迟没有上线,以前的Jenkins也不能用了,但是前后端需要有环境进行联调,后端还好说每个模块本地起个服务就行,前端就尴尬了本地起服务限制太多,所以需要想办法搞个环境,遂想着悄悄咪咪(深藏功与名…)搞了个环境让大家能先跑起来不至于耽误工期。

满足以下要求:

  1. 前端更新简单
  2. 无脑一条命令搞定start\stop\restart
  3. 于生成环境尽量贴合

实操

因为生成环境是基于docker做部署,所以先弄个nginx的镜像。nginx的镜像茫茫多,不过大体都很全面,我觉得太重,所以就弄了个最简单的。

docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
version: '3.1'
web:
my-nginx:
container_name: new-monitor-nginx
image: nginx
restart: always
logging:
driver: "journald"
volumes:
- /home/xxx/mount/nginx.conf:/etc/nginx/nginx.conf
- /home/xxx/mount/html:/etc/nginx/html

ports:
- "443:443"
network_mode: "bridge"
environment:
- TZ=Asia/Shanghai
ulimits:
core: 0

Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 用官方的nginx镜像
FROM nginx
# ENTRYPOINT 类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,用于传参。
# eg:docker run nginx:one -c /etc/nginx/new.conf,容器内会运行nginx -c /etc/nginx/new.conf
ENTRYPOINT ["nginx", "-c"]

#COPY 复制,从上下文目录中复制文件或者目录到容器里指定路径。
COPY nginx.conf /etc/nginx/nginx.conf
COPY html /etc/nginx/html

# RUN,在 docker build时作用
RUN rm /etc/nginx/nginx.conf /etc/nginx/conf.d/default.conf

# EXPOSE 声明端口,镜像服务的守护端口,以方便配置映射。
EXPOSE 8088


# VOLUME 用于挂载数据卷
VOLUME /var/log/nginx/log

#CMD 类似于 RUN 指令,在docker run 时运行。
CMD ["nginx", "-g", "daemon off;"]

构建、启动容器

1
2
3
4
docker build -t xxx
docker-compose down
sleep 10
docker-compose up -d

后续直接

1
docker restart 容器名

后续

其实最终我又把docker干掉了….因为有同事悄悄咪咪手动装了nginx,优化了一下把nginx挂到了系统服务上,同样的也是直接systemctl xxx就行了。

反正结果是好的,结合alibaba toolkit也是玩的飞快了。

干研发的不就得折腾吗,对吧。

背景

最近新产品线,因为架构调整,增加了一个node应用,关于node混淆部署了解了一下,保护自主知识产权嘛。

在这儿简单做个记录。

正文

https://sailsjs.com/

安装 forever:sudo npm install -g forever
更多关于 forever 的资讯:https://github.com/nodejitsu/forever
或安装 PM2:sudo npm install pm2 -g –unsafe-perm
更多关于 PM2 的资讯:https://github.com/Unitech/pm2

https://github.com/Unitech/pm2

https://github.com/vercel/ncc

背景

前面提到过我们要从头做一个产品,暂定代号”新监控“,现阶段是进行概设、预研等阶段,因为涉及到很多模块做概设,业务专家、产品经理、研发、测试等在参与,为了能更有效的产出,所以架构组拎出来了DDD,借用DDD这一套东西期望能让大家跑的更顺一些,当然DDD也仅仅是当成工具来使用,不生搬硬套。

DDD

我知道DDD是,前几年突然就有很多文章提了这个概念挺火的(后面好像有段时间又有很多吐槽DDD的文章),为了了解是个啥玩意,我还还专门买了本书领域驱动设计,说实话没看完,对当年的我来说有点抽象了,因为经历的少了,找不到对应的场景来理解DDD,所以暂时就搁置了,刚好借这个机会,基于实践来学习这玩意,看到底咋样。

先把架构师给的一些资料做个记录

背景

you know年纪大了就想着研究研究自个 儿。

我这人其实工作上是真的从不拖延,正儿八经的,不过生活中就相反了。我就很想找找其中的原因。

读书

遂选了几本书,看我还能不能抢救一下:

  1. 《图解心理学》
  2. 《心理学通识》
  3. 《少有人走的路》
  4. 《拖延心理学》
  5. 《理解人性》

编号就是读的顺序,此篇先占位,待我都读完之后来写写收获。

背景

之前有提过现阶段要把之前的产品推翻重做,做带中台调性的新监控。其中一块核心的内容就是CMDB,虽然CMDB的设计主要是架构师以及后端同学的活,但是架不住我爱掺合啊,这块的前端是我来弄,所以也有正当理由摸鱼撒。所以简单也做个记录,稍微深入的了解下CMDB这玩意,也不能细说一个是我没那么精通另一个我不得保护我们的产品知识产权吗不然老板不把我弄死啊…跑题了。

开始

CMDB刚开始接触的时候我把它笼统的理解为资产管理,当然这种理解肯定是不对的,太局限。

因为我们的产品大多数情况下都是部署在客户的内网,CMDB在我们产品中的定位:

  • 运维对象管理系统。
  • 支撑监控能力的基础设施。
  • 以应用为核心对象串联其它资源。

识别运维对象,主要分为两个部分:

基础设施层面:网络设备、服务器、存储设备等;

应用层面:应用元信息、告警配置信息等

当我们识别出运维对象和对象间的关系,并形成了统一的标准之后,接下来要做的事情就是将这些标准固化,固化到信息管理平台中,也就形成了我们说的CMDB(配置管理)。

运维对象识别

思路跟下图很像,从消费场景入手,识别对象以及对象具有的元素应该有哪些。

最终细化一下会识别出几种类型,一个是基础资源对象,一个是应用对象,一个是逻辑对象(组织和人),把这几种类型对象按照相应的规则的建立关系,从而管理属性、关系、状态、场景。

发现

前一小结确定了运维对象识别的核心思想,其中一个大的作用就是指导资源发现,我们第一版的发现的方式包含两种:

  • 网络拓扑发现(自动):通过SNMP扫描网络,发现其中的网络设备,并判定其间的网络连接关系。

  • 指定类型发现(人工或者流程):用户指定资产类型,发现时不需要依据判定规则。

    下图是我傍的一个后端大佬画的,我悄悄盗过来,镇场面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
classDiagram

class 发现规则 {
+ 判定规则
+ 收集规则
}


任务 "1" -- "N" 任务执行
发现代理 -- 发现结果
任务执行 -- 发现代理
任务执行 -- 资产数据
资产数据 -- CMDB

发现规则 -- 发现代理
全局例外 -- 发现代理
SNMP特殊判定 -- 发现规则
任务 "1" -- "N" 连接信息

建立关系

首先明确一点,以应用为中心,从应用的视角去看,从应用的角度构建资源管理的关系(拓扑关系)。

看了几篇相关的文章基本上,关系类型可简化为下面几种:

  • 主从关系。这种关系是一种强父子关系,主不存在了,则从就不存在了。比如主机和网卡、硬盘。
  • 依赖关系。是一种对象属性级之间的关联关系,比如说服务器放在机柜上,机柜摆在某个机房内,这是对象级别的关系。通过对象的属性关联来表达。
  • 连接关系。主机和存储、主机和网络设备的关系,是连接关系。这种关系是动态生成的,是一种实例级的关系。

依赖关系和连接关系有什么不同?

  • 依赖是一对多的关系,并且这个关系是靠人维护的,比如说机柜上放了很多服务器。
  • 连接是多对多关系,并且这个关系是因为某种“连接”产生的,比如说服务器连接了交换机。通常是通过自动发现来实现。

我们产品第一版关系设计跟上诉差不太多,只是叫法有一些区别。

比如主从关系:属于、包含。

依赖关系:运行在

连接关系:连接

由于各种原因,大概就整理这么多。

小结

大体围绕CMDB的设计思路如下图。

设计的时候考虑的要点:

  1. 领导要参与,团队理解要一致。通过场景带入的方式。
  2. 领域分析是核心,除非有必要,不然不考虑技术实现。
    1. 为啥DDD,建立研发、产品等各方的通用语言。
    2. 为啥DDD,DDD是一套完整而系统的设计方法,基于这套方法使面对高复杂度的业务,设计思路能更加清晰,设计过程也能更加规范。
  3. 从应用视角切入,应用是核心。
  4. CMDB最好分为两个维度的内容进行共创:配置管理、资源管理(资源管理 ≠ 资产管理)。
  5. 整理的资源都是服务于各种消费场景的,原则上不应该存在游离态的资源。
  6. CMDB的模型定义一定是有层次的,比如分为核心模型和扩展模型。
    1. 核心模型记录业务、应用和主机,有了核心模型系统基本就能跑起来了。
    2. 扩展模型是依赖核心模型扩展出来的,比如基于主机找到关联的机柜等信息,这块信息是有核心模型驱动逐步完善的。

本文引用的图片,如有侵权请联系我删除。

感谢