Service Mesh 及其主流开源实现解析

什么是 Service mesh

Service Mesh 直译过来是 服务网格,目的是解决系统架构微服务化后的服务间通信和治理问题。服务网格由 sidecar 节点组成。在介绍 service mesh 之前,我们先来看一下什么是 sidecar.

Sidecar 在软件系统架构中特指边车模式。这个模式的灵感来源于我们生活中的边三轮:即在两轮摩托车的旁边添加一个边车的方式扩展现有的服务和功能。在绝地求生吃鸡游戏中,摩托车是无敌的,应该也与这个模式有关吧? 这个模式的精髓在于实现了数据面(业务逻辑)控制面的解耦:原来两轮摩托车的驾驶者集中注意力跑赛道,边车上的领航员专注周围信息和地图,专注导航。

具体到微服务架构中,即给每一个微服务实例(也可以是每个宿主机host)同步部署一个 sidecar proxy:

该 sidecar proxy 负责接管对应服务的入流量和出流量。并将微服务架构中以前有公共库、framework实现的熔断、限流、降级、服务发现、调用链分布式跟踪以及立体监控等功能从服务中抽离到该 proxy 中:

当该 sidecar 在微服务中大量部署时,这些 sidecar 节点自然就形成了一个网格:

这就是我们说的 service mesh 了。对 service mesh 有了一个感性认识后,我们看一下 Linkerd 和 Conduit 的作者 William Morgan 在What’s a service mesh? And why do I need one? 中是如何诠释什么是 Service Mesh:

A service mesh is a dedicated infrastructure layer for handling service-to-service communication. It’s responsible for the reliable delivery of requests through the complex topology of services that comprise a modern, cloud native application. In practice, the service mesh is typically implemented as an array of lightweight network proxies that are deployed alongside application code, without the application needing to be aware.

Service Mesh 这个服务网络专注于处理服务和服务间的通讯。其主要负责构造一个稳定可靠的服务通讯的基础设施,并让整个架构更为的先进和 Cloud Native。在工程中,Service Mesh 基本来说是一组轻量级的与应用逻辑服务部署在一起的服务代理,并且对于应用服务是透明的。

Service Mesh的特点

  • 是一个基础设施
  • 轻量级网络代理,应用程序间通讯的中间层
  • 应用程序无感知,对应用程序透明无侵入
  • 解耦应用程序的重试/超时、监控、追踪和服务发现等控制层面的东西

Service Mesh 有哪些开源实现

Service Mesh 的概念从2016年提出至今,已经发展到了第二代。

第一代 service mesh 以 LinkerdEnvoy 为代表。

Linkerd 使用Scala编写,是业界第一个开源的service mesh方案。作者 William Morgan 是 service mesh 的布道师和践行者。Envoy 基于C++ 11编写,无论是理论上还是实际上,后者性能都比 Linkderd 更好。这两个开源实现都是以 sidecar 为核心,绝大部分关注点都是如何做好proxy,并完成一些通用控制面的功能。 但是,当你在容器中大量部署 sidecar 以后,如何管理和控制这些 sidecar 本身就是一个不小的挑战。于是,第二代 Service Mesh 应运而生。

第二代service mesh主要改进集中在更加强大的控制面功能(与之对应的 sidecar proxy 被称之为数据面),典型代表有 IstioConduit

Istio 解析

Istio 是 Google 和 IBM 两位巨人联合 Lyft 的合作开源项目。是当前最主流的service mesh方案,也是事实上的第二代 service mesh 标准。

Google 和 IBM 之所以要带上小弟 Lyft 一起玩耍是因为他们不想从头开始做数据面的组件,于是在 Istio 中,直接把 Lyft 家的 Envoy 拿来做 sidecar. 除了sidecar, Istio中的控制面组件都是使用Go编写。Istio架构如下图所示:

对于一个仅提供服务与服务之间连接功能的基础设施来说,Istio的架构算不上简单。但是架构中的各个组件的理念的确非常先进和超前。

  • Envoy: 扮演sidecar的功能,协调服务网格中所有服务的出入站流量,并提供服务发现、负载均衡、限流熔断等能力,还可以收集大量与流量相关的性能指标。
  • Pilot: 负责部署在service mesh中的Envoy实例的生命周期管理。本质上是负责流量管理和控制,是将流量和基础设施扩展解耦,这是Istio的核心。感性上,可以把Pilot看做是管理sidecar的sidecar, 但是这个特殊的sidacar并不承载任何业务流量。Pilot让运维人员通过Pilot指定它们希望流量遵循什么规则,而不是哪些特定的pod/VM应该接收流量。有了 Pilot 这个组件,我们可以非常容易的实现 A/B 测试和金丝雀Canary测试:

  • Mixer: Mixer在应用程序代码和基础架构后端之间提供通用中介层。它的设计将策略决策移出应用层,用运维人员能够控制的配置取而代之。应用程序代码不再将应用程序代码与特定后端集成在一起,而是与Mixer进行相当简单的集成,然后Mixer负责与后端系统连接。也就是说,Mixer可以认为是其他后端基础设施(如数据库、监控、日志、配额等)的sidecar proxy:

  • Istio-Auth: 提供强大的服务间认证和终端用户认证,使用交互TLS,内置身份和证书管理。可以升级服务网格中的未加密流量,并为运维人员提供基于服务身份而不是网络控制来执行策略的能力。Istio的未来版本将增加细粒度的访问控制和审计,以使用各种访问控制机制(包括基于属性和角色的访问控制以及授权钩子)来控制和监视访问您的服务,API或资源的人员。

Istio 的很多设计理念的确非常吸引人,又有 Google 和 IBM 两个巨人加持,理论上这条赛道上的其他选手都可以直接退赛回家了。但是 Istio 发布的前几个版本都在可用性和易用性上都差强人意。此外,service mesh 布道师、 Linkerd 作者 William Morgan 也心有不甘。因此, William Morgan一方面在2017年7月11日,Linkerd 发布版本 1.1.1,宣布和 Istio 项目集成,一方面夜以继日的开发Conduit.

Conduit 解析

Conduit 各方面的设计理念与 Istio 非常类似。但是作者抛弃了 Linkerd, 使用Rust重新编写了sidecar, 叫做 Conduit Data Plane, 控制面则由Go编写的 Conduit Control Plane接管:

从Conduit的架构看,作者号称Conduit吸取了很多 Linkerd 的 Scala 的教训,比 Linkerd 更快,还轻,更简单,控制面功能更强可信度还是挺高的。与Istio比较,个人其实更喜欢Conduit的架构,一方面是它足够简单,另一方面对于要解决的问题足够聚焦。

nginMesh 凑热闹?

Service Mesh 最基础的功能毕竟是 sidecar proxy. 提到 proxy 怎么能够少了 nginx? 我想nginx自己也是这么想的吧? 毫不意外,nginx也推出了其 service mesh 的开源实现:nginMesh.

不过,与 William Morgan 的死磕策略不同,nginMesh 从一开始就没有想过要做一套完整的第二代Service Mesh 开源方案,而是直接宣布兼容Istio, 作为Istio的 sidecar proxy. 由于 nginx 在反向代理方面广泛的使用,以及运维技术的相对成熟,nginMesh在sidecar proxy领域应该会有一席之地。

反思

对于大规模部署微服务(微服务数>1000)、内部服务异构程度高(交互协议/开发语言类型>5)的场景,使用service mesh是合适的。但是,可能大部分开发者面临的微服务和内部架构异构复杂度是没有这么高的。在这种情况下,使用service mesh就是一个case by case的问题了。

理论上,service mesh 实现了业务逻辑和控制的解耦。但是这并不是免费的。由于网络中多了一跳,增加了性能和延迟的开销。另一方面,由于每个服务都需要sidecar, 这会给本来就复杂的分布式系统更加复杂,尤其是在实施初期,运维对service mesh本身把控能力不足的情况下,往往会使整个系统更加难以管理。

本质上,service mesh 就是一个成规模的sidecar proxy集群。那么如果我们想渐进的改善我们的微服务架构的话,其实有针对性的部署配置gateway就可以了。该gateway的粒度可粗可细,粗可到整个api总入口,细可到每个服务实例。并且 Gateway 只负责进入的请求,不像 Sidecar 还需要负责对外的请求。因为 Gateway 可以把一组服务给聚合起来,所以服务对外的请求可以交给对方服务的 Gateway。于是,我们只需要用一个只负责进入请求的 Gateway 来简化需要同时负责进出请求的 Sidecar 的复杂度。

小结:service mesh不是银弹。对于大规模部署、异构复杂的微服务架构是不错的方案。对于中小规模的微服务架构,不妨尝试一下更简单可控的gateway, 在确定gateway已经无法解决当前问题后,再尝试渐进的完全service mesh化。

扩展阅读

GopherChina 2018 keynote 点评

作为一名参加了两届GopherChina的「老人」,今年为了去沟里吃樱桃,就没去现场凑热闹了。不过,会议的keynote是绝不会错过的。AstaXie也在会议结束后的第一时间放出了会议的ppt. 看了一下,里面的ppt并不完整,缺了第二天的第一个keynote. 手上有这个资源的同学可以分享我一下?

1.1 基于Go构建滴滴核心业务平台的实践

介绍了滴滴老服务迁移到Go的过程。很多内容感同身受,因为在一年前,我们也完成了类似的操作。从slides看,其日志收集、分布式调用追踪等微服务演进过程中解决的问题都是一笔带过,但是其实都是挺花时间的事情。可以参考微服务troubleshooting利器——调用链

比较遗憾的是没有看到其在服务迁移的时候如何确定服务边界和问题领域,更没有深入谈如何拆分低耦合高内聚的微服务的思考。

解决WaitGroup和GC问题比较有意思,了解一下即可。

最后介绍了两个开源工具Gendryjsonitr, 典型的瑞士军刀、直击目标风格,很棒。

Gendry是一个数据库操作辅助工具,核心是sql builder。我非常喜欢其设计理念:为什么要开发Gendry。简单讲,就是在不透明和不易调优的ORM与繁琐、低效的裸写sql之间找一个平衡。

jsosniter则是一个高效的json encodec. 虽然benchmark亮眼,但是我想大部分场景下,我还是会优先选择标准库。因为很多json序列换和反序列化的细节处理上,标准库还是最完善的。

1.2 Go在Grab地理服务中的实践

从slides看,应该是最容易听懂的一个keynote吧。没有贬义的意思,而是对于作者的思维清晰程度和表达能力非常佩服。基于地理位置做供需匹配的同学可以把这个当做范文,看看作者是如何把系统从基于PostGIS开始逐步演进到geohash/redis/shard/cell方案的。

整个内容非常顺畅,似乎作者在现场还普及了一个「能够做叫车服务就能够做送外卖」的梗。

1.3 Rethinking Errors for Go 2

来自 Golang 核心组的 Marcel 同学向大家介绍了Go 2中可能会引入的 error 处理机制。我个人还是能够接受Go 2中这个draft阶段的错误处理方式的。

作者在demo中使用errcerrd两个lib做演示,想了解细节的同学可以直接点进去看看如何使用。

与现有的错误处理方式比较,能够显著减少 if err != nil 这种代码,并且有更强的语言表达能力。虽然很多人吐槽说 Go 2 最终还是可能会引入关键字 try,但是从 Marcel 的介绍看,这只不过是一个语法糖而已,编译时候就inline掉了。另外,即使最终的方案通过 try 实现了更多的其他功能,也没有必要一定要避免try关键字与其他语言撞车的事实吧。毕竟语言设计追求的是尽可能的合理性和正确性,而不是独特性。

Go在区块链的发展和演进

仅从slides看,就是个区块链科普文,当然,不排除作者现场演讲能力比较强,抖了很多现场才能听到的料。如果你已经对区块链比较了解,可以略过。

Badger_ Fast Key-Value DB in Go

一个pure go的基于LSM tree的 key-value 数据库。如果你不是很了解LSM Tree, 可以参考鄙人的拙文:LSM Tree/MemTable/SSTable基本原理。Badger主要有以下几个特点:

  1. pure go实现,没有cgo依赖。
  2. Badger的LSM tree存储的是 {key, value_pointer},因此可以有效降低LSM tree的大小, 直接load到内存。
  3. 印度小哥现场跑分,读写性能比boltDB 和 RocksDB 都有相当优势。
  4. bloom-filter和file merge实现中规中矩。
  5. 支持无锁的并发事物。

开源那是必须的,想进一步研究的同学可以移步dgraph-io/badger.

Golang在阿里巴巴调度系统Sigma中的实践

slides看不出太多架构、思路和案例的内容,可能是一个干货在心中的speaker,ppt只是提词器罢了。Golang语言采坑部分比较基础,稍微有经验的gopher应该都知道。不过从去年对阿里分享的失望看,今年大家对阿里的分享好评率要好很多。

罗辑思维Go语言微服务改造实践

都说这次大会speaker的幽默水平历届最高,来自罗辑思维的方圆老师更是重新定义了「系统可用性」:只要老板觉得是就可以。

分享的内容涵盖了一个中小互联网企业微服务化的方方面面:api gateway, 服务注册、服务发现、多级缓存、熔断降级。基本可以作为一个公司微服务进程第一阶段的范文来研究。微服务化的后续阶段,比如容器化以及与之配合的CI/CD、日志管理、分布式追踪、auto-scale、立体监控,从其展望上看也有计划。因此可以持续关注方圆老师的后续动作。

Golang打造下一代互联网-IPFS全解析

本质上是一个p2p的去中心化分布式存储系统。基于其之上,可以构建各种应用。最promising的当然是http服务。

整个IPFS使用的基本都是现成的,但是却组合出了一个非常有意思的场景应用。因为之前也有关注IPFS,内容本身没有太多其他收获。权当是一次复习吧。

如何用GO开发一个区块链项目

从slides看,就是介绍了一些区块链的基础概念,后面两页ppt才遇到go,一笔带过. 个人没有太多收获。

Bazel build Go

对Bazel不是太熟悉,在看这个keynote前,只在tensorflow 教程中跟着走过一下Bazel,因此看到国内有公司把 Bazel 拿来在实际开发中应用还是心生敬仰的。

就我经历的项目看,go的build和依赖管理都有不错的轻量级工具,使用 Bazel 来 build 应该更加适合大型的多语言混合项目。

基于Go-Ethereum构建DPOS机制下的区块链

又是一个区块链科普文,不过相对更加聚焦到共识算法上。

深入CGO编程

打开slides我就被震惊了,足足145页ppt, 内容也毫无灌水,问题聚焦,示例丰富。 本来觉得自己是知道什么叫做CGO的,但是看完以后,感觉自己才真正开始入门。建议谢大应该给这样负责的speaker加鸡腿。

这应该是我见过的关于Golang中使用CGO最全面、丰富、深入的资料了。虽然在大部分场景下,都会避免使用CGO,但是如果遇到绕不开的场景的时候,这绝对是第一手的学习资料。

runv-kata-gopher-china

kata container: 安全如虚拟机,快如容器。在去年的kubeCOn’17 就发布了,目前还没有看到国内有公司在生成环境使用。持观望态度吧。slides内容太少,脑补不出来。不评价细节了。

Go toolchain internals and implementation based on arm64

介绍了golang arm64 的编译工具链。除了开始提到的AST分析最近体会较深(基于AST写代码生成器),其他的还停留在概念了解上。不过还是向作者深入钻研的精神致敬。

Go在探探后端的工程实践

又是一个公司落地go生态的例子。亮点是在测试部分做得非常全面和细致。对于在落地完善CI流程的同学(比如我),这部分有非常深远的参考意义。

其他

golang从出生开始就提供了非常完善的基于 go pprof 的一系列性能profiling工具,这是很多其他语言羡慕不来的。而今年的会议有一个共同点是,性能调优工具除了使用 go pprof 以外,都会结合使用 Uber 开源的golang火焰图工具go-torch:

著名开源项目OpenResty作者章亦春也非常推崇使用火焰图来诊断性能问题。看来火焰图真的越来越火了?

看到去了现场的不少同学吐槽这次会议区块链内容比较多。其实我觉得这个topic还好,毕竟会议也需要结合一些当前的热点。比较遗憾的是区块链相关的 slides 质量都不是很高,这可能才是被吐槽的真正原因。

公司层面,现在不仅中小互联网公司大量使用go做基础架构,也越来越多大厂开始使用go构建一些基础组件。相信以后gopher不仅会在创业公司持续活跃,也会有更多到大厂工作的机会。

正则表达式中匹配 Unicode 的常用类别和命名块

大概两年前,在Golang正则表达式使用及简单示例中提到了在正则表达式中使用\p{Lu}来匹配Unicode 类别或 Unicode 块:

但是,在日常使用的时候经常不知道自己要匹配的那个 Unicode 字符属于拿一个类别。于是翻了一下 Golang 所遵循的 RE2 列别表。把一些常用的类别和命名块列举如下:

常用 Unicode 常规类别

类别 描述
Ll 小写字母
Lu 大写字母
Lt 首字母大写字母
Lo 其他字母(注音字母、表意文字等)
L 字母,== Lu | Ll | Lt | Lm | Lo
Sm 数学符号
Sc 货币符号

比较遗憾的是,目前还没有专门的 Emoji 类别。因此,目前如果你想匹配 Emoji 表情的话,还是需要写 Unicode 范围表达式,诸如 [\x{1F600}-\x{1F6FF}|[\x{2600}-\x{26FF}],来匹配表情符号。

常用 Unicode 命名块

名称 描述
Greek 希腊语
Han 汉语
Tibetan 藏语
Thai 泰语
Latin 拉丁语
Hebrew 希伯来语

扩展阅读