Service mesh 服务网格 | 雷达哔哔哔

位置

2018年11月第19期技术雷达,技术象限,建议评估。(最新版技术雷达已经发布,点击这里下载

标签

Microservices,linkerd,Istio

目标受众

系统架构师、开发人员

关注问题

目前的微服务架构大多基于类似于Spring Cloud全家桶的框架构建,尽管这样可以基本满足构建微服务系统架构在技术上的一些基础需求,例如常见的服务发现、配置管理、熔断、跟踪,安全等。但是也同样也带来了一些限制和成本,例如对于代码的侵入性较强、编程语言绑定、学习成本高等。

解决方案

将解决分布式架构下安全、熔断、限流、降级、服务发现、调用链分布式跟踪等功能从业务服务中彻底分离,打包放到Sidecar(边车)中,并挂接到服务上,实现业务逻辑部分与微服务技术架构部分的完全解耦。

解读

近几年微服务热度不减,而构建微服务架构一般要解决两个问题,一个是业务服务划分的问题,一个就是微服务基础技术架构的问题。

在技术架构层面,目前业界可选的通用方案也不多。在Java阵营目前相对主流的方案就是基于Spring Boot+Spring Cloud+Kubernetes来构建微服务基础架构,并辅以ELK,Zipkin,Swagger,Prometheus,Grafana等工具做一些运维监控的工作。

这样虽然可以满足我们在微服务架构(本质上就是一个松耦合的分布式架构)上的一些技术要求,但是也同样也带来了一些新的问题,例如上文提到的代码侵入性强,耦合高,开源框架拼接导致的技术学习成本高,协调配合需要打磨等。

Service Mesh(服务网格)的产生就是为了解决这个问题,而遵循的还是软件行业那句古老的谚语:

“任何软件工程遇到的问题都可以通过增加一个中间层来解决”

Service Mesh就是添加了这么一个中间层,并给他起了个形象的名字:Sidecar。

图片来源于架构之路微信公众号,请参见延展阅读

区分出了这个Sidecar(边车),我们的服务就将精力更多的专注于自身的业务本身。而微服务架构下服务间通讯涉及的所有脏活儿、累活儿就都交给Sidecar去处理。Sidecar和服务是松耦合的,可以挂接上去,也可以分离开。只要接口匹配,对于业务服务完全无侵入,无语言绑定。

Sidecar就像一个一个“助理”一样兢兢业业,而服务则享受老板的待遇,什么事只需要跟Sidecar交代一下,其他就不用管了,而Sidecar也只能通过其他服务的Sidecar与服务交互。 Sidecar之间相互通信,就形成了一张“网格”,这也是服务网格名字的寓意。

图片来源于架构之路微信公众号,请参见延展阅读

是的,Service Mesh添加的这一新的层次,就是我们一直在苦苦追寻的“微服务基础设施层”。这层的沉淀和浮现,让程序员的关注层次又上升了一个抽象,离业务也更近了一步。

顺手抛出个观点:软件开发的演进就是我们程序员所写的程序逐渐靠近业务本身的过程。

其他该抽象的抽象,该沉淀的沉淀:包括操作系统,编译器,开发语言,声明式编程,DSL,各种框架工具,ServiceMesh都是在做这些非业务部分的抽象和沉淀而已。

那再下一步会是什么?按照我上面的观点,目前来看一个很有竞争力的选手就是Serverless architecture,以后我们有机会再聊:)

相关Blip

延展阅读

支持工具

Share

如何增强Linux内核中的访问控制安全

背景

前段时间,我们的项目组在帮客户解决一些操作系统安全领域的问题,涉及到windows,Linux,macOS三大操作系统平台。无论什么操作系统,本质上都是一个软件,任何软件在一开始设计的时候,都不能百分之百的满足人们的需求,所以操作系统也是一样,为了尽可能的满足人们需求,不得不提供一些供人们定制操作系统的机制。当然除了官方提供的一些机制,也有一些黑魔法,这些黑魔法不被推荐使用,但是有时候面对具体的业务场景,可以作为一个参考的思路。

Linux中常见的拦截过滤

本文着重介绍Linux平台上常见的拦截:

  1. 用户态动态库拦截。
  2. 内核态系统调用拦截。
  3. 堆栈式文件系统拦截。
  4. inline hook拦截。
  5. LSM(Linux Security Modules)

动态库劫持

Linux上的动态库劫持主要是基于LD_ PRELOAD环境变量,这个环境变量的主要作用是改变动态库的加载顺序,让用户有选择的载入不同动态库中的相同函数。但是使用不当就会引起严重的安全问题,我们可以通过它在主程序和动态连接库中加载别的动态函数,这就给我们提供了一个机会,向别人的程序注入恶意的代码。

假设有以下用户名密码验证的函数:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
char passwd[] = "password";
if (argc < 2) {
printf("Invalid argc!\n");
return;
}
if (!strcmp(passwd, argv[1])) {
printf("Correct Password!\n");
return;
}
printf("Invalid Password!\n");
}

我们再写一段hookStrcmp的程序,让这个比较永远正确。

#include <stdio.h>
int strcmp(const char *s1, const char *s2)
{
/* 永远返回0,表示两个字符串相等 */
return 0;
}

依次执行以下命令,就会使我们的hook程序先执行。

gcc -Wall -fPIC -shared -o hookStrcmp.so hookStrcmp.c
export LD_PRELOAD=”./hookStrcmp.so”

结果会发现,我们自己写的strcmp函数优先被调用了。这是一个最简单的劫持 ,但是如果劫持了类似于geteuid/getuid/getgid,让其返回0,就相当于暴露了root权限。所以为了安全起见,一般将LD_ PRELOAD环境变量禁用掉。

Linux系统调用劫持

最近发现在4.4.0的内核中有513多个系统调用(很多都没用过),系统调用劫持的目的是改变系统中原有的系统调用,用我们自己的程序替换原有的系统调用。Linux内核中所有的系统调用都是放在一个叫做sys_ call _table的内核数组中,数组的值就表示这个系统调用服务程序的入口地址。整个系统调用的流程如下:

当用户态发起一个系统调用时,会通过80软中断进入到syscall hander,进而进入全局的系统调用表sys_ call _table去查找具体的系统调用,那么如果我们将这个数组中的地址改成我们自己的程序地址,就可以实现系统调用劫持。但是内核为了安全,对这种操作做了一些限制:

  1. sys_ call _table的符号没有导出,不能直接获取。
  2. sys_ call _table所在的内存页是只读属性的,无法直接进行修改。

对于以上两个问题,解决方案如下(方法不止一种):

  1. 获取sys call table的地址 :grep sys _ call _table /boot/System.map-uname -r
  2. 控制页表只读属性是由CR0寄存器的WP位控制的,只要将这个位清零就可以对只读页表进行修改。
/* make the page writable */
int make_rw(unsigned long address)
{
unsigned int level;
pte_t *pte = lookup_address(address, &level);//查找虚拟地址所在的页表地址
pte->pte |= _PAGE_RW;//设置页表读写属性
return 0;
}
/* make the page write protected */
int make_ro(unsigned long address)
{
unsigned int level;
pte_t *pte = lookup_address(address, &level);
pte->pte &= ~_PAGE_RW;//设置只读属性
return 0;
}

开始替换系统调用

本文实现的是对 ls这个命令对应的系统调用,系统调用号是 _ NR _getdents。

static int syscall_init_module(void)
{
orig_getdents = sys_call_table[__NR_getdents];
make_rw((unsigned long)sys_call_table); //修改页属性
sys_call_table[__NR_getdents] = (unsigned long *)hacked_getdents; //设置新的系统调用地址
make_ro((unsigned long)sys_call_table);
return 0;
}

恢复原状

static void syscall_cleanup_module(void)
{
printk(KERN_ALERT "Module syscall unloaded.\n");
make_rw((unsigned long)sys_call_table);
sys_call_table[__NR_getdents] = (unsigned long *)orig_getdents;
make_ro((unsigned long)sys_call_table);
}

使用Makefile编译,insmod插入内核模块后,再执行ls时,就会进入到我们的系统调用,我们可以在hook代码中删掉某些文件,ls就不会显示这些文件,但是这些文件还是存在的。

堆栈式文件系统

Linux通过vfs虚拟文件系统来统一抽象具体的磁盘文件系统,从上到下的IO栈形成了一个堆栈式。通过对内核源码的分析,以一次读操作为例,从上到下所执行的流程如下:

内核中采用了很多c语言形式的面向对象,也就是函数指针的形式,例如read是vfs提供用户的接口,具体底下调用的是ext2的read操作。我们只要实现VFS提供的各种接口,就可以实现一个堆栈式文件系统。Linux内核中已经集成了一些堆栈式文件系统,例如Ubuntu在安装时会提醒你是否需要加密home目录,其实就是一个堆栈式的加密文件系统(eCryptfs),原理如下:

实现了一个堆栈式文件系统,相当于所有的读写操作都会进入到我们的文件系统,可以拿到所有的数据,就可以进行做一些拦截过滤。

以下是我实现的一个最简单的堆栈式文件系统,实现了最简单的打开、读写文件,麻雀虽小但五脏俱全。

https://github.com/wangzhangjun/wzjfs

inline hook

我们知道内核中的函数不可能把所有功能都在这个函数中全部实现,它必定要调用它的下层函数。如果这个下层函数可以得到我们想要的过滤信息内容,就可以把下层函数在上层函数中的offset替换成新的函数的offset,这样上层函数调用下层函数时,就会跳到新的函数中,在新的函数中做过滤和劫持内容的工作。所以从原理上来说,inline hook可以想hook哪里就hook哪里。

inline hook 有两个重要的问题:

  1. 如何定位hook点。
  2. 如何注入hook函数入口。

对于第一个问题:

需要有一点的内核源码经验,比如说对于read操作,源码如下:

在这里当发起read系统调用后,就会进入到sys read,在sys read中会调用vfs read函数,在vfs read的参数中正好有我们需要过滤的信息,那么就可以把vfs_ read当做一个hook点。

对于第二个问题:

如何Hook?这里介绍两种方式:

第一种方式:直接进行二进制替换,将call指令的操作数替换为hook函数的地址。

第二种方式:Linux内核提供的kprobes机制。

其原理是在hook点注入int 3(x86)的机器码,让cpu运行到这里的时候会触发sig trap信号,然后将用户自定义的hook函数注入到sig trap的回调函数中,达到触发hook函数的目的。这个其实也是调试器的原理。

LSM

LSM是Linux Secrity Module的简称,即linux安全模块。是一种通用的Linux安全框架,具有效率高,简单易用等特点。原理如下:

LSM在内核中做了以下工作:

  1. 在特定的内核数据结构中加入安全域。
  2. 在内核源代码中不同的关键点插入对安全钩子函数的调用。
  3. 加入一个通用的安全系统调用。
  4. 提供了函数允许内核模块注册为安全模块或者注销。
  5. 将capabilities逻辑的大部分移植为一个可选的安全模块,具有可扩展性。

适用场景

对于以上几种Hook方式,有其不同的应用场景。

  1. 动态库劫持不太完全,劫持的信息有可能满足不了我们的需求,还有可能别人在你之前劫持了,一旦禁用LD_ PRELOAD就失效了。
  2. 系统调用劫持,劫持的信息有可能满足不了我们的需求,例如不能获取struct file结构体,不能获取文件的绝对路径等。
  3. 堆栈式文件系统,依赖于Mount,可能需要重启系统。
  4. inline hook,灵活性高,随意Hook,即时生效无需重启,但是在不同内核版本之间通用性差,一旦某些函数发生了变化,Hook失效。
  5. LSM,在早期的内核中,只能允许一个LSM内核模块加载,例如加载了SELinux,就不能加载其他的LSM模块,在最新的内核版本中不存在这个问题。

总结

篇幅有限,本文只是介绍了Linux上的拦截技术,后续有机会可以一起探讨windows和macOS上的拦截技术。事实上类似的审计HOOK放到任何一个系统中都是刚需,不只是kernel,我们可以看到越来越多的vm和runtime甚至包括很多web组件、前端应用都提供了更灵活的hook方式,这是透明化和实时性两个安全大趋势下最常见的解决方案。


更多精彩洞见,请关注微信公众号:思特沃克

Share

DDD该如何学?

2006年,国内互联网才刚刚萌芽,大家甚至还不习惯网购,大多数在校生都在宿舍里刷魔兽世界副本。但企业软件开发却得到了蓬勃发展,各大公司和事业单位都纷纷进行信息化转型。

然而大家很快发现,企业应用业务逻辑的复杂度要远远高于技术本身,且企业IT人员很难描述清楚他们真正的业务,广大程序员也普遍缺乏挖掘真正需求的能力。整个开发过程更多的是瀑布式,开发人员一次性收集需求,可能半年后才会和业务人员再次沟通。大多数企业软件就是在这样的环境下硬着头皮上线的,其质量可想而知。

随着《领域驱动设计》中文版的首次发布,DDD(Domain-Driven Design,领域驱动设计)的概念正式进入中国。当时业界普大喜奔,认为它能指导程序员更精准地收集领域知识,进行更合理的设计,企业应用的银弹出现了。

当时的我正处于多层架构的启蒙阶段,挣扎于企业系统的泥潭,又刚刚被Martin Fowler的《企业应用架构》洗了一遍脑,自然也随波逐流地买了一本,捧在手里翻来翻去,但反反复复就是看不懂。当时以为在贫血模型里面加几个方法就是领域模型了,把DAL或DAO改名成Repository就是资源库了。而且身为程序员,自然愿意去关注那些能指导我们写代码的战术设计方法,对那些真正能帮助我们进行合理设计的战略设计方法,则视而不见(可能是因为看也看不懂)。

多年过去,这本书仍然作为我的镇宅之宝戳在书架显眼的位置,希望能有识货的朋友来访时能一眼瞧见,伸出大拇指羡慕嫉妒地说“这么老的书你都有”。或者偶尔拿出来拍张照片在朋友圈晒晒,以炫耀自己当初的见识。顺便翻开一页,把鼻子凑上去闻一闻来自12年前的墨香。

7年之后Vaughn Vernon出版了Implement Domain-Driven Design,简称IDDD。一年之后由同事翻译的中文版《实现领域驱动设计》也相应出版,当时被看做是能让DDD落地的书(毕竟书名里有个“实现”嘛)。然而我在项目技术负责人的带领下,在众多有经验的架构师的指导下,仍然没有弄明白。之前看过的相关知识均已遗忘殆尽。限界上下文、上下文映射这些名词只是似曾相识。

两年之后《领域驱动设计模式、原理与实践》问世,简称PPPDDD。社区对这本书的评价非常之高,甚至认为在IDDD之上。只可惜这本书的翻译质量并不高,我翻了几页之后又束之高阁了。

今年年初,项目上的架构小组又开始组织学习DDD。所使用的“教材”是英文版的PPPDDD。在同事的激励下,我开始重整旗鼓,啃这本英文版大部头。开始精读之后,才发现这是一本很水的好书。说它水是因为它的编排并不足够细心,甚至有不同章节的两段文字完全相同的现象,还会花30页的篇幅去介绍一个基于NHibernate的资源库实现。说它好是因为面面俱到,把所有战略模式和战术模式都介绍了个遍,还有大量代码去帮你实现各种战术模式,可以说相当落地。

在学习的过程中,我常常翻阅IDDD中的相关章节进行补充阅读,发现当初晦涩难懂的概念慢慢变得容易起来。应用服务和领域服务不再傻傻分不清楚,不同的上下文映射方式也能在工作中找到对应的例子。对于DDD,感觉快要开始入门了。

与此同时,IDDD的精华版DDDD(Domain-Driven Design Distilled)也出版了。作者总结了过去几年在DDD方面的实战经验,将IDDD中的诸多内容精简升华。在很多概念处都标注了IDDD中的相关章节,可以算是IDDD的一个索引。

其中文版《领域驱动设计精粹》由ThoughtWorks同事覃宇和笪磊合作翻译。这是我读过的最良心的一本书籍,因为它包含了大量译者注解,解释了很多书中没有解释清楚的概念(毕竟是精粹本)。还有些有争议的观点,译者也毫不客气地给出自己的看法。

像这样注解超过原文的情况在其他书中是很少见的。每一处注解都倾注了译者的心血和精力,这背后势必包括大量资料的查阅和研究,而且很多解释都夹带了浓浓的ThoughtWorks特色,使得这样一本薄薄的书变得丰满充实起来。

如果你读书快的话,可能两个小时就读完这样一本书。但如果把原书和注解中的推荐文章和书籍读完,恐怕要一个月。我顺着书中的指引,找到了ThoughtWorks洞见上的所有DDD文章,读完之后,世界观崩塌了,感觉自己刚要入门就要放弃了。具体原因请参考链接中的文章。

其实,不管是DDD、IDDD、PPPDDD还是DDDD,讲的都是理论,充其量会附加一些作者杜撰的示例。相信我,光学习理论是没有用的,你必须将其应用于实践,在自己的真实项目里演练DDD。这时你才会发现,那些白纸黑字的概念,在读书时似乎搞清楚了,但一使用起来,反而更迷惑了。就像最基本的子域和限界上下文的关系问题,ThoughtWorks的首席咨询师肖然就和Vaughn Vernon的理解就相去甚远。到底该“信谁”?那就似乎更要通过实践来出真知了。


更多精彩洞见,请关注微信公众号:思特沃克

Share

从王安石变法看“规模化敏捷”

[摘要]

当前敏捷在大型组织中的规模化落地,某种程度上很像历史上的变法。回顾王安石变法的历史,它为什么会失败?对比敏捷的规模化实施,探讨“规模化敏捷”到底可不可取?以史为鉴,我们应该怎么做?


最近在阅读《罗辑思维》全集时,看到一章很有意思的内容,详细分析历史上的变法,其中王安石变法部分让我不断联想起规模化敏捷的实施。两相对比分析,有诸多启发。

一、历史回顾:王安石变法

大事记

王安石变法为什么会失败?

回顾王安石变法过程,会发现其初心很好,实施策略看似做得特别成功:

  • 变法初心:王安石改革之前,司马光问王安石怎么改革?王安石说他有一个方法叫“民不加赋而国用饶”。王安石认为,北宋国家贫苦的症结,不在于开支过多,而在于生产过少;农民之所以贫苦和不能从事生产,一方面是由于官僚富豪兼并了大量土地,另一方面是由于政府把繁重的徭役加在农民身上。因此,最好的理财富国之路,是依靠天下所有的劳动力去开发自然资源,是积极开源而不是消极节流。即不增加赋税,激发社会活力而让国家变得富强。
  • 实施策略:
    • 先定义蓝图:上《本朝百年无事札子》,谈改革之蓝图;
    • 获得高层支持:北宋皇帝宋神宗,本来久慕王安石之名,其变法之道得到了他的绝对支持,他常常跟周围的人说,他跟王安石就是一个人;
    • 清除反对力量:王安石在获得高层的绝对支持后,对变革有异见的大臣比如欧阳修、司马光进行清除,扫清障碍;
    • 全面推广:在蓝图获准、清除变法异己之后,快节奏地全面推广。

然而,结果却失败了。为什么呢?浅析一下,原因可能是:

1. 目标与执行上的不匹配

历史上的变法,可以分为两类:追求效率的“效率型变法”和追求活力的“活力型变法”。

  • “效率型变法”:效率优先、集中财富但不制造资源和财富增量。简单来说,就是围绕单一目标展开,国家要达到这个目标,不惜一切代价集中所有资源都要实现,比如商鞅变法,春秋时代层层分封的财富分配体系被商鞅全部拆掉,他把国家的每一个老百姓、每一粒粮食都镶嵌在国家这个战争机器上,所以秦国变得非常富强。
  • “活力型变法”:激发活力、制造财富和资源增量。这种变法会同时考虑多个目标比如朝廷财源比较丰沛、老百姓安居乐业、市场繁荣稳定、军队能够有战斗力、政府高效廉洁等;在实施时不是调整存量分配,而是想法设法制造财富和资源增量。比如1933年到1941年期间,由美国第32任总统富兰克林·罗斯福发起的罗斯福新政,他围绕经济、农业与工业、社会保障以及政策制度等几个大的维度展开,后人用核心3R来总结,即救济(Relief)、复兴(Recovery)和改革(Reform),也称三R新政。最终,美国经济复苏,政治制度上建立了以总统为中心的三权分立格局,人民生活得到极大改善。

王安石变法,初心上是“追求活力”、有多重目标的,但实施上却采取了“效率优先”的方法,核心是依靠权力把效率推进下去。“效率型变法”要想成功,有一个天然的前提,就是先有蓝图,然后集中快速施工。对一个单目标系统,预先设定一个“正确的”蓝图尚且挑战重重,如果是针对一个多目标系统,几乎是不可能的。这种目标与执行策略上的不匹配,成为失败的原因之一。

2. 低估了制度成本

其实王安石的变法,从制度设计角度是非常好的,比如青苗法的初衷就很好:给老百姓提供低息贷款,避免去借高利贷,以保护和赈济民户。但问题出在制度的实施是表面上附和但内心未必接纳的各级官吏来推进的。最后变成什么了呢?那就是领导意志。比如当时有一个小官叫做邓绾,为了巴结神宗,当听到神宗称赞王安石为“当今之古人”后,瞬间明白领导意思,就神乎其神地夸王安石变法各种好,虽然他不一定懂新法但却得到了重用。这些变法的落地执行者,为了达到目标或超额完成目标,强制摊派老百姓贷款。最后,实际结果适得其反,不仅没有降低老百姓的压力,反而变成了一种变相的赋税。所以,良好的蓝图在实施过程中遇到了制度成本——执行变法的人,没有理解其真实意图,只是围绕既定KPI开展工作,结果自然不好

3. 忽视长期、可持续的变革力量

任何一种变革都不可能一次性成功,新法也可能会失败。如果失败了,那些反对派一定会站出来攻击王安石。所有效率型改革,只要失去高层支持,无论是主动的还是被动的,出路只有一条,那就是失败。宋神宗一死,继位的高太后一上台就把王安石搞下台,新法完全没有了机会,退出历史舞台!王安石阵营在变法期间,并没有能够培养出持续推动变革的力量,比如王安石卸任时推荐了他信任的吕惠卿继任,这个人不但没有继承变法大志,反而落井下石,跟邓绾一起诬陷王安石叛乱。因此,后面即使没有高太后和司马光,变法也难以为继。

二、敏捷的规模化落地与变法实施

敏捷(Agile)是2001年由17位资深软件领域专家们一起发起的针对软件开发工作价值观的倡议。作为一种软件开发理念,与之伴随出现了很多实践框架和方法,如Scrum、Kanban和XP。而这些方法目前已经逐步成为了我们软件开发的实施标准,类似持续集成(Continuous Integration)这种10年前被大家认为是“极限”的方法,现在也已经成了开发团队的一个标配。但在很多大型组织里,敏捷的大规模应用仍然是一个非常有挑战性的任务。软件自身的多样性和这个行业的高速发展,造成了敏捷方法落地的种种挑战。

1. 敏捷的规模化落地,本质是个多目标系统

敏捷的初心,即面向市场和商业模式变化如何提升业务响应力,为用户带来真正有价值、高质量的产品。在整个组织里落地敏捷是一个多目标系统工程,目标至少包含发布高质量、满足用户需求的产品,打造有创造力的文化,建立高响应力的团队等。这带来的第一个挑战就是“蓝图的设定”。没有人可以预先为这个多目标系统设定清晰而正确的蓝图。

最近几年在国际银行届比较知名的数字化转型成功案例非BBVA(西班牙对外银行Banco Bilbao Vizcaya Argentaria)莫属。作为一家拥有6520亿欧元资产的全球银行集团,2007年应对全球金融危机时,BBVA开启其创新旅程,对集团进行数字化革新。尽管拥有上层认可及远见蓝图,BBVA的数字化转型之旅也并非按部就班的沿着蓝图前行,其转型过程大致经历了三个阶段:IT内部创新、扩大团队与银行业务发展重点的创新、数字银行分支匹配敏捷项目管理。一路上尝试不同的结构和方法,通过实验不断调整,最终成为数字化转型的成功典范。

2. 敏捷的规模化落地,是追求活力而不是追求效率

正如前文所述,敏捷的规模化落地,其核心还是围绕敏捷的核心价值观和原则展开,只不过参与的产品更多、人员更多而已。它追求的不是单一目标 —— 规模化,而是价值、质量与其他约束条件的调和所带来的多方优化结果。

笔者曾经参与过一家大型外资银行的敏捷转型辅导,其高管层的目标就是在一年内针对其全球六七万人的IT团队实施敏捷转型,所有部门,包括PMO以及外部教练都要围绕这一目标展开。大部分时间花在了制定各种对团队进行评估的模型上,而团队级别的敏捷实践深入辅导不足,团队对于敏捷理解程度有限。一年之后,形式上虽然绝大部分都参与过敏捷培训,或者敏捷教练辅导,但是对于产品质量和价值的提升非常有限。当这种疾风扫落叶的培训活动结束后,能够保留下来真真正正实施敏捷的团队已经屈指可数,大部分又回到了过去的开发方式。

上面两点,决定了敏捷的规模化落地,如果采用“王安石变法式”的实施方式,必然会失败。

三、规模化敏捷框架与王安石变法

当前大量组织级的敏捷转型需求,催生出了如SAFe、LeSS这样的“规模化敏捷”框架。如果详细研究SAFe的实施过程可以看到,它有完整的白皮书、官网,市面上有各种培训和认证,特别是SAF4.5已经给出了4个经典配置以及10个步骤的实施路线图,看似为组织给出了一个“清晰而正确”的蓝图。

SAFe的蓝图和实施路线图看似很完美,但在落地过程中会遇到诸多挑战,一如一开始看起来很美的“王安石变法”。

1. 追求活力的多目标,实施时变形为单一目标

在实施SAFe的时候,容易想到在组织内部去寻找宋神宗这样的人,这并没有错。但是,根据组织心理学家William Schneider提出的组织文化理论:其中,95%的商业组织都属于控制型文化,这种组织文化强调的是上传下达,领导意志。

(文化四象限)

因此,如果在组织内部发起变革,很容易变成目标问题。也就是王安石遇到宋神宗之后面临的挑战。本来是一个追求活力的变革,最后实施时变成了效率型方式。

2. 因为制度成本,忘记真实意图,仅仅围绕效率指标

前面讲到王安石变法过程中的制度成本问题,在规模化敏捷实施时也普遍存在。无论组织是自我变革,还是请外部教练,很容易变成在领导意志下围绕一个“蓝图”按部就班地展开。而且为了考核规模化的进展,就会设立规模化的指标,比如组织内部百分之多少的团队纳入规模化敏捷运作框架等。再加上出发点就是错误的,失之毫厘谬以千里,离变革成功就越来越远。不难想象,跟王安石变法类似,在实施过程中为了追求规模化的效率指标,往往忽视内功修炼。

笔者刚经历的一个实际案例,某家全球性企业正在实施SAFe,总部请了一名资深的SAFe教练,飞到该公司各个地区负责实施和辅导。当我们作为一线敏捷教练后续进入对中国区团队实施辅导时,某个团队Scrum Master(SM)有一天问我,Sprint长度是不是必须两周?因为他们团队之前一直是三周一个Sprint。我当时很奇怪,问他为什么这么问?他回答说来自Global的教练说了,Sprint必须两周,要不然就不对。我当时听到觉得很震惊,竟然僵化到如此程度!作为一线的SM有此疑惑竟然得不到有说服力的答疑,那么执行过程中僵化就在所难免。

3. 忽视人才和生机型文化,一旦推进不力,一夜回到解放前

当变革推进不力时,反对派的反扑就来了,一夜回到解放前的实际案例不胜枚举。再加上大公司的组织结构重组(re-org)是经常的事,如果支持者不在其位,那么围绕其建立的变革实施人员的动力就会大打折扣。前面提到的大型外资银行的例子,之所以敏捷转型实施一年多以后就没有继续,其中一个很大的原因是全球的高管团队进行了更换,新上任的CXO们并没有认为敏捷转型有价值,所以放弃了之前转型带来的初步成果。更重要的是,在过去一年多的转型过程中,大部分精力并没有放到转型人才的持续培养上,所以当高管团队决定不再投资外部教练,内部的转型力量也没有跟上,变革也就不了了之。

所以,SAFe的蓝图和实施路线图看似很完美,但如同“王安石变法”,注定难以成功。

四、敏捷的规模化落地,如何破?

敏捷的规模化落地,本质是个Complex问题(Cynefin模型)。规模化敏捷框架的最大问题是将一个Complex领域的问题当成Complicated或者Simple领域问题来处理。一个多目标问题,其实是没有Good或Best practices的,唯一有指导意义的是一些价值观和基本原则。对待Complex领域问题,要采取探索-感知-响应模式,快速探索,感知问题的存在,采取行动响应,然后及时调整,对应问题的实践在这个过程中则会涌现出来,而不应该按照一个框架蓝图就开始配置实施。

比如前面提到的BBVA的案例:

  1. 远大愿景:基于对行业趋势的判断,BBVA清楚地认识到,客户的地位在提升,监管的要求趋严,新技术的涌现让银行业面临着前所未有的竞争。BBVA开始以增强与客户的关系作为愿景的转型之路,战略目标定位为成为“数字化时代最好的银行”。而且,基于此愿景制定了6大优先级战略引领集团转型,涵盖了客户体验新标准、驱动数字销售、新商业模式等。所以可见,他们要平衡的是一个多目标系统,既包含用户也包含技术与商业模式等。
  2. 快速行动:BBVA采用实验与学习的方法,将员工、高校研究机构、创新机构、BBVA创新中心、BBVA创业公司比赛、Innova黑客马拉松挑战赛、BBVABeta测试器、BBVA风投以及收购和合作伙伴融入整个创新生态圈,通过各种方式将最新成果快速应用于为客户创造价值,为企业创造收入。
  3. 及时调整:BBVA认为企业应先设立其创新计划并随时准备根据变化对其进行调整,而非盲目的执着于最初计划。比如BBVA的创新项目选择非常灵活,设立指导委员会并根据优先级选择项目,灵活的预算编制以及敏捷的实施,在实施过程中学习,及时调整并探索。

所以,变革成功的哲学是要有一个远大的愿景 —— Think Big;赶紧行动,摸着石头感知河水的深浅、流速,找到可以过河的方案 —— Start Small;根据实验结果,持续学习并改进 —— Learn Fast。

总结

当我们要在一家企业推动敏捷时,首先它是一个活力型变革,多目标问题,而且是一个Complex领域问题,这不是靠套用一种 “规模化敏捷” 框架就能解决的。不要忘记敏捷的初衷,切忌把规模化本身当成目标。当我们忘记“蓝图”,把握“Think BIg, Start Small, Learn Fast”这样的基本原则,更容易找到适合特定企业、组织的敏捷实施方式,比如肖然在《忘记“规模化敏捷”》里所说的“建立持续改进的内部力量、系统思考整个开发过程,以及为创新建立安全试错环境”等。


更多精彩商业洞见,请关注微信公众号:ThoughtWorks商业洞见

Share

Event streaming as the source of truth——历史永铭记、时间任穿梭 | 雷达哔哔哔

写在前面

ThoughtWorks每年都会出品两期技术雷达,这是一份关于科技行业的技术趋势报告,在四个象限:技术、平台、工具以及语言和框架对每一个条目(Blip)做采用、试验、评估、暂缓的建议。(第十九期雷达已发布,可至官网下载

一直以来,我们都未对每一个Blip做进一步的解读,而这次决定尝试一个新的专栏——《雷达哔哔哔》,由作者根据自己实践与理解,对雷达中部分条目作出解析,致力于用一篇篇短小精悍的文字,帮助读者加深对雷达的理解。

今天是《雷达哔哔哔》的第六篇,依然关注架构,Blip是Event streaming as the source of truth

位置

2018年5月第18期技术雷达,技术象限,建议评估

标签

Kafka,Event Sourcing,ETL,Integration

目标受众

系统架构师

关注问题

在以微服务架构为代表、分布式系统架构越来越成为主流的当下,“如何保证不同限界上下文中数据的一致性”一直是系统架构设计上的一个主要挑战。尤其是在只留存数据最终镜像(Snapshot)的数据持久化方案下。有没有一种方案可以让数据同步变得简单、可靠且可溯源可重建?这一直是系统架构师在思考和追寻的。

解决方案

将事件(Event)作为主数据源(Single source of truth),在上下文内则可以直接使用事件溯源(Event Sourcing)获取领域对象的最新数据快照,对外则可以直接使用类似于Kafka的工具通过事件的传递和广播进行不同上下文间的同样基于事件溯源(Event Sourcing)的数据同步和转换(ETL)。

解读

可能很多同学看到上述的问题描述和解决方案后,还没看到这就儿就已经走了……

这其实也可以理解,太多术语让人不知所云。什么Event、Sourcing、Source of Truth、Snapshot,感觉这些词就是为了架构师彰显身份创造的……

其实吧,很简单,我们做个比喻大家就都清楚了。就拿大家熟悉的Git举例子,Git就是一个就是基于事件(Commit)和事件溯源(Commit Chain)的好例子:

假设你有一份你的代码(数据),我有一份我的代码(数据),两份代码处理不同的事情。突然有一天我发现你的代码(数据)其中有一块我也能用,在通过一通“亲切友好”的沟(暴)通(揍)后,我把你的最新代码直接拷贝过来,放到了我的代码库里,并定时拷贝这块最新的代码过来,这就叫做同步(Synchronizing)。

后来我发现你的代码和我的代码还有一些不匹配,很多逻辑我并不用,只需要很少一部分,而且还得修改一下才能与我的代码对接,这就叫做ETL(Extract-Transform-Load)。

作为一个有追求的程序员,我为这个拷贝转换的过程(ETL)写了个程序,每天早上7点工作前自动完成。但是这就引入了一个新的问题,这个程序经常出问题(不要问我为什么……),导致我的代码(数据)和你的最新代码(数据)不一致,我需要知道我最新的代码是哪一次同步的、是否完整,以及如何重新同步代码到最新,这个过程就叫溯源(Sourcing)和重建(Rebuild)。

随着我同步的代码(数据)越来越多,如何保证这些来自不同源的代码(数据)始终保持时间一致性,可溯源,可重建就慢慢成为一件比较难的事情,也就是我上面提到的这个Blip关注的问题。

在代码拷贝这个场景里,Git给我们提供了另一种解决问题的思路。即我们不再保存我们的代码在某一个时间点的完整代码即代码镜像(Snapshot),而只是保存Commit信息,而一个Commit可以理解成就是一个事件(Event)。当我们需要最新代码的时候,不是从代码库里直接复制出最新的完整的代码版本,而是通过Commit链,从头开始将一个一个Commmit Apply到一起,最终形成了代码最新的样子,这个过程就叫做事件溯源(Event Souring),这样我们不仅记录了某个时刻的数据,而且记录了整个历史!

Event streaming as the source of truth,所描述的就是将这种基于Event存储方案应用于我们的系统数据管理,即用存储Event来代替存储数据现状快照,这样我们就可以基于Event和Event Souring处理数据的同时,大大简化和增强不同上下文数据同步的能力。

也就具备了Git般的威力,可以在数据的历史中穿梭,可以基于某个时间点做不同数据源的一致性同步,可以溯源,可以回滚,可以重建。而数据同步也会像Fork,Fetch,Merge一样简单。

相关Blip

支持工具

延展阅读


更多精彩洞见,请关注微信公众号:思特沃克

Share