转载,原文 敖小剑  https://skyao.io/talk/202004-mecha-mesh-through-to-the-end/

摘要

Servicemesh落地实践三年,效果一直并不理想,到了该反思的时候了。Mecha作为面向服务的分布式能力抽象层,是Servicemesh模式的自然进化版本,预计也将是云原生化和Mesh化的必然趋势,让我们将Mesh进行到底。

Mecha介绍

什么是Macha?

Mecha一词,相信爱好动漫的同学应该都不陌生。是的,就是大家熟悉的那个Mecha(机甲):

Mecha这个词之所以出现在这里,主要是因为 Bilgin Ibryam 的这个博客文章 “Multi-Runtime Microservices Architecture”,提出了微服务架构的一个新的设想:Multiple Runtime。

备注:这篇博客文章强烈推荐阅读,我甚至建议在阅读本文之前先阅读这篇文章,因为我今天的内容,可以视为对这个文章的深度解读和思考。为了方便,这里提供一份中文翻译版本 多运行时微服务架构

在这篇博客中,Bilgin Ibryam 首先分析并总结了分布式应用的四大需求:

  • 生命周期(Lifecycle)
  • 网络(Networking)
  • 状态(State)
  • 捆绑(Binding)

由于每种需求存在的问题和局限性,导致传统解决方案如企业服务总线(ESB)及其变体(例如面向消息的中间件,更轻量级的集成框架等)不再适用。随着微服务架构的发展,以及容器和Kubernetes的普及和广泛使用,云原生思想开始影响这些需求的实现方式。未来的架构趋势是通过将所有传统的中间件功能移至其他运行时来全面发展,最后的目标是在服务中只需编写业务逻辑。

备注:详情请见原文,为了节约篇幅,这里只做简单概述,不完全引用原文内容。

下图是传统中间件平台和云原生平台的对比,传统中间件以各种SDK的方式提供能力,而云原生平台则通过各种外围Runtime(典型如大家熟悉的Servicemesh/Istio):

因此作者引入了Multiple Runtime的概念:

作者提出:很可能在将来,我们最终将使用多个运行时来实现分布式系统。多个运行时,不是因为有多个微服务,而是因为每个微服务都将由多个运行时组成,最有可能是两个运行时-自定义业务逻辑运行时和分布式原语运行时。

对多运行时微服务架构和Mecha的说明:

您还记得电影《阿凡达》和科学家们制作的用于去野外探索潘多拉的 Amplified Mobility Platform (AMP)“机车服”吗?这个多运行时架构类似于这些 Mecha-套装,为类人驾驶员赋予超能力。在电影中,您要穿上套装才能获得力量并获得破坏性武器。在这个软件架构中,您将拥有构成应用核心的业务逻辑(称为微逻辑/micrologic)和提供强大的开箱即用的分布式原语的sidecar mecha组件。Micrologic与mecha功能相结合,形成多运行时微服务,该服务将进程外功能用于其分布式系统需求。最棒的是,Avatar 2即将面世,以帮助推广这种架构。我们最终可以在所有软件会议上用令人赞叹的机甲图片代替老式的边车摩托车;-)。接下来,让我们看看该软件架构的详细信息。

这是一个类似于客户端-服务器体系结构的双组件模型,其中每个组件都是独立的运行时。它与纯客户端-服务器架构的不同之处在于,这两个组件都位于同一主机上,彼此之间有可靠的网络连接。这两个组件的重要性相当,它们可以在任一方向上发起操作并充当客户端或服务器。其中的一个组件称为Micrologic,拥有非常少的业务逻辑,把几乎所有分布式系统问题都剥离出去了。另一个伴随的组件是Mecha,提供了我们在本文中一直讨论的所有分布式系统功能(生命周期除外,它是平台功能)。

作者在这里正式提出了Mecha的理念:

思路大体是:Smart Runtime, Dumb Pipes

我对Mecha的理解是:业务逻辑在编码开始阶段应该是“裸奔”的,专注于业务逻辑的实现,而尽量不涉及到底层实现逻辑;而在运行时,则应该装备“机甲”,全副武装,大杀四方。熟悉的味道是吧?标准而地道的云原生思想。

Mecha的本质

作者在原文中探讨了Mecha运行时的特性:

  1. Mecha是通用的,高度可配置的,可重用的组件,提供分布式原语作为现成的能力。
  2. Mecha 可以与单个Micrologic组件一起部署(Sidecar模式),也可以部署为多个共享(注:我称之为Node模式)。
  3. Mecha不对Micrologic运行时做任何假设。它与使用开放协议和格式(例如HTTP/gRPC,JSON,Protobuf,CloudEvents)的多语言微服务甚至单体一起使用。
  4. Mecha以简单的文本格式(例如YAML,JSON)声明式地配置,指示要启用的功能以及如何将其绑定到Micrologic端点。
  5. 与其依靠多个代理来实现不同的目的(例如网络代理,缓存代理,绑定代理),不如使用一个Mecha提供所有这些能力。

下面是我对上述特性的个人理解:

  1. Mecha提供的是能力,以分布式原语体现的各种能力,而不局限于单纯的网络代理。
  2. Mecha的部署模型,不局限于Sidecar模式,Node模式在某些场景下(如Edge/IoT,Serverless FaaS)可能会是更好的方式。至少,Mecha下有机会按需选择,而不是绑死在Sidecar模式上
  3. Mecha和Micrologic之间的交互是开放而有API标准的,Mecha和Micrologic之间的“协议”体现在API上,而不是TCP通讯协议。这提供了一个契机:一个统一Micrologic和Mecha之间通讯方式的契机。
  4. Mecha可以以声明式的方式进行配置和控制,这非常符合云原生的理念,同样也使得API更关注于能力本身,而不是能力如何配置。
  5. 应用需要的能力如此之多(参见上面的图:分布式应用的四大需求),如果每个能力都对应一个代理(不管是Node还是Sidecar),数量会非常夸张,带来的运维压力会很可怕。因此,如Mecha这个名字暗示的,运行时应该是整套的形式提供能力,而不是分散。

如果用一句话来总结,那么我认为Mecha的本质应该是:

“面向应用的分布式能力抽象层”

如Servicemesh的本质是服务间通讯的抽象层一样,Mecha的本质是应用需要的各种分布式能力和原语,包括但不限于服务间通讯。

从这个角度上说,Mecha覆盖的范围是Servicemesh的超集:毕竟Servicemesh只覆盖到应用的部分需求(服务间通讯,还只限于同步/一对一/request-response模式),还有更多的分布式能力和原语有待覆盖。

换一句话说,Mecha的目标应该是:“将Mesh进行到底!”

Mecha的优势和未来

作者指出:Mecha的好处是业务逻辑和越来越多的分布式系统问题之间的松耦合。

下图是业务逻辑和分布式系统问题在不同架构中的耦合:

其实思路和Servicemesh是一脉相承的,只是覆盖的分布式能力更广泛一些。

有一个问题:Mecha会不会成为微服务架构的演进的下一个形态?我个人的答案:是,随着云原生的推进,分布式能力(以传统中间件为典型代表)下沉是大势所趋,Mesh化的范围必然会继续扩大,也就离Mecha的形态越来越近了。这也就是本文标题的立意所在,Mecha会是微服务乃至云原生的下一站。

微软Dapr

在介绍完 Mecha/Multiple Runtime 的理念之后,我们来看看目前微软新推出来的Dapr项目 —— 这应该是业界第一个Multiple Runtime的开源实践项目。

项目地址:https://github.com/dapr/dapr。

Dapr介绍

Dapr 是 Distributed Application Runtime (分布式应用运行时)的缩写,官方介绍说Dapr是“一种可移植的,事件驱动的运行时,用于构建跨云和边缘的分布式应用”。

Dapr的详细介绍是:

Dapr是一种可移植的,serverless的,事件驱动的运行时,它使开发人员可以轻松构建弹性,无状态和有状态微服务,这些服务运行在云和边缘上,并包含多种语言和开发框架。

Dapr 整理了构建微服务应用为开放,独立的构建块的最佳实践,使您能够使用自己选择的语言和框架来构建可移植的应用程序。 每个构建块都是独立的,您可以在应用中使用其中的一个或多个。

Dapr的功能和定位,下面这一张图就可以概括了:

  • 最底下基础设施是各种云平台(主流公有云都支持)或者边缘环境
  • 其上是dapr提供的分布式能力,dapr称之为“building block”。
  • 这些building block的能力,以统一的API(支持HTTP和gRPC)对外提供服务
  • 应用可以用各种语言编写,然后通过dapr提供的API使用这些能力,dapr也提供客户端类库来简化对API的调用,实现了多语言的支持。

Dapr提供的具体分布式能力(building block)如下图所示:

每个building block提供的具体能力请参加 Dapr 的官方文档:https://github.com/dapr/docs/tree/master/concepts。

Dapr的API例子

我们来看一下应用调用Darp API的例子,体验一下使用Dapr的方式。

以 Service Invocation / 服务调用为例:

部署和调用方式与 Servicemesh/Istio 极为相似,但是,差别在于:Dapr是以提供API的方式提供API背后的能力,而不是提供提供协议代理的方式。

上图中1,是ServiceA发起请求来调用一个远程服务。其HTTP request 如下所示:

POST/GET/PUT/DELETE http://localhost:<daprPort>/v1.0/invoke/<appId>/method/<method-name>

其中:

  • 参数 daprPort 是Dapr Runtime启动的监听端口,用来接受应用的 outbound 请求
  • 参数 appId 是远程应用在darp中的关联id,每个注册到dapr的应用都有一个唯一的appId
  • 参数 method-name 是要调用的远程应用的方法名或者URL

负载可以存放在HTTP body中随请求发送,如 json。

注意,虽然都是提供相同的功能,这里体现了Dapr(或者说背后的Mecha)和Servicemesh在方式上的差异:暴露API还是代理通讯协议。

我们看一个更明显的例子,dapr提供的 “publish/subscriptions” 能力,让应用可以方便的发布消息,或者订阅主题并接收消息。下图是应用发布消息,请求直接发给dapr即可:

例子中,参数 topic 指定了消息要发往的主题(例子中是 deathStarStatus)。后续dapr会完成将消息入queue,然后推送到订阅了该topic的应用。接收消息的方式也类似,不过这次是darp主动发起:

  1. dapr首先会请求应用,咨询应用需要订阅那些主题(topic),如例子中应用返回的的TopicA / TopicB
  2. dapr实现主题订阅,在接收到消息之后,再把消息发送给应用,通过URL参数的不同来区分不同的主题

注意在这个调用期间,无论是收发消息,应用完全不用理会底层pub/sub的实现机制(比如是kafka,还是rocketmq,还是其他公有云提供的消息机制),也完全不用引入该实现机制的客户端SDK,只是简单的使用darp定义的API即可,从而实现了和底层的解耦,以及“厂商不绑定”。

为了进一步简化调用的过程(毕竟发一个最简单的HTTP GET请求也要应用实现HTTP协议的调用/连接池管理等),dapr提供了各个语言的SDK,如 java / go / python / dotnet / js / cpp / rust 。另外同时提供HTTP客户端和gRPC客户端。

我们以 Java 为例,java的 client API 接口定义如下:

public interface DaprClient {  
   Mono<Void> publishEvent(String topic, Object event);
   Mono<Void> invokeService(Verb verb, String appId, String method, Object request);
    ......
}

具体可见:

https://github.com/dapr/java-sdk/blob/master/sdk/src/main/java/io/dapr/client/DaprClient.java

分析和总结

前面介绍了Multiple Runtime / Mecha 的架构思想,以及参考实现之一的微软Dapr项目。

由于 Multiple Runtime / Mecha 这个思想非常的新,刚刚提出不久,而微软 Dapr 项目也是一个新出来的项目,不管是理论思想还是实践都处于非常早期的状态,也还没有形成完善的方法论。

特别申明:以下内容更多是我个人当下的理解和感悟,仅代表个人意见,肯定有很多不成熟甚至谬误的地方,欢迎指正和探讨。

Mecha和Dapr的启示

  1. Mesh 模式应该推向更大的领域

    随着云原生的深入,应用需要的分布式能力应该全面下沉,而不仅仅局限于Servicemesh提供的服务间通讯能力;应用形态会朝纯业务逻辑这个目标更进一步,应用更加的云原生化。

    这是大势所趋,也是Mecha架构出现和发展的原动力。

  2. Mecha强调是“提供能力”,而不是通讯代理

    Mecha的使用方式和Servicemesh有非常大的差异:Mecha强调的是提供分布式能力给应用使用,这些能力最终以封装完善的API的方式呈现。API体现的是应用对能力的“需求”和“意愿”,不涉及到如何实现,实现是Mecha的职责,采用什么样的实现也是由Mecha来控制。

    在Servicemesh下,不存在这个需求:Servicemesh提供的是服务间通讯能力,这个能力是由sidecar来提供,没有其他的更底层的实现,不存在隔离和替换的可能。受服务通讯协议和报文schema的限制,Servicemesh只能做请求的“转发”,能力聚焦在“如何转发”上,没有其他需要隔离和替代的能力。

    当Mecha把能力扩展到Servicemesh之外时,很多能力是由外部系统提供:比如 pub-sub 能力可以由不同的Message Queue实现;状态管理能力可以连接不同的Key-Value实现。此时能力的隔离性和可替代性就成为关键需求:解耦应用和能力实现,容许Mecha替换底层实现(进而实现供应商不锁定等)。

  3. 不强求“零侵入”

    在Servicemesh中,“零侵入”是一个非常强调的特性,为此不惜引入 iptables 等流量劫持方案。“零侵入”在某些特殊场景下会发挥巨大的优势,如旧有应用不做改造的前提下接入servicemesh。好处自然不言而喻,但零侵入也有自身的限制:客户端必须能发出符合服务器端要求的网络通讯请求,这个过程外部无法插手。

    对于服务间通讯,这个不是大问题。但是对于其他能力,由于有和实现解耦的需求,再通过客户端自行发起原生协议的请求就不合适了。因此,Mecha中更倾向于采用低侵入的轻量级SDK方案,同样也可以实现跨语言和跨平台,只是需要付出实现各种语言SDK的代价。由于这个SDK足够轻量,因此代价还不算很高。

    而这些少量的工作量,少量的侵入,可以换取轻量级SDK能提供的各种便利和配合(简单理解:开后门),可以实现能力的抽象和API的封装。权衡利弊,Mecha下更倾向于轻量级SDK方案。

  4. 不限定 Sidecar 部署

    Sidecar部署模式,存在资源占用、维护成本增加等缺点,在某些情况下可能并不合适:

    • 边缘网络,IoT场景:资源非常有限,不适合启动太多Sidecar
    • FaaS场景:应用自身足够轻量,甚至比Sidecar还要轻量
    • Serverless场景:Scale to Zero时,对冷启动速度有严格要求,Sidecar的启动和初始化可能拖累应用启动速度

    Mecha下,部署模式不限定于 Sidecar ,在合适时容许选择 Node 模式,甚至 Node 模式和 Sidecar 模式混合使用。

  5. API和配置是关键

    API是分布式能力的抽象,需要要对(开发上层业务应用的)客户友好,简单好用,稳定不变。这些API 也需要标准化,被社区广泛接受和采纳,才能实现厂商不锁定和自由迁移,提升客户价值。

    另外,API还需要配合配置使用,在把能力抽象为API时,是不提供能力的细节控制的。这些控制将在运行时由Mecha根据配置实现,可以理解为:“API + 配置 = 完整的能力”。

    API和配置的制订以及标准化,预计将会是Mecha成败的关键。

Mecha的精髓

Program to an interface, not an implementation.

Design Patterns: Elements of Reusable Object-Oriented Software (GOF, 1994)

Mecha的精髓,要从上面这句名言开始:

  1. 在Mecha下,为了实现解耦可替换, Runtime 隔离了底层实现,因此演变为:”Program to an Runtime, not an implementation.“”
  2. 考虑到 Runtime 不管是部署为Sidecar模式,还是部署为 Node 模式,都是Localhost,因此有: “Program to an Localhost, not an implementation.”
  3. 为了简化开发,Mecha还是会提供轻量级SDK,提供API作为能力的抽象:“Program to an API, not an implementation.”
  4. 考虑到 API 通常是以 interface 的形式提供,因此绕了一圈,Mecha最后还是回到原点:“Program to an interface, not an implementation.”

个人理解,Mecha的精髓就在于这几个关键点:隔离/抽象/解耦/可替换。如下图所示:

  • 在Mecha下,MicroLogic(也就是业务逻辑的代码实现)不容许直接使用底层实现提供的分布式能力
  • Mecha Runtime将为Micro Logic提供分布式能力,同时隔离应用和底层实现
  • 为了方便使用,提供轻量级SDK,其中的API层实现了分布式能力的抽象,应用只需面向API编程
  • 轻量级SDK和Mecah Runtime配合,完成对底层实现的解耦和可替换。

Mecha的实现原则

在Mecha的实现上,我理解的原则是这样:

  1. Runtime 是主力,要做厚
  2. 轻量级SDK 主要是给 Runtime 打配合,要做薄

具体的职责划分:

  1. 轻量级SDK:实现多语言接入,低侵入(但不追求零侵入)
  2. API 接口:由轻量级SDK中提供统一,目标社区化+标准化,给开发者提供一致的编程体验,同时提供可移植性
  3. 应用:轻量级SDK/Runtime配合,提供各种分布式能力,应用无感,只需简单使用API,不耦合底层实现

在Mecha架构中,Runtime 自然是整个架构的核心,扮演类似Servicemesh中数据平面的角色

  • 所有分布式能力使用的过程(包括访问内部生态体系和访问外部系统)都被 Runtime 接管和屏蔽实现
  • 通过CRD/控制平面实现声明式配置和管理(类似Servicemesh)
  • 部署方式上 Runtime 可以部署为Sidecar模式,或者Node模式,取决于具体需求,不强制

备注:Mecha有非常多的能力,实现上也有非常多的细节,这里先做一个High Level的概述。细节后面会有一系列文章一一覆盖,欢迎多交流讨论。

Mecha总结

大概是在3月初,当时我第一次阅读 “Multi-Runtime Microservices Architecture” 这篇文章,有一种豁然开朗的感觉,尤其是有很多之前在反复考虑和权衡但是下不了结论的问题,在这个文章中得到了清晰的解答。可谓受益匪浅。

在Servicemesh探索和实践的这三年中,遇到很多问题,有很多之前没有想到过的问题浮现。比如,以前一直觉得Servicemesh中引入Sidecar带来的最大麻烦会是性能,但实际上,从目前的实践看,Sidecar引入后带来的维护代价才是更令人头疼的事情,相比之下Sidecar引入带来的性能损失显得无伤大雅。

总结一下我个人对 Mecha 架构的核心理解,主要是两点:

  1. Mecha是云原生化和Mesh化的必然趋势:云原生在继续发展,应用需要的分布式能力需要继续下沉,越来越多的能力会以sidecar的形式出现,这是大势所趋。但不可能出现一个应用部署十几个sidecar的局面,这会是运维地狱。因此,必然需要出现新的形态来解决Sidecar过多的问题,合并为一个或者多个Sidecar就会成为必然。
  2. Mecha是Servicemesh模式的自然进化版本:Servicemesh落地实践三年了,效果一直并不理想,到了该反思反省的时候了;而且Mecha的范围也远不止服务间通讯,新的需求下应该有新的思考和突破。Servicemesh现有的固定模式,在Mecha下可以尝试打破以探索新的方式:不必拘泥于Sidecar,试试Node模式;不必拘泥于通讯协议转发,试试Runtime提供能力解耦底层实现;不必拘泥于零侵入,试试在应用中保留一个足够轻的轻量级SDK。

正如曾有说法,说“微服务是SOA实践中正确的部分(the Good Part)”,我希望在 Mecha 的探索和实践中,能够从Servicemesh的实践中吸取成功的经验和失败的教训,希望 Mecha 也能成为Servicemesh的Good Part。希望在云原生的演进路线中,Mecha 可以继微服务和Servicemesh之后,成为云原生落地实践的下一站。

回到现实,目前 Mecha 和 Multi-Runtime 还是一个非常新的想法,Dapr 项目也才刚刚起步,Mecha 要探索的道路还很漫长,一切都还需要摸索着前进。

附录:参考资料

在文章的最后,特别鸣谢 “Multi-Runtime Microservices Architecture”一文的作者 “Bilgin Ibryam”,我非常认可这篇文章中的思想和理念,分析归纳的非常到位,提炼和升华的能力令人佩服。

作者介绍:

Red Hat的首席架构师,Apache Software Foundation 的 committer 和成员。开源的布道师,博客作者,偶尔演讲,著有书籍 Kubernetes Patterns 和 Camel Design Patterns 。

本文参考了 Bilgin Ibryam 出品的如下内容:

0. 前言

关键字

号码隐私保护

虚拟号码保护

号码保护

手机小号

虚拟号码

隐私号码

定义

A、B一般代表用户真实号码

X一般代表PASS平台中的小号,通常和A进行绑定(也叫中间号、隐私号、虚拟号)

Y一般代表PASS平台中的小号,用于解决回呼,通常用于B回呼A

x小x代表分机号

1. 号码隐私保护通话模式介绍

1.1 AX (AXN)模式

只保护号码A,号码X不能复用

一对多隐私保护

呼叫前预先建立A和X的绑定关系

为用户A分配隐私号X,A对外的号码都以X替代,所有与A的通话都通过X建立,保护A号码不泄漏

隐私保护通话AX模式中,A为业务受益用户,为了保护A的真实号码不被泄露,在隐私保护通话平台为A绑定一个虚拟号码X。

  • 绑定关系建立后:
    • 所有人均可拨打X联系A,保护A的真实号码不被泄露。
    • 用户A呼叫其他用户时,企业需要通过API(AX模式设置临时被叫接口)指定呼叫对象(如B),然后A拨打X号码呼叫B。
  • AX模式下的X号码只专属于A号码,即一个虚拟号码X同时只能绑定一个A号码。但1个A号码可以同时绑定5个X号码。
  • 当X号码和A号码解除绑定关系后,该X号码可以被回收,供其他号码绑定。

AX模式下具有如下关键功能:

点击放大

1.2 AXB模式

保护号码A和B,号码X可以复用

一对一隐私保护

用户A与B之间建立一对一绑定关系,双方通过X号码联系对方,保护双方号码隐私。同一X号码可以复用在不同的绑定关系中

呼叫前A和B都必须是已知号码,预先建立AXB的绑定关系

用户A和B不知道对方真实号码,通过平台分配的临时隐私号X联系对方,保护双方号码不泄漏

隐私保护通话AXB模式中,A和B为相互保密的两个业务受益用户,A和B用户都不知道对方真实号码的存在,为了双方的真实号码不被泄露,在隐私保护通话平台为A和B用户绑定一个虚拟号码X,A和B用户对对方只呈现X号码,A和B之间的通信都是通过X号码进行转接。

  • AXB模式下X号码允许被多组号码(建议不超过1000组号码)绑定,即支持多路并发呼叫,但是多组号码中的A和B号码不能重复。例如,允许同时绑定AXB和CXD,但不允许同时绑定AXB和BXC。
  • 同一个A号码若要绑定不同B号码,需要使用不同的X号码进行绑定。如,若已存在AXB,还需绑定A和C,需使用别的X号码,如AX1C。
  • 当X号码和A、B号码解除绑定关系后,该X号码可以被回收,供其他号码绑定。

AXB模式下具有如下关键功能:

点击放大

1.3 X模式

该模式由企业自身维护绑定关系,控制力强

PASS平台仅提供呼叫和短信的管道能力,由企业管理隐私号X与用户的绑定关系,通过灵活利用绑定关系,节省号码资源,创新更丰富的应用

隐私保护通话X模式中,PASS平台对外提供X号码呼叫和短信能力,PASS平台侧不存储任何绑定关系,小号平台接收到呼叫或短信后到第三方系统获取绑定关系。

X模式下具有如下关键功能:

点击放大

点击放大

1.4 AXE (AXN分机、AXx分机)模式

AX模式上增加分机号E

一对多隐私保护

只保护号码A,号码X通过增加分机的方式复用

为用户A分配隐私号X+分机号,A对外的号码都以X+分机号替代,所有与A的通话都通过X+分机号建立,保护A号码不泄漏,提高号码利用率

隐私保护通话AXE模式中,A为业务受益用户,为了保护A的真实号码不被泄露,隐私保护通话平台为A绑定一个分机主号码X和一个分机号E。

  • 绑定关系建立后,其他用户拨打X号码再输入分机号E即可联系A用户。A用户拨打X号码可回呼之前通话用户或企业指定号码。
  • AXE模式下1个X号码可以绑定多个A号码,每个A号码分配不同的分机号E;分机号E最大4位(即0001~9999),但建议一个X号码不要绑定超过200个A号码。

AXE模式下具有如下关键功能:

点击放大

点击放大

1.5 AXG模式

一对组隐私保护

image-20200522234702286

1.6 AXYB模式

保护号码A和号码B

多关系隐私保护

隐私保护通话AXYB模式中,隐私保护通话平台为需要通话的一对或多对用户分别分配对应的隐私号码,保护通话的双方真实号码不被泄露。如隐私保护通话平台为A、B分别绑定隐私号码X和Y,建立了AXYB的绑定关系。

  • A用户对B用户只呈现X号码,与A用户建立的通信通过X号码建立;B用户对A用户呈现Y号码,与A用户建立的通信通过Y号码建立。
  • 一个X号码同时只能绑定一个A号码,一个A号码可以同时绑定5个X号码。Y号码可绑定的最大关系数量为1000(Y号码绑定一个AX关系计为一次绑定关系),但绑定的AX关系不可重复。例如,允许同时绑定A1X1Y1B1和A2X2Y1B2,但不允许同时绑定A1X1Y1B1和A1X1Y1B2。

AXYB模式下具有如下关键功能:

点击放大

1.7 AXxYB分机模式

AXxYB在AXYB上增加分机号码,小x就是分机号,通过增加分机号的方式节约号码X的使用

多关系隐私保护

2. 号码资源

目前号码资源有

虚商号码170/171

标准手机号码

固话号

95呼叫行业专用

3. 业务场景模式适配

AX (AXN)

1对多场景下的隐私保护,在不占用手机SIM卡槽的情况下为用户A增加一个第二号码,保护用户A的隐私,其他人都是通过拨打X号码接通用户A。

商业号码随身行

隐私号码作为商业号码绑定私人手机,随身接通客户电话。非工作时段可设置关机,防止骚扰

优势:

  • 客户资源不遗失

    员工离职,公司收回隐私号码,原有客户资源保持企业所有

  • 企业号码不变更

    客户只需拨打1个企业号码,不需随业务人员离职而频繁变更联系号码

  • 商业行为可追溯

    员工与客户的呼叫记录可追溯,方便管理;支持录音,促进企业服务质量提升

AXB

AXB中间号于1对1场景下的隐私保护,前置条件是A、B的联系方式已知;业务在绑定时候把A、B的联系方式通过api传递到号码隐私保护平台。应用场景:打车、短租、O2O服务等

用车出行、网约车出行

司机和乘客通过平台临时分配的隐私号码呼叫对方,不暴露自己的真实号码

在打车出行场景中,用车订单生成后,司机与乘客间,建立绑定关系,服务过程双方通过隐私号码联系对方,有效保护隐私,服务结束后,解除绑定,避免骚扰纠纷。

X

企业自行维护绑定关系

扫码挪车

车主申请隐私号码并和二维码绑定,其他人扫描二维码,拨打隐私号码联系车主

优势:

  • 隐私保护

    保护双方号码不泄漏,阻断第三方数据采集;可设置二维码关闭,防止骚扰

  • 方便快捷

    无繁琐的界面设定,手机扫码,一键通知挪车

  • 多管齐下

    电话和短信多渠道联系车主,确保通知到位

AXE (AXN分机、AXx分机)

一对多场景下的隐私保护;针对单一面单一利润较低,为了实现X号码复用,引入分机号概念大大降低了单一面单分摊的X号码接通用户A。应用场景:房产中介等

快递派送

快递员拨打隐私号,听到语音提示后,输入分机号转接至收件人

优势:

  • 隐私保护

保护收件人号码不泄露,派送结束隐私号码失效,防止后续骚扰

  • 节省号码资源

    1个隐私号可设置1万个分机号,提高号码复用率,降低号码成本

  • 灵活易用

    分机号根据业务规模灵活设置;交易结束快速回收隐私号,循环利用,无需设置号码冷却期

AXG

一对组场景下的隐私保护;主要针对有团队协作场景的行业,如招聘,通过API将A和企业的人事和用人部门等绑定,进行联系。应用场景:招聘、银行、保险等

AXYB

A和B双方保护

此模式主要针对企业严格把控企业会员隐私情况而定,适用场景较符合当前市场的房产中介类企业。此模式对于保护会员隐私效果最好

AXxYB

A和B双方保护

美团外卖典型的场景

骑手手机号B,拨打号码X 分机号x 转接到点餐人A,你的餐到了

点餐人A拨打 Y 转接到骑手B,我的餐为什么还没到

4. 参考

https://cloud.tencent.com/product/npp

https://support.huaweicloud.com/PrivateNumber/index.html

https://www.aliyun.com/product/pls

5. 提供商

如果你遇到了号码隐私保护上的技术问题,可以加流水理鱼的微信,拉你进技术群讨论

转载,原文 张建飞(Frank)解决问题黄金三步:定义问题—分解问题—归

我们经常说软件开发中,没有银弹。的确,单看软件领域,很少有什么方法论是普适的。然而再拔高一些,跳出软件的范畴,是有一些东西是普世的,比如老子说的“道”。

好吧,“道”说的有点大。不过,最近我的确有一个新发现——我发现“归类分组”在我们解决问题中起着巨大的作用。而这里的“问题”不仅仅限于软件设计,从日常生活、产品设计到公司战略、生物分类。都有它的身影。

这是为什么呢?究其原因可能有两个:

一、归类分组是抽象的重要方法,解决复杂问题我们离不开抽象。

二、归类分组是结构化的重要步骤,结构化的表达离不开归类分组。

当然,归类分组要先有素材可以“归类”才行。因此,完整的方法论是这样的,我给他起了一个好听的名字——解决问题黄金三步:定义问题——分解问题——归类分组

这三步的详细操作如下:

第一步,定义问题:也就是要清楚我们要解决的问题是什么?
第二步,分解问题:对问题进行分析拆解,形成平铺的多个子问题,此步可以尽量发散。
第三步,归类分组:对子问题进行归纳、剪枝,将趋同的子问题,合并成一类问题。

image.png

如上图所示,通过黄金三步以后,我们就可以得到一个形同“金字塔”的结构,也就是我们经常说的金字塔结构。根据问题的复杂程度,这个金字塔结构可能是三层,也可能是二层和多层。

看似简单的三步操作会有那么大的作用吗?废话不多说,直接上案例。

在日常生活中的运用

你出门买报纸,你老婆说家里冰箱空了,顺便带点东西回来吧。她给你列了一个清单,里面有葡萄,橘子,咸鸭蛋,土豆,鸡蛋。你说就这么多了吗?她说苹果和胡萝卜也可以买一点。当你准备出门的时候,她说家里的牛奶和酸奶也没有了,最好也买一点回来。

你觉得你能把老婆交代的东西都买齐吗?我看很难,因为我们的大脑短期记忆无法一次容纳7个以上的记忆项目,超过5个时,我们就会开始将不同的项目归类到不同的逻辑范畴,以便于记忆

如果我们将葡萄,橘子,牛奶,咸鸭蛋,土豆,鸡蛋、胡萝、苹果,酸奶。按照逻辑关系,进行下归类分组,比如把葡萄、橘子、苹果归为水果类,把土豆、胡萝卜归为蔬菜类,便可以大大帮助我们提高记忆效率。

注意,这里分类的作用不只是将一组9个概念,分成每组各有4个、3个和2个概念的3组概念,因为这样还是9个概念,你所要做的是提高一个抽象层次,将大脑需要处理的9个项目变成3个项目。

如果你已经这么做了,恭喜你,你已经在实践黄金三步了:

定义问题:冰箱空了,需要购买补给。
分解问题:你要分析购买哪些东西呢?
归类分组:为了方便记忆,你将要买的9个东西按性质分成了三组,形成如下的金字塔结构。

image.png

这就是结构化思维,下次你再接到老婆这样的“需求”,记得把这个方法论用上,她一定会对你刮目相看。

在工作汇报中的运用

定义问题

“小张,客户对销售报告和库存报告不满意,你去看一下什么原因。” 面对这种典型的工作问题,你打算怎么处理呢?

分解问题

首先,你肯定要去调研客户不满意的原因,经过调查你发现,客户不满意主要有以下原因造成的:

  1. 提交报告的周期不恰当;
  2. 库存数据不可靠;
  3. 获得库存数据的时间太迟;
  4. 库存数据与销售数据不吻合;
  5. 客户希望能改进报告的格式;
  6. 客户希望除去无意义的数据;
  7. 客户希望突出说明特殊情况;
  8. 客户希望减少手工计算。

虽然你做了大量的工作,调查也很充分。但是,如果你要是把这8个原因直接给老板汇报,估计效果不会太好。 你可以自己做个试验,仔细阅读上面的列表3分钟,你能从中获得什么?同样的,你老板看到这样的罗列也会是一脸懵逼,不知道重点在哪。

归类分组

所以我们有必要加上第三步,对问题进行进一步的归类分组,我们可以将8个问题概括为3组:

  1. 报告中含有不可靠的数据;
  2. 报告的格式混乱;
  3. 产生报告的时间太晚,无法采取有效措施

进行分组之后,我们就可以得到如下的金字塔结构:

image.png

这种结构化的表达,很明显让问题的表述更加清晰。领导也可以很快抓住问题的要点,并作出相应的决策。当然,这里是为了着重介绍归类分组,真正的汇报你还要提供对应的解决方案,然后让老板做“选择题”。

在写代码时的运用

你遇到一个相当复杂的业务场景,在这个业务操作中,涉及到大量的校验和执行操作。这种代码,如果没有一定的策略,很容易写成大泥球。

定义问题

例如,在我们的业务中有一个商品上架的操作,是一个非常复杂的业务操作。

分解问题

对这种业务问题的分解,通常是产品经理的职责,但是作为工程师也不能完全依赖PRD。因为产品视角和工程视角还是有差别的。

针对“商品上架”,我们做了如下的功能分解:
image.png

归类分组

同样,在分解之后,我们需要有一个归类分组的过程。否则,这些步骤的平铺会让代码显得凌乱,不方便记忆和维护。通过分析,我们可以把分解后的步骤分成三个阶段:

  1. 初始化阶段
  2. 校验阶段
  3. 执行阶段

通过黄金三步,我们可以得到如下的金字塔结构:

image.png

最后,我们按照这个结构去组织我们的代码,整个代码结构会更加清晰,代码的可维护性也会好很多。

image.png

在应用架构中的运用

应用架构主要解决的是模块、组件定义和模块、组件关系的问题。

image.png

从宏观层面来说,架构设计也是遵循这三个步骤的。比如,我们的架构要如何分层,分模块、分组件就是在做问题分解。然后,模块和组件要归属在哪个层次,要如何命名,就是在做抽象,在做分类归组。

在综合考虑功能属性+质量属性,然后通过黄金三步,就能得到我们想要的架构设计。例如,我们的COLA 2.0架构也是在这个方法论的指导下完成的。
image.png

在产品架构中的运用

定义问题

“小张,为了做新零售,我们打算做一款智能互联网POS机,你先做一下产品设计。”

分解问题

通过调研你发现,作为POS机,其核心功能是收银和经营管理。所以至少需要包含收银的功能、服务核销的功能、商品管理的功能、库存管理的功能等等。

但如果仅仅是满足这些功能,和传统的POS并没有多大的区别,为了满足“智能”和“互联网”的要求,你去深入百度了一下智能POS应该具备的功能,大致包含以下功能:

刷脸支付;
支持品牌商营销;
支持自主营销;
智能定价;
外卖对接;
彩票对接;
虚拟充值等等。
在问题分解阶段,我们应该尽量多的收集信息,多发散,多头脑风暴。

归类分组

发散完,我们还是要收回来。在收敛之前,我们先看一下产品框架应该包含哪些东西,通常,一个产品架构至少要包含三个层次:

用户感知层(在何种场景下通过何种方式触达用户);
功能模块层(通过哪些功能模块实现产品的核心功能,和哪些外部平台功能有信息交互);
数据层(产品的数据从哪里来、产品的数据沉淀到何处去)。
在这三个层次的基础上,我们再对每个层次内的模块进行分组。例如在功能模块层,我们要对功能进行分类,让分散的功能点内聚成更大的产品模块(体现在用户界面上,往往是一级菜单和子菜单的关系)。

比如对于POS的收银产品模块,我们可以提供以下的产品功能:

  1. 支付宝收银
  2. 现金收银
  3. 微信收银
  4. 刷脸支付
  5. 记账等

通过层次划分,模块划分我们就可以得到一个相对清晰的产品架构,以智能POS为例,我们可以画出如下的产品架构:

image.png

分类是科学也是艺术

通过上面的案例,我想你已经领会到黄金三步:定义问题——分解问题——归类分组的要义了。其中前两步相对比较直观,而第三步往往是不容易做好,也容易被忽略的关键步骤。

实际上,对事物的归类分组是我们人类的天性。人类大脑会自动将发现的所有事物以某种持续组织起来。基本上,大脑会认为同时发生的任何事物之间都存在某种关联,并且会将这些事物按某种逻辑模式组织起来。

比如,下面这张图片:
image.png

无论是谁,乍一看到上面的六个黑点,都会认为共有两组墨点,每组三个。造成这种印象的原因主要是有些黑点之间的距离比另一些黑点之间的距离大。

空间是一个相对比较直观的逻辑关系,然而,并不是所有的逻辑关系都是如此的显性化。实际上,很多的概念会在多个维度进行交叉耦合,这就给我们的归类分组带来了很大的挑战。

生物分类学

生物分类学通常直接称分类学(Taxonomy),是一门研究生物类群间的异同以及异同程度,阐明生物间的亲缘关系、基因遗传、物种进化过程和发展规律的基础科学。

最流行的分类是五界系统。通常包括七个主要级别:界(Kingdom)、门(Phylum)、纲(Class)、目(Order)、科(Family)、属(Genus)、种(Species)。种(物种)是基本单元,近缘的种归合为属,近缘的属归合为科,科隶于目,目隶于纲,纲隶于门,门隶于界。

不过分类学到不是一门很严谨的“科学”。就像比尔.布莱森在《万物简史》里说的:

分类学有时候被描述成一门科学,有时候被描述成一种艺术,但实际上那是一个战场。即使到了今天,那个体系比许多人认为的还要混乱。以描述生物基本结构的门的划分为例。许多生物学家坚持认为总数30个门,但有的认为20来个门,而爱德华在《生命的多样性》一书里提出的数字高达令人吃惊的89门。

由此可见,分类并不像我们想的那么简单。我们观察事物的视角不同,对问题的认知程度不同,得出来的分类很可能也完全不同。

特别是当概念之间有交叉情况,分类就会变得更加棘手。比如,在你的笔记本中,有“读书笔记”和“哲学笔记”两个平级的分类,此时你阅读了一本哲学书籍,那么你会把这本书的读书笔记放在哪个分类里呢?

分类的原则

分类的基本原则是MECE法则。透过结构看世界,说的就是MECE法则。

MECE法则即mutually exclusive collectively exhaustive的缩写,是麦肯锡咨询顾问芭芭拉·明托在《金字塔原理》中提出的一个思考工具,意思是“相互独立,完全穷尽”,也常被称为“不重叠,不遗漏”。

image.png

MECE原则的思想精髓,就是全维度的去分析一件事情,不要有遗漏和重复的部分。

我们可以借助已有的结构化思维模型(分类的框架)来分析问题,确保每一层要素之间“不重复、不遗漏”。

分类的思维模型

实际上,在上文中我们已经提到了一些分类的思维模型。比如,在应用架构中,我们通常有展现层、控制层、应用层、领域层和基础实施层;在产品架构中,有用户感知层、功能模块层、数据层。这些框架可以有效的指导我们在各自领域中开展工作。

类似于这样的分类思考模型还有很多,比如:
1、制定市场营销策略的“4P”模型,即产品策略(Product Strategy)、价格策略(Price Strategy)、渠道策略(Place Strategy)、促销策略(Promotion Strategy)。

2、分析问题的“5W2H”模型,即Why、What、Who、When、Where、How和How much。

3、思考组织战略的“7S”模型,即经营策略(Srategy)、组织结构(Structure)、运营系统(System)、经营风格(Style)、职员(Staff)、组织技能(Skill)和共享价值观(Shared value)。

4、分析竞争力的SWOT模型,SWOT分析代表分析企业优势(Strengths)、劣势(Weakness)、机会(Opportunity)和威胁(Threats)。

5、制定目标的SMART模型,即制定目标要满足确定性(Specific) 、可度量性(Measurable)、可实现性(Attainable)、相关性(Relevant)和时效性(Time-based)。

这些思维模型都是宝贵的经验总结,相当于已经帮我们做好了第三步“归类分组”的工作,我们只需要按照模型制定的框架往里面填充要素即可。

因此擅用模型,活用框架。可以极大的提升我们解决问题的效率,同时帮助我们做更加全面的、更加结构化的思考。做了“无遗漏,不重复”。

Android、iOS的webview注入JavaScript代码修改网页内容

需要修改webview中的网页内容、网页元素动作

1.实现原理

webview导航栏方法中执行JavaScript代码

浏览器地址栏是支持运行JavaScript代码的

javascript:开头后跟要执行的语句

// 弹窗
javascript:alert('hello world!');

ps:不可以复制粘贴的方式来测试,这样是无效的,至少“javascript:”是手写补全才可以

那么webview是否也可以呢?

在webview中同样适用本方法

虽然webview没有可见的地址栏,但是webview提供操作导航导航栏的方法

2.常用JavaScript代码片段

// 通过class查找隐藏本element
javascript:(function() {
   document.getElementsByClassName('your_class_name')[0].style.display='none'; 
})();
                
// 通过id查找因此本element
javascript:(function() {
   document.getElementById('your_id').style.display='none';
})();


// 某个element点击事件 并且修改打开一个弹窗后的页面element
javascript:(function() {
// 首次页面加载必须有的element
var url = 'https://www.baidu.com';
var text = 'p标签文本被替换了';
var bottom = document.getElementsByClassName('bottom')[0];
bottom.onclick = function(){
  // 弹窗element
    var dialogButton = document.getElementsByClassName('button')[0];
    var dialogItem = document.getElementsByClassName('item')[2].getElementsByTagName('p')[1];
    dialogButton.onclick = function(){window.open(url, '_self');}; // 点击事件跳转
    dialogItem.replaceWith(text); // 修改标签文本内容
};
})();

// 如果某些页面元素是在页面完成后出现的
// 也就是webview 到了 onPageFinished 周期中,页面元素还未加载出来,需要使用定时器来处理
// 定时器轮询检查页面元素对象,直到找到需要的处理的页面元素对象后进行处理,然后销毁定时器
var timer = setInterval(function () {
    if (document.getElementsByClassName("class name")[0]) {
      // 你的业务代码
      
      clearInterval(timer); // 销毁定时器
    }
  }, 1000);

3.Android中实现webview注入JavaScript代码

// java
final WebView webview = (WebView)findViewById(R.id.browser);
 
    webview.getSettings().setJavaScriptEnabled(true);
 
    webview.setWebViewClient(new WebViewClient() {
     @Override
    public void onPageFinished(WebView view, String url)
    {
        // hide element by class name
        webview.loadUrl("javascript:(function() { " +
                "document.getElementsByClassName('your_class_name')[0].style.display='none'; })();");
        // hide element by id
        webview.loadUrl("javascript:(function() { " +
                "document.getElementById('your_id_name').style.display='none';})();");
 
    }
    });
 
webview.loadUrl(url);

4.iOS中实现webview注入JavaScript代码

// objective-c

5.Flutter中实现webview注入JavaScript代码

参考 Android 和 iOS

6.进阶注入外部引入的hook.js代码

注入的JavaScript代码需要修改重新发包?

注入的JavaScript很大一段硬编码到APP包中如何管理?

进阶实现注入外部hook.js

把JavaScript代码单独写在hook.js并放置在cdn上

// https://www.iamle.com/hook.js
'v0.0.1 app webview hook'

;(function (window) {
    //your code
})(window)


// app 用webview.loadUrl方法中注入js
javascript:(function() {
   var script = document.createElement('script');
   script.type = 'text/javascript';
   script.src = 'https://www.iamle.com/hook.js';
     document.body.appendChild(script);
})();

// Android java为例
 @Override
    public void onPageFinished(WebView view, String url)
    {
        String js = "javascript:(function() {";
        js += "var script = document.createElement('script');";
        js += "script.type = 'text/javascript';";
        js += "script.src = 'https://www.iamle.com/hook.js';";
        js += "document.body.appendChild(script);";
        js += "})();";
        view.loadUrl(js);
    }

这样后续修改只需要修改hook.js即可,APP不用重新打包📦

7.经验

在webview中不支持window.location.href进行网址导航

但是支持使用window.open, window.open(‘https://www.iamle.com‘, ‘_self’);

应用错误跟踪系统:对软件系统运行过程中产生的错误日志进行收集从而实现监控告警。

虽然软件错误❌是不可避免的,但是可以降低错误数。

提高对错误的治理能力能让错误带来的损失降到最低 ​👍🏻 。

错误日志监控在最顶层的业务层监控,有他将会帮助你打造更好的软件!

我们需要业务场景下自己发现Bug的速度快于用户报告Bug的速度,毕竟让用户报告已经晚了。

典型的这套系统的架构:错误日志(前端、后端) => 传输(HTTP)=>错误跟踪平台(收集、展示、分析、告警)。

本文不讨论哪款软件来解决这个问题,只讲Sentry这个轮子,SASS版本和私有部署版体验几乎一致

img

本文主要讲了

应用错误需要监控的场景案例

Sentry有哪些功能

Sentry如何在k8s中私有化部署

本文由 www.iamle.com 流水理鱼 原创,wx公众号搜索 流水理鱼 或 liushuiliyu

SEO 关键字

Sentry私有化部署

Sentry helm部署

Sentry kubernets部署 k8s部署

1. 需要应用错误监控的场景案例

  • 多年的老web项目需要https适配,因为老项目大量写死了http://所以文件修改数巨大,即便测试很仔细也不能保证完全没问题
  • 运营发现落地页转化异常了,有访问没表单提交,找到技术,技术发现是某个JS报错,导致表单无法提交,造成推广费用的损失
  • 上线了一个功能,由于环境差异,只在生产环境才触发,用户报告了才去查问题
  • 应用错误日志虽然通过ELK采集了,但是缺乏及时分析和告警
  • 特定的环境才产生错误,要解决问题的先去构建环境并复现错误,因为不清楚发生错误时的具体参数

2. Sentry有哪些功能

Sentry is cross-platform application monitoring, with a focus on error reporting.

Sentry跨平台应用监控,专注错误报告。

Sentry英文直译中文叫“哨兵”。

Sentry提供了一个应用程序监视平台,可以帮助您实时识别问题。

  • 提供WEB UI
  • 提供SASS版和私有部署2种方式
  • 开源,授权协议为BSL,只要你不拿他来做SASS服务卖钱,自用可以免费商用
  • 提供几乎所有主流开发语言和框架的SDK
  • 提供完整的错误详情
  • 支持自动上报错误和手动上报错误
  • 支持WEB前端、后端、APP
  • 支持多项目管理
  • 支持账号权限管理
  • 提供统一错误的聚合分析

  • 今日头条等很多大公司都在用

img

3. 在Kubernets中部署Sentry

目前网上大部分都是讲DockerCompose的部署方式

其实Helm Hub上可以找到Sentry的helm charts包(⑤),那么直接用Helm部署Sentry是最快速方便的

3.1 Sentry部署资源要求

下面给出一个部署资源情况参考,这基本也就是最低资源消耗情况了

  • Kubernets存储已经支持了动态PVC

  • Kubernets已经支持Ingress

  • 准备一个子域名 (例如, sentry.iamle.com)

  • Helm部署Sentry会部署sentry-corn、sentry-web()、sentry-worker、Redis、PostgreSQL

  • 空负载资源详细sentry-corn(220MB/0.01Core)、sentry-web(850MB/0.012Core)、sentry-worker(2048MB/0.04Core)、Redis(132+179MB/0.04+0.054Core)、PostgreSQL(506MB/0.03Core)

  • 空负载整体资源占用情况,内存:3935MB、CPU核数:0.2 Cores、存储PVC:34G

image-20200419182509260

3.2 Helm 部署 Sentry

helm为helm3

使用国内加速的charts(微软azure)

helm repo add stable http://mirror.azure.cn/kubernetes/charts
helm repo add incubator http://mirror.azure.cn/kubernetes/charts-incubator
helm repo update
helm search repo sentry
#NAME                              CHART VERSION   APP VERSION DESCRIPTION
#stable/sentry                     4.2.0           9.1.2       Sentry is a cross-platform crash reporting and ...
#已经可以在stable repo 找到 sentry
## helm安装sentry
kubectl create namespace sentry
helm install sentry stable/sentry \
-n sentry \
--set persistence.enabled=true,user.email=i@iamle.com,user.password=i@iamle.com \
--set ingress.enabled=true,ingress.hostname=sentry.iamle.com,service.type=ClusterIP \
--set email.host=smtp.yourhost.com,email.port=25 \
--set email.user=user,email.password=password,email.use_tls=false \
--wait
#参数一看就懂,更多安装参数看文末的参考⑤
#第一次安装需要耗时10分钟以上,等等等,db-init-job初始化数据库的时候花费了太多时间
#在安装完成之前,访问sentry.iamle.com会出现服务器内部错误

#出现下面的内容就表示部署好了
#NAME: sentry
#LAST DEPLOYED: Sun Apr 19 21:01:26 2020
#NAMESPACE: sentry
#STATUS: deployed
#REVISION: 1
#TEST SUITE: None
#NOTES:
#1. Get the application URL by running these commands:
#  export POD_NAME=$(kubectl get pods --namespace sentry -l "app=sentry,role=web" -o jsonpath="{.items[0].metadata.name}")
#  echo "Visit http://127.0.0.1:8080 to use your application"
#  kubectl port-forward --namespace sentry $POD_NAME 8080:9000
#
#2. Log in with
#
#  USER: i@iamle.com
#  Get login password with
#    kubectl get secret --namespace sentry sentry -o jsonpath="{.data.user-password}" | base64 --decode

#查看登陆密码,也就是user.password设置的值
#kubectl get secret --namespace default sentry -o jsonpath="{.data.user-password}" | base64 --decode

#删除sentry
#helm uninstall sentry -n sentry

ingress根据自己实际环境做一些微调,笔者的环境下ssl证书使用 cert-manager 自动管理,备注annotations就自动配置ssl

# sentry ingress示例
kubectl apply -f - <<EOF
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: sentry
  namespace: sentry
  annotations:
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  rules:
  - host: sentry.iamle.com
    http:
      paths:
      - path: /
        backend:
          serviceName: sentry
          servicePort: 9000
  tls:
  - hosts:
    - sentry.iamle.com
    secretName: sentry-cert
EOF

浏览器访问

https://sentry.iamle.com 使用安装时设置的账号密码登陆即可

3.3 Sentry新增一个项目

理论上是需要给每个项目都在Sentry中创建一个对应的项目

但是实际上这么干会比较麻烦,笔者建议对访问量不大,比如后台类的相同类型的新建一个项目即可

发现混在一起已经不好区分了,再去拆开也不迟

对于本身访问量巨大的“前端”项目,建议在Sentry管理后台的一对一配置

Projects 》 Add new Project 选择一个JavaScript类型

image-20200419211855759

image-20200419212037861

⚠️ 官方提供的SDK是国外的CDN,实际使用的时候需要把https://browser.sentry-cdn.com/5.5.0/bundle.min.js下载后放在自己的OSS+CDN上

SDK和初始化代码一般放入全站头部当中,当页面有js错误的时候已经会自动上报了

我们用chrome的console手动上报一个消息测试下sentry是否工作正常

Sentry.captureMessage("流水理鱼 www.iamle.com")

image-20200419212444197

image-20200419212641447

3.4 Helm安装sentry常见问题及解决

  • 解决数据库不能初始化的问题

    如果安装过程数据库不能初始化,可以手动初始化

kubectl exec -it -n sentry $(kubectl get pods  -n sentry  |grep sentry-web |awk '{print $1}') bash
sentry upgrade

  • 手动创建一个新的管理员账号
kubectl exec -it -n sentry $(kubectl get pods  -n sentry  |grep sentry-web |awk '{print $1}') bash
sentry createuser

4. 总结

本文看起来洋洋洒洒一大篇,实际上15分钟就能部署好sentry🎉

Sentry不仅仅支持WEB前端,也支持桌面、APP、后端,全平台💯

注意如果要升级sentry,先备份数据先,之前的数据会被清空 ⚠️

如果遇到Sentry的部署、使用等问题可以在博客 www.iamle.com 中找到我的wx,加群讨论📣

5. 参考

1. 导言

绝大多数业务场景都是需要知道客户端IP的
在k8s中运行的业务项目,如何获取到客户端真实IP?
本文总结了通行的2种方式
要答案的直接看方式一、方式二和总结
SEO 关键字
nginx ingress客户端真实ip
kubernets获取客户端真实ip
rke获取客户端真实ip
rancher获取客户端真实ip
本文由 www.iamle.com 流水理鱼 原创,wx公众号同名

1.1 流量链路介绍

7层转发链路 Client(客户端) > Nginx > K8s Ingress(Nginx ingress)
4层转发链路 Client(客户端) > 公有云LB > K8s Ingress(Nginx ingress)
ps: 实际业务会串联更多层级的转发。WAF、CDN、Api Gateway一般是http 7层转发,LB一般是4层tcp转发

1.2 准备whoami探针

whomai是一个go编写的调试探针工具,回显http头信息
在k8s中部署一个containous/whoami用来作为探针,配置好ingress公网和访问,这样客户端web访问可以看到基本的http头信息,方便调试

kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: whoami
  namespace: default
  labels:
    app: whoami
spec:
  replicas: 1
  selector:
    matchLabels:
      app: whoami
  template:
    metadata:
      labels:
        app: whoami
    spec:
      containers:
      - image: containous/whoami
        imagePullPolicy: Always
        name: whoami
        ports:
        - containerPort: 80
          name: 80tcp02
          protocol: TCP
      dnsPolicy: ClusterFirst
      restartPolicy: Always
EOF 

ps:ingress自行增加

客户端web访问,回显http头示例

Hostname: whoami-65b8cc4b-6vwns
IP: 127.0.0.1
IP: 10.42.2.12
RemoteAddr: 10.42.1.0:47850
GET / HTTP/1.1
Host: whoami.iamle.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.162 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-TW;q=0.6,la;q=0.5
Cookie: _ga=GA1.2.30707523.1570429261;
Dnt: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: cross-site
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
X-Forwarded-For: 8.8.8.8, 10.0.0.1
X-Forwarded-Host: whoami.iamle.com
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Original-Forwarded-For: 8.8.8.8
X-Original-Uri: /
X-Real-Ip: 8.8.8.8
X-Request-Id: 3852c9780589ffba4c1f9f2785691d5f
X-Scheme: https

2. 两种方式 7层http头X-Forwarded-For透传 和 4层Proxy Protocol透传

获得客户端真实IP有针对7层和针对4层两种方式

2.1 7层http头X-Forwarded-For透传介绍

http工作在网络第7层,http中有个X-Forwarded-For字段

大部分CDN、WAF、LB用X-Forwarded-For字段来存客户端IP,也有用X-Real-Ip字段,cloudflare、百度云加速还扩展了CF-Connecting-IP字段
标准数据为

X-Forwareded-For:Client,proxy1,proxy2,proxy3……

第一个ip是客户端ip,后面的proxy为路过一层就加一层的ip
这里的proxy可以是WAF、CDN、LB、Api Gateway等

2.2 4层Proxy Protocol透传

tcp工作在网络第4层,Proxy Protocol就是在tcp中增加一个小的报头,用来存储额外的信息

代理协议即 Proxy Protocol,是haproxy的作者Willy Tarreau于2010年开发和设计的一个Internet协议,通过为tcp添加一个很小的头信息,来方便的传递客户端信息(协议栈、源IP、目的IP、源端口、目的端口等),在网络情况复杂又需要获取客户IP时非常有用。
其本质是在三次握手结束后由代理在连接中插入了一个携带了原始连接四元组信息的数据包。

目前 proxy protocol有两个版本,v1仅支持human-readable报头格式(ASCIII码),v2需同时支持human-readable和二进制格式,即需要兼容v1格式
proxy protocol的接收端必须在接收到完整有效的 proxy protocol 头部后才能开始处理连接数据。因此对于服务器的同一个监听端口,不存在兼容带proxy protocol包的连接和不带proxy protocol包的连接。如果服务器接收到的第一个数据包不符合proxy protocol的格式,那么服务器会直接终止连接。

Proxy protocol是比较新的协议,但目前已经有很多软件支持,如haproxy、nginx、apache、squid、mysql等等,要使用proxy protocol需要两个角色sender和receiver,sender在与receiver之间建立连接后,会先发送一个带有客户信息的tcp header,因为更改了tcp协议头,需receiver也支持proxy protocol,否则不能识别tcp包头,导致无法成功建立连接。
nginx是从1.5.12起开始支持的

3. 方式一 X-Forwarded-For配置

适用于7层http转发

3.1 NGINX Ingress Controller X-Forwarded-For配置

查看NGINX Ingress Controller的ConfigMaps配置文档,可以找到以下配置项
use-forwarded-headers
如果为true,NGINX会将传入的 X-Forwarded-* 头传递给upstreams。当NGINX位于另一个正在设置这些标头的 L7 proxy / load balancer 之后时,请使用此选项。
如果为false,NGINX会忽略传入的 X-Forwarded-* 头,用它看到的请求信息填充它们。如果NGINX直接暴露在互联网上,或者它在基于 L3/packet-based load balancer 后面,并且不改变数据包中的源IP,请使用此选项。
ps: NGINX Ingress Controller直接暴露互联网也就是Edge模式不能开启为true,否则会有伪造ip的安全问题。也就是k8s有公网ip,直接让客户端访问,本配置不要设为true!

forwarded-for-header
设置标头字段以标识客户端的原始IP地址。 默认: X-Forwarded-For
ps:如果 NGINX Ingress Controller 在CDN,WAF,LB等后面,设置从头的哪个字段获取IP,默认是X-Forwarded-For
这个配置应该和use-forwarded-headers配合使用

compute-full-forwarded-for
将远程地址附加到 X-Forwarded-For 标头,而不是替换它。 启用此选项后,upstreams应用程序将根据其自己的受信任代理列表提取客户端IP

修改configmap nginx-configuration配置

kubectl -n ingress-nginx edit cm nginx-configuration

在apiVersion: v1下,kind: ConfigMap上加入

data:
  compute-full-forwarded-for: "true"
  forwarded-for-header: "X-Forwarded-For"
  use-forwarded-headers: "true"

或者直接apply附加配置

kubectl apply -f - <<EOF
apiVersion: v1
data:
  compute-full-forwarded-for: "true"
  forwarded-for-header: X-Forwarded-For
  use-forwarded-headers: "true"
kind: ConfigMap
metadata:
  labels:
    app: ingress-nginx
  name: nginx-configuration
  namespace: ingress-nginx
EOF

ps:如果nginx-configuration不在namespace ingress-nginx中就在namespace kube-system中找

3.2 Nginx作为边缘节点(Edge)配置

作为Edge需要重写remote_addr,保证了客户端IP不会被伪造
必须:X-Forwarded-For 重写为 $remote_addr
非必须扩展:X-Real-IP 重写为 $remote_addr

upstream wwek-k8s {
    server 8.8.8.8:443;
    server 8.8.8.7:443;
    server 8.8.8.6:443;
}

map $http_upgrade $connection_upgrade {
    default Upgrade;
    ''      close;
}
server {
    if ($http_x_forwarded_proto = '') {
        set $http_x_forwarded_proto  $scheme;
   }
location / {

        proxy_set_header Host              $http_host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Port  $server_port;
        #proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-For   $remote_addr;
        proxy_set_header X-Real-IP         $remote_addr;

        proxy_pass          https://wwek-k8s;
        proxy_http_version  1.1;
        proxy_set_header    Upgrade $http_upgrade;
        proxy_set_header    Connection $connection_upgrade;
        proxy_read_timeout 900s;
        proxy_buffering off;
    }
}

3.3 X-Forwarded-For是否可以伪造

客户端是否能伪造IP,取决于边缘节点(Edge)是如何处理X-Forwarded-For字段的。
客户端直接连接的首个proxy节点都叫做边缘节点(Edge),不管是网关、CDN、LB等只要这一层是直接接入客户端访问的,那么他就是一个边缘节点。

不重写-不安全的边缘节点(Edge)
边缘节点如果是透传http头中的X-Forwarded-For字段,那么这个就是不安全的,客户端可以在http中实现包含X-Forwarded-For字段值,这个值又被透传了。

#不安全
X-Forwareded-For:Client(Edge不重写,只透传),proxy1,proxy2,proxy3……

重写-安全的边缘节点(Edge)
边缘节点(Edge)如果重写remote_addr到X-Forwarded-For,那么这就是安全的。边缘节点(Edge)获取的remote_addr就是客户端的真实IP

#安全
X-Forwareded-For:Client(Edge获取的remote_addr),proxy1,proxy2,proxy3……

4. 方式二 Proxy Protocol 配置实例

适用于4层tcp转发
公有云的负载均衡LB一般都支持Proxy Protocol

查看NGINX Ingress Controller的ConfigMaps配置文档,可以找到如何配置Proxy Protocol
use-proxy-protocol
启用或禁用roxy Protocol,以接收通过代理服务器和负载均衡器(例如HAProxy和Amazon Elastic Load Balancer(ELB))传递的客户端连接(真实IP地址)信息。

NGINX Ingress Controller 作为receiver角色 Proxy Protocol配置

kubectl -n ingress-nginx edit cm nginx-configuration

在apiVersion: v1下,kind: ConfigMap上加入

data:
  use-proxy-protocol: "true"

或者直接apply附加配置

kubectl apply -f - <<EOF
apiVersion: v1
data:
  use-proxy-protocol: "true"
kind: ConfigMap
metadata:
  labels:
    app: ingress-nginx
  name: nginx-configuration
  namespace: ingress-nginx
EOF

ps: 注意需要上一层LB支持Proxy Protocol,才能这么配置,否则会导致无法链接

5. 总结

7层http头X-Forwarded-For透传
链路proxy有透传X-Forwarded-For
访问链路上多层proxy,任意一个节点不支持Proxy Protocol

4层协议Proxy Protocol透传
上下游可控都支持Proxy Protocol协议
链路proxy中丢失了http头
https反向代理http(某些情况下由于Keep-alive导致不是每次请求都传递x-forword-for

应该用那种方式?
7层用X-Forwarded-For,4层用Proxy Protocol
如果链路的边缘节点(Edge)X-Forwarded-For字段是安全的,建议用X-Forwarded-For
如果链路proxy全路径都支持Proxy Protocol,那么建议用Proxy Protocol
如果有4层tcp业务应用,那么获取客户端IP就的用Proxy Protocol
总之搞清楚了这2种方式的原理按照场景选择

5. 参考