
什么是 Serverless 架构?按照 CNCF 对 Serverless 计算的定义,Serverless 架构应该是采用 FaaS(函数即服务)和 BaaS(后端服务)服务来解决问题的一种设计。这个定义让我们对 Serverless 的理解稍显清晰,同时可能也造成了一些困扰和争论。 随着需求和技术的发展,业界出现了一些 FaaS 以外的其它形态的 Serverless 计算服务,比如 Google Cloud Run,阿里云推出的面向应用的 Serverless 应用引擎服务以及 Serverless K8s,这些服务也提供了弹性伸缩能力和按使用计费的收费模式,具备 Serverless 服务的形态,可以说进一步扩大了 Serverless 计算的阵营; 为了消除冷启动影响,FaaS 类服务如阿里云的函数计算和 AWS 的 Lambda 相继推出了预留功能,变得不那么“按使用付费”了; 一些基于服务器(Serverful)的后端服务也推出了 Serverless 形态产品,比如 AWS Serverless Aurora,阿里云 Serverless HBase 服务。 这样看来,Serverless 的界线是有些模糊的,诸多云服务都向着 Serverless 方向演进。一个模糊的东西如何指导我们解决业务问题呢?Serverless 有一个根本的理念是一直没有改变的,即让用户最大化地专注业务逻辑,其它的特征如不关心服务器、自动弹性、按使用计费等,都是为了实现这个理念而服务。 著名的 Serverless 实践者 Ben Kehoe 这样描述 Serverless 原生心智,当我们在业务中考虑做什么时可以体会一下这种心智: 我的业务是什么? 做这件事情能不能让我的业务出类拔萃? 如果不能,我为什么要做这件事情而不是让别人来解决这个问题? 在解决业务问题之前没有必要解决技术问题。 在实践 Serverless 架构时,最重要的心智不是选择哪些流行服务和技术,攻克哪些技术难题,而是时刻将专注业务逻辑铭记在心,这样更容易让我们选择合适的技术和服务,明确如何设计应用架构。人的精力是有限的,组织的资源是有限的,Serverless 的理念可以让我们更好地用有限的资源解决真正需要解决的问题,正是因为我们少做了一些事情,转而让别人做这些事情,我们才可以在业务上做的更多。 接下来我们介绍一些常见的场景,并探讨如何使用 Serverless 架构支持这些场景。我们主要会采用计算、存储和消息通信等技术来设计架构,从可运维性、安全性、可靠性、可扩展性、成本几个角度来衡量架构的优劣。为了让这种讨论不过于抽象,我们会用一些具体的服务作为参考,但是这些架构的思想是通用的,可以用其它类似产品实现。 假如我们要做一个信息展示的网站,需求很简单,就像早年的中国黄页那样,信息更新很少,大概有以下几种主要选择: 买台服务器放在 IDC 机房里托管,运行站点; 去云厂商上买台云服务器运行站点,为了解决高可用的问题又买了负载均衡服务和多个服务器; 采用静态站点方式,直接由对象存储服务(如 OSS)支持,并使用 CDN 回源 OSS。 这三种方式由云下到云上,由管理服务器到无需管理服务器,即 Serverless。这一系列的转变给使用者带来了什么变化呢?前两种方案需要预算,需要扩展,需要实现高可用,需要自行监控等,这些都不是马老师当年想要的,他只想去展示信息,让世界了解中国,这是他的业务逻辑。Serverless 正是这样一种理念,最大化地让人去专注业务逻辑。第三种方式就是采用了 Serverless 架构去构建一个静态站点,它有其它方案无法比拟的优势,比如: 可运维性:无需管理服务器,比如操作系统的安全补丁升级、故障升级、高可用性,这些云服务(OSS,CDN)都帮着做了; 可扩展性:无需对资源做预估和考虑未来的扩展,因为 OSS 本身是弹性的,使用 CDN 使得系统延迟更小、费用更低、可用性更高; 成本:按实际使用的资源付费,包括存储费用和请求费用,没有请求时不收取请求费用; 安全性:这样一个系统甚至看不到服务器,不需要通过 SSH 登录,DDoS 攻击也交给云服务来解决。 静态页面和站点适合用于内容少、更新频率低的场景,反之,就需要动态站点了。比如淘宝的商品页面,采用静态页面方式管理商品信息是不现实的。如何根据用户请求动态地返回结果呢?我们来看两种常见的解决方案: Web 单体应用:所有的应用逻辑都在一个应用中完成,结合数据库,这种分层架构可以快速实现一些复杂度较低的应用; 微服务应用:随着业务发展,功能多了,访问量高了,团队大了,这时候一般就需要将单体应用中的逻辑拆分成多个执行单元,比如商品页面上的评论信息、售卖信息、配送信息等,都可以对应一个单独的微服务。这种架构的好处是每个单元是高度自治的,易于开发(比如使用不同技术)、部署和扩展。但是这种架构也引入了分布式系统的一些问题,如服务间通信的负载均衡、失败处理等。 处在不同阶段不同规模的组织可以选择适合自身的方式,来解决它面临的首要业务问题,淘宝最初被人们接受一定不是因为它使用了哪种技术架构。但是无论选择哪种架构,上面提到的 Serverless 原生心智都有助于我们专注业务。比如: 是否需要自己购置服务器安装数据库,实现高可用、管理备份、升级版本等,还是可以把这些事情交给托管的服务如 RDS;是否可以使用表格存储、Serverless HBase 等 Serverless 数据库服务,实现按使用的弹性扩容缩容和付费; 单体应用是需要自己购置服务器运行,还是可以交给托管服务,如函数计算和 Serverless 应用引擎; 是否可以通过函数来实现轻量级微服务,依赖函数计算提供的负载均衡、自动伸缩、按需付费、日志采集、系统监控等能力; 基于 Spring Cloud、Dubbo、HSF 等实现的微服务应用是否需要自己购置服务器部署应用,管理服务发现,负载均衡,弹性伸缩,熔断,系统监控等,还是可以将这些工作交给诸如 Serverless 应用引擎服务。 上图右侧的架构引入了 API 网关、函数计算或者 Serverless 应用引擎来实现计算层,将大量的工作交给了云服务完成,让用户最大程度上专注实现业务逻辑。其中系统内部多个微服务的交互如下图所示,通过提供一个商品聚合服务,将内部的多个微服务统一呈现给外部。这里的微服务可以通过 SAE 或者函数实现。 这样的架构还可以继续扩展,比如如何支持不同客户端的访问,如上图右侧所示。现实中这种需求是常见的,不同的客户端需要的信息可能是不同的,手机可以根据位置信息做相关推荐。如何让手机客户端和不同浏览器都能受益于 Serverless 架构呢?这又牵扯出了另一个词——Backend for fronted(BFF),即为前端定做的后端,这受到了前端开发工程师的推崇,Serverless 技术让这个架构广泛流行,因为前端工程师可以从业务角度出发直接编写 BFF,而无需管理服务器相关的令前端工程师更加头疼的事情。 事件触发 前面提到的动态页面生成是同步请求完成的,还有一类常见场景,其中请求处理通常需要较长时间或者较多资源,比如用户评论中的图片和视频内容管理,涉及到如何上传图片和处理图片(缩略图、水印、审核等)及视频,以适应不同客户端的播放需求。 如何对上传多媒体文件实时处理呢?这个场景的技术架构大体经历了以下演变: 基于服务器的单体架构:多媒体文件被上传到服务器,由服务器处理,对多媒体的显示请求也由服务器完成; 基于服务器的微服务架构:多媒体文件被上传到服务器,服务器处理转存到 OSS,然后将文件地址加入消息队列,由另一组服务器处理文件,将处理结果保存到 OSS,对多媒体的显示请求由 OSS 和 CDN 完成; Serverless 架构:多媒体直接上传到 OSS,由 OSS 的事件触发能力直接触发函数,函数处理结果保存到 OSS,对多媒体的显示请求由 OSS 和 CDN 完成。 基于服务器的单体架构面临以下问题: 如何处理海量文件?单台服务器空间有限,购买更多的服务器; 如何扩展 Web 应用服务器?Web 应用服务器是否适合 CPU 密集型任务? 如何解决上传请求的高可用? 如果解决显示请求的高可用? 如何应对请求负载的波峰波谷? 基于服务器的微服务架构很好地解决了上述的大部分问题,但是仍然面临一些问题: 管理应用服务器的高可用性和弹性; 管理文件处理服务器的弹性; 管理消息队列的弹性。 而第三种 Serverless 架构很好地解决了上述所有问题。开发人员原来需要做的负载均衡、服务器的高可用和弹性伸缩、消息队列都转移到了服务内部。我们可以看到随着架构的演进,开发人员做的事情越来越少,系统更加成熟,业务上更加聚焦,大大提升了交付速度。 这里的 Serverless 架构主要体现的价值是: 事件触发能力:函数计算服务与事件源(OSS)的原生集成让使用者无需管理队列资源,队列自动扩展,实时处理上传的多媒体文件; 高弹性和按需付费:图片和视频(不同大小的视频)需要的计算资源规格是不同的,流量的波峰波谷对资源的需求是不同的,现在这种弹性由服务提供,按照用户的真实使用去扩容缩容,让用户 100% 地利用资源,无需为闲置资源付费。 事件触发能力是 FaaS 服务的一个重要特性,这种 Pub-Sub 事件驱动模式不是一个新的概念,但是在 Serverless 流行之前,事件的生产者、消费者以及中间的连接枢纽都是用户负责的,就像前面架构演进中的第二个架构。 Serverless 让生产者发送事件,维护连接枢纽都从用户职责中省略了,而只需关注消费者的逻辑,这就是 Serverless 的价值所在。 函数计算服务还集成其它云服务事件源,让你更方便地在业务中使用一些常见的模式,如 Pub/Sub、事件流模式、Event Sourcing 模式。关于更多的函数组合模式可以参见:函数组合的 N 种方式。 服务编排 前面的商品页面虽然复杂,但是所有的操作都是读操作,聚合服务 API 是无状态、同步的。我们来看一下电商中的一个核心场景——订单流程。 这个场景涉及到多个分布式写的问题,这是引入微服务架构导致的最麻烦的一个问题。单体应用在一定程度上可以比较容易地处理这个流程,因为使用了一个数据库,可以通过数据库事务保持数据一致性。但是现实中可能不得不去跟一些外部服务打交道,需要一定的机制保证流程的前进和回退顺利完成,解决这个问题的一个经典模式是 Saga 模式,而实现这种模式有两种不同架构: 一种做法是采用事件驱动模式,驱动流程完成。在这个架构里,有一个消息总线,感兴趣的服务如库存服务监听事件,监听者可以使用服务器或者函数。借助于函数计算和消息主题的集成,这个架构也可以完全不使用服务器。 这个架构模块是松耦合的,职责清晰。不足之处是随着流程变得更长更加复杂,这个系统变得难以维护。比如很难直观地了解业务逻辑,执行时的状态也不宜跟踪,可运维性比较差。 另外一种架构是基于工作流的 Saga 模式。在这个架构里,各个服务之间是独立的,也不通过事件传递信息,而是有一个集中的协调者服务来调度单个业务服务,业务逻辑和状态由集中协调者维护。而实现这个集中的协调者通常面临以下问题: 编写大量代码来实现编排逻辑、状态维护和错误重试等功能,而这些实现又很难被其它应用重用; 维护运行编排应用的基础设施,以确保编排应用的高可用性和可伸缩性; 考虑状态持久性,以支持多步骤长时间运行流程并确保流程的事务性。 依赖于云服务,比如阿里云的 Serverless 工作流服务,这些事情都可以交给平台来做,用户又回到了只需关注业务逻辑的状态。 下图右侧是流程定义,我们可以看到这实现了前面基于事件的 Saga 模式的效果,并且流程大大简化,提升了可观测性。 数据流水线 随着业务的进一步发展,数据变得越来越多,这时候就可以挖掘数据的价值。比如,分析用户对网站的使用行为并做相应的推荐。一个数据流水线包括数据采集、处理、分析等多个环节。这样的服务如果从头搭建虽然是可行的,但是也是复杂的,我们这里讨论的业务是电商,而不是去提供一个数据流水线服务。有了这样一个目标,我们做选择时就会变得简单明确。 日志服务(SLS)提供了数据采集、分析和投递功能; 函数计算(FC)可以对日志服务的数据进行实时处理,将结果写入其它服务,如日志服务、OSS; Serverless 工作流服务可以定时批量处理数据,通过函数定义灵活的数据处理逻辑,构建 ETL 作业; 数据湖分析(DLA)提供了 Serverless 化的交互式查询服务,它使用标准 SQL分析对象存储(OSS)、数据库(PostgreSQL / MySQL等)、NoSQL(TableStore 等)等多个数据源的数据。 总结 限于篇幅,我们只讨论了 Serverless 架构在几个场景中的应用,但是在实践中我们可以看出一种共性,即如何将业务逻辑中与业务不相关的工作剥离出去,交给平台和服务完成。这种各司其职、分工协作的做法在其它场合并不陌生,但是 Serverless 的思想让这种形态更为明确。Less is more,少的不只是 Server 和围绕 Server 相关的负担,还可以是业务以外的方方面面,多的是专注的业务和产品的核心竞争力。 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
什么是函数计算? 大家都了解,Serverless 并不是没有服务器,而是开发者不再需要关心服务器。下图是一个应用从开发到上线的对比图: 在传统 Serverful 架构下,部署一个应用需要购买服务器,部署操作系统,搭建开发环境,编写代码,构建应用,部署应用,配置负载均衡机制,搭建日志分析与监控系统,应用上线后,继续监控应用的运行情况。而在 Serverless 架构下,开发者只需要关注应用的开发构建和部署,无需关心服务器相关操作与运维,在函数计算架构下,开发者只需要编写业务代码并监控业务运行情况。这将开发者从繁重的运维工作中解放出来,把精力投入到更有意义的业务开发上。 上图展示了函数计算的使用方式。从用户角度,他需要做的只是编码,然后把代码上传到函数计算中。上传代码就意味着应用部署。当有高并发请求涌入时,开发者也无需手动扩容,函数计算会根据请求量毫秒级自动扩容,弹性可靠地运行任务,并内置日志查询、性能监控、报警等功能帮助开发者发现问题并定位问题。 函数计算核心优势 1. 敏捷开发 使用函数计算时,用户只需聚焦于业务逻辑的开发,编写最重要的 “核心代码”; 不再需要关心服务器购买、负载均衡、自动伸缩等运维操作; 极大地降低了服务搭建的复杂性,有效提升开发和迭代的速度。 2. 弹性扩容 函数计算根据请求量自动进行弹性扩容,无需任何手动配置; 毫秒级调度计算资源,轻松应对业务洪峰。 3. 稳定高可用 函数计算分布式集群化部署,支持多可用区; 如果某个可用区因自然灾害或电力故障导致瘫痪,函数计算会迅速切换到同区域其他可用区的基础设施运行函数,确保服务高可用。 4. 有竞争力的成本 函数计算提供了丰富的计量模式,帮助您在不同场景获得显著成本优势; 后付费模型按实际使用计算资源计费,不占用计算资源则不计费,资源利用率高达 100% ; 预付费模型根据业务负载估算提前预购计算力,单价更低,组合使用后付费和预付费方式将有效降低成本。 函数计算使用场景 从使用场景来说,主要有三类: Web 应用:可以是各种语言写的,这种可以是使用 Serverless 框架新编写的程序,也可以是已有的应用。比如可能是小程序后端,也可能是 Web API;- 对计算能力有很强的弹性诉求的应用:比如 AI 推理、音视频处理、图文转换等; 事件驱动型的应用:比如通过其他阿里云产品驱动的场景,Web Hook、定时任务等。 函数计算已经与很多产品进行了打通,比如对象存储、表格存储、定时器、CDN、日志服务、云监控等十几个产品,可以非常快速地组装出一些业务逻辑。 函数计算工作原理 1. 函数计算调用链路 上图展示了函数计算完整的请求和调用链路。函数计算是事件驱动的无服务器应用,事件驱动是说可以通过事件源自动触发函数执行,比如当有对象上传至 OSS 中时,自动触发函数,对新上传的图片进行处理。函数计算支持丰富的事件源类型,包括日志服务、对象存储、表格存储、消息服务、API 网关、CDN 等。 除了事件触发外,也可以直接通过 API/SDK 直接调用函数。调用可以分为同步调用与异步调用,当请求到达函数计算后,函数计算会为请求分配执行环境,如果是异步调用,函数计算会将请求事件存入队列中,等待消费。 2. 函数计算调用方式 同步调用的特性是,客户端期待服务端立即返回计算结果。请求到达函数计算时,会立即分配执行环境执行函数。 以 API 网关为例,API 网关同步触发函数计算,客户端会一直等待服务端的执行结果,如果执行过程中遇到错误, 函数计算会将错误直接返回,而不会对错误进行重试。这种情况下,需要客户端添加重试机制来做错误处理。 异步调用的特性是,客户端不急于立即知道函数结果,函数计算将请求丢入队列中即可返回成功,而不会等待到函数调用结束。 函数计算会逐渐消费队列中的请求,分配执行环境,执行函数。如果执行过程中遇到错误,函数计算会对错误的请求进行重试,对函数错误重试三次,系统错误会以指数退避方式无限重试,直至成功。 异步调用适用于数据的处理,比如 OSS 触发器触发函数处理音视频,日志触发器触发函数清洗日志,都是对延时不敏感,又需要尽可能保证任务执行成功的场景。如果用户需要了解失败的请求并对请求做自定义处理,可以使用 Destination 功能。 3. 函数计算执行过程 函数计算是 Serverless 的,这不是说无服务器,而是开发者无需关心服务器,函数计算会为开发者分配实例执行函数。 如上图所示,当函数第一次被调用的时候,函数计算需要动态调度实例、下载代码、解压代码、启动实例,得到一个可执行函数的代码环境。然后才开始在系统分配的实例中真正地执行用户的初始化函数,执行函数业务逻辑。这个调度实例启动实例的过程,就是系统的冷启动过程。 函数逻辑执行结束后,不会立即释放掉实例,会等一段时间,如果在这段时间内有新的调用,会复用这个实例,比如上图中的 Request 2,由于执行环境已经分配好了,Request 2 可以直接使用,所以 Request 2 就不会遇到冷启动。 Request 2 执行结束后,等待一段时间,如果这段时间没有新的请求分配到这个实例上,那系统会回收实例,释放执行环境。此实例释放后,新的请求 Request 3 来到函数计算,需要重新调度实例、下载代码、解压代码,启动实例,又会遇到冷启动。 所以,为了减小冷启动带来的影响,要尽可能避免冷启动,降低冷启动带来的延时。 使用预留实例可以完全避免冷启动,预留实例是在用户预留后就分配实例,准备执行环境;请求结束后系统也不会自动回收实例。 预留实例不由系统自动分配与回收,由用户控制实例的生命周期,可以长驻不销毁,这将彻底消除实例冷启动带来的延时毛刺,提供极致性能,也为在线应用迁移至函数计算扫清障碍。 如果业务场景不适合使用预留实例,那就要设法降低冷启动的延时,比如降低代码包大小,可以降低下载代码包、解压代码包的时间。Initializer 函数是实例的初始化函数,Initializer 在同一实例中执行且只执行一次,所以可以将一些耗时的公共逻辑放到 Initializer 中,比如在 NAS 中加载依赖、建立连接等等。另外要尽量保持请求连续稳定,避免突发的流量,由于系统已启动的实例不足以支撑大量的突发流量,就会带来不可避免的冷启动。 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
今年 5 月,阿里云和微软云共同宣布,Open Application Model (OAM) 社区携手知名混合云管理项目 Crossplane 社区,联合发布了 OAM 在 Kubernetes 平台上的标准实现与核心依赖库。本次合作达成后,OAM 社区成功的将标准应用定义和标准化的云服务管理能力统一起来,迈出了实现真正意义上的无差别云端应用交付的关键一步 。 去年 10 月 ,阿里云和微软共同推出了 OAM 项目,旨在构建围绕 Kubernetes 的云原生应用规范。OAM 描述了一个模型 —— 开发人员可以在其中定义应用程序组件;应用程序操作员负责创建这些组件的实例并为它们分配应用程序配置;基础架构运营商负责定义、安装和维护平台上可用的基础服务。 本次合作是阿里云、微软与 Crossplane 社区的三方技术合作,主要围绕 OAM 在 Kubernetes 上的标准实现以及 Crossplane 项目的 OAM 化展开。因为 Kubernetes 社区在落地 OAM 模型的过程中,提出了关于 OAM 标准实现的诉求。所以这次合作的一个重点,就是三方工程师使用 Go 语言开发了一个 OAM Kubernetes 核心依赖库。这个项目的名字叫做 oam-kubernetes-runtime。OAM Kubernetes Runtime 将会成为 OAM 社区官方维护的基础组件,目标是在 Kubernetes 上提供稳定且统一的 OAM 核心插件。 为进一步了解本次合作的细节以及 OAM 项目的现状,阿里云高级技术专家 Andy Shi 以及阿里云技术专家孙健波(花名:天元)接受开源中国的专访,共同探讨了 OAM 项目存在的意义。 详情戳:https://www.oschina.net/question/4487475_2317219 OAM 因何而生 我们知道,应用容器技术自诞生开始,就以 “彻底改变了软件打包与分发方式” 的魅力迅速征服了几乎所有的云厂商与数据中心。不过,软件打包与分发方式的革新,并没有能够让软件本身的定义与描述发生本质的变化,基于 K8s 的应用管理体验,也没有让业务研发与运维的工作变得更简单。 实际上,Kubernetes 带来的云原生技术革命,在于实现了基础设施层的标准化和抽象,但这一层抽象距离业务研发与运维还是太过遥远了。一个最典型的例子,直到今天,Kubernetes 里面始终都没有 “应用” 这个概念,它提供的是更细粒度的 “工作负载” 原语,比如 Deployment 或者 DaemonSet。 而在实际环境中,一个应用往往是由一系列独立组件的组合,比如一个 “PHP 应用容器” 和一个 “数据库实例” 组成的电商网站;一个 “参数服务节点” 和一个 “工作节点” 组成的机器学习训练任务;一个由 “Deployment + StatefulSet + HPA + Service + Ingress” 组成的微服务应用。 “应用” 这个概念在 Kubernetes 项目中的缺失,既是一个有意而为之的设计,却也造成了今天云原生应用管理生态的极度碎片化和极高的学习门槛。如何通过标准化的方式去解决这个 “Kubernetes 里到底什么是应用” 的问题,正是 OAM 项目发布的最初始动机。 有什么意义? 在 OAM 发布之前,云原生生态里其实并没有一个叫做 “应用” 的概念。哪怕在今天,全世界几乎每一个在落地云原生的团队,都有一个自己定义的 “应用” 的概念,它们的抽象程度层次不齐,定义方式也丰富多样,这就导致了所有围绕着这些 “应用” 构建出来的系统,就成为了一个又一个的大烟囱。 对于整个云原生生态来说,这种应用层的碎片化和烟囱化,其实对于整个生态演进是非常不利的。而今天的现状也已经证明了这一点,在 Kubernetes 逐渐标准化了基础设施能力的接入方式之后,原本更加接近用户、更加重要的应用管理层,却几乎停滞了演进,在最近几年里没有提出任何一个创新性的思想出来。 应用管理层停滞不前的结果,就是全世界的业务研发和运维一夜之间都被迫变成了 “容器专家”,一边学习着根本不应该是他们关心的各种 “基础设施即数据(Infrastructure as Data)” 领域的概念(比如:声明式 API,控制器等),一边吐槽 Kubernetes 实在是太复杂了、设计太奇葩了。 简而言之,Kubernetes 作为一个面向基础设施工程师的系统级项目,主要负责提供松耦合的基础设施语义,这就使得用户学习和操作 Kubernetes YAML 文件的时候,往往会感觉这些文件里的关注点非常底层,学习门槛很高。 实际上,对于Kubernetes 真正的最终用户比如业务研发人员和运维人员来说,他们并不想配置这些如此底层的资源信息,而是希望有更高维度的抽象。这就要求一个真正面向最终用户侧的应用定义,需要能够为业务研发和应用运维人员提供各自视角的应用定义原语。所以说,OAM 带来的第一个改变,就是提供了一种大家都可以遵循的、标准化的方式来定义更高层级的应用层抽象,并且把“关注点分离”作为这个定义模型的核心思想。 而 OAM 带来的第二个变化,则是为 Kubernetes 项目带来了应用定义,更确切地说,是对应用本身和它所需运维能力进行定义与描述的标准开源规范。站在 Kubernetes 项目的角度来讲,OAM 是一个 Kubernetes 原生的标准的“应用定义”项目,同时也是一个专注于封装、组织和管理 Kubernetes 中各种“运维能力”、以及连接“运维能力”与“应用”的平台层框架。 详细的说,OAM 基于 Kubernetes API 资源模型(Kubernetes Resource Model)来标准化应用定义的规范,它强调一个现代应用是多个组件的集合,而非一个简单的工作负载或者 K8s Operator。所以在 OAM 的语境中,一个 PHP 容器和它所依赖的数据库,以及它所需要使用的各种云服务,都是一个“电商网站”应用的组成部分。更进一步的,OAM 把这个应用所需的“运维策略”也认为是一个应用的一部分,比如这个 PHP 容器所需的 HPA(水平自动扩展策略): 以 Crossplane 项目为例,它在本次合作中通过 OAM 升级之后得到了怎样的变化呢? “ 作为混合云管理领域中的佼佼者,Crossplane 的 OAM 化保证了今天任何一个符合 OAM 规范的待运行程序、运维能力和它所依赖的云服务,可以组成一个整体在混合云环境中无缝漂移。” 这种平台无关的应用定义范式,使得应用研发人员只需要通过 OAM 规范来描述他们的应用程序,那么该应用程序就可以在任何 Kubernetes 群集或者 Serverless 应用平台甚至边缘环境上运行,而无需对应用描述做任何修改。本次合作中 Crossplane OAM 版的发布,则意味着 OAM 社区正在将标准应用定义和标准化的云服务管理能力统一起来,从而实现真正的 “云端应用交付” 。 OAM 如何发挥作用? 那么 OAM 在一个项目中是如何运作的呢? 据介绍,OAM 以原生插件的方式运行在 Kubernetes 当中。OAM 强调整个模型是关注点分离的。即业务研发人员负责定义和维护组件 (Component) 来描述服务单元,而运维人员定义运维特征 (Trait),并将其附加到前面的组件上,最后构成 OAM 可交付物 ——ApplicationConfiguration。 这种设计是 OAM 在能够无限接入 Kubernetes 各种能力的同时,保证给业务研发与运维人员提供最佳的使用体验和最低的心智负担的重要基础。与此同时,基础设施工程师可以随时在 Kubernetes 中添加更多工作负载(例如 FaaS)以运行无服务器功能,或者添加运维特性(例如 CronHPA)来定义 CronJob 类型的 HPA 策略。OAM 以标准的声明方式在整个平台中管理应用交付能力和流程,并且提供面向各个角色的 API 原语来表达各自的诉求,最后通过 Kubernetes 把这些诉求落实。 什么样的项目需要 OAM? 实际上,几乎所有基于 Kubernetes 的应用管理平台都对通过 OAM 来以标准化的方式去构建自己的应用模型有明确的诉求。另一方面,由于 OAM 是原生的 Kubernetes API 资源模型,这里的迁移过程难度很低,可以通过 API 对象灰度纳管的方式逐步完成迁移操作(通过 OAM 对象逐步接管现有 Kubernetes 对象)。 而相比于传统 PaaS 封闭的、不能同 “以 Operator 为基础的云原生生态” 衔接的现状,基于 OAM 和 Kubernetes 构建的现代云原生应用管理平台,本质上是一个 “以应用为中心” 的 Kubernetes ,保证了这个应用平台在能够无缝接入整个云原生生态。同时,OAM 可以进一步屏蔽掉容器基础设施的复杂性和差异性,为平台的使用者带来低心智负担的、标准化的、一致的应用管理与交付体验。这就使得一个基于OAM 构建的 Kubernetes 应用平台,首先能够隐藏底层基础设施的细节(例如,是云还是物联网),专注于应用层抽象,提供以应用为中心的资源模型。 其次,OAM 划分了应用交付路径上的开发、运维、基础架构三种角色,分离了关注点,让流程更加清晰和易于管理。 第三,OAM 站在 K8s API 资源模型的肩膀之上,提供了可移植的应用与基础设施抽象,让一个应用描述可以完全不加修改的云、边、端等任何环境下直接交付运行起来。 除此之外,OAM 还定义了一组核心工作负载/运维特征/应用范畴,作为应用程序交付平台的基石。而平台开发者也可以添加更多工作负载(例如 FaaS 或者任意云服务),或者添加运维特性(例如 CronHPA)来定义 CronJob 类型的 HPA 策略。OAM 以标准的声明方式在整个平台中管理应用交付能力和流程。当模块化的 Workload 和 Trait 越来越多,就会形成组件市场。而 OAM 就像是这个组件市场的管理者,处理组件之间的关系,把许多组件集成起来变成一个产品交付给用户。OAM 加持下的 Kubernetes 应用管理平台,可以像乐高积木一样灵活组装底层能力、运维特征、以及开发组件。使得应用管理变得统一,功能却更加强大。 OAM 社区现状 谈到 OAM 项目社区的现状。“ 作为一个没有同商业诉求绑定的中立开源社区,OAM 生态自成立以来保持着较高的活跃度和参与度,大量的社区 Issue/PR贡献都来自阿里和微软之外的团队比如 AWS、腾讯、字节跳动、谐云、青云、好雨云、第四范式等生态参与者。除了阿里和微软本身以及基于 OAM 实现了内部应用管理架构的统一和标准化之外,不少基于 OAM 的云服务比如阿里云 EDAS 也已经上线。” 与此同时,OAM 技术体系也开始在很多大型社区用户(比如 MasterCard 万事达卡)中落地,同时也出现了产品和商业化的实践(比如:谐云的可视化OAM实现),甚至来自其它云厂商比如 AWS 的开源项目整合与对接。可以看到,OAM 社区正在迅速成长和壮大中。 开源社区的运作模式一直是我们比较好奇的地方。据介绍,OAM 项目目前完全由社区驱动,由各子项目的 Maintainer 小组进行维护和管理。社区有每两周一次的社区会议(美国和北京时间各一个)来进行重大事项的讨论与决策和同步项目进度。整个社区的的工作流程按照 Maintainer 席位的投票机制来运转,同时兼顾最终用户的投票权。目前 OAM 社区的核心 Maintainer 来自阿里云,微软和 Crossplane 项目原有的成员。在推广策略上,由多个国际化大厂团队维护的 OAM 项目从诞生起就是完全面向国际化开源社区的运作方式,凭借阿里与微软自身场景,以及整个云原生社区和贡献者的高质量输入来驱动整个项目向正确的方向持续演进,在沟通、分享、协作的氛围中鼓励贡献和发展社区。这种模式下,一旦突破早期破冰阶段,在随后社区传播和推广方面会带来病毒式的效果。 目前 OAM 的版本是 v1alpha2 ,OAM 的版本之后会不停迭代,根据实际的场景持续演进;当然,同时 spec 本身也会保证规范的稳定和兼容。这个标准的更新速度主要是取决于用户的接受程度和反馈的情况,并且会在今年发布 Beta 版。本次合作中,OAM 已经发布了 Kubernetes 的标准实现与核心依赖库,这也就意味着未来整个开源生态都可以直接通过对接 Crossplane 或者 oam-kubernetes-runtime 来支持 OAM 标准,所以这样的项目很快会越来越多。 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
大家都知道,应用开放模型 Open Application Model(OAM) 将应用的工作负载(Workload)分为三种 —— 核心型、标准型和扩展型,这三者的主要区别在于一个 OAM 平台对于具体某一类工作负载进行实现的自由度不同。其中,OAM 社区中目前唯一一个核心工作负载是 Containerized Workload,它用来描述一个基于容器的工作负载,可以理解为是 Kubernetes Deployment 的简化版(去掉了 PodSecurityPolicy 等大量与业务研发无关的字段)。 不过,很多读者可能会有疑问:对于 Kubernetes 内置的工作负载 OAM 是否还能直接支持呢? 答案当然是肯定的,而且这是 OAM 作为 Kubernetes 原生的应用定义模型的默认能力。 下面,本文就以 Deployment 为例,介绍如何使用 OAM 基于 Kubernetes 的内置工作负载来定义和管理云原生应用。 示例准备 基于 GitHub FoodTrucks (旧金山美味街边小吃地图应用)项目,构建镜像 zzxwill/foodtrucks-web:0.1.1,加上依赖的 Elasticsearch 镜像,在默认情况下,它的 Deployment 描述文件 food-truck-deployment.yaml 如下所示: apiVersion: apps/v1kind: Deploymentmetadata: name: food-trucks-deployment labels: app: food-trucks spec: selector: matchLabels: app: food-trucks template: metadata: labels: app: food-trucks spec: containers: - name: food-trucks-web image: zzxwill/foodtrucks-web:0.1.1 env: - name: discovery.type value: single-node ports: - containerPort: 5000 - name: es image: docker.elastic.co/elasticsearch/elasticsearch:6.3.2 ports: - containerPort: 9200 - containerPort: 9300 如果将上述 yaml 文件提交到 Kubernetes 集群,通过 port-forward 可以通过浏览器查看效果: 定义 Component 与 Workload 在 OAM 中, 一个应用是由多个 Component(组件)构成的,而一个 Component 里的核心字段,就是 Workload(工作负载)。 所以说,像 Kubernetes Deployment、StatefulSet 等内置的工作负载,其实天生就可以被定义为 OAM Component 中的 Workload。比如下面这个 sample-deployment-component.yaml 文件,可以看到,.spec.workload 的内容,就是一个 Deployment,也就是 food-truck-deployment.yaml 里定义的 Deployment。 接下来,我们就将上述 OAM Component 提交到 Kubernetes 集群验证一下。 部署这个应用 在 OAM 中,我们需要编写一个应用配置 ApplicationConfiguration 来组织所有的 OAM Component。由于只有一个 Component,本例中的 sample-applicationconfiguration.yaml 非常简单,如下所示: apiVersion: core.oam.dev/v1alpha2kind: ApplicationConfigurationmetadata: name: example-deployment-appconfigspec: components: componentName: example-deployment 提交 OAM Component 和 ApplicationConfiguration YAML 文件给 Kubernetes: ✗ kubectl apply -f sample-deployment-component.yamlcomponent.core.oam.dev/example-deployment created✗ kubectl apply -f sample-applicationconfiguration.yamlapplicationconfiguration.core.oam.dev/example-deployment-appconfig created 不过,如果这个时候你查看 example-deployment-appconfig 的执行情况,会发现如下报错: ✗ kubectl describe applicationconfiguration example-deployment-appconfigName: example-deployment-appconfig...Status: Conditions: Message: cannot apply components: cannot apply workload "food-trucks-deployment": cannot get object: deployments.apps "food-trucks-deployment" is forbidden: User "system:serviceaccount:crossplane-system:crossplane" cannot get resource "deployments" in API group "apps" in the namespace "default" Reason: Encountered an error during resource reconciliation ... 这是因为 OAM 的 Kubernetes 插件权限不足导致的,所以不要忘记设置合理的 ClusterRole 和 ClusterRoleBinding。 提交如下的授权文件 rbac.yaml,ApplicationConfiguration 可以执行成功。 继续查看 deployments,并设置端口转发: 通过 http://127.0.0.1:5000 就可以在旧金山美味街边小吃地图里找到汉堡包的店了: 什么时候使用 Deployment ? 看到这里,大家可能会有另一个疑问,那么我什么时候该使用 Deployment、什么时候该使用 ContainerizedWorkload 来作为 OAM 的工作负载呢? **其实,Deployment 和 ContainerizedWorkload 的主要区别,在于抽象程度不同。** 简单说,如果你的用户希望看到一个极简的、没有一些”乱七八糟“字段的 Deployment 的话;或者,你希望对你的用户屏蔽掉 Deployment 里面与用户无关的字段(比如:不想允许研发自行设置 PodSecurityPolicy),那你就应该给用户暴露 ContainerizedWorkload。这时候,这个工作负载需要的运维操作和策略,则是由另一个 OAM 对象 Traits(运维特征) 来定义的,比如 ManualScalerTrait。这种“关注点分离”的做法,也是 OAM 提倡的最佳实践。 反之,如果你的用户对 Deployment 里的各种运维、安全相关的字段并不排斥,你也不需要对用户屏蔽掉这些字段,那你大可以直接暴露 Deployment 出去。这个工作负载需要的其他运维能力,依然可以通过 OAM Traits 来提供。 为什么使用 OAM Component 来定义应用? 你有可能还有另外一个疑问,既然 OAM Component 里面的 Workload 就是 Kubernetes 里的各种 API 对象,那么使用 OAM 模型来定义应用又有哪些好处呢? 这就要说到 OAM 带来的好处了,相信大家在基于 Kubernetes 构建应用平台的时候,一定遇到过一系列的难题,比如依赖管理、版本控制、灰度发布等等,另一方面,应用平台为了跟云资源结合起来,纯粹使用 K8s 原生的 Workload 是做不到的。 而通过 OAM ,你不仅可以将云资源与应用统一描述,OAM 实现框架还将帮你解决了依赖管理、版本控制、灰度发布等一系列难题。这些我们将在后续的文章中为大家介绍。 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
OpenYurt介绍 5 月 29 号 OpenYurt 正式开源了。OpenYurt 作为公共云服务 ACK@Edge 的核心框架,已经应用于 CDN、音视频直播、物联网、物流、工业大脑、城市大脑等实际应用场景中,并服务于阿里云 LinkEdge、盒马、优酷、视频云等多个业务或项目中。目前开源的能力包括: 边缘自治能力 原生 K8s 集群一键式转换为边缘集群 边缘自治特性 1. 特性介绍 将 Kubernetes 系统延展到边缘计算场景,边缘节点将通过公网和云端连接,从公网的不稳定性以及成本等因素考虑,边缘要求断网状态或者弱网状态下边缘业务可以持续运行。我们从 Gartner 的边缘计算报告中提到的边缘计算诉求中,边缘自治也是主要诉求之一: 而从 Kubernetes 系统架构来看,主要因为 Slave Agent(Kubelet) 中的容器信息保存在内存中,断网状态下因为无法从云端获取业务数据,如果节点或者 Kubelet 重启,将无法进行业务容器恢复。如下图: 2. 边缘自治需要解决的问题 因此边缘自治在 Kubernetes 系统里,需要解决下面的问题: 问题 1: 节点异常或重启时,内存数据丢失,网络断连时业务容器无法恢复; 问题 2: 网络长时间断连,云端控制器对业务容器进行驱逐; 问题 3: 长时间断连后网络恢复时,边缘和云端数据的一致性保障。 1)问题 1 的解决方案 解决方案 1: 重构 kubelet 组件,复用或者参考 kubelet 的 checkpoint 功能,持久化容器业务数据到本地磁盘,网络断连状态下利用本地缓存数据实现业务恢复。 该方案经过重构 kubelet,成功解决边缘自治的需求,具备如下优点: 通过重构 kubelet,方便在 kubelet 中集成对端设备的管理能力; 通过重构 kubelet,可以对 kubelet 进行轻量化改造。此优点但是也意味着原生 kubelet 功能缺失的问题。 但是也有如下不足: 可维护性差: 侵入式修改 kubelet core,跟随社区版本升级非常困难,熟悉kubelet的同学都会有同感,kubelet 组件由于直接负责跟计算,存储,网络交互,所以其代码结构和逻辑异常复杂。因此持续维护一个深度修改过的 kubelet 的工作量可想而知; 可扩展性差: 因为自治能力直接做到重构的 kubelet 组件中,这样边缘节点如果其他组件(如网络组件)想复用边缘自治能力将面临重复造轮子的境地; 场景耦合更深: 例如在 kubelet 重构中增加了 IOT 设备管理,将可能使 kubelet 和 IOT 场景深度耦合。 解决方案 2 (OpenYurt使用方案): 在边缘节点上增加 web 缓存及请求代理 hub(取名为 YurtHub,商业产品中名为 edge-hub),边缘侧组件(kubelet)和云端通信将经由该组件。YurtHub 相当于带有数据缓存功能的”透明网关“,和云端网络断连状态下,如果节点或者 kubelet 重启,将从 YurtHub 中获取到业务容器相关数据,有效解决边缘自治的问题。 相比解决方案 1,有如下优势: kubelet 零修改,意味原生 kubelet 能力天然具备,同时跟随 Kubernetes 版本升级零负担; 可扩展性强,节点其他组件轻松复用 YurtHub; 与 Kubernetes 设计理念契合,YurtHub 非常容易扩展出更多的能力。 当然 OpenYurt 的解决方案,也会面临如下的问题:原生 kubelet 比较 high-weight,在资源紧张场景下应用会比较挑战。目前商业产品中节点规格推荐 2U4G 起步。 2)问题 2 和 3 的解决方案 问题 2 和问题 3 的解决方案相比比较简单,因此不展开做过多的方案说明和比较。 问题 2:原生云端组件 kube-controller-manager 对 Pod 驱逐解决 该问题正是通过开源组件 yurt-controller-manager 中的 Node Controller 来解决的。如 github 主页介绍所示: 问题 3: 网络恢复时,边缘和云端网络一致性 Kubernetes 系统中,用户是通过云端对边缘进行管控的(如应用部署,升级,扩缩容等),因此当边缘和云端网络断联时,边缘节点将不会从云端同步用户对节点的管控操作,因此断网期间,只要 YurtHub 保持本地缓存数据和断网时刻一致(即断网期间边缘缓存数据不更新),而网络恢复时,再从云端同步最新数据即可。 后续展开 OpenYurt 刚刚开源,也意味这块工作刚刚开始,相信我们更贴近云原生的架构设计,会支持 OpenYurt 走的更远。同时 OpenYurt 设计理念: Extending your native Kubernetes to edge,相信也会让云原生爱好者更为接受。 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
dubbo 是一个基于 Java 开发的高性能的轻量级 RPC 框架,dubbo 提供了丰富的服务治理功能和优秀的扩展能力。而 dubbo-go 在 java 与 golang 之间提供统一的服务化能力与标准,是涂鸦智能目前最需要解决的主要问题。本文分为实践和快速接入两部分,分享在涂鸦智能的 dubbo-go 实战经验,意在帮助用户快速接入 dubbo-go RPC 框架,希望能让大家少走些弯路。另外,文中的测试代码基于 dubbo-go版本 v1.4.0。 dubbo-go 网关实践 dubbo-go 在涂鸦智能的使用情况如上图,接下来会为大家详细介绍落地细节,希望这些在生产环境中总结的经验能够帮助到大家。 1. 背景 在涂鸦智能,dubbo-go 已经作为了 golang 服务与原有 dubbo 集群打通的首选 RPC 框架。其中比较有代表性的 open-gateway 网关系统(下文统一称 gateway,开源版本见 https://github.com/dubbogo/dubbo-go-proxy)。该 gateway 动态加载内部 dubbo 接口信息,以HTTP API 的形式对外暴露。该网关意在解决上一代网关的以下痛点。 通过页面配置 dubbo 接口开放规则,步骤繁琐,权限难以把控; 接口非 RESTful 风格,对外部开发者不友好; 依赖繁重,升级风险大; 并发性能问题。 2. 架构设计 针对如上痛点,随即着手准备设计新的 gateway 架构。首先就是语言选型,golang 的协程调用模型使得 golang 非常适合构建 IO 密集型的应用,且应用部署上也较 java 简单。 经过调研后我们敲定使用 golang 作为 proxy 的编码语言,并使用 dubbo-go 用于连接 dubbo provider 集群。provider 端的业务应用通过使用 java 的插件,以注解形式配置 API 配置信息,该插件会将配置信息和 dubbo 接口元数据更新到元数据注册中心(下图中的 redis )。这样一来,配置从管理后台页面转移到了程序代码中。开发人员在编码时,非常方便地看到 dubbo 接口对外的 API 描述,无需从另外一个管理后台配置 API 的使用方式。 3. 实践 从上图可以看到,网关能动态加载 dubbo 接口信息,调用 dubbo 接口是基于 dubbo 泛化调用。泛化调用使 client 不需要构建 provider 的 interface 代码,在 dubbo-go 中表现为无需调用 config.SetConsumerService 和 hessian.RegisterPOJO 方法,而是将请求模型纯参数完成,这使得 client 动态新增、修改接口成为可能。在 apache / dubbo-sample / golang / generic / go-client 中的有泛化调用的演示代码。 func test() { var appName = "UserProviderGer" var referenceConfig = config.ReferenceConfig{ InterfaceName: "com.ikurento.user.UserProvider", Cluster: "failover", Registry: "hangzhouzk", Protocol: dubbo.DUBBO, Generic: true, } referenceConfig.GenericLoad(appName) // appName is the unique identification of RPCService time.Sleep(3 * time.Second) resp, err := referenceConfig.GetRPCService().(*config.GenericService). Invoke([]interface{}{"GetUser", []string{"java.lang.String"}, []interface{}{"A003"}}) if err != nil { panic(err) } } 泛化调用的实现其实相当简单。其功能作用在 dubbo 的 Filter 层中。Generic Filter 已经作为默认开启的 Filter 加入到 dubbo Filter 链中。其核心逻辑如下: func (ef *GenericFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { if invocation.MethodName() == constant.GENERIC && len(invocation.Arguments()) == 3 { oldArguments := invocation.Arguments() if oldParams, ok := oldArguments[2].([]interface{}); ok { newParams := make([]hessian.Object, 0, len(oldParams)) for i := range oldParams { newParams = append(newParams, hessian.Object(struct2MapAll(oldParams[i]))) } newArguments := []interface{}{ oldArguments[0], oldArguments[1], newParams, } newInvocation := invocation2.NewRPCInvocation(invocation.MethodName(), newArguments, invocation.Attachments()) newInvocation.SetReply(invocation.Reply()) return invoker.Invoke(ctx, newInvocation) } } return invoker.Invoke(ctx, invocation) } Generic Filter 将用户请求的结构体参数转化为统一格式的 map(代码中的 struct2MapAll ),将类( golang 中为 struct )的正反序列化操作变成 map 的正反序列化操作。这使得无需 POJO 描述通过硬编码注入 hessain 库。 从上面代码可以看到,泛化调用实际需要动态构建的内容有 4 个,ReferenceConfig 中需要的 InterfaceName、参数中的 method、ParameterTypes、实际入参 requestParams。 那么这些参数是如何从 HTTP API 匹配获取到的呢? 这里就会用到上文提到的 provider 用于收集元数据的插件。引入插件后,应用在启动时会扫描需要暴露的 dubbo 接口,将 dubbo 元数据和 HTTP API 关联。插件使用方法大致如下,这里调了几个简单的配置作为示例,实际生产时注解内容会更多。 最终获得的 dubbo 元数据如下: { "key": "POST:/hello/{uid}/add", "interfaceName": "com.tuya.hello.service.template.IUserServer", "methodName": "addUser", "parameterTypes": ["com.tuya.gateway.Context", "java.lang.String", "com.tuya.hello.User"], "parameterNames": ["context", "uid", "userInfo"], "updateTimestamp": "1234567890", "permissionDO":{}, "voMap": { "userInfo": { "name": "java.lang.String", "sex": "java.lang.String", "age": "java.lang.Integer" } }, "parameterNameHumpToLine": true, "resultFiledHumpToLine": false, "protocolName": "dubbo", .......} Gateway 从元数据配置中心订阅到以上信息,就能把一个 API 请求匹配到一个 dubbo 接口。再从 API 请求中抓取参数作为入参。这样功能就完成了流量闭环。 以上内容,大家应该对此 gateway 的项目拓扑结构有了清晰的认知。我接着分享项目在使用 dubbo-go 过程中遇到的问题和调优经验。19 年初,当时的 dubbo-go 项目还只是构建初期,没有什么用户落地的经验。我也是一边参与社区开发,一边编码公司内部网关项目。在解决了一堆 hessain 序列化和 zookeeper 注册中心的问题后,项目最终跑通了闭环。但是,作为一个核心应用,跑通闭环离上生产环境还有很长的路要走,特别是使用了当时稳定性待测试的新框架。整个测试加上功能补全,整整花费了一个季度的时间,直到项目趋于稳定,压测效果也良好。单台网关机器( 2C 8G )全链路模拟真实环境压测达到 2000 QPS。由于引入了比较重的业务逻辑(单个请求平均调用 3 个 dubbo 接口),对于这个压测结果,是符合甚至超出预期的。 总结了一些 dubbo-go 参数配置调优的经验,主要是一些网络相关配置。 大家在跑 demo 时,应该会看到配置文件最后有一堆配置,但如果对 dubbo-go 底层网络模型不熟悉,就很难理解这些配置的含义。目前 dubbo-go 网络层以 getty 为底层框架,实现读写分离和协程池管理。getty 对外暴露 session 的概念,session 提供一系列网络层方法注入的实现,因为本文不是源码解析文档,在这里不过多论述。读者可以简单的认为 dubbo-go 维护了一个 getty session池,session 又维护了一个 TCP 连接池。对于每个连接,getty 会有读协程和写协程伴生,做到读写分离。这里我尽量用通俗的注释帮大家梳理下对性能影响较大的几个配置含义: protocol_conf: # 这里是协议独立的配置,在dubbo协议下,大多数配置即为getty session相关的配置。 dubbo: # 一个session会始终保证connection_number个tcp连接个数,默认是16, # 但这里建议大家配置相对小的值,一般系统不需要如此多的连接个数。 # 每隔reconnect_interval时间,检查连接个数,如果小于connection_number, # 就建立连接。填0或不填都为默认值300ms reconnect_interval: 0 connection_number: 2 # 客户端发送心跳的间隔 heartbeat_period: "30s" # OnCron时session的超时时间,超过session_timeout无返回就关闭session session_timeout: "30s" # 每一个dubbo interface的客户端,会维护一个最大值为pool_size大小的session池。 # 每次请求从session池中select一个。所以真实的tcp数量是session数量*connection_number, # 而pool_size是session数量的最大值。测试总结下来一般程序4个tcp连接足以。 pool_size: 4 # session保活超时时间,也就是超过session_timeout时间没有使用该session,就会关闭该session pool_ttl: 600 # 处理返回值的协程池大小 gr_pool_size: 1200 # 读数据和协程池中的缓冲队列长度,目前已经废弃。不使用缓冲队列 queue_len: 64 queue_number: 60 getty_session_param: compress_encoding: false tcp_no_delay: true tcp_keep_alive: true keep_alive_period: "120s" tcp_r_buf_size: 262144 tcp_w_buf_size: 65536 pkg_wq_size: 512 tcp_read_timeout: "1s" # 每次读包的超时时间 tcp_write_timeout: "5s" # 每次写包的超时时间 wait_timeout: "1s" max_msg_len: 102400 # 最大数据传输长度 session_name: "client" dubbo-go 快速接入 前文已经展示过 dubbo-go 在涂鸦智能的实践成果,接下来介绍快速接入 dubbo-go 的方式。 第一步:hello world dubbo-go 使用范例目前和 dubbo 一致,放置在 apache/dubbo-samples 项目中。在 dubbo-sample/golang 目录下,用户可以选择自己感兴趣的 feature 目录,快速测试代码效果。 tree dubbo-samples/golang -L 1dubbo-samples/golang├── README.md├── async├── ci.sh├── configcenter├── direct├── filter├── general├── generic├── go.mod├── go.sum├── helloworld├── multi_registry└── registry 我们以 hello world 为例,按照 dubbo-samples/golang/README.md 中的步骤,分别启动 server 和 client 。可以尝试 golang 调用 java 、 java 调用 golang 、golang 调用 golang 、java 调用 java。dubbo-go 在协议上支持和 dubbo 互通。 我们以启动 go-server 为例,注册中心默认使用 zookeeper 。首先确认本地的 zookeeper 是否运行正常。然后执行以下命令,紧接着你就可以看到你的服务正常启动的日志了。 export ARCH=macexport ENV=devcd dubbo-samples/golang/helloworld/dubbo/go-serversh ./assembly/$ARCH/$ENV.shcd ./target/darwin/user_info_server-2.6.0-20200608-1056-dev/sh ./bin/load.sh start 第二步:在项目中使用 dubbo-go 上面,我们通过社区维护的测试代码和启动脚本将用例跑了起来。接下来,我们需要在自己的代码中嵌入 dubbo-go 框架。很多朋友往往是在这一步遇到问题,这里我整理的一些常见问题,希望能帮到大家。 1)环境变量 目前 dubbo-go 有 3 个环境变量需要配置: CONF_CONSUMER_FILE_PATH:Consumer 端配置文件路径,使用 consumer 时必需; CONF_PROVIDER_FILE_PATH:Provider 端配置文件路径,使用 provider 时必需; APP_LOG_CONF_FILE:Log 日志文件路径,必需; CONF_ROUTER_FILE_PATH:File Router 规则配置文件路径,使用 File Router 时需要。 2)代码注意点 注入服务 : 检查是否执行以下代码 注入序列化描述 :检查是否执行以下代码 3)正确理解配置文件 references / services 下的 key ,如下面例子的 "UserProvider" 需要和服务 Reference() 返回值保持一致,此为标识改接口的 key。 references:"UserProvider": registry: "hangzhouzk" protocol : "dubbo" interface : "com.ikurento.user.UserProvider" cluster: "failover" methods : name: "GetUser" retries: 3 注册中心如果只有一个注册中心集群,只需配置一个。多个 IP 用逗号隔开,如下: registries :"hangzhouzk": protocol: "zookeeper" timeout : "3s" address: "172.16.120.181:2181,172.16.120.182:2181" username: "" password: "" 4)java 和 go 的问题 go 和 java 交互的大小写 :golang 为了适配 java 的驼峰格式,在调用 java 服务时,会自动将 method 和属性首字母变成小写。很多同学故意将 java 代码写成适配 golang 的参数定义,将首字母大写,最后反而无法序列化匹配。 第三步:拓展功能 dubbo-go 和 dubbo 都提供了非常丰富的拓展机制。可以实现自定义模块代替 dubbo-go 默认模块,或者新增某些功能。比如实现 Cluster、Filter 、Router 等来适配业务的需求。这些注入方法暴露在 dubbo-go/common/extension 中,允许用户调用及配置。 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
随着5G技术的日益成熟,未来射频功率放大器(RF PA)市场将出现显著成长快速增长阶段。近期了解到一家专注于射频/微波集成电路芯片,模块和系统解决方案的设计、开发和供应商——「南京米乐为微电子科技有限公司」 「南京米乐为微电子科技有限公司」成立于2012年5月14日,专注射频/微波集成电路芯片的设计开发和供应,致力为国内外客户提供自主研发的高频率、高性能、高集成度的芯片和模块产品,据公开信息显示,其产品覆盖了全频段(DC -110GHz)以及全品类,具有超宽频、跨多个工艺线的技术优势。 主要经营范围包括半导体集成电路系统研发、销售,通信设备、射频组件及系统研发加工及销售技术研发以及电子元器件及系统技术服务、设计。工商信息显示,公司目前参保人数32人,持有 42项专利。 产品方面,据公司官网显示,「米乐为微电子」主要推出产品包括是全频段(DC-100GHz)、全品类微波射频芯片,包含低噪声放大器、功率放大器、混频器、倍频器等众多种类单功能射频芯片及集成多功能射频芯片,其产品可应用于微波基站、车载雷达、毫米波5G通讯以及相控阵雷达等领域。据公司官网介绍,目前「米乐为微电子」正针对5G中射频器件持续加大投入,已经开发出可以应用于5G的模拟移相器、Doherty放大器、开关、数控衰减器等器件。据公开消息显示,2019年「米乐为微电子」与上海多知互联网科技有限公司达成战略合作。 在射频/微波集成电路芯片的市场竞争方面,滤波器传统的SAW市场已趋于饱和,大部分市场被MuRata、TDK、太阳诱电占据,而作为升级替代产品的BAW滤波器市场也被Avago和Qorvo两家公司瓜分。据公司官网介绍,为了提高产品竞争力,目前「米乐为微电子」正针对5G中射频器件持续加大投入,已经开发出可以应用于5G的模拟移相器、Doherty放大器、开关、数控衰减器等器件。 「米乐为微电子」董事长兼总经理姜鑫,毕业于东南大学电子工程系,后获得美国北卡罗来纳州立大学硕士学位,密西根大学电子工程系获得博士学位。毕业后加入美国讯泰微波(Hittite Microwave)公司,任首席工程师,主要研究方向集中在微波、毫米波单片集成电路芯片(MMIC)和系统、高功率放大器和毫米波收发信等。 据工商信息显示,「米乐为微电子」现已完成开展三轮融资。最新一轮于2019年4月获得梅花创投,盈富泰克,新流域投资和中域资本4家投资方的Pre-B轮融资,之前于2016年9月获得A轮融资,千合资本和海达投资参投,2018年12月获得梅花创投A+轮融资,三轮融资金额均未披露。 图片:天眼查 钉钉扫码加入钉群,邀你同跑创业赛道。 本文转自<36氪>——恋迦
在2019阿里云峰会上,阿里云智能总裁张建锋首次系统性阐述了阿里云战略加速的四级火箭:“达摩院加持的云、数据智能的云、最佳实践的云和被集成的云”,从技术、产品、商业和生态层面开启阿里云的下一个十年。 “被集成”把很大部分发展空间和主动权明确留给生态伙伴,阿里云自己则专注于云生态核心能力的精益求精。 “我们有丰富的业务场景,让我们把前面这些事情做的比别人好,这是我们的优势。但大量行业的业务还是需要行业内专业的人去做。”张建锋在接受媒体采访时表示。 阿里AI赛道明星班首期学员鲸仓科技就是这样一个典型案例,主要提供仓储自动化整体解决方案,此前已经推出立体仓库解决方案魔方、“Picking Spider 拣选蜘蛛系统”。目前全套服务器都部署在阿里云上,集成了阿里云的基础技术和服务,稳定性和安全性是他们最为看重的。 仓储公司的核心主要是坪效和人效的问题,而效率问题恰恰是AI和机器人技术擅长解决的问题;同时,仓储又是一个非常复杂和接地气的场景,如何将创新技术合理有效的应用在仓储场景,才是智能仓的核心命题。 通过阿里AI赛道明星班,鲸仓科技和阿里云建立了深度的链接,阿里云满足云系统需求,提供技术链接,鲸仓科技提供自动化设备,面对制造业企业,双方深度合作提供解决方案,达成多项合作。 目前,鲸仓的模式主要为自建并运营智能共享仓。这一模式很重,为什么鲸仓要选择这样的模式呢?鲸仓CEO李林子分析,这主要是因为以下三点原因: 一是智能仓是一个系统化工程,做智能仓不能光卖设备,一定要自己运营才能得到系统化的解决方案;二是企业服务大部分是决策者和使用者是分离的,老板拍板买设备,仓储经理去使用,两个角色都不够专业,又是大额投资,决策风险巨大,采取智享仓的模式,客户只需要决策外包价格,不用投资,能降低客户的决策门槛,获得爆发式增长的机会;三是全球仓储物流业务70%以上都是通过外包来解决,因此外包是主流需求,智能仓作为一个技术密集型和资金密集型的行业,需要更加专业的服务商。 对于客户来说,降本增效才很可能是买单的重要依据。鲸仓告诉36氪,深圳仓双十一当天,订单下达后16小时内发货的比例高达99.8%,在订单增加6倍的情况下,仍然保持发货时效不变;双十一订单量增加6倍,人数仅增加50%,85人日发6.5万单(40000个SKU没有预包装情况下);郑州保税仓面积仅6千平,双十一单日发货超过28万件;此外,最早的智能仓已经连续2年无故障运行。 鲸仓认为,相比于“规模”,“单位效率”可能是这个赛道真正的创投逻辑。 仓储主要to B , 数量不产生规模效应和品牌效应;而规模扩大的同时,意味着管理成本和难度大幅增加;同时,仓储物流的坪效具有随机性,与包括管理人员的精神状态等主观因素在内的原因都相关。 未来的云服务一定有很多细分的专业服务商,阿里AI赛道明星班联合阿里云及各个赛道创新者,将基础实力与创新能力发挥到极致,实现共创,洞察未来。 钉钉扫码加入钉群,邀你同跑创业赛道。 本文转自<36氪>——蓝色理想
7月22日-23日,两天的直播会议,8分钟发布会联合阿里云复盘疫情突围关键,对话未来新趋势。聚焦「Hard模式下的产业突围」。同时,还邀请了智慧社区、智慧零售、智慧医疗赛道的数十家企业进行8分钟路演排位赛,为优质企业搭建发声舞台,促进企业间相互合作。 36氪企业服务及增长负责人余雯君表示,在上半场,互联网革了实体经济的命,我们坚信,下半场互联网一定需要进行一场自我革命,也必将迎来商业进化的一个新时代。 合纵连横的商业新周期 活动第一天,余雯君对话了阿里云创业孵化副总经理蔡素卿,就企业下半年战略调整变化以及阿里赛道明星班创立的初衷展开了讨论。 蔡素卿认为,“在2020年,企业要从数量型的博弈转变成质量型的博弈。” 为帮助服务的学员企业更好发展,截至目前,阿里赛道明星班已经集结了100多家资本,提供业务融合直通车,资本融合直通车,媒体直通车三大板块服务。阿里融通业务线已达到71条,相较之前,新增天猫、天猫精灵与区块链三块业务线。并且,阿里还联动了27家资本形成了紧密的投资联盟,首批汇集数百亿专项基金,以此为启动基础,打造生产明星型企业的流水线,帮助学员快速成长。 “在更广泛的范围内,新的合纵连横正在形成并发挥作用。但是我们也相信所谓不破不立,结构性的机会也正在涌现。”蔡素卿表示。 “寒冬之下,创业已经不是冷兵器时代了,更多考量的是一个创始人和创始团队能否整合大量内外部资源,形成一个类似于大军团作战的模式,这在今天是极为重要的。” 除了创业端以外,后疫情时代的投资端相较于以往出手更加谨慎,且关注点从商业模式和想象空间转移到了更为实际的营收情况和利润。在创业者看来,这个信号代表了投资人手里没钱或不敢投,但其实投资端有自己的考量。 山行资本创始合伙人徐诗认为“创业实际上还是要去挣认知梯度差的钱,我们要看市场的结构性机会在哪,看一个大型赛道里是不是能出来一个顶尖玩家。” 据CINNIC的数据显示,2020年,我国网民数量已经达到了8.8亿,而且依旧以1.9%的速度持续增长。 网络正在逐渐拉近人与人之间的认知差,“认知梯度差”的钱越来越难赚,这也对企业提出了全新要求。 当下人们对“消费升级”的需求将会带动智慧产业的飞速发展,也将促进新产业与新技术的革新,实现创新型产业、智慧城市、云计算与大数据、物联网、家庭服务O2O、智能硬件等产业融合创新与大架构创新发展,从当前形势看来,智慧产业无疑成为了新时代红利期最为明显的创业方向。 智慧时代下的赛道突围新趋势 随着疫情逐渐消退,社会进入有条不紊的复工复产阶段,国家对企业的扶持以及新基建政策也逐渐升温,对于以“创新、高增长、移动互联网、大数据、云计算”为标签的智慧产业,无疑是最好的增长契机。 虽然我国智慧产业目前的发展仍处于起步阶段,存在诸如区域发展不均衡、核心竞争力尚未形成等问题,但这也意味着产业拥有极大的发展空间。 据《2019-2025年中国智慧城市行业竞争现状及投资方向研究报告》中对物联网的前景猜测,2022年物联网行业规模增速虽会放缓,但规模也会达到72376亿元。 智慧行业作为未来发展的趋势,前景又该如何展望?此次活动也邀请了智慧城市、企业服务与智慧零售三大赛道多位投资人与创业大咖,分享自己对行业的感知,共同探讨数字化转型的下半场,智慧企业要如何把握时代运行的轨迹。 嘉宾们就智慧社区、智慧医疗、未来交通、智慧城市、金融科技、办公/人力、智慧物流、零售场景七大细分赛道的新趋势分享了自己的观点。 谈到疫情突围、产业破局时,蔚来汽车北京城市公司总经理蒲阳表示,蔚来的成功来源于对自身战略定位的明确,更重要还在于用优质产品及服务对消费者心智的牢牢把控。这个经验对于整个智慧行业都是共通的,那就是:得用户者得天下。 智慧产业相较于传统产业,可以解决诸如效率低下和人工成本等问题,也对企业自身的数字化建设水平、新技术的应用提出了更高要求。以此为前提,企业自身战略的布局重心该往何处偏移?如何将技术与场景匹配,找到变现路径?通过这次发布会上创业大咖对自身经历的讲述,或许可以找到答案。 优质项目有机会参与阿里云与36氪共同打造的阿里赛道明星班第四期,与阿里的钉钉、高德地图、天猫精灵、淘宝等六十多条业务线资源对接合作,获得成长加速。 本文转自<36氪>——36氪深度服务
今天来讲,在 Serverless 这个大领域中,不只有函数计算这一种产品形态和应用类型,而是面向不同的用户群体和使用习惯,都有其各自适用的 Serverless 产品。例如面向函数的函数计算、面向应用的 Serverless 应用引擎、面向容器的 Serverless Kubernetes,用户可以根据自己的使用习惯、使用场景或者应用类型,去选择使用什么样的 Serverless 产品。下面通过本文给大家介绍一下,阿里云都有哪些可供大家选择的 Serverless 产品。 Serverless 产品及分层 众所周知,最早提出 Serverless 的是 AWS,其在 Serverless 领域的旗舰产品是 function compute。同样阿里云也有函数计算的产品,帮助用户构建 Serverless 函数。但 Serverless 不仅仅是函数,如下图所示,其实用户会期望在应用、容器等层面也能够享受到 Serverless 的好处,包括按量付费、极致弹性等,这样也更符合用户原有的使用习惯。 在上图中,大家能够看到,阿里云针对函数、应用和容器都推出了对应的 Serverless 产品,用户可以针对自己的使用场景选择不同的产品。 函数计算 1. 函数计算介绍 上图展示了函数计算的使用方式。从用户角度,他需要做的只是编码,然后把代码上传到函数计算中。这个时候还不会产生费用,只有到被调用的时候才有费用。调用的方式可以是产品提供的 API/SDK,也可以通过一些事件源,比如阿里云的 OSS 的事件。比如用户往 OSS 里的某一个 bucket 上传了一个文件,希望这个文件被自动处理;比如上传一个 zip 包,希望能够自动解压到另外一个 bucket,这都是很典型的函数场景。 另外,函数计算能够提供非常好的弹性能力,最终的费用是根据时长和内存数进行计费的,如果调用量小的话,只会有很少的费用。并且它在语言方面也非常丰富,常用的 nodejs、php、python、java 都直接支持。同时提供自定义的运行环境,可以支持任意的可执行的语言。 2. 函数计算典型场景 从使用场景来说,主要有三类: Web 应用:可以是各种语言写的,这种可以使用 Serverless 框架新编写的程序,也可以是已有的应用。比如小程序后端、或者发布到 API 市场的 API 后端应用等; 对计算能力有很强的弹性诉求的应用:比如 AI 推理、音视频处理、文档转换等; 事件驱动型的应用:比如通过其他阿里云产品驱动的场景、Web Hook、定时任务等,函数计算已经与很多产品进行了打通,比如对象存储、表格存储、定时器、CDN、日志服务、云监控等,可以非常快速地组装出一些业务逻辑。 3. 函数计算核心竞争力 函数计算对客户的一个最大的价值,就是能够让用户只关注自己的业务逻辑开发,完全不需要管理运维,诸如计算资源、网络设置等都不需要关心。在隔离性上提供 vm 级别的隔离,保证用户在运行时的数据安全、运行时安全等;在可用性方面默认提供 3az 的高可用架构,保证客户默认就是高可用的最佳实践架构;在弹性方面,可以做到毫秒级的弹性效率,满足客户突发的流量冲击;在计费方面也非常灵活,真正按照用户的请求情况进行收费,也支持对 long run 的应用更友好的预付费模式。 Serverless 应用引擎 1. SAE 概述 SAE 是业内首款面向应用的 Serverless Paas 平台。这个产品以面向应用的视角,帮助用户在不做任何修改的前提下把存量应用上到云端。在资源层,用户不再需要自己管理和运维机器及集群,只需要关注自己应用所需要使用的规格以及实例数,不再需要关心底层是虚机还是容器。 SAE 从资源层面提供计算资源、弹性、隔离性等能力,让用户只需要关注自己的应用。在应用层,SAE 提供了监控、日志、微服务治理等能力,帮助用户解决应用可观测性和治理需求。同时提供网络配置、流量控制能力,提供了和 CICD 良好的集成,用户可以使用已有 CICD 部署到 SAE,比如 jenkins、云效等,可以说覆盖了应用上云的完整场景。 2. SAE 典型场景 SAE 有几个典型的使用场景,一个是存量业务上云,特别是微服务、java 应用,同时也支持其他语言的单体应用,都能够通过 SAE 这个平台运行在阿里云上,并且不需要做任何代码的修改。在行业方面,SAE 特别适合有比较大的流量波动的在线业务,比如电商大促、在线教育等行业的场景。另外 SAE 作为应用平台也可以被上层的行业 Saas 所集成,帮助用户更快地构建行业 Saas。 3. SAE 特性 通过上面的场景介绍,我们可以看到 SAE 除了 Serverless 体验本身所带来的极致弹性、免运维等特性之外,重点在应用层给用户提供全栈的能力,包括对微服务的增强支持,以及整合了和应用息息相关的能力,包括配置、监控、日志、流量控制等。再加上用户零代码的改动,是企业在线业务平滑上云非常好的选择。 Serverless Kubernetes 1. ASK 概述 另一个阿里云提供的 Serverless 产品是 Serverless K8s。但是 K8s 怎么还能 Serverless 呢?这就需要先了解一下技术架构的演进历程。 最早的时候大家都把 Docker 镜像部署在虚机里,用户需要购买 ECS,然后部署镜像,最后是网络的一些配置,比如 SLB、EIP 等。在这个过程中,用户需要自己完成部署动作,扩容需要自己重复上面的动作,或者自己构建一套自动化脚本,相对来说成本和稳定性都比较低。 之后有了 K8s 来帮大家解决容器编排的问题。这种标准化的方式确实大大提高了大家的生产力。用户通过使用 deployment、service 等标准的 K8s 的方式进行编排,并进行部署。但 K8s 的运维和管理还是相对比较复杂的,技能要求比较高,用户需要运维 ECS 以及通过 ECS 构建出来的 K8s。另外一个痛点时 K8s 集群里的 ECS 是需要预先购买的,如果客户的负载有比较大的波动,就会出现比较多的资源浪费。虽然技术上也有解决方案,比如 worker node 的弹性,但这对于初级用户来说,还是有比较高的复杂度。 那有没有一种方案可以让用户既能享受到 K8s 提供的容器编排能力,又能够不需要关心 ECS 和 K8s 的运维、管理和弹性问题呢?这就是 Serverless K8s 的方案。对应到阿里云的产品就是 ASK。在 ASK 的方案里,用户创建一个 ASK 集群,但不需要指定任何 ECS 节点,然后通过标准的 K8s 容器编排、deployment 等部署镜像。ASK 会根据用户的负载需求,自动在底层资源池构建需要的 POD 并进行弹性伸缩,用户不再需要关心容量规划、ECS 机器运维、资源限制等 LaaS 层的问题,非常便利。 2. ASK 典型场景 那 ASK 主要用在哪些场景里呢?首先可以用来跑在线业务,部署模式灵活,可以是 deployment、helm chart 等所有的 K8s 原生模式,特别是能够很好地应对突发流量,极致弹性,可以在 30 秒完成 500 个容器实例的弹性。这样的弹性效率,可以很好地支撑大数据计算类的任务,比如 Spark、Presto 等,也可以在需要的时候即时获取资源,支撑 10000 以上 Pod 的规格,有效降低客户成本。 另外一个非常适合的场景是用来构建随需启动的构建任务,比如在 ASK 中运行 jenkins、Gitlab-Runner 等。在有构建任务的时候,即时启动。没有任务的时候 0 消费,成本做到最低。这里只是列出了一些例子的场景,实际上基于 ASK 的这个特性,用户可以运行很多 K8s 原生的需要极致弹性的工作负载。 3. ASK 特性 ASK 完全容器部署,通过容器进行隔离。在使用的过程中,用户无需运维 ECS 或者 K8s 集群,也不需要考虑集群升级、容量规划、OS 及系统软件问题等事情,理论上可以提供无限的弹性容量。因为是完全按照使用量进行收费,所以就不需要为限制资源付费。 总结 总结一下,可以看到阿里云今天在 Serverless 领域有非常多样的产品,既有面向函数的函数计算,用户可以只关注代码,快速开发交付;也有面向应用的 Serverless 应用引擎,让用户更关注应用视角,并且提供了围绕应用的一系列能力,包括监控、日志、流量等能力的集成;对于更习惯 K8s 生态的用户,ASK 让用户在不改变当前 K8s 使用习惯的前提下,也能享受到 Serverless 的优势。多样的产品,在满足不同用户诉求的同时,也使用户体验到了 Serverless 的免运维、极致弹性、按量付费等优势。 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
Arthas 是个不错的工具,这里要再安利一波,当然整个过程还用到了其他工具,如 MAT、YourKIT(这个是付费的),结合起来使用更加便于发现和解决问题。期间还和开发大佬多次沟通,分别获取了不同的信息。 一键安装并启动 Arthas 方式一:通过 Cloud Toolkit 实现 Arthas 一键远程诊断 Cloud Toolkit 是阿里云发布的免费本地 IDE 插件,帮助开发者更高效地开发、测试、诊断并部署应用。通过插件,可以将本地应用一键部署到任意服务器,甚至云端(ECS、EDAS、ACK、ACR 和 小程序云等);并且还内置了 Arthas 诊断、Dubbo工具、Terminal 终端、文件上传、函数计算 和 MySQL 执行器等工具。不仅仅有 IntelliJ IDEA 主流版本,还有 Eclipse、Pycharm、Maven 等其他版本。 推荐使用 IDEA 插件下载 Cloud Toolkit 来使用 Arthas:http://t.tb.cn/2A5CbHWveOXzI7sFakaCw8 方式二:直接下载 地址:https://github.com/alibaba/arthas。 现象 建索引的后台应用,感觉用不到那么大内存,现在用到了并且隔两天就会 oom,需要重启; 有全量数据和增量数据,OOM 大多发生在全量数据写入阶段,且 OOM 基本都在凌晨首次触发全量数据更新时出现; 业务应用使用了 G1 收集器(高级高级...)。 内心 OS:糟糕,G1 还不熟可怎么办,先想个办法把大佬们支开,我自己再慢慢研究。 我还有点别的事儿,我等会再看 苟胆假设 在现有掌握的信息下判断,大胆假设一下,反正猜错了又不会赔钱。 是否是因为全量数据写入,超过了堆的承载能力,导致了 OOM? 业务是否有 static 容器使用不当,一直没回收,一直往里 put 元素,所以需要两天 OOM 一次? 内存不够,是哪些对象占用最多,先找出来看看? 有没有大对象? 发抖求证 基本信息 进程启动参数 -Xms12g -Xmx12g-XX:+UseG1GC -XX:InitiatingHeapOccupancyPercent=70 -XX:MaxGCPauseMillis=200 -XX:G1HeapWastePercent=20 -XX:+PrintAdaptiveSizePolicy -XX:+UseStringDeduplication -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintTenuringDistribution -Xloggc:/home/search/fse2/gc.log-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=2 -XX:GCLogFileSize=512M -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMTStatistics -XX:NativeMemoryTracking=summary 可以看到,使用了 G1 收集器,这个之前做业务开发的时候还不常见呢,毕竟是为大内存打造的追求低延迟的垃圾回收器。关于 G1 收集器的一些基本特性,大家可以去搜集一些资料。大概主要包括以下几项: Region 分区机制 SATB,全称是 Snapshot-At-The-Beginning,由字面理解,是 GC 开始时活着的对象的一个快照 Rset,全称是 Remembered Set,是辅助 GC 过程的一种结构,空间换时间思想 Pause Prediction Model 即停顿预测模型 从 G1 的这些属性来看,它期望做到减少人为操作调优,实现自动化的调优(说到这里,感觉本次出现的 OOM 似乎跟垃圾收集器本身关联并不大,并不是因为业务量大堆内存不够用,可能根本原因在代码逻辑层面),并且适应在硬件成本降低,大内存堆逐渐变多的场景(后面还有 ZGC 和 Shenandoah,同样是可以管理超大内存并停顿很低的神奇垃圾收集器)。 已经有GC日志了,那先看看日志,发现一处异常: 这里Heap回收的还只是300多M内存 [Eden: 316.0M(956.0M)->0.0B(1012.0M) Survivors: 48.0M->44.0M Heap: 359.2M(12.0G)->42.0M(12.0G)] [Times: user=0.31 sys=0.01, real=0.05 secs]2020-06-09T02:59:23.489+0800: 2020-06-09T02:59:23.489+0800: 35.922: Total time for which application threads were stopped: 0.0578199 seconds, Stopping threads took: 0.0000940 seconds35.922: [GC concurrent-root-region-scan-start].................. 这个点Heap回收的就是11G内存了 [Eden: 724.0M(1012.0M)->0.0B(540.0M) Survivors: 44.0M->72.0M Heap: 11.5G(12.0G)->355.6M(12.0G)] [Times: user=1.51 sys=0.07, real=0.26 secs]2020-06-09T03:12:36.329+0800: 828.762: Total time for which application threads were stopped: 0.2605902 seconds, Stopping threads took: 0.0000600 seconds 初次调试 增加 -XX:G1ReservePercent 选项的值,为“目标空间”增加预留内存量。 减少 -XX:InitiatingHeapOccupancyPercent 提前启动标记周期 怀疑在 GC 的当时,有大量数据全量写入,内存还没回收完,写进大量对象,导致了 OOM,于是调了启动周期,占比由 70 下降到55,提前触发,预留更多堆空间。 GC 变得频繁了,但是内存占用过大的问题并没有得到解释。并且是否会再次在凌晨出现 OOM 还需要等。。。于是还要继续看看有没有别的问题。 继续挖掘 有疑点没有解答,肯定是要继续挖掘的,抄起键盘就是干,一顿操作猛如虎。 Arthas 不知道哪位已经安装了,先拿来用用看吧,大概用到了以下几个命令: dashboard 看看基本情况,线程数,堆大小,老年代大小,使用占比情况。首次看到这里时,eden 和 old 区的占用都挺高的, 70~80% 吧(当时忘了截图)。内存使用率比较高。 thread 看看线程数有没有异常,线程数正常,只是找出了资源占用比较高的线程,其中一个线程在后面其他信息统计中还会出现: YJPAgent-Telemetry ctrl-bidsearch-rank-shard1YJPAgent-RequestListenerctrl-bidsearch-rank-shard1 居然有两个线程是 YourKit 监控工具的 agent,看来这个监控工具对性能影响还挺大的。 profiler 分别采集一下内存和 CPU 的火焰图数据: CPU 内存 从 CPU 火焰图看到,G1 收集器线程居然占了一半资源,可能采集当时正在进行 GC,不过,除此之外,基本都能定位到是一个叫 IncrementIndexService 的类,使用了比较多的 CPU 和内存资源。 如果业务代码真的存在缺陷,那么一定在这个流程里,后来经过沟通,发现这个应用处理任务主要入口的确是在这里面。先持有保留意见。 thread 看到的线程和 profiler 看到的 class,都最终定位到是同一个业务流程。 开始验证之前的猜想: 1. 是否在全量数据写入的时候有大量对象涌入内存? 计算了一些业务代码获取数据的量,元数据大约也就在 1.3G 左右,就算全量写入,也不应该占用 12G 的堆内存,所以猜测全量数据写入时,代码逻辑可能有什么缺陷,导致 1.3G 的原数据被封装成远大于 1.3G 的对象了。 2. 是否有 static 容器? 有,但是经过 watch 观察,没有发现容器只 put 不 remove 的情况,命令如下: watch com.xxx.classname method "{params,target}" -x 3 3. 有没有大对象? 对于 G1,默认将堆分成 2048 个 Region,12G 的堆,一个 Region 是 6M,超过 3M 才是大对象。 jmap histo 30123 至少输出的数据中,大对象不是 G1 定义的大对象。 MAT 既然没什么发现,就把堆 dump 出来吧。如果不想或者不能 dump,也可以用 jmap histo 查看内存占用,优点是不用 dump,缺点是通常不能很好的和业务代码之间建立关联。 警告:jmap 或者 Arthas 的 heapdump 操作之前一定要断开流量。好在我们这个服务没有线上流量,建索引有延迟,可能短暂影响搜索体验。 dump 出来之后,发现有 7 个 G,这么大的文件一般很难传到本地来分析了,于是用 MAT,占用服务器 1 个 G 内存进行分析,分析完成的结果下载到本地。 线程名称和之前发现的信息吻合,点开 detail 有惊喜。 一串奇怪的字符串,有点像 XML,好像在拼装着什么,于是找到业务大佬请教,发现的确有拼装 solrDocument 的逻辑,而且,经过 YourKit 输出部分对象值的片段,可以发现有大部分是在重复拼装,大概意思如下: 代码逻辑修改其实比较简单了,业务开发大佬们比较熟悉业务流程,很快就就有了修改方案。 调整之后发现,内存使用量下降了很多。 直接降到了 4-5G 左右,如果是这样的话,即便全量数据写入时,正在做垃圾回收,应该还是够用的。但是感觉这个代码逻辑里面,应该还有优化空间,不过,先解决问题,优化可以做下一步操作。 复盘 无论从哪个工具得出的数据,都显示 IncrementIndexService 这个类的逻辑可能有问题,最终问题的根本和 G1 参数设置好像也没什么关系,代码逻辑缺陷才是根源,再扩大内存可能或者调整 JVM 参数,也只能将故障缓解,但是不能解决。 从进程到线程到代码 获取 JVM 基本信息,收集器,启动参数等信息 查看现有的日志,GC 日志,业务日志 沟通业务场景,了解输入数据规模等等 猜想可能存在的原因,大胆的猜 使用工具(Arthas、MAT、YourKit、JDK 自带命令等等)挖掘信息,火焰图、耗能线程、线程栈 堆dump,占比分析 大对象 ... 结合数据重新梳理,发现业务代码的关联和可能存在的缺陷,可以尝试调整参数 业务代码若有 bug,修复 bug 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
Arthas 是一款 Java应用开源诊断工具,由于其强大的问题排查及诊断能力,自其开源以来广受开发者的关注和使用,多次登顶 GitHub Trending,并得到国内多家技术媒体的推荐分享。 一. 定制化功能改造 Arthas 可以通过简单的命令交互模式,接入运行的 JVM,快速定位和诊断线上程序运行问题。在不重启服务的情况下,实时、动态的修改相关 code,并实时生效。具体工作原理如下: 连接JVM:通过attach机制,通过attach pid连接正在运行的JVM; 查看及修改JVM字节码:通过instrument技术对运行中的JVM附加或修改字节码来实现增强的逻辑。 2018 年底,中原银行开始投入人员对 Arthas 进行调研,在开源社区了解了主要功能,并通过阅读 Arthas 工程大纲,明晰整体工程结构,整个执行过程如下:Arthas 底层调用 rt.jar 包的 ManagementFactory 获取整个 jvm 内部信息,通过命令集成与后端交互,执行,返回结果,整个工程简单清晰,容易上手。 2019 年初,中原银行技术团队开始使用推广 Arthas 定位和诊断线上问题。 出于保护客户敏感信息的严格要求,同时切实保障生产环境业务系统的稳定运行,我们对 Arthas 的部分功能进行了定制化改造,对一些命令进行了隐藏: watch:watch方法可以在没有打印日志的情况下,看到方法的入参和返回值,有可能暴露客户的敏感信息; mc、redefine:mc组合rdefine可以对代码进行热更新,不能满足我行生产运行管理规范要求。 同时,出于使用需要,定制化开发了 gc 等命令: gc:实时动态展示年轻代,年老代垃圾百分比,回收次数及耗时等情况。 下一步,我行计划在全部开发测试环境、部分生产环境推广使用 Arthas 来进行问题排查与定位诊断。同时采用内部技术分享的形式向行内应用开发团队普及推广 Arthas 的使用。 二. 重点使用功能 除了日常问题排查使用到的方法外,Arthas 还有一些强大的功能,深受中原银行技术团队喜爱。 1.target-ip arget-ip 为指定绑定的IP,如果不指定IP,Arthas只listen 127.0.0.1,所以如果想从远程连接,则可以使用 --target-ip参数指定listen的IP,java -jar arthas-boot.jar --target-ip IP 绑定远程访问IP后,可以在通过telnet或http的方式远程连接 Arthas 进行问题排查。 web端访问地址:ip:8563 /telnet访问:ip:3658 当线上应用出现问题时,可以将问题机器隔离起来,通过Arthas在启动时指定target-ip,多方技术人员可同时通过远程连接进行问题排查。 2.trace 查看方法内部调用路径,并输出方法路径上的每个节点上耗时 trace ClassName methodName 使用 trace 命令可以一层一层追踪耗时在哪里 ,在进行性能调优的时候十分有效。 3.ognl ognl 是应用于 Java 中的一个开源的表达式语言,作用是对数据进行访问,它拥有类型转换、访问对象方法、操作集合对象等功能,通过 ognl 可以完成一些列强大的操作。 执行静态方法 使用ognl调用静态方法 ognl “@类名@方法名(参数)” 获取静态属性 使用ognl获取静态属性 ognl “@类名@属性名” 示例:修改日志等级 查找当前类的classloader hashcode sc -d 类名 | grep classLoaderHash 用OGNL获取logger ognl -c * '@类名@logger' 单独设置该类的logger level ognl -c * '@类名@logger.setLevel(@ch.qos.logback.classic.Level@DEBUG)' 全局设置logger level ognl -c * '@org.slf4j.LoggerFactory@getLogger("root").setLevel(@ch.qos.logback.classic.Level@DEBUG)' 4.gc gc 是我行定制化开发的功能,源自于 jstat -gcutil pid timeinterval 命令,其中 pid 可以从 Arthas 中获取,timeinterval(单位为毫秒)表示 gc 每次时间间隔,默认为 1s。 查看应用gc情况(timeinterval表示间隔时间,单位毫秒,默认为1S) gc -i timeinterval -n 5 三. 应用实践案例 下面记录一些我行 Arthas 应用实践案例(由于行内代码保密性要求,下文所示案例均为场景复现所写示例代码) 案例一:系统 CPU 使用率高 问题描述:业务人员反馈后台管理系统其中一个页面响应时间很长,登录服务器上发现 CPU 使用率较高,达到 80% 左右。 1. 启动 Arthas,附加到对应的 java 进程 注意:Arthas 启动时要使用与 Java 进程相同的启动用户。 启动Arthas java -jar arthas-boot.jar [INFO] arthas-boot version: 3.2.0 [INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER. #选择要附加的java进程编号2 ... 2. thread 命令查看 CPU 使用率高的线程 启动 Arthas,附加到对应的 java 进程,执行 thread -n 5 查看 CPU 使用率最高的 5 个线程的堆栈。 3. 通过 monitor 命令查看方法的调用次数与耗时 通过 thread 命令已经定位到 CPU 主要消耗在 TreeUtil 的 findMenuChildren 方法上,通过 monitor 命令查看方法的具体调用次数与耗时。 5s为一个统计周期,统计TreeUtil中findMenuChildren方法的耗时 monitor -c 5 *.TreeUtil findMenuChildren 通过 monitor 命令可以明确该方法单次调用平均耗时为 17 ~ 20ms,但是调用次数多,所以整体上页面响应慢。 4. 通过 jad 命令反编译 TreeUtil 类,查看源码 [arthas@12196]$ jad com.durian.ddp.utils.TreeUtilClassLoader:+-sun.misc.Launcher$AppClassLoader@18b4aac2 +-sun.misc.Launcher$ExtClassLoader@244038d0Location:/* Decompiled with CFR.* Could not load the following classes: *.ResourceTreeVo*/ ...public class TreeUtil { public static ResourceTreeVo findMenuChildren(ResourceTreeVo resourceTreeVo, List<ResourceTreeVo> treeNodes) { for (ResourceTreeVo resource : treeNodes) { if (!resourceTreeVo.getResourceId().equals(resource.getResourceParentId())) continue; if (resourceTreeVo.getChildResourceVo() == null) { resourceTreeVo.setChildResourceVo(new ArrayList()); } resourceTreeVo.getChildResourceVo().add(TreeUtil.findMenuChildren(resource, treeNodes)); } return resourceTreeVo; } public static List<ResourceTreeVo> recursiveTree(List<ResourceTreeVo> list) { ArrayList<ResourceTreeVo> trees = new ArrayList<ResourceTreeVo>(); for (ResourceTreeVo treeNode : list) { if (!StringUtils.isEmpty(treeNode.getResourceParentId())) continue; trees.add(TreeUtil.findMenuChildren(treeNode, list)); } return trees; } } 通过 jad 命令查看源码可以发现,此处的业务逻辑大致是通过 ResourceTreeVo 对象的 resourceParentId 字段把一个列表构建一个树。在 findMenuChildren 方法中存在递归调用,而且每一次调用都需要遍历整个 ResourceTreeVo 列表来查找子节点,时间复杂度为 O(n)。所以在 ResourceTreeVo 列表元素比较多的时候,会很耗时。 5. 解决问题 定位到问题就方便解决了,可以通过提前基于 list 构建一个 parentId->List 的 map,每个节点查找子节点列表的时候可以从 map 中获取。这样整个构建树的时间算法为 O(n)。 案例二:应用线程连接数异常 问题描述:服务器句柄数耗尽,查看发现某个应用占用句柄数较多。 1. thread 命令查看线程信息 启动 Arthas,附加到对应的 java 进程,执行 thread 查看线程情况。 查看线程情况 thread 看到有大量的 MasterListener-mymaster-* 线程处于连接状态,一直没有释放。 选择其中一个线程查看堆栈信息 thread id 发现这些线程是由 redis.clients.JedisSentinelPool$MasterListener 产生的,那么接下来就来查看一下 JedisSentinelPool$MasterListener 的调用情况。 2. stack 命令查看堆栈信息 stack redis.clients.jedis.JedisSentinelPool$MasterListener 触发一次应用请求,打印出如下堆栈信息: 通过调用链定位到 RedisUtil 类,发现每次请求否会触发 RedisUtil.getJedis 方法调用 JedisSentinelPool$MasterListener,那么下一步我们反编译一下 REedisUtil 类。 3. jad 命令反编译查看代码 反编译RedisUtil类 jad cn.com.zybank.testredis.starter.RedisUtil 查看 getJedis 方法,发现 getJedis 每调用一次都会新建一个 JedisSentinelPool 。 通过分析发现,每次使用 redis 时,都会调用 getJedis 方法创建一个新的 JedisSentinelPool,从而启动一个 MasterListener-mymaster-* 线程,由于该线程会一直保持监听,不会自动释放,故随着应用请求的增加线程数一直增加从而导致连接数占满。 4. 解决问题 针对该问题,只需创建一个全局的 JedisSentinelPool,每次获取 redis 连接时都从该连接池获取即可,这里不再对代码进行展示。 四.总结建议 我行在使用 Arthas 以前,线上问题排查往往需要查网络、jps、jstack、jmap、jhat、jstat、hprof 等一系列操作,费时费力。目前,大多数的常见问题都可以使用 Arthas 轻松定位,迅速解决。 一键安装并启动 Arthas 方式一:通过 Cloud Toolkit 实现 Arthas 一键远程诊断 Cloud Toolkit 是阿里云发布的免费本地 IDE 插件,帮助开发者更高效地开发、测试、诊断并部署应用。通过插件,可以将本地应用一键部署到任意服务器,甚至云端(ECS、EDAS、ACK、ACR 和 小程序云等);并且还内置了 Arthas 诊断、Dubbo工具、Terminal 终端、文件上传、函数计算 和 MySQL 执行器等工具。不仅仅有 IntelliJ IDEA 主流版本,还有 Eclipse、Pycharm、Maven 等其他版本。 推荐使用 IDEA 插件下载 Cloud Toolkit 来使用 Arthas:http://t.tb.cn/2A5CbHWveOXzI7sFakaCw8 方式二:直接下载 地址:https://github.com/alibaba/arthas。 随着我行全面深化使用 Arthas,也发现了一些有待改进提升的功能,希望进一步优化完善。 在进行 trace 的时候,只要调用链中有异步,堆栈就会断掉,无法 trace 到子线程内部,只能手动逐层跟进 trace,效率较低。希望 tt 命令能够添加异步开关,如果开关开启, 那么 COST 即可显示异步得到结果的耗时。截至目前,Arthas GitHub Star 已经突破 2.4 万了,希望 Arthas 能够获得更多全球开发者的关注和喜爱,也期待更多像 Arthas 一样的国内优质项目能够开源。 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
生产环境的 bug 开发环境无法复现怎么办?关键位置没有打印日志信息不足怎么办?莫慌,骚年。让强大的 Arthas法师来 carry,带你去生产环境"遨游"闯关。 刚接触 Arthas,就被它能够 watch 方法的输入参数和返回值的功能震惊到了。这简直太酷炫了,让你可以像本地单步调试一样,跟踪到每一步的执行结果和获取当前的变量数值。以前要定位线上问题,信息不足就需要加日志打印,定位问题,可能需要反复重启应用。用了 Arthas,根本不需要加日志打印,重启应用这些操作。花了大概一个周末下午,在本地跑了下官方 demo,熟悉了下常用操作。脑子里对 Arthas 能够做什么,能解决什么,怎么解决,已经有了大概的了解。后面需要用的时候,就能派上大用场了(学了就一定有 bug 会找上门的=.=)。 下面介绍一个特意找上门的 bug。 背景:同一个聊天交友类产品,对外以一个主品牌以及多个新品牌进行发布。服务端是共用一套数据的,但是所有对外展示的信息,涉及到品牌相关的,需要进行文案替换。在同一个群组里,主品牌和新品牌的用户可以互相聊天。 问题现象: 线上某个群组里面,同一条聊天消息涉及到需要替换文案的内容时,主品牌侧的用户有的显示正常,有的显示为新品牌文案; 不同的群聊天消息,同一个用户有的展示正常,有的异常; 新品牌侧的用户看到的群聊天消息文案替换正常。 先贴下相关的代码(用 Arthas 的 jad 直接反编译的源码): 群聊消息下发方法: write 方法文案替换逻辑代码: PublishMessage 是下行消息类,replaceMsgMap 是提前生成好的各个新品牌对应文案,主品牌使用原始消息文案。 乍一看,文案替换逻辑没啥毛病(但问题就在这,大家可以先思考下),感觉自己又要去面对一个扑朔迷离的玄学 bug 了(永远不要把程序 bug 归结为灵异事件)。代码看不出问题,本地单步调试鼓捣了一早上,也没复现出来,看来只能在生产环境定位了,Arthas 要登场了。 由于生产环境的消息转发量很大,直接 attach 进程风险太高,且不利于单条消息观察定位。所以选择预发布环境进行 attach,请求量可控,数据和线上一致,也只有读操作,不会影响到生产环境。 用 Arthas 的 watch 命令,观察 write 方法的输入参数。 -x 表示遍历深度,可以调整来打印具体的参数和结果内容,默认值是 1 -b 表示观察方法调用前 可以看到,publishMessage、userSession 参数的值都显示出来了。接着就可以在预发布触发消息下行进行数据观察了。 建了个测试群,除了自己一个主品牌的测试用户还有另外一个新品牌用户。最初开始发送了几条群聊消息都正常,后面又拉了一个新品牌用户以及主品牌测试用户,复现的概率就高了许多。观察了下同一条群聊消息发给每个群成员的 publishMessage 值,发现如果先遍历到了新品牌用户,再遍历到主品牌用户时,publishMessage 的文案居然是新品牌文案!!!心里猛的一惊,是了,就是这个低级错误造成的 bug,大家应该也猜到原因了。 下面揭晓下这个问题产生的原因: 遍历群成员传递的 publishMessage 形参,每次改变 payload 都会影响到被传递进去的 publishMessage 实参 replaceMsgMap 里只存储了新品牌文案 主品牌根据 appName 获取对应文案时为空,则不设置 payload,使用最传递进来的 publishMessage 的 payload 遍历群组成员时,顺序是随机性的 如果某个主品牌用户在新品牌用户之后被遍历到,那么 publishMessage 的 payload 字段就会被设置为新品牌文案。而主品牌在 replaceMsgMap 里找不到对应文案,就不更新 payload了,复用了上一次被遍历用户的 payload,就会出现文案显示异常。 知道原因后就好处理了,replaceMsgMap 里把主品牌的文案也加进去,每次遍历到主品牌,也更新 payload 字段,保证文案正常显示。 整个定位过程,无需增加 log 日志,线上应用也无需重启,便能获取足够的信息进行问题排查定位,是不是贼好用了。花个半天时间,摸索鼓捣下,要用时就能省下不少工夫。详细使用文档见->官方文档 一键安装并启动 Arthas 方式一:通过 Cloud Toolkit 实现 Arthas 一键远程诊断 Cloud Toolkit 是阿里云发布的免费本地 IDE 插件,帮助开发者更高效地开发、测试、诊断并部署应用。通过插件,可以将本地应用一键部署到任意服务器,甚至云端(ECS、EDAS、ACK、ACR 和 小程序云等);并且还内置了 Arthas 诊断、Dubbo工具、Terminal 终端、文件上传、函数计算 和 MySQL 执行器等工具。不仅仅有 IntelliJ IDEA 主流版本,还有 Eclipse、Pycharm、Maven 等其他版本。 推荐使用 IDEA 插件下载 Cloud Toolkit 来使用 Arthas:http://t.tb.cn/2A5CbHWveOXzI7sFakaCw8 方式二:直接下载 地址:https://github.com/alibaba/arthas。 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
导读:存储服务支撑了应用的状态、数据的持久化,是计算机系统中的重要组成部分,也是所有应用得以运行的基础,其重要性不言而喻。在存储服务演进过程中,每一种业务类型、新技术方向都会对存储的架构、性能、可用性、稳定性等提出新的要求,而在当今技术浪潮走到云原生技术普及的时代,存储服务需要哪些特性来支持应用呢? 从本文开始,我们将用一个系列文章对云原生存储进行方方面面的探析,该系列文章将从云原生存储服务的概念、特点、需求、原理、使用、案例等方面,和大家一起探讨云原生存储技术新的机遇与挑战,欢迎大家讨论: "There is no such thing as a 'stateless' architecture" - Jonas Boner 云原生存储系列文章(一):云原生应用的基石云原生存储系列文章(二):容器存储与K8S存储卷 云原生存储系列文章(三):Kubernetes存储架构云原生存储系列文章(四):K8S存储实践-Flexvolume云原生存储系列文章(五):K8S存储实践-CSI云原生存储系列文章(六):存储卷高可用方案云原生存储系列文章(七):存储调度与容量感知云原生存储系列文章(八):数据卷扩缩容能力云原生存储系列文章(九):云原生存储安全云原生存储系列文章(十):高性能计算场景的存储优化 本节会介绍云原生存储的基本概念和常用的存储方案。 云原生存储 1.概念 要理解云原生存储,我们首先要了解云原生技术的概念,CNCF 对云原生定义如下: 云原生技术有利于各组织在公有云、私有云和混合云等新型动态环境中,构建和运行可弹性扩展的应用。云原生的代表技术包括容器、服务网格、微服务、不可变基础设施和声明式 API。 这些技术能够构建容错性好、易于管理和便于观察的松耦合系统。结合可靠的自动化手段,云原生技术使工程师能够轻松地对系统作出频繁和可预测的重大变更。 简言之:云原生应用和传统应用并没有一个标准的划分界限,其描述的是一种技术倾向,即越符合以下特征的应用越云原生: 应用容器化 服务网格化 声明式 API 运行可弹性扩展 自动化的 DevOps 故障容忍和自愈 平台无关,可移植的 云原生应用是一簇应用特征能力的集合,而实现了这些能力的应用在可用性、稳定性、扩展性、性能等核心能力都会有大幅的优化。优异的能力代表了技术的方向,云原生应用正在引领各个应用领域实现云原生化,同时也在深刻改变着应用服务的方方面面。存储作为应用运行的基石,也在服务云原生化过程中提出了更多的需求。 云原生存储的概念来源于云原生应用,顾名思义:一个应用为了满足云原生特性的要求,其对存储所要求的特性是云原生存储的特性,而满足这些特性的存储方案,可以称其为倾向云原生的存储。 2.云原生存储特征 1)可用性 存储系统的可用性定义了在系统故障情况下访问数据的能力,故障可以是由存储介质、传输、控制器或系统中的其他组件造成的。可用性定义系统故障时如何继续访问数据,以及在部分节点不可用时如何将对数据的访问重新路由到其他的可访问节点。 可用性定义了故障的恢复时间目标(RTO),即故障发生与服务恢复之间的时长。可用性通常计算为应用运行时间中的可用时间的百分比(例如 99.9%),以及以时间单位度量的 MTTF(平均故障时间)或 MTTR(平均修复时间)来度量。 2)可扩展性 存储的可扩展性主要衡量以下参数指标: 扩展可以访问存储系统的客户端数量的能力,例如:一个 NAS 存储卷同时支持多少客户端挂载使用; 扩展单个接口的吞吐量和 IO 性能; 扩展存储服务单实例的容量能力,例如:云盘的扩容能力。 3)性能 衡量存储的性能通常有两个标准: 每秒支持的最大存储操作数 - IOPS; 每秒支持的最大存储读写量 - 吞吐量; 云原生应用在大数据分析、AI 等场景得到广泛应用,在这些重吞吐 / IO 场景中对存储的需求也非常高,同时云原生应用的快速扩容、极致伸缩等特性也会考验存储服务在短时间内迎接峰值流量的能力。 4)一致性 存储服务的一致性是指在提交新数据或更新数据后,访问这些新数据的能力;根据数据一致性的延迟效果,可以将存储分为:“最终一致”和“强一致”存储。 不同的服务对存储一致性的敏感度是不一样的,像数据库这类对底层数据准确性和时效性要求非常高的应用,对存储的要求是具有强一致性的能力。 5)持久性 多个因素应用数据的持久性: 系统冗余等级; 存储介质的耐久性(如:SSD 或 HDD); 检测数据损坏的能力,以及使用数据保护功能重建或恢复损坏数据的能力。 3.数据访问接口 在云原生应用系统中,应用访问存储服务有多种方式。从访问接口类型上可以分为:数据卷方式、API 方式。 数据卷:将存储服务映射为块或文件系统方式共应用直接访问,使用方式类似于应用在操作系统中直接读写本地目录文件。例如:可以将块存储、文件存储挂载到本地,应用可以像访问本地文件一样对数据卷进行访问; API:有些存储类型并不能使用挂载数据卷的方式进行访问,而需要通过调用 API 的方式进行。例如:数据库存储、KV 存储、对象存储,都是通过 API 的方式进行数据的读写。 需要说明的是对象存储类型,其标准使用方式是通过 Restful API 对外提供了文件的读写能力,但也可以使用用户态文件系统的方式进行存储挂载,使用方式模拟了块、文件存储数据卷挂载的方式。 下面表格列举了各种存储接口的优缺点: 4.云原生存储分层 1)编排和操作系统层 这一层定义了存储数据的对外访问接口,即应用访问数据时存储所表现的形式。同上节所述,可以分为数据卷方式和 API 访问方式。这一层是容器服务存储编排团队关注的重点,云原生存储对敏捷性、可操作性、扩展性等方面的需求都在这一层集成、实现。 2)存储拓扑层 这一层定义了存储系统的拓扑结构和架构设计,定义了系统不同部分是如何相互关联和连接的(如存储设备、计算节点和数据)。拓扑结构在构建时影响存储系统的许多属性,因此必须加以考虑。 存储拓扑可以是集中式的、分布式的、或超融合的。 3)数据保护层 定义如何通过冗余的方式对数据进行保护。在存储系统出现故障时,如何通过数据冗余对数据进行保护、恢复非常重要。通常有以下几种数据保护方案: RAID(独立磁盘冗余阵列):用于在多个磁盘之间的分发数据技术,同时考虑到冗余; 擦除编码:数据被分成多个片段,这些片段被编码,并与多个冗余集合一起存储,保证数据可恢复; 副本:将数据分布在多个服务器上,实现数据集的多个完整副本。 4)数据服务 数据服务补充了核心存储功能以外的存储能力,例如可以提供存储快照、数据恢复、数据加密等服务。数据服务所提供的补充能力也正是云原生存储所需要的,云原生存储正是通过集成丰富数据服务来实现其敏捷、稳定、可扩展等能力。 5)物理层 这一层定义了存储数据的实际物理硬件。物理硬件的选择影响系统的整体性能和存储数据的持续性。 5.存储编排 云原生意味着容器化,而容器服务场景中通常需要某种管理系统或者应用编排系统。这些编排系统与存储系统的交互可以实现工作负载与存储数据的关联。 如上图所示: 负载:表示应用实例,会消费底层存储资源; 编排系统:类似于 K8s 一样的容器编排系统,负责应用的管理和调度; 控制平面接口:指编排系统调度、运维底层存储资源的标准接口,例如 K8s 里面的 Flexvolume,已经容器存储通用的 CSI 接口; 工具集:指控制平面接口运维存储资源时所依赖的三方工具、框架; 存储系统:分为控制平台数据数据平面。控制台平面对外暴露接口,提供存储资源的接入、接出能力。数据平面提供数据存储服务。 当应用负载定义了存储资源需求时,编排系统会为应用负载去准备这些存储资源。编排系统通过调用控制平面接口,进而实现对存储系统控制平面的调用,这样就实现了应用负载对存储服务的接入、接出操作。当实现了存储系统接入后,应用负载可以直接访问存储系统的数据平面,即可以直接访问数据。 常见的云原生存储方案 1.公有云存储 每个公有云服务提供商都会提供各种云资源,其中也包含了各种云存储服务。以阿里云为例,其几乎提供了能够满足所有业务需要的存储类型,包括:对象存储、块存储、文件存储、数据库等等。公有云存储的优势是规模效应,足够大的体量实现了巨大研发、运维投入的同时,提供低廉的价格优势。而且公有云存储在稳定性、性能、扩展性方面都能轻松满足您的业务需求。 随着云原生技术的发展,各个公有云厂商都开始对其云服务进行云原生化改造或适配,提供更加敏捷、高效的服务来适应云原生应用的需求。阿里云存储服务也在云原生应用适配做了很多优化,阿里云容器服务团队实现的 CSI 存储驱动无缝的衔接了云原生应用和存储服务之间的数据接口。实现了用户使用存储资源时对底层存储无感知,而专注于自己的业务开发。 优点如下所示: 高可靠性:多数云厂商都可以提供服务稳定性、数据可考虑下都非常优异的服务,例如:阿里云ebs提供了9个9的可靠性服务,为您的数据安全提供了强有力的基础保障能力; 高性能:公有云对不同的服务提供了不同等级的存储性能适配,几乎可以满足所有应用类型对存储性能的需求。阿里云 EBS 可以提供百万级别的 IOPS 能力,接近本地盘的访问性能。NAS 服务最大提供每秒数十 G 的吞吐能力,在数据共享的应用场景满足您的高性能需求。而 CPFS 高性能并发文件系统最高可以提供近 TB 级别的吞吐能力,更是可以满足一些极端高性能计算对存储的需求; 扩展性好:公有云存储服务一般都提供了容量扩容能力,让您在应用对存储的需求增加的时候可以动态的实现容量伸缩,且实现应用的无感知; 安全性高:不同的云存储服务都提供了数据安全的保护机制,通过 KMS、AES 等加密技术实现数据的加密存储,同时也实现了客户端到服务的链路加密方案,让数据传输过程中也得到加密保护; 成熟的云原生存储接口:提供了兼容所有存储类型的云原生存储接口,让您的应用无缝的接入不同存储服务。阿里云容器服务提供的 CSI 接口驱动已经支持了:云盘、OSS、NAS、本地盘、内存、LVM等多种存储类型,可以让应用无感知的访问任何的存储服务类型; 免运维:相对于自建存储服务来说,公有云存储方案省去了运维的难度 缺点如下所示: 定制化能力差:由于公有云存储方案需要服务所有用户场景,其能力主要集中在一些通用需求,而对某些用户个性化需求很难满足。 2.商业化云存储 在很多私有云环境中,业务方为了实现数据的高可靠性通常会购买商业化的存储服务。这种方案为用户提供了高可用、高效、便捷的存储服务,且运维服务、后期保障等也都有保证。私有云存储提供商也意识到云原生应用的普及,也会为用户提供完善的、成熟的云原生存储接口实现方案。 优点如下所示: 安全性好:私有云部署,可以从物理上实现数据的安全隔离; 高可靠、高性能:很多云存储提供商都是在存储技术上深耕多年,具有优异的技术能力和运维能力,其商业化存储服务可以满足多数应用的性能、可靠性的需求; 云原生存储接口:从多家存储服务提供商开源的项目可以看出,其对云原生应用的支持已经实现或者展开。 缺点如下所示: 贵:商业化的存储服务加个多数都很昂贵; 云原生存储接口兼容性:商业化的云原生存储接口都是针对其一家的存储类型。多数用户会使用不同的存储类型,而使用了不同家的存储服务,很难实现统一的存储接入能力。 3.自建存储服务 对于一些 SLA 要是不是很高的业务数据,很多公司都会选择使用自建的方式提供存储服务。业务方需要通过当前的开源的存储方案,并结合自建的业务需求进行方案选择。 文件存储:考虑 CephFS、GlusterFS、NFS 等方案。其中 CephFS,GlusterFS 在技术的成熟度上还需要进一步验证,且在高可靠、高性能场景上也存在不足。而 NFS 虽然已经成熟,但是自建集群在性能上很难达到高性能应用的需求。 块存储:例如 RBD、SAN 等是常见的块存储方案,技术也相对比较成熟,已经有较多的公司将其应用在自己的业务上。但其复杂度也相对很高,需要有专业的团队来运维支持。 优点如下所示: 业务匹配度高、灵活性好:可以在众多开源方案中选择最适合自己业务的方案,且可以在原生代码的基础上进行二次开发来优化业务场景; 安全性好:如果搭建在公司内部使用,则具有物理隔离的安全性; 云原生存储接口:常用的开源存储方案都可以在社区找到云原生存储接口的实现,且可以在其基础上进行开发、优化。 缺点如下所示: 性能欠佳:多数开源的存储方案其原生的性能表现并不是很好。当然您可以通过架构设计、物理硬件升级、二次开发等方案进行优化; 可靠性差:开源存储方案在可靠性方面无法和商业化的存储比较,所以更多场景是应用在 SLA 低的数据存储场景; 云原生存储插件鱼龙混杂:目前网上开源的云原生存储驱动版本众多,且质量参差不齐,有些项目存在 bug,且长期无人维护,所以使用时需要更多的甄别和调测工作; 专业的团队支撑:自己搭建的服务需要自己负责,面对并不是十分成熟的开源方案,需要组建一个具有较强技术能力的专业团队来运维、开发存储系统。 4.本地存储 一些业务类型不需要高可用分布式存储服务,而会选择使用性能表现更优的本地存储方案。 数据库类服务:对存储的 IO 性能、访问时延有很多的要求,一般的块存储服务并不能很好的满足这方面的需求。且其应用本身已经实现了数据的高可用设计,不再需要底层实现多副本的能力,即分布式存储的多副本设计对这类应用是一种浪费。 存储作为缓存:部分应用期望保存一些不重要的数据,数据在程序执行完成即可以丢掉,且对存储的性能要求较高,其本质是将存储作为缓存使用。云盘的高可用能力对这样的业务并没有太大的意义,且云盘在 IO 性能、价格方面的表现(相对本地盘)也没有优势。 所以本地盘存储在很多关键能力上要比分布式块存储弱很多,但在特定场景下仍然有其使用的优势。阿里云存储服务提供了基于 NVMe 的本地盘存储方案,以更好的性能表现和更低的价格优势,在特定的应用场景得到了用户的青睐。 阿里云 CSI 驱动供了云原生应用使用本地存储的接入实现,支持:lvm 卷、本地盘裸设备、本地目录映射等多种接入形式,实现数据的高性能访问、quota、iops 配置等众多适配能力。 优点如下所示: 性能高:提供相对分布式存储更优的 IOPS、吞吐能力; 低价:通过物理裸设备直接提供本地盘,在价格上相对于多副本的分布式存储具有优势。 缺点如下所示: 数据可靠性差:本地盘保存的数据丢失后不能找回,需要从应用层实现数据的高可用设计; 灵活性差:不能像云盘一样实现数据迁移到其他节点使用。 5.开源容器存储 随着云原生技术的发展,社区提供了一些开源的云原生存储方案。 1)Rook Rook 作为第一个 CNCF 存储项目,是一个集成了 Ceph、Minio 等分布式存储系统的云原生存储方案,意图实现一键式部署、管理方案,且和容器服务生态深度融合,提供适配云原生应用的各种能力。从实现上,可以认为 Rook 是一个提供了 Ceph 集群管理能力的 Operator。其使用 CRD 方式来对 Ceph、Minio 等存储资源进行部署和管理。 Rook 组件: Operator:实现自动启动存储集群,并监控存储守护进程,并确保存储集群的健康; Agent:在每个存储节点上运行,并部署一个 CSI / FlexVolume 插件,和 Kubernetes 的存储卷控制框架进行集成。Agent 处理所有的存储操作,例如挂载存储设备、加载存储卷以及格式化文件系统等; Discovers:检测挂接到存储节点上的存储设备。 Rook 将 Ceph 存储服务作为 Kubernetes 的一个服务进行部署,MON、OSD、MGR 守护进程会以 pod 的形式在 Kubernetes 进行部署,而 rook 核心组件对 ceph 集群进行运维管理操作。 Rook 通过 ceph 可以对外提供完备的存储能力,支持对象、块、文件存储服务,让你通过一套系统实现对多种存储服务的需求。同时 rook 默认部署云原生存储接口的实现,通过 CSI / Flexvolume 驱动将应用服务与底层存储进行衔接,其设计之初即为 Kubernetes 生态所服务,对容器化应用的适配非常友好。 Rook 官方文档参考:https://rook.io/ 2)OpenEBS OpenEBS 是一种模拟了 AWS 的 EBS、阿里云的云盘等块存储实现的开源版本。OpenEBS 是一种基于 CAS 理念的容器解决方案,其核心理念是存储和应用一样采用微服务架构,并通过 Kubernetes 来做资源编排。其架构实现上,每个卷的 Controller 都是一个单独的 Pod,且与应用 Pod 在同一个 Node,卷的数据使用多个 Pod 进行管理。 架构上可以分为数据平面(Data Plane)和控制平面(Control Plane)两部分: 数据平面:为应用程序提供数据存储; 控制平面:管理 OpenEBS 卷容器,通常会用到容器编排软件的功能; 数据平面: OpenEBS 持久化存储卷通过 Kubernetes 的 PV 来创建,使用 iSCSI 来实现,数据保存在 node 节点上或者云存储中。OpenEBS 的卷完全独立于用户的应用的生命周期来管理,和 Kuberentes 中 PV 的思路一致。 OpenEBS 卷为容器提供持久化存储,具有针对系统故障的弹性,更快地访问存储,快照和备份功能。同时还提供了监控使用情况和执行 QoS 策略的机制。 控制平面: OpenEBS 控制平面 maya 实现了创建超融合的 OpenEBS,并将其挂载到如 Kubernetes 调度引擎上,用来扩展特定的容器编排系统提供的存储功能; OpenEBS 的控制平面也是基于微服务的,通过不同的组件实现存储管理功能、监控、容器编排插件等功能。 更多关于 OpenEBS 的介绍可以参考:https://openebs.io/ 3)Heketi 类似于 Rook 是 Ceph 开源存储系统在云原生编排平台(Kubernetes)的一个落地方案,Glusterfs 同样也有一个云原生实践方案。Heketi 提供了一个 Restful 管理接口,可用于管理 Gluster 存储卷的生命周期。使用 Heketi,Kubernetes 可以动态地为 Gluster 存储卷提供任何支持的持久性类型。Heketi 将自动确定集群中 brick 的位置,确保在不同的故障域中放置 brick 及其副本。Heketi 还支持任意数量的 Gluster 存储集群,为云服务提供网络文件存储。 使用 Heketi,管理员不再管理或配置块、磁盘或存储池。Heketi 服务将为系统管理所有硬件,使其能够按需分配存储。任何在 Heketi 注册的物理存储必须以裸设备方式提供,然后 Heketi 在提供的磁盘上使用 LVM 进行管理。 更多详解参考:https://github.com/heketi/heketi) 6. 优势 上述几种云原生存储方案其设计之初既充分考虑了存储与云原生编排系统的融合,具有很好的容器数据卷接入能力; 在 Quota 配置、QoS 限速、ACL 控制、快照、备份等方面有较好的云原生集成实现,云原生应用使用存储资源时更加灵活、便利; 开源方案,社区较为活跃,网络资源、使用方案丰富,让您更容易入手。 7. 劣势 成熟度较低,目前上述方案多在公司内部测试环境或者 SLA 较低的应用中使用,很少存储关键应用数据; 性能差:和公有云存储、商业化存储相比,上述云原生存储方案在 IO 性能、吞吐、时延等方面都表现欠佳,很难应用在高性能服务场景; 后期维护成本高:虽然上面方案部署、入门都很简单,但一旦运行中出现问题解决起来非常棘手。上述项目属于初期阶段,并不具备生产级别的服务能力,如果使用此方案需要有强有力的技术团结加以保障。 现状和挑战 1.敏捷化需求 云原生应用场景对服务的敏捷度、灵活性要求非常高,很多场景期望容器的快速启动、灵活的调度,这样即需要存储卷也能敏捷的根据 Pod 的变化而调整。 需求表现在: 云盘挂载、卸载效率提高:可以灵活的将块设备在不同节点进行快速的挂载切换; 存储设备问题自愈能力增强:提供存储服务的问题自动修复能力,减少人为干预; 提供更加灵活的卷大小配置能力。 2.监控能力需求 多数存储服务在底层文件系统级别已经提供了监控能力,然后从云原生数据卷角度的监控能力仍需要加强,目前提供的PV监控数据维度较少、监控力度较低; 具体需求: 提供更细力度(目录)的监控能力; 提供更多维度的监控指标:读写时延、读写频率、IO 分布等指标; 3.性能要求 在大数据计算场景同时大量应用访问存储的需求很高,这样对存储服务带来的性能需求成为应用运行效率的关键瓶颈。 具体需求: 底层存储服务提供更加优异的存储性能服务,优化 CPFS、GPFS 等高性能存储服务满足业务需求; 容器编排层面:优化存储调度能力,实现存储就近访问、数据分散存储等方式降低单个存储卷的访问压力。 4.共享存储的隔离性 共享存储提供了多个 Pod 共享数据的能力,方便了不同应用对数据的统一管理、访问,但在多租的场景中,不同租户对存储的隔离性需求成为一个需要解决的问题。 底层存储提供目录间的强隔离能力,让共享文件系统的不同租户之间实现文件系统级别的隔离效果。 容器编排层实现基于名词空间、PSP 策略的编排隔离能力,保证不同租户从应用部署侧即无法访问其他租户的存储卷服务。 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
“其实疫情对我们来说,是个催化剂。我们做投资绝对不会因为疫情去选择投还是不投”,张松是这么说的,也是这么做的。2020年1月,在新冠疫情大规模爆发之前,清松资本完成了对义翘神州的投资,而这家在媒体上鲜有露面的企业正是于1月22日全球首发新冠病毒关键靶点蛋白试剂的公司。在后续的几个月内,义翘神州陆续上线并向各大科研机构和生物医疗研发企业提供了一系列与2019-nCoV相关的重要蛋白和抗体试剂,支持病毒基础研究、创新药物疫苗以及检测试剂开发,成为了这场科技抗疫医药研发竞赛中有力的支持者。 基因突变正是病毒进化的基础,一旦突变就可以使病毒逃避宿主的免疫应答,并且更有利于病毒的传播。尤其是在疫苗和治疗性药物的开发过程中,对该突变体的生物特性认知就变得至关重要。任何关注新冠疫苗研发的普通人,哪怕毫无生物学背景,也一定看到过“Spike病毒蛋白”这几个字眼,正是义翘神州这家公司仅花了12天开发出了一系列的病毒关键靶点蛋白以满足研究者们对新冠突变株的研究。除此之外,这家公司在2014年埃博拉疫情愈演愈烈之时,也在很短的时间内完成了埃博拉(EBOV NP, GP, VP40, VP24) 相关试剂的研发和投产,H1N1甲流,H7N9禽流感,埃博拉病毒,每次疫情袭来,这家公司往往都在第一时间完成病毒靶点试剂开发,助力全球病毒研究。“这家公司是踏实做事的,不太会让媒体关注到。”去年底,张松就与这家公司达成了投资意向,“义翘神州是生物医药企业中埋头踏实做事的代表,公司的积累很扎实,一旦行业遇到了一个关键的转折点,在其他企业难以快速反应或者能力跟不上的时候,这样的企业就会快速崛起。” 张松是清松资本的创始合伙人和CEO。这位年轻的80后投资人在过去10余年的一级市场投资生涯里,经历过鼎晖这样大型PE机构的关键成长周期,也熟悉国内生物医疗行业和产业变化动荡的周期。2017年,他创立了这个专注于生物医疗垂直赛道的投资平台。与被投企业风格类似,这家成立近3年的机构即便在医疗投资行业内已小有名气,但鲜少在媒体上露面。 据不完全统计,自新冠肺炎疫情爆发以来,全球共有约875万人感染新冠病毒,并且已导致46万人死亡,这个数字还在不断攀升。几乎所有行业遭受重创的同时,生物医疗赛道的投资人忙碌不堪。贯穿整个第二季度,36氪与张松的采访是断断续续进行的,他在路上、在酒店登记入住前、还在与项目方会议开始前的一点点时间缝隙中分享了他对生物医疗投资的洞见。 “投资看重的是商业逻辑和企业本身的壁垒。我们希望哪怕在疫情期间,选择这些企业做投资的标准依然成立,甚至更严格”。在他眼里,疫情期间做投资决策等同于另外一个词,对企业的“极端压力测试”。 依然以“义翘神州”为例,作为一个数百人规模的研发型企业,在中国春节前后爆发的新冠疫情期间,是否还能抗住压力并在极端条件下运转自如,甚至超负荷运转,正是张松作为投资人最为看重的。“如何组织员工,尤其是外地和国外员工继续工作?如何组织上下游产业链,尤其可能在很多原料也供应不上的情况下继续开展研发?在这种时候,疫情考验每一个企业,去证明自己在这种情况下还能运转良好。同时也在考验每一个投资人,在企业需要的时候提供他们需要的帮助,成为企业在压力环境下想到的第一个人”,张松说。 义翘神州显然抗住了压力,并且在压力中成功“出圈”。 “这家公司本身具有极强的生产工艺和产能,目前已经成为全球新冠病毒蛋白和抗体试剂的重要供应商”,张松说。 独到且高度专业的投资眼光,加之快速的决断力,清松资本所投资的这家企业一季度的业绩实现了大幅上涨,张松也提到了该企业近期很可能会启动上市。 除此之外,清松资本也对今年的投资计划作了上调。“我们过去一年投5个左右,今年从过完春节到现在(6月底),已经有3-4个标的接近close了,还有3到5个正在洽谈过程中”。张松在电话那头不紧不慢地分析,“事实上,我们觉得新冠疫情会加速生物医疗企业的发展,尤其是跟疫情相关的产业链条中企业的快速成长,最终会导致行业进一步分化,奠定一些优质企业在行业里的竞争力和影响力。” 显然,尽管外部经济周期的变化在给整个资本市场带来扰动,但这也正是新一代投资人和具有真正竞争优势的企业蓄力、成长的机遇。伴随着行业的洗牌和重塑,一批真正在行业扎根的投资人和具有真正核心竞争力的企业正在成为主流。 清松资本给自己的定位是“更具备生物医疗产业思维的财务投资人”。 无论环境如何变化,清松资本的团队坚持探索医疗产业各细分领域的优质项目,致力于与创新型生物医疗企业共同成长,深度参与被投企业的发展,深耕生物技术、医药、医疗器械、创新型医疗服务以及大健康产业与前沿科技的交叉领域。对于每一个被投企业,清松都乐于向企业提供他们真正需要的投后赋能,希望能够陪伴这些优秀的创新型生物医疗企业共同成长。因此张松对基金的投资策略非常清晰,“总体来讲,我们会更注重技术驱动型的公司,尤其是有明确的技术壁垒的、有能力成为细分领域龙头企业。” 克睿基因是另外一个带着清松资本典型打法的投资案例。这家由CRISPR技术的先行者创办的转化型生物科技企业,致力于解决难治愈肿瘤、复杂遗传性疾病的临床需求,专注于新型医药、分子诊断领域中CRISPR技术原创性应用的开发。“实际上,我本人也是克睿的创始人之一,这个公司是我们几个同学和清华的师弟师妹一起创立起来的。我在这个公司里面会负责一些资本角度的发展战略问题,我们基金选择投进去也是顺理成章的事情。”这一略显特殊的案例,也反过来代表了清松资本在各个被投企业中的位置和定位。在看准的赛道中“亲自下场”,参与企业从创立到成长的全过程代表了清松资本除了少数股权投资之外在生物医疗行业中的另一坚定执行的投资策略。 “找到真正的行业龙头”,这句话行业里确实有很多人说,但在如何一以贯之地执行上张松有自己的经验,他说,“我在这个行业已经干了十几年了,整个医疗领域的产业链条,从医院一直到上游的医疗器械、医疗耗材、药品,再往上游的科研服务等等。每一个细分链条中前端的研发驱动到底是什么,作为投资人一定要足够的熟悉,这就意味着你得在行业里做足够专注和长期的跟踪,这样才可能在合适的时间点上以合适的价格投资优质的企业。但话说回来,优质的企业同样对投资人非常的挑剔,你即便想投对方不一定会答应,你说是吧?” 除了长期跟踪以外,清松资本在投资效率上表现出色,“按照现在的进展,到今年年底,我们投资的企业大概率会有4家或者5家完成IPO,当然这取决于IPO的速度。 所以,寻找一个真正的行业龙头这句话,简单说说容易,但能做到的真的不多”。但可以肯定的是,此次疫情使得风险投资对医疗行业的重要性以及未来投资的价值更加认可,也将会有更多的资金涌进医疗健康业。随着“金矿”出现,掘金的人也会变多,新的问题随之而来:如何对抗细分领域的估水涨船高?热潮之下,是否所有的短期需求都会衍生出长线价值? 成功投资的关键是对估值体系和成长性的双重把握。新冠疫情偶然间成就了生物医疗赛道的风口,但同时也要记得,风口的兴起从来不是第一次,也不会是最后一次。在这样种种不确定性加剧的时代背景下,那些大力投入研发、掌握平台级核心技术的企业,在细分领域里长期且专注的专业投资人,构筑了深厚的安全边际,也许就足以对抗宏观经济与资本市场的不确定性了。 本文转自<36氪>——36氪VClub
最近我们线上有个应用服务器有点上头,CPU总能跑到99%,我寻思着它流量也不大啊,为啥能把自己整这么累?于是我登上这台服务器,看看它到底在干啥! 以前碰到类似问题,可能会考虑使用 top -Hp 加 jstack 命令去排查,虽然能大致定位到问题范围,但有效信息还是太少了,多数时候还是要靠猜。今天向大家推荐一款更高效更精准的工具:Arthas!Arthas 是 Alibaba 开源的 Java 诊断工具,能够帮助我们快速定位线上问题。基本的安装使用可以参考官方文档:https://alibaba.github.io/arthas 这次我们利用它来排查 CPU 负载高的问题。CPU 负载过高一般是某个或某几个线程有问题,所以我们尝试使用第一个命令:thread,这个命令会显示所有线程的信息,并且把 CPU 使用率高的线程排在前面。 [arthas@384]$ threadThreads Total: 112, NEW: 0, RUNNABLE: 26, BLOCKED: 0, WAITING: 31, TIMED_WAITING: 55, TERMINATED: 0ID NAME STATE %CPU TIME108 h..ec-0 RUNNABLE 51 4011:48 100 h..ec-2 RUNNABLE 48 4011:51... 为了方便阅读,删掉了一些不重要的信息 可以看到,CPU 资源几乎被前两个线程占满,并且已经执行了 4000 多分钟,我们服务器也就启动了两天,可见这两天它们是一刻也没闲着!那它们究竟在干什么呢?我们可以使用命令:thread id,查看线程堆栈。 [arthas@384]$ thread 108"http-nio-7001-exec-10" Id=108 cpuUsage=51% RUNNABLE at c.g.c.c.HashBiMap.seekByKey(HashBiMap.java) at c.g.c.c.HashBiMap.put(HashBiMap.java:270) at c.g.c.c.HashBiMap.forcePut(HashBiMap.java:263) at c.y.r.j.o.OaInfoManager.syncUserCache(OaInfoManager.java:159) 也可以使用 thread -n 3 命令打印出 CPU 占比最高的前三个线程,这差不多是 > top -Hp> & > printf> & > jstack> 三令合一的效果了> 可以看到,这个线程一直在执行 HashBiMap.seekByKey 方法(可以重复执行几次 thread id 确保该线程执行的方法没有时刻在变化),造成这个问题一般有两个原因: seekByKey 方法被循环调用 seekByKey 内部有死循环 先看一下是不是第一种,我们使用 tt 命令监听一下这个方法的调用情况: tt -t com.google.common.collect.HashBiMap seekByKey -n 100 注意:在线上执行这个命令的时候,一定要记得加上 -n 参数,否则线上巨大的流量可能会瞬间撑爆你的 JVM 内存执行结果显示,seekByKey 方法并没有被一直调用,那大概率是 seekByKey 方法内部有死循环。看下这个方法内部的逻辑,我们可以使用 jad com.google.common.collect.HashBiMap seekByKey 命令反编译这个方法,这样做的好处是显得比较高端,不过我还是打算直接找到源码,说不定还有注释。源码如下: private BiEntry seekByKey(@Nullable Object key, int keyHash) { for (BiEntry<K, V> entry = hashTableKToV[keyHash & mask]; entry != null; entry = entry.nextInKToVBucket) { if (keyHash == entry.keyHash && Objects.equal(key, entry.key)) { return entry; } } return null; } 然后并没有注释,还好这个方法逻辑比较简单,也很容易看懂。 通过 hash 找到 bucket,每个 bucket 是一个链表; 遍历链表,找到这个 key 对应的 entry。这里要留意下 entry 的下一个节点是 nextInKToVBucket,后文中会用到。 发生了死循环,我们猜想可能是因为这个链表有环路。那么有没有办法验证这个猜想呢?答案是有!那么如何验证呢?首先我们要获得这个 HashBiMap 对象,以便于查询对象里的数据。获得这个对象有很多办法,比如监听这个对象的某个方法,然后主动触发这个方法。这里向大家介绍一种更为通用的方法,这个方法在 SpringMVC 程序里非常好用。因为我们是 SpringMVC 应用,所有请求都会被 RequestMappingHandlerAdapter 拦截,我们通过 tt 命令,监听 invokeHandlerMethod 的执行,然后在页面随便点点,就会得到以下内容: [arthas@384]$ tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod -n 10Press Q or Ctrl+C to abort.Affect(class-cnt:1 , method-cnt:1) cost in 622 ms. INDEX COST(ms) OBJECT CLASS METHOD 1000 481.203383 0x481eb705 RequestMappingHandlerAdapter invokeHandlerMethod 1001 3.432024 0x481eb705 RequestMappingHandlerAdapter invokeHandlerMethod... tt 命令会记录方法调用时的所有入参和返回值、抛出的异常、对象本身等数据。INDEX 字段代表着一次调用,后续tt还有很多命令都是基于此编号指定记录操作。 我们可以通过 -i 参数后边跟着对应的 INDEX 编号查看这条记录的详细信息。再通过 -w 参数,指定一个 OGNL 表达式,查找相关对象: [arthas@384]$ tt -i 1000 -w 'target.getApplicationContext()'@AnnotationConfigServletWebServerApplicationContext[ reader=@AnnotatedBeanDefinitionReader[org.springframework.context.annotation.AnnotatedBeanDefinitionReader@50294e97], scanner=@ClassPathBeanDefinitionScanner[org.springframework.context.annotation.ClassPathBeanDefinitionScanner@5eeeaae2], annotatedClasses=@LinkedHashSet[isEmpty=true;size=0], basePackages=null, OGNL 使用文档:https://commons.apache.org/proper/commons-ognl/language-guide.html Arthas 会把当前执行的对象放到 target 变量中,通过 target.getApplicationContext() 就得到了 SpringContext 对象,然后,我们就可以为所欲为了! 接下来我们需要用 OGNL 写一个函数,来实现链表的环路检测,在 OGNL 里写一段环路检测代码里是不太容易的,这里我用了一个取巧的伪实现。 因为我知道一个 bucket 不太可能有 50 个以上的节点,所以就通过遍历次数是否大于 50 来判断是否有环路。 完整的命令: tt -i 1000 -w 'target.getApplicationContext().getBean("oaInfoManager").userCache.entrySet().{delegate}.{^ #loopCnt = 0,#foundCycle = :[ #this == null ? false : #loopCnt > 50 ? true : (#loopCnt = #loopCnt + 1, #foundCycle(#this.nextInKToVBucket))], #foundCycle(#this)}.get(0)' -x 2 命令解析: 获取 HashBiMap 对象:target.getApplicationContext().getBean("oaInfoManager").userCache 遍历所有 entry,取出第一个有环路的 entry -x 参数指定展开层级,我们需要将这个参数设置的比环要大一些,才能确保可以发现环路。这里我们的环路非常小,所以设置成了 2 执行结果如下:@BiEntry[ key=@String[张三], value=@Long[1111], nextInKToVBucket=@BiEntry[ key=@String[李四], value=@Long[2222], nextInKToVBucket=@BiEntry[张三=1111] ] ] 可以看到是有 张三->李四->张三 这样一个环路。至此,造成死循环的原因确定了下来。结合两个线程几乎同时启动,又同时在执行 HashBiMap.forcePut 方法,容易想到是因为并发导致了数据的不一致,这一点也可以验证,不过由于篇幅有限,这里就不再赘述。找到了问题,就成功了 99%,解决这个问题的方法非常简单,就是对 syncUserCache 方法加一个 synchronized 关键字! 结语 这次遇到的问题并不复杂,用 jstack 命令也可以解决的了。但我们希望通过这样一个案例,向大家展示 Arthas 一些强大的功能,帮助大家打开思路,未来在遇到更复杂场景时,可以多一些趁手的工具! 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
近年来,容器技术以迅雷不及掩耳之势迅速蔓延开来,容器技术基于各种场景的尝试与实践更是层出不穷,它的崛起改变了我们创建、发布与运行应用的方式,实现了资源的独立与隔离、相较于传统的虚拟化技术,它更加轻量化。 随着以 Docker 为代表的容器技术在越来越多的业务场景中被应用,越来越多开发者开始习惯将应用打包成标准格式镜像并存放在镜像仓库中以便完成日常部署发布、团队协作等工作。但在真正的生产环境中,我们会产生大量镜像,不管是第三方还是自建,而这些镜像伴随而来的是部署、保存、分发使用等大量繁琐、重复的部署、管控工作。 对于开发者而言,在本地 IDE、镜像仓库、容器等多个产品间来回切换,不仅耗费大量工时与精力,而且繁琐细碎的操作更容易造成一系列失误。 说到底,哪个开发者想做一个天天机械性重复工作的工具人呢?谁不想做点有创造性的工作呢? 面对这样的难题,Cloud Toolkit 给出了答案~ 在 IntelliJ IDEA 安装和配置 Cloud Toolkit 后,只需在配置界面设置部署参数即可实现自动化部署,将应用快速部署到镜像仓库,并借助阿里云提供安全的镜像托管能力,高效管理镜像 。 那么,我们来看看到底如何做,才能实现部署提速 8 倍?首先,先和大家聊一聊搭配免费 IDE 插件即可实现一键部署、持续集成与交付的的容器镜像仓库 ACR。 容器镜像仓库 ACR 阿里云镜像仓库 ACR 分为默认实例版与企业版,虽然结合阿里云产品做了多维度优化,但是并不与阿里云强制绑定。ACR 默认实例版面向容器开发者,提供安全的镜像托管、便捷的镜像授权功能,方便用户进行镜像全生命周期管理,并且简化了 Registry 的搭建运维工作,支持全球 20 个地域的镜像托管。 ACR 企业版面向安全需求高、业务多地域大规模部署的企业级客户,提供大规模镜像分发能力、企业级的安全独享特性,以及云原生应用交付链,全链路可观测、可跟踪以及可设置,可实现一次应用变更,多场景自动化交付。 官方链接地址:https://www.aliyun.com/product/acr 如何实现部署提速 8 倍? Step 1:前提条件 已安装和配置Docker; 已安装和配置 Cloud Toolkit。 Step 2:镜像仓库配置 使用 Cloud Toolkit 将应用部署到镜像仓库具体步骤如下: 在 IntelliJ IDEA 界面左侧的 Project 中右键单击待部署的工程名,在快捷菜单中选择 Alibaba Cloud > Deploy to Registry / Kubernetes > Deploy to Registry; 在 Deploy to Registry 对话框设置部署参数; 部署镜像仓库参数说明:Build Image - Context Directory(文件目录)、Dockerfile (Docker 文件)、Version(镜像版本号);Image Repositories - Alibaba Cloud Container Registry(阿里云镜像仓库)、Custom Container Registry (自建镜像仓库); 说明 Context Directory 和 Dockerfile 通常会根据您的本地应用工程自动识别并设置。 Step 3-1:部署应用到 ACR 在部署参数页面选择 Alibaba Cloud Container Registry; 选择地域; 选择命名空间; 选择镜像仓库; 在 Advanced 下拉选项中选择网络类型:Internet - 公有网络;VPC Network - VPC 网络;Classic Network - 经典网络; 先单击 Apply,然后单击 Run。 Step 3-2:部署应用到其它镜像仓库 在部署参数页面选择 Custom Container Registry; 单击 Add,配置 Registry 信息; 在 Registry 页面配置镜像仓库 Name、Address、Username 和 Password。 说明:建议单击 Test Connection,测试远程仓库是否连接成功。 选择 Apply > OK; 在 Repository 对话框填入您的镜像地址; 先单击 Apply,然后单击 Run。 Step 4:结果验证 以阿里云容器镜像服务平台为例,可通过查看您的镜像版本更新时间来确认镜像是否推送成功。 正如上述,仅需简单设置,即可完成的相关部署工作,简直是“真.一键部署”。 除了对 IntelliJ IDEA 的支持,Cloud Toolkit 现在也已经全面支持 Eclipse 的镜像仓库快速部署。 仅需一个 Cloud Toolkit 插件,减轻开发者的工作量,就是这么简单~ 作为集开发、测试、诊断、部署为一体的免费本地 IDE 插件,Cloud Toolkit 帮助开发者真正实现一键式研发部署。提升研发部署速度 8 倍以上,大幅降低研发成本。 所以,别再犹豫,各大 IntelliJ IDEA、Vs Code、Eclipse 等市场搜索“Cloud Toolkit”安装体验吧! 获取方式: IntelliJ IDEA 版 / PyCharm 版本:https://plugins.jetbrains.com/plugin/11386-alibaba-cloud-toolkit Visual Studio Code 版本:https://marketplace.visualstudio.com/items?itemName=alibabacloud-cloudtoolkit.toolkit-vscode Eclipse 版本:https://marketplace.eclipse.org/content/alibaba-cloud-toolkit Maven 版本:https://help.aliyun.com/document_detail/108682.html 使用教程:https://help.aliyun.com/document_detail/147728.html 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
20 年,马云退休,阿里完成了一代核心管理者的交棒。这家公司用 20 年走到了超过 4300 亿美元的市值,可能是胜在时机、胜在赛道,但很大一部分,是胜在文化。 “所有新经济公司里,只有阿里的组织能力过关,包括美团在内都不过关”,去年美团点评联合创始人、高级副总裁王慧文曾这么表达他对组织的看法···当我们今天看向创业浪潮的时候,在意且懂得如何做好文化、做好组织的创业者们少之又少。 那么,阿里的组织能力究竟厉害在哪?它如何能让一家公司基业长青?它又对从这个体系内走出的创新者会产生什么样的影响? 6 位有着阿里烙印的创业者和36氪聊了聊他们的想法。他们当中有的在 1999 年就加入了阿里,有的经历了公司几次大的业务变革。从他们的自述中,我们能感受到阿里的组织文化作为一种底层“基因”的影响力。 佰万仓杨宁:阿里系的人喜欢创业,看到有机会能改变点什么时都会冲进去 阿里是1999年9月10月成立的,我10月的时候就进去了,是阿里第28号员工。早期什么都干过,除了技术没做,客服、网站运营、销售、编辑都做过了。 阿里系统建立自己的文化是在2001年初,当时处于一个比较关键的时期,互联网泡沫破灭,阿里也面临巨大的挑战。当时找来了COO关明生,主导建立了整个价值观体系。 (注:这里指阿里最早的价值观“独孤九剑”:激情、创新、教学相长、开放、简易、群策群力、专注、质量、服务与尊重。) 阿里文化考核日常占比50%,非常严格,马云每次讲话都会讲到。可以说,阿里其实是一种挑选自己需要的人的文化,因为不是每个人都能适应阿里文化的。 阿里对我后面所有的创业都有很大的影响,尤其是马云。总结来说是三个关键词:眼光、胸怀、超越伯乐——眼光要看到足够远,能看到未来的机会;要包容,要和各种各样的人合作,马云也有句话叫“男人的胸怀是委屈撑大的”;超越伯乐,就是要有人才培养的能力。 从阿里出来一年半之后,我加入慧聪网,从总监做到总裁,期间帮助公司转型,市值涨了150多倍。经过这几年,我现在觉得跟价值观相比,核心组织能力的考核会更重要。 全员考核的时候,一般评奖是按照部门去评,最佳客服、最佳销售等等,问题是局部最优,但未必是公司最优。我们现在会按照组织能力去考核,就是根据公司的价值导向走,比如公司用户第一,那就把所有员工中把对用户价值做得最好的人挑出来。 受阿里“用户第一”的影响,阿里出来的创业者都很重视用户价值,公司时刻思考的是对用户端的价值什么,而不会to VC。佰万仓也是,我们以用户为第一导向,服务是最重要的。 但也不会说,阿里系创业者的能力就特别突出,只能说阿里系的人,是打硬战打出来的,会有一些商业的敏感度和经验的判断等。 阿里系的人都喜欢创业,像我之前已经是上市公司的总裁,还是要出来创业。阿里系的人有这种精神,看到有机会能改变点什么的时候,都会冲进去。 马云退休也是,其实不影响什么。退而不休,大家都是用不同的方式都在创业,永远都在路上。 海拍客赵晨:阿里塑造了很多人,对整个互联网的文化都产生了一些影响 我在阿里待了9年,出来创业之前是天猫国际负责人,带领团队从0-1把天猫国际做起来,2015年离开的时候规模做到20个亿。在这之前,我在阿里内部还有过一个0-1做项目的经验。 总结来说,阿里对我有两个很重要的影响: 一是内部 0-1 做出项目的经验,这是很珍贵的财富。在阿里内部做项目相当于简单版本的创业,阿里会给流量、钱和其它资源,有种富二代创业的感觉。在这个经验的基础上,自己创业就只是稍微拓宽了领域,可能原来你只要关心5件事,现在要多关心10件事。 这个经验可以帮助你理解创业是怎么一回事。第一次创业会很焦虑,空闲下来就觉得不对,但你在内部经历过两次,心态上就会比较好,否则你可能很容易有挫败感。 其次节奏感也会把握得比较好。创业初期要做很多事,选择业务方向、搭建团队、确定业务细节,优先级很容易乱,到底是先融资还是找人、还是先做业务,这需要经验来支持你作出判断。 然后另一个大的影响是价值观,这个非常明显。海拍客的几个合伙人都是阿里出来的,大家对于什么事情先做、什么时候后做,什么事情能做、什么事情不能做,可以很快达成共识 海拍客打造的文化价值观和阿里非常像,客户第一、诚信、拥抱变化、团队合作,已经融入到血液里,这对创业非常重要。 举个例子,我们刚出来的时候跟一家连锁店合作,销售开始跟老板谈得很好,但之后一直推拖,觉得不对再仔细去问,发现是买路钱没交。这样的潜规则很多地方都存在,那海拍客要不要也这么干?我们1分钟就做了判断,不干,哪怕先不做这业务。 阿里给到我们很多宝贵的东西,你在里面的时候可能没有感觉,出来后就会特别感谢。平等、简单、开放、透明,在阿里得到的价值观太好了。 互联网文化我是进了阿里之后感受到的。跟以前我待过的传统公司非常不一样,之前高层、中层和普通员工的牌子颜色都不一样,等级分明。但是在阿里,多大的老板都叫名字或者花名,你有什么想法可以直接沟通,高层没有独立办公室,出差住宿的标准都是一样的。 阿里开创了一个时代,塑造了很多人,是体现在底层的性格和方法论上。不只是阿里的这群人,可以说阿里对整个互联网的文化都产生了一些影响。 好命天生李顺:全世界几乎所有主流商业形态都能在阿里找得到,你的认知和能链接的资源会很全面 我从阿里出来前是在阿里巴巴淘宝极有家的行业运营团队。 在阿里的时候,我感触最深的一点是,在阿里这个“经济体内部“,你几乎能找到全世界当下所有的主流商业形态。即便只是手淘这一个平台,除了商品交易,你还能看到大量的内容、社交以及很多横向整合的玩法。 阿里内部很提倡“链接”,鼓励你主动去“撩”各种资源,不仅是在自己BU内部,还可以跨BU去找支付宝、大文娱、饿了么、阿里云等不同的业态去横向合作。 因此,在阿里一个是你所能连接的业态是很多元的,同时你能连接的牛人也是很多的,阿里人都很朴实,有时在园区迎面走来一个穿普通 T-shirt 的人,一问居然个人工智能算法博士,这样的人很多。 我觉得阿里最强的地方是平台化的思维和能力,像我现在做的项目「好命天生」,是为年轻一代养宠人群提供高颜值、高品质、贴心价格的宠物消费品品牌,如果只是做一个垂直行业的品牌,根据目前行业的整体规模,短期内做到 3-4 亿可能就会遇到比较明显的天花板,这不是我想要的,所以一开始我就给公司设了更高的目标,在完成产品体系的构建和品牌打造后,要去走平台型的模式,这样才能打破天花板。 阿里的体量和能力无疑是惊人的,但其实最厉害的还是阿里的组织和文化。马老师也经常说阿里是一家使命和价值观驱动的公司,阿里有个词很有意思,叫“阿里味”,就是员工和企业对不对胃口,是不是“一卦”的。 所以出来创业之后,我对公司的价值观非常的重视,因为员工都是 95 年前后,客户也是这个年纪,我就用了当下年轻人很喜欢的 3 个词作为文化价值观:be real、be love、be young。 be real,这个词来自阿里一句让我印象很深的话:“说了不一定有用,但是不说肯定没用”。我们鼓励同事去真实客观地表达自己的想法,不要因为是老板,是上级就轻易改变自己的观点和看法 —— 你要说服我这件事情是对的,即便你是 leader,也要善于表达自己。 这样的好处是每个人都觉得这是自己的事,是自己的决定,而不是老板强压给我的。就像动车相比传统火车的最大的优点是每节车厢都是有动力的,而不是只靠火车头拉着跑,这样才跑的更快。 be love,这一条是说要懂宠爱宠,用户第一、有情有义。阿里有两句话, 一是和一群有情有义的人一起做一件有价值的事情,另一句是,用户第一是一种能力,这不是一句虚的话,如果你不懂宠物、不懂用户,你就没办法去正确的“爱”,就更没办法把用户放在第一去做决策。 be young,我们解释这条是说要永远保持“年轻人的心态”, 保持好奇、拥抱变化、追求造物。要有年轻人的创造力,和对年轻人的洞悉。比如我们做用品的时候,对消费者的洞悉是:泛 95 后消费者很难被单纯停留在颜值层面的品牌吸引,还要满足他们对不同玩法的需求,商品不仅在颜值、品质价格上有很好的匹配,还需要有社交属性,满足“玩”的需求。 奇点云张金银:阿里是一家梦想驱动的公司,有了梦想之后,会选择去做正确的事情 2004 年加入阿里之前,我是写软件代码出身的,那一年马云太太张瑛通过猎头找到我,说阿里想建立自己数据体系和自己的团队。我就去了,开始着手建立阿里的数字仓库团队,到 2016 年 12 月份离开,待了 12 年时间。 在我经历的阿里搭建数据体系的历史上,有很多难关,比如:2012 年的时候,陆兆禧兼任了阿里的 CDO 首席数据官,成立了阿里巴巴的数据平台部,阿里巴巴就从数据仓库迈向数据平台时代。 这里面的困难是什么呢?阿里巴巴的业务增长特别快,也很复杂,收购过来了 200 多个 BU,为了让业务跑得快,各业务线以前是分散开的,但是数据却不通。 数据不通带来的问题是,比如你用支付宝去交水电煤、用高德去导航,那时候还是 PC 时代,各个服务都有一个不同的用户账号,但实际上这些账号背后是同一个人,如果数据分散在 BU 里,我们对每个用户的理解都是片面的,如果打通放在一起,对阿里巴巴整体是受益的。 所以数据平台部负责人就安排我做 TCIF,就是以淘宝的消费者为核心,把各个 BU 的数据整合到一起。 这里面的挑战很多:以前都是散落在各个 BU、或者子公司自己的数据系统里面,我们怎么样能把这些数据存在一起?我们就构建了一个庞大的集群,把阿里巴巴内部一百多个小的集群都迁移到这个大集群上,这个叫“登月计划”; 存在一起不够,还要打通,比如你要知道不同的账户是同一个人,这个是需要算法去识别的,我们就做了 id mapping。 这件事情做好了之后是很有价值的,一个例子是 UC:当时 UC 刚刚被收购,浏览器最大也是比较难解决的问题就是冷启动的问题:如果用户第一次用 UC 浏览器,我怎么知道你喜欢什么新闻?这个时候数据的整合和打通就很有用了,我们能够通过他的淘宝记录去推测他可能的内容偏好。 还有一个印象比较深的难关,是 2015 年的时候,阿里的数据平台部被并入阿里云,孙权也接替了王坚博士。当时孙权觉得阿里云只卖以前 IaaS 的服务是不够的,需要去卖更多数据服务 —— 所以我们就要去考虑怎么把内部使用的人工智能和大数据的工具能够提供给外部客户使用。 但内部和外部的诉求是不一样的,如果拿自动驾驶类比的话,外部客户可能还在 L1 阶段,但我们内部的服务其实已经到了 L4 阶段,是不符合外部用户的实际需求的,就需要做很多简化,后来我就带队内部创业,去做了“数加”,也就是 data.aliyun.com,我是这个事情的创始人。 文化在迈过这些难关的过程当中,是能起到很多帮助的。 阿里的“梦想驱动”也做得很好。马云当时会经常和我们讲他的梦想是什么?一开始说的梦想是希望阿里巴巴成为全球 10 大网站,后来就慢慢演变为“成为一个经济体”。其实阿里的工作强度是很大的,但有了梦想之后,工作起来不会很累,并且会选择去做正确的事情。 我 2004 年刚进入公司的时候,就能感受到阿里在文化层面的投入,当时阿里是一个成立只有 5 年、员工只有 2000 人左右的公司,但阿里会给新人 2 周的脱产培训,去讲阿里的文化、阿里的价值观。我记得非常清晰,我们价值观的课是关明生上的,他还问我们怎么理解价值观是什么?阿里的成就和它对文化的重视是分不开的。 另一个文化是拥抱变化。阿里基本一年调很多次组织架构,甚至一个人进来半年可能换 5 个老板。如果是很多传统公司这么折腾的话,公司都散架了,但是阿里有这个文化,大家就会觉得创业的初心不变。 KKS王莺:阿里是一家“三个臭皮匠顶个诸葛亮”的公司,它让我看到个人能力的局限 我是2009年加入阿里巴巴,当时参与了1688从信息平台到交易平台的转型,之后筹建菜鸟,是菜鸟最后一公里的总设计和总负责,包括现在的菜鸟各种自提站点、柜子等。 我算是见证了阿里从1-10的过程,我觉得价值观是阿里成功的一个重要因素。阿里有很多正能量的“土话”,我到创业之后才明白这些土话的力量,比如拥抱变化,比如客户第一、员工第二。 不过自己创业的时候,这些价值观也不能直接生搬硬套过来。跟在阿里内部创业相比,你完全没有任何背书,资源全靠个人,起点还是要差很多。KKS的价值观借鉴了阿里,但根据自己团队的情况做了一些调整。 阿里其实是一家“三个臭皮匠顶个诸葛亮”的公司,大家进去都是普通的人。它让我看到了个人能力是非常有限的,你原来的经验和认知在这里可能没办法直接使用,哪怕你是100分也要依靠团队的力量。 阿里对我的影响非常深刻,中间当然学到了很多事情的方法论,但在这里培养的最大能力还是对于自己的正确认知。人有一个很大的问题是会自我设限,但我认为,你这个人本身是什么样子决定了事情的走向,尤其是创业公司早期,CEO是公司的灵魂。 现在我觉得,能否持续想出更好的办法直到成功,这就是运气,也是久而久之人与人之间形成的差异。Try your best and show your best。 阿里成就了我人生一段很重要的经历,我对它有很深的感情,但现在创业,我在尽量去掉这样的标签。俗话说,好汉不提当年勇,我们应该寻找同一个目标下,渴望成功并愿意为之持续努力的伙伴,无论他来自哪里。 人是有多样性的,重要的是看他在什么时间段做成了什么,擅长什么,跟现在创业做的这件事关系大不大。成功的人可能会有很多共性,但你得找他们的特质而不是背景。 精准学杨仁斌:不让候选人、投资人因为阿里创业者光环而有过高的成功预期 我在从阿里出来创业前是手机淘宝首席产品经理,整体上负责手机淘宝各产品线,主导了淘宝各产品线实现千人千面,以及打造个性化的推荐引擎。 拥有阿里系创业者这个标签,我首先反而会时时提醒自己,不要照搬阿里的方法,不要经验主义,理解阿里背后的思考,否则大概率会挫败。 其次我也不希望让候选人和投资人因为阿里创业者光环而有过高的成功预期,避免大家带着一夜暴富的预期,反而更强调创业的困难和风险。 我也不会去承诺候选人,他们创业一定会拿到巨额回报。对每一个看中的候选人,我都会反复强调我们只有10%的成功率,甚至有时候我讲我们最近下降到5%,或者我们觉得你加入进来之后,有可能提升到15%。 但与此同时,我们一定会强调的是,在这里,他有机会跟一群每天都追求自己进步的人共事,有从0到1的去做一件事,有挫败后大家一起帮你复盘的机会,当然,也有取得成绩后大家给他的喝彩。 阿里让我认知了使命、愿景和价值观这种看似很虚的事,其实有着非常实际意义。精准学从创立之初给自己定的使命是“让每个孩子都拥有终身学习的能力”。为此,我们组建实了研发团队,去做人工智能自适应教育算法体系搭建,来帮助学生提升成绩,我认为这是一项非常有意义的事业。 我们的公司继承了一部分阿里的文化,类似于“今天的最好表现,是明天的最低要求”,但也有自己独特的部分,那就是“开放性”和“拿结果说话”,这是一种成长性的文化,要求人有更快的学习和迭代。 在精准学,大家常常挂在嘴边的是,“初速度重要,加速度更重要”,也会常听到讲“自归因”,讲“结果决定你有没有奖金,过程决定给不给你机会”,还有员工会常常向上级主管索要“及时精准负反馈”。 一个企业的文化,不是挂在墙上的标语,而是每一个内部会议上、每一个内部讨论过程中,每一位员工都时不时蹦出来的一些“土话”,那就是我们的文化。 本文转自<36氪>——陈淑雅
Arthas watch 命令使用指南 Arthas 是我很喜欢的一款 Java 领域的开发调试工具。 每次测试遇到问题的时候,当别人为了加一条日志而重发代码,我都会欣慰地拿出我的 Arthas 并且告诉他们:少年,你不用再为了加日志就重发代码而烦恼了。Arthas,你值得拥有。 这次我要介绍的是我使用最多的一个功能:watch。Arthas 功能虽多,但我最喜欢的还是这一个。使用 watch 之后,我再也不用为了观察函数调用而加日志了。 Arthas 是什么 Arthas 官网是这么介绍自己的: Arthas 是 Alibaba 开源的 Java 诊断工具,深受开发者喜爱。 当你遇到以下类似问题而束手无策时,Arthas 可以帮助你解决: 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception? 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了? 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗? 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现! 是否有一个全局视角来查看系统的运行状况? 有什么办法可以监控到 JVM 的实时运行状态? 怎么快速定位应用的热点,生成火焰图? 一键安装并启动 Arthas - 方式一:通过 Cloud Toolkit 实现 Arthas 一键远程诊断 Cloud Toolkit 是阿里云发布的免费本地 IDE 插件,帮助开发者更高效地开发、测试、诊断并部署应用。通过插件,可以将本地应用一键部署到任意服务器,甚至云端(ECS、EDAS、ACK、ACR 和 小程序云等);并且还内置了 Arthas 诊断、Dubbo工具、Terminal 终端、文件上传、函数计算 和 MySQL 执行器等工具。不仅仅有 IntelliJ IDEA 主流版本,还有 Eclipse、Pycharm、Maven 等其他版本。 推荐使用 IDEA 插件下载 Cloud Toolkit 来使用 Arthas:http://t.tb.cn/2A5CbHWveOXzI7sFakaCw8 方式二:直接下载 地址:https://github.com/alibaba/arthas。 curl -O https://alibaba.github.io/arthas/arthas-boot.jar && java -Dfile.encoding=UTF-8 -jar arthas-boot.jar复制代码 稍微解释一下上面这条 shell 命令。命令分为两部分,&& 之前的部分是下载 Arthas,之后的部分是启动 Arthas。 你可能会疑惑下载文件为什么不用 wget 而是用 curl?这是因为有些服务器是没有预装 wget 的,但是基本都预装了 curl。如果你的服务器预装了 wget 的话完全可以把 'curl' 改成 wget。 如果使用 wget 的话命令可以改成: wget 版命令 wget https://alibaba.github.io/arthas/arthas-boot.jar && java -Dfile.encoding=UTF-8 -jar arthas-boot.jar复制代码 另外一个需要解释的点是 -Dfile.encoding=UTF-8,这个 Java 设置是为了让 Arthas 输出中文的时候不会乱码,这一点可以看一下我以前的文章 由 Arthas 中文乱码引发的 Java 默认编码思考。 Arthas watch 命令 watch 让你能方便地观察到指定方法的调用情况。能观察到的范围为:返回值、抛出异常、入参(还能观察执行函数的对象本身,不知道为什么官方介绍的时候没说这个」,通过编写 OGNL 表达式进行对应变量的查看。 watch -h USAGE watch [-b] [-e] [-x ] [-f] [-h] [-n ] [-E] [-M ] [-s] class-pattern method-pattern express [condition-express]复制代码 1. 观察方法返回结果 returnObj 使用方式看着复杂,其实很简单。来个最简单的示例: 假设我们要观察下面这段代码中字符串的 contains 方法。 public class App { public static void main(String[] args) throws IOException { String hello = "Hello Arthas"; while (true) { boolean contains = StringUtils.contains(hello, "Arthas"); System.out.println(contains); } } }复制代码 可以使用如下语句: -n 3 表示只执行三次,这参数挺常用,不然很容易被输出刷屏。 2. 过滤不关心的调用 condition-express 显然,真实的案例肯定不会如上面的示例那么简单。 真实的服务代码中,肯定不止一个地方调用了 String 的 contains 方法。我们需要把无关的调用过滤掉。 观察 contains 返回结果,并且过滤掉无关调用 [arthas@11939]$ watch org.apache.commons.lang3.StringUtils contains returnObj 'params[1]=="Arthas"' 入参是一个很容易把不同调用区分开的方法,通过 params[1]=="Arthas" 这个 condition-express,我们可以只保留第二个入参是 Arthas 的函数调用。 3. 同时观察入参和结果 [arthas@11939]$ watch org.apache.commons.lang3.StringUtils contains {params,returnObj} 'params[1]=="Arthas"' 通过 {} 把字段包起来,可以同时观察想观察的字段。可以注意到一个点,params 是一个数组,但是打印 params 的时候并没有把具体内容打印出来,这个时候可以使用 -x 2 来指定打印对象的属性遍历深度。 arthas@11939]$ watch org.apache.commons.lang3.StringUtils contains {params,returnObj} 'params[1]=="Arthas"' -x 2 4. 给大家来几个我实际用到的例子 在陌陌做动态推荐开发的时候,测试时经常会遇到查看某个用户是否开启了相应的业务开关,经常就会需要查看某个实验开关是否开启。 我还经常会根据入参的陌陌用户 id 进行判断,查看返回结果或者异常: watch com.momo.plugins.shuffler.IMorecShuffler shuffle throwExp 'params[0].morecRequest.momoId=="123454567"'ts=2019-03-27 20:54:29; [cost=46.642339ms] result=java.lang.IndexOutOfBoundsException: Index: 12, Size: 11 at java.util.ArrayList.rangeCheckForAdd(ArrayList.java:665) at java.util.ArrayList.add(ArrayList.java:477) at com.momo.plugin.shuffler.RoomShuffler.shuffle(RoomShuffler:45) 复制代码 一些小提示 上面我只是列了一下常用的观察方式和参数,watch 支持的命令还有很多,你可以查看 Arthas 的 watch 命令官方文档。 还可以通过启动 arthas 命令之后使用 watch -h 查看。 使用 Arthas 的过程中很多人会觉得获取类的全限定名很费劲,其实这个可以通过 Idea 的 Copy Refrence 快捷键解决。我自己定义的快捷键是 ⌥⇧⌘C。 还有一点就是写代码的时候最好把代码拆细,尽量把小功能也封装成单独的函数,等你需要使用 Arthas 观察函数调用的时候,你会回来感谢自己的。 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
云办公、云课堂、云看病、云社区……突然袭来的疫情,在重塑日常生活、工作场景之余,也带动了在线教育、远程办公、视频会议、智慧社区等细分赛道的火爆。 这次疫情给各行各业带来巨大损失和挑战,中小企业是疫情重灾区,创企更是重中之重,但阿里赛道明星班的学员仍然勇于承担社会责任,借力发展自身业务,交出了一批漂亮的成绩单。 赛道明星班老学员战“疫”成绩单 疫情肆虐期间,赛道明星班学员们不仅快速复工复产,承担社会责任,助复工、促复产、拉动就业,更是通过“黑科技”技术,推出一个又一个硬核产品和解决方案,与各地政府及机构合作,冲在抗击疫情的最前线。 学员擎朗智能的无人配送机器人,启用机器人代替人工提供全自主无接触三餐配送服务,不但避免人员交叉感染,还减轻工作人员的餐食配送负担,专项用于疫情期间的三餐、水果、矿泉水、药品等生活必需品的免接触配送服务。 学员高新兴集团,自主研发出升级版5G警用巡逻机器人,可在机场、车站、广场、医院、社区以及重点卡口路段,启用疫情防控模式,借助移动式红外测温筛查、循环播报提醒等功能,实现远程可视化指挥,协助一线民警。 还有之前刷爆全网的盒马“员工共享”计划,就是出自猎聘HRO·勋厚人力。疫情期间,云海肴、青年餐厅将自有员工的“使用权”租赁给盒马鲜生,助力盒马鲜生实现了订单增长期间人力资源的优化配置,解决了其人员紧缺的现状,同样也缓解了战“疫”时期餐饮行业的企业用人成本高的难题,更为餐饮业员工的收入提供了保障。 在危险、高强度的工作环境中完成排查、防控任务,不仅分担了执勤工作任务,也有助于避免人员交叉感染。 响应国家号召,积极承担社会责任,为抗“疫”贡献力量,是赛道明星班每位成员义不容辞的责任。疫情当前,也为彼此、为社会送上最真诚的支持与鼓励。 赛道明星班,高效对接项目与资本 疫情期间,赛道明星班抱团取暧,不仅班委参与,学员也齐上阵,互相整合资源、渠道、供应链,如友杰智新帮助喜圈科技完成产品原型,芯片超人充分调动平台资源为学员带货,增进相互合作,为彼此雪中送炭。 为帮助学员发声,帮助项目与投资机构对接,阿里赛道明星班也采取了一系列行动。其中,“明星直播间”采用线上公开直播方式,由阿里云创业孵化事业部副总经理蔡素卿担任主持和项目讲解,把赛道明星班项目推向更多投资机构。 “赛道明星的战‘疫’时刻”采用钉钉社群直播,每周邀请多位行业专家,为赛道班学员和众多中小企业,在获客、资本、供应链整合、营销等层面进行指导,帮助企业度过难关。 阿里赛道明星班自成立以来,不断为学员企业输送阿里生态业务合作,和社会各界资源,学员企业成长也有目共睹。 在参与赛道明星班前两场明星发布活动后,即便在疫情期间,擎朗智能也获得了源码资本领投的2亿元B轮融资,人人云图3月13日也完成琥珀资本领投的5000万元A轮融资,成绩喜人。 此前成功举办的三期AI赛道明星班,已聚集100位AI企业创始人,与阿里60多条业务线产生1000+业务合作,近200家资本参与赛道明星班项目,达成56笔投资,学员企业总估值增长263%,已达到718亿。 赛道明星班表示,在经济下行、资本趋冷的当下,要投入更多资源推出链接活动,链接各方资源和资本,帮助优质项目快速发展,推动行业生态构建、不断升级。 阿里赛道明星班第四期招募继续 疫情拐点已到,众多企业也准备好触底反弹,阿里云联合36氪开启的阿里赛道明星班第四期-云双创加持计划也如火如荼进行中。 寻找最具创新力、成长力和价值的项目,通过阿里生态赋能、行业专家指导、投资机构对接以及36氪品牌曝光四大核心权益,帮助他们实现资本和资源的高效链接,以进一步促进云产业生态建立及创新项目发展。 阿里赛道明星班IP上线至今,受到业内一致好评。未来,还将深入各个热门赛道,以阿里为中心汇集行业能量,为优质创新企业全面赋能,加速成长。 产业互联网时代已经到来,企服市场即将踏上高速跑道,阿里赛道明星班第四期-云双创加持计划招募继续,欢迎报名参加,共同探索行业创新机会,谱写企业成长新篇章。 本文转自<36氪>——36氪深度服务
在 6 月 9 日 2020 阿里云线上峰会上,阿里云智能基础产品事业部高级研究员蒋江伟重磅发布了云原生裸金属方案。 新一代容器服务 ACK,可以将最新神龙弹性裸金属实例的强大性能发挥得淋漓尽致,具备极致性能、高效调度、全面安全的特点: 新一代神龙架构具备业界第一的 I/O 转发能力,提供最高 100G 网络带宽;阿里云高速 Terway 容器网络通过网卡直通和数据平面加速,延迟下降 30%; 第 7 代实例最大支持 192 个 vCPU。ACK 智能 CPU 调度可以轻松释放强大算力,无需应用调整可以实现 QPS 20~30% 提升;结合 ENI 网卡密度提升,可以缩减 50% 的计算成本; 弹性裸金属实例支持阿里云安全容器,提升端到端安全隔离能力,与开源方案相比性能提升 30%,也支持阿里云首发机密计算容器,基于软硬一体技术有效保护数据隐私。 在阿里巴巴内部,神龙架构已大规模应用于淘宝、天猫、菜鸟等业务,解决了高峰值下的业务性能和稳定性问题。在外部,尤其是在这次疫情影响下,很多企业面临快速扩容的压力,如在线教育行业,通过阿里云容器+神龙方案,企业可以从容应对流量突增的难题。 视源股份(CVTE)的希沃系列教育平稳应对疫情期间指数级增长的课堂流量,视源电子运维负责人许坤丰称,“疫情之下,希沃课堂作为教育信息化应用和服务工具提供商,免费向全国师生开放希沃云课堂在线直播方案。不久前,全国超过 30 万教师使用希沃云课堂开课,共开设超过 200 万节课程。面对指数级增长的流量,我们在阿里云容器服务 ACK 上使用神龙服务器和 ECI,顺利完成扩容,让系统得以正常运行。ECI 的简单易用,海量节点的特性加上神龙服务器高性能,零抖动的特点,极大缓解了扩容的压力,让我们把更多精力放在产品本身,给全国老师和学生提供更好的服务。” 云计算开源产业联盟上周公布了“云原生应用十大优秀案例”评选结果,阿里云支持的申通通用云原生计算平台顺利入选。申通基于云原生裸金属方案完成迁云,实现了围绕快递包裹生命周期的高效管理,平稳度过 双11 业务高峰。 云计算开源产业联盟对申通通用云原生计算平台评价称“该平台解决了传统应用升级缓慢、架构臃肿、不能快速迭代等问题,通过云原生架构体系,在成本、稳定性、效率、赋能业务等四个维度获得显著成效。目前核心业务系统已经在云上完成流量承接,每天处理订单量在千万级别,处理物流轨迹在亿级别,每天产生的数据量在 1T,使用 1300+ 个计算节点来实时处理业务。” 神龙架构是容器的最佳载体 2017 年 10 月,阿里云在全球率先推出了同时融合物理机和虚拟机特性的“跨界”云服务器——弹性裸金属服务器神龙 X-Dragon,它采用了自主研发的虚拟化 2.0 技术,兼具“虚拟机的心脏”和“物理机的肌肉”,被认为是云计算领域的新物种。从 2017 年发布第一代神龙架构开始,历经了软件虚拟化、通用硬件虚拟化、专用硬件芯片虚拟化三个阶段后,第三代神龙架构实现了裸金属服务器、ECS 虚拟机,弹性容器实例 ECI 等多种计算平台的架构统一和全面优化。 蒋江伟在演讲中也提到,客户普遍有个共识,那就是容器与物理服务器的结合是最佳搭档。但是普通物理服务器天然具有一些缺陷,比如运维复杂度高,缺乏弹性。而以神龙架构为基础的裸金属服务器,搭配容器服务 ACK,不仅提供非常好的性能,同时具备虚拟机的运维灵活性,正好弥补了物理服务器的弹性缺陷,对于构建容器环境而言,裸金属是更好的选择。 据蒋江伟介绍,云原生裸金属具备极致的弹性、高效的调度能力和更全面的安全能力。在普通的应用场景下,基于神龙架构的容器服务ACK与自建容器相比,可以实现QPS提升30%,计算成本下降50%,容器安全性能提升30%。 对于线下传统物理机服务器,企业客户最大的痛点就是缺乏弹性,运维复杂,无法应对快速发展的业务需求。神龙裸金属服务器,具备虚拟机的体验,物理机的性能。扩容交付周期几周缩短到分钟,与虚拟机相比性能“零损耗”、“零抖动”,与传统物理机相比性价比提升 20% ,是用户上云的最佳选择。 钉钉以前 100% 部署在普通物理机上,疫情突发之后,政府、企业和学校对在线协作的需求猛增。通过云上神龙裸金属+容器弹性部署方案,快速地实现了钉钉业务应用 10 万核扩容需求;借助神龙+容器的超高性能支撑钉钉扛住了有史以来最大的流量洪峰。 此外,社区版本 K8s 容器调度技术存在一定局限,无法充分使用神龙裸金属服务器强大的算力。应用在多 CPU 核心场景下,可能会引起资源争抢、CPU 频繁切换等情况。通过开启容器服务 ACK 的智能 CPU 调度,可以提升缓存的命中率、减少 CPU 中断和切换次数,有效提升性能,在不增加硬件资源的情况下性能提升 20%,QPS 从 25 万提升到 30 万。 容器服务 ACK 不但支持对 CPU 的高效调度,还新增了对业界最强算力 AI 芯片 - 含光 800 的多核调度支持,可以成倍提升 AI 业务资源利用率和性价比。阿里自研的含光 800 芯片具备强大的应用算力,在淘宝的拍立淘场景中,对商品库每天新增 10 亿商品图片,使用传统 GPU 算力识别需要 1 小时,使用含光 800 后可缩减至 5 分钟。对于强大的含光 NPU 芯片,阿里云容器服务 ACK 独创了面向容器的虚拟化和共享能力,充分发挥含光 800 多核资源,把多种业务精确调度到同一含光 800 芯片,充分利用计算资源,显著降低计算成本! 传统企业,尤其是一些大型企业,对从私有数据中心迁移到公有云上并不放心。其中数据安全问题是首要关切,需要独享使用物理机才会有安全感。云原生裸金属方案,结合阿里云安全沙箱容器技术,提供从基础设施到应用运行时端到端安全,非常适合对隐私和隔离要求较高的应用场景,而且与开源方案相比性能提升 30%。 阿里云此次首发机密计算容器,基于软硬一体技术实现全链路加密,有效解决数据泄露、非法数据访问等问题,可以应用在区块链、金融交易、基因计算等业务场景中。 云计算的下一站,是云原生 神龙是面向云原生设计的新一代云基础设施架构,同时支持裸金属服务器、ECS 虚拟机,ECI 弹性容器实例等多种计算形态。神龙架构采用软硬一体设计,可以将网络和存储的转发任务卸载到神龙芯片上,避免了底层资源争抢而导致的 ECS 虚拟机性能波动。第三代神龙架构还引入硬件级别 QoS 能力,为客户关键业务带来更强的保障。基于神龙架构的 ECI 弹性容器实例,性能优于虚拟机中运行的相同规格 Docker 容器;具备极致的弹性能力,可以在一分钟内扩容 1000 业务容器实例。 客户可以在一个 ACK K8s 集群中划分不同节点池统一管理弹性裸金属实例,ECS 虚拟机实例和弹性容器实例。根据应用负载特性,可以充分优化计算效率、提升资源利用率、降低计算成本。对于需要极致性能和强安全隔离场景,用户可以采用裸金属实例;对于存在明显业务峰谷的场景,虚拟机实例可以提供更灵活的弹性。而弹性容器实例可以更好应对突发业务流量,提供免运维的用户体验。 容器服务 ACK 已经成为企业云原生操作系统,与 EDAS 微服务架构,ARMS 端到端可观测能力全面集成,全面提升 IT 敏捷性,为企业数字化转型提速。 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
随着物联网技术以及 5G 技术的高速发展,将云计算的能力延伸至边缘设备端,并通过中心进行统一交付、管控,已成为云计算的重要发展趋势。为服务更多开发者把握这一趋势,5 月 29 日,阿里巴巴正式对外开源了基于 ACK@Edge(边缘集群托管服务)的云原生边缘计算框架 —— OpenYurt。 自 OpenYurt 开源以来受到了开发者的关注,今天这篇文章将带大家快速上手 OpenYurt ,介绍如何使用 OpenYurt 提供的命令行管理工具 Yurtctl, 高效快速地部署 OpenYurt 集群。 OpenYurt 介绍 OpenYurt 主打“云边一体化”概念,依托 Kubernetes 强大的容器应用编排能力,满足了云-边一体化的应用分发、交付、和管控的诉求。相较于其他基于 Kubernetes 的边缘计算框架,OpenYurt 秉持着“最小修改”原则,通过在边缘节点安装 Yurthub 组件,和在云端部署 Yurt-controller-manager,保证了在对 Kubernetes 零侵入的情况下,提供管理边缘计算应用所需的相关能力。OpenYurt 能帮用户解决在海量边、端资源上完成大规模应用交付、运维、管控的问题,并提供中心服务下沉通道,实现和边缘计算应用的无缝对接。在设计 OpenYurt 之初,我们就非常强调保持用户体验的一致性,不增加用户运维负担,让用户真正方便地 “Extending your native kubernetes to edge”。 Yurtctl:一键让原生 K8s 集群具备边缘计算能力 为了让原生 K8s 集群具备边缘计算能力,OpenYurt 以 addon 为载体,非侵入式给原生 K8s 增强了如下能力: 边缘自治能力(YurtHub: 已开源),保证在弱网或者重启节点的情况下,部署在边缘节点上的应用也能正常运行; 云边协同能力(待开源),通过云边运维通道解决边缘的运维需求,同时提供云边协同能力; 单元化管理能力(待开源),为分散的边缘节点,边缘应用,应用间流量提供单元化闭环管理能力; 其他一些能力。 对于大家比较关心的问题:如何将增强的边缘计算能力和原生 K8s 无缝融合。基于过往 ACK@Edge 的线上运维经验,我们开源了 Yurtctl 命令行工具,帮助实现了原生 Kubernetes 和 OpenYurt 之间的无缝转换以及对 OpenYurt 相关组件的高效运维。 Yurtctl 的工作原理 图 1 - Yurtctl convert 流程 Yurtctl 是一个中心化的管控工具。在 OpenYurt 云-边一体的架构里,Yurtctl 将直接与 APIServer 进行交互。它借助原生 Kubernetes 的 Job workload 对每个 node 进行运维操作。如图1所示,在执行转换(convert)操作时,Yurtctl 会通过 Job 将一个 servant Pod 部署到用户指定的边缘节点上,servant Pod 里的容器执行的具体操作请参考:https://github.com/alibaba/openyurt/blob/master/config/yurtctl-servant/setup_edgenode。 由于 servant Pod 需要直接操作节点 root 用户的文件系统(例如将 yurthub 配置文件放置于 /etc/kubernetes/manifests 目录下),并且需要重置系统管理程序(kubelet.service), servant Pod 中的 container 将被赋予 privileged 权限,允许其与节点共享 pid namespace,并将借由 nsenter 命令进入节点主命名空间完成相关操作。当 servant Job 成功执行后,Job 会自动删除。如果失败,Job 则会被保留,方便运维人员排查错误原因。借由该机制,Yurtctl 还可对 Yurthub 进行更新或者删除。 案例:一键转换 OpenYurt 集群 注:在 ACK 上做 demo 实验 1. 获取 yurtctl OpenYurt github 仓库包括了 yurtctl 的源码,下载 OpenYurt 仓库之后,即可通过编译获得 yurtctl,具体命令如下: $ make build WHAT=cmd/yurtctlhack/make-rules/build.sh cmd/yurtctlBuilding cmd/yurtctl 编译成功之后,yurtctl 可执行文件就可以在 _output/bin/ 目录下找到。 2. 将 Kubernetes 转换为 OpenYurt 如果我们想将一个双节点(node1 和 node2)的 Kubernetes 集群转换成 OpenYurt 集群,并且只想让 node2 成为自治边缘节点,那么可以通过执行 yurtctl convert 来实现,具体命令如下: $ yurtctl convert --cloud-nodes node1 --provider ackI0603 14:34:33.714304 40825 convert.go:164] mark node1 as the cloud-nodeI0603 14:34:33.719816 40825 convert.go:172] mark node2 as the edge-nodeI0603 14:34:33.736609 40825 convert.go:198] deploy the yurt controller managerI0603 14:34:33.742272 40825 convert.go:210] deploying the yurt-hub and resetting the kubelet service...I0603 14:34:53.810165 40825 util.go:168] servant job(yurtctl-servant-convert-node2) has succeeded 成功配置节点之后,我们需要将边缘节点标记为自治状态,具体命令如下: $ yurtctl markautonomous # 如果用户只想标记部分边缘节点,则可以使用 --autonomous-nodes 选项指定I0602 11:22:05.610222 89160 markautonomous.go:149] mark node2 as autonomous 接着我们就可以测试 node2 在断网环境下是否能实现节点自治。首先,在 node2 上部署一个测试 pod: $ kubectl apply -f-<apiVersion: v1kind: Podmetadata: name: bboxspec: nodeName: node2 containers: image: busyboxcommand: topname: bbox EOF pod/bbox created 登陆到 node2 上,将 Yurthub 的 --server-addr 参数设置为一个不可访问的地址: sudo sed -i 's|--server-addr=.*|--server-addr=https://1.1.1.1:1111|' /etc/kubernetes/manifests/yurt-hub.yaml 耐心等待 40 秒,我们将观察到,即使 node2 已经处于 NotReady 状态,pod1 仍然处于 Running 状态。这说明当边缘节点处于自治状态时,即使 node 不在线,Pod 也不会被云端 node controller 驱逐。 $ kubectl get node NAME STATUS ROLES AGE VERSIONnode1 Ready master 14m v1.14.8node2 NotReady 12m v1.14.8$ kubectl get podNAME READY STATUS RESTARTS AGEbbox 1/1 Running 0 5m12s 这时如果将 node2 重启,我们可以用 docker ps (假设节点使用 docker 作为 container runtime)命令来验证 bbox Pod 会被重新拉起。 $ docker ps --format 'table {{.ID}}t{{.Image}}t{{.RunningFor}}' | grep busyboxd0c8134fddc1 busybox About a minutes ago 这是因为 Kubelet 会从 Yurthub 读取缓存的数据,恢复重启前的Pod状态。这部分技术细节我们会在后续的文章里详细介绍。 3. 将 OpenYurt 转换回 Kubernetes 相对的,通过运行 yurtctl revert 命令,用户可以将一个 OpenYurt 集群转换回 Kubernetes 集群。假设我们想将上述双节点 Kubernetes 集群转换回 Kubernetes 模式,那么只需运行以下命令即可(运行该命令前,请先将 node2 上的 yurthub 重新连上 apiserver): $ yurtctl revertI0603 14:38:55.522376 41016 revert.go:106] label alibabacloud.com/is-edge-worker is removedI0603 14:38:55.527998 41016 revert.go:116] yurt controller manager is removedI0603 14:38:55.548354 41016 revert.go:130] ServiceAccount node-controller is createdI0603 14:39:05.572686 41016 util.go:168] servant job(yurtctl-servant-revert-node2) has succeededI0603 14:39:05.572718 41016 revert.go:142] yurt-hub is removed, kubelet service is reset 如果还想了解更多 yurtctl 的使用方法,请参考 OpenYurt github 仓库下的[ ]()yurtctl的教程:https://github.com/alibaba/openyurt/tree/master/docs/tutorial。 What's Next Yurtctl 目标是成为运维人员管理 OpenYurt 集群的有力工具。因此我们会持续演进 Yurtctl 以支持 OpenYurt 的新功能和新增的运维流程或场景。例如,不久之后 OpenYurt 还将开源 Yurttunnel,Yurtunit 等组件,Yurtctl 也将对这些组件提供支持。我们同时欢迎大家提出对 Yurtctl 的需求,一起努力使其更加完善。 社区建设 OpenYurt 社区欢迎新用户加入和参与共建。用户可以通过 Github issue 获取技术支持、报告 bug、提出需求意见。 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
前言 当您第一次接触 Serverless 的时候,有一个不那么明显的新使用方式:与传统的基于服务器的方法相比,Serverless 服务平台可以使您的应用快速水平扩展,并行处理的工作更加有效。这主要是因为 Serverless 可以不必为闲置的资源付费,不用担心预留的资源不够。而在传统的使用范式中,用户必须预留成百上千的服务器来做一些高度并行化但执行时长较短的任务,而且必须为每一台服务器买单,即使有的服务器已经不再工作了。 以阿里云 Serverless 产品——函数计算为例,便可以完美解决您上述所有顾虑: 如果您的任务本身计算量不是很大,但是有大量的并发任务请求需要并行处理, 比如多媒体文件处理、文档转换等; 一个任务本身计算量很大,要求单个任务很快处理完,并且还能支持并行处理多个任务。 在这种场景下,用户唯一关注的就是:您的任务是可以分治拆解并且子任务是可以并行处理的,一个需要一个小时才能处理完的长任务,可以分解成 360 个独立的 10 秒长的子任务并行处理,这样,以前您要花一个小时才能处理完的任务,现在只需要 10 秒就可以搞定。由于采用的是按量计费的模型,完成的计算量和成本是大致相当的,而传统模型则因为预留资源肯定会存在浪费,浪费的费用也是需要您去承担的。 接下来,将详细阐述 Serverless 在大规模数据处理上的实践。 极致弹性扩缩容应对计算波动 在介绍相关的大规模数据处理示例之前, 这里先简单介绍一下函数计算。 1. 函数计算简介 开发者使用编程语言编写应用和服务,函数计算支持的开发语言请参见开发语言列表; 开发者上传应用到函数计算; 触发函数执行:触发方式包括 OSS、API 网关、日志服务、表格存储以及函数计算 API、SDK 等; 动态扩容以响应请求:函数计算可以根据用户请求量自动扩容,该过程对您和您的用户均透明无感知; 根据函数的实际执行时间按量计费:函数执行结束后,可以通过账单来查看执行费用,收费粒度精确到 100 毫秒。 详情:函数计算官网 至此,您大约可以简单理解到函数计算是怎么运作的,接下来以大量视频并行转码的案例来阐述:假设一家在家教育或娱乐相关的企业,老师授课视频或者新的片源一般是集中式产生,而您希望这些视频被快速转码处理完以便能让客户快速看到视频回放。比如在当下疫情中,在线教育产生的课程激增,而出课高峰一般是 10 点、12 点、16 点、18 点等明显的峰值段,特定的时间内(比如半个小时)处理完所有新上传的视频是一个通用而且普遍的需求。 2. 弹性高可用的音视频处理系统 OSS 触发器 如上图所示,用户上传一个视频到 OSS,OSS 触发器自动触发函数执行,函数计算自动扩容,执行环境内的函数逻辑调用 FFmpeg 进行视频转码,并且将转码后的视频保存回 OSS。 消息触发器 如上图所示,应用只需要发一个消息,自动触发函数执行音视频处理的任务即可,函数计算自动扩容,执行环境内的函数逻辑调用 FFmpeg 进行视频转码, 并且将转码后的视频保存回 OSS。 直接手动调用 SDK 执行音视频处理任务 以 python 为例,大致如下: python # -*- coding: utf-8 -*- import fc2 import json client = fc2.Client(endpoint="http://123456.cn-hangzhou.fc.aliyuncs.com",accessKeyID="xxxxxxxx",accessKeySecret="yyyyyy") # 可选择同步/异步调用 resp = client.invoke_function("FcOssFFmpeg", "transcode", payload=json.dumps( { "bucket_name" : "test-bucket", "object_key" : "video/inputs/a.flv", "output_dir" : "video/output/a_out.mp4" })).data print(resp) 从上面我们也可以看出,触发函数执行的方式也很多,同时简单配置下 SLS 日志,就可以很快实现一个弹性高可用、按量付费的音视频处理系统,同时能提供免运维、具体业务数据可视化、强大自定义监控报警等超强功能的 dashboard。 目前已经落地的音视频案例有 UC、语雀、躺平设计之家、虎扑以及几家在线教育的头部客户等,其中有些客户高峰期间,弹性使用到了万核以上 CPU 计算资源,并行处理的视频达到 1700+,同时提供了极高的性价比。 详情可以参考: simple-video-processing) fc-oss-ffmpeg 任务分治,并行加速 这种将任务分而治之的思想应用在函数计算上是一件有趣的事情,在这里举一个例子,比如您有一个超大的 20G 的 1080P 高清视频需要转码,即使您使用一台高配机器,需要的时间可能还是要按小时计,如果中途出问题中断转码,您只能重新开始再重复一遍转码的过程,如果您使用分治的思想+函数计算,转码的过程衍变为 分片-> 并行转码分片-> 合并分片,这样就可以解决您上述的两个痛点: 分片和合成分片是内存级别的拷贝,需要的计算量极小,真正消耗计算量的转码,拆分成了很多子任务并行处理,在这个模型中,分片转码的最大时间基本等同于整个大视频的转码时间; 即使中途某个分片转码出现异常,只需要重试这个分片的转码即可,不需要整个大任务推倒重来。 通过将大任务合理的分解,配合使用函数计算,编写一点 code,就可以快速完成一个弹性高可用、并行加速、按量付费的大型数据处理系统。 在介绍这个方案之前,我们先简单介绍一下 Serverless 工作流,Serverless 工作流可以很好地将函数和其他云服务和自建服务有组织地编排起来。 1. Serverless 工作流简介 Serverless 工作流(Serverless Workflow)是一个用来协调多个分布式任务执行的全托管云服务。在 Serverless 工作流中,您可以用顺序、分支、并行等方式来编排分布式任务,Serverless 工作流会按照设定好的步骤可靠地协调任务执行,跟踪每个任务的状态转换,并在必要时执行用户定义的重试逻辑,以确保工作流顺利完成。Serverless 工作流简化了开发和运行业务流程所需要的任务协调、状态管理以及错误处理等繁琐工作,让您聚焦业务逻辑开发。 详情:Serverless 工作流官网 接下来以一个大视频快速转码的案例来阐述 Serverless 工作编排函数,实现大计算任务的分解,并行处理子任务,最终达到快速完成单个大任务的目的。 2. 大视频的快速多目标格式转码 如上图所示,假设用户上传一个 mov 格式的视频到 OSS,OSS 触发器自动触发函数执行,函数调用 FnF 执行,FnF 同时进行 1 种或者多种格式的转码(由 template.yml 中的 DST_FORMATS 参数控制),假设配置的是同时进行 mp4 和 flv 格式的转码。 一个视频文件可以同时被转码成各种格式以及其他各种自定义处理,比如增加水印处理或者在 after-process 更新信息到数据库等; 当有多个文件同时上传到 OSS,函数计算会自动伸缩,并行处理多个文件,同时每次文件转码成多种格式也是并行; 结合 NAS + 视频切片,可以解决超大视频的转码,对于每一个视频,先进行切片处理,然后并行转码切片,最后合成,通过设置合理的切片时间,可以大大加快较大视频的转码速度; fnf 可以跟踪每一步执行情况,并且可以自定义每一个步骤的重试,提高任务系统的鲁棒性,如:retry-example 详情可以参考:fc-fnf-video-processing 在任务分治,并行加速具体的案例中,上面分享的是 CPU 密集型任务分解,但也可以进行 IO 密集型任务分解,比如这个需求:上海的 region 的 OSS bucket 中的一个 20G 大文件,秒级转存回杭州的 OSS Bucket 中。这里也可以采用分治的思路,Master 函数在接到转存任务之后,将超大文件进行分片的 range 分配给每个 Worker 子函数,Worker 子函数并行转存属于自己那部分的分片,Master 函数待所有子 Worker 运行完毕之后,提交合并分片请求,完成整个转存任务。 详情可以参考:利用函数计算多实例并发实现秒级转存超大文件 总结 本文探讨了 Serverless 服务平台可以使您的应用快速水平扩展,并行处理的工作更加有效,并给出了具体的实践案例,无论在 CPU 密集型还是 IO 密集型场景,函数计算 + Serverless 都能完美解决您以下顾虑: 不必为闲置的资源付费 不用担心计算资源预留不够 大计算量的任务需要快速处理完毕 更好的任务流程跟踪 完善的监控报警、免运维、业务数据可视化等 .... 本文中对于 Serverless 音视频处理只是一个示例,它展示的是函数计算配合 Serverless 工作流在离线计算场景中的能力和独一无二的优势。我们可以用发散的方式去拓展 Serverless 在大规模数据处理实践的边界,比如AI、基因计算、科学仿真等。希望本篇文章能吸引您,开启您的 Serverless 奇妙之旅。 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
数字时代的企业流量该如何进行逆势增长? 相信这是每个企业都特别关心的话题,也是必须要面对、解决的问题。 7月15日周三下午14:00-15:30,我们邀请《流量黑洞》作者老胡,为大家用经典案例的方式分析流量背后的用户情绪、理解用户、内容精准洞察,这是一次高层次的企业增长对话,欢迎大家准时参加群直播!
概述 社区版本 Dubbo 从 2.7.5 版本开始,新引入了一种基于实例(应用)粒度的服务发现机制,这是我们为 Dubbo 适配云原生基础设施的一步重要探索。版本发布到现在已有近半年时间,经过这段时间的探索与总结,我们对这套机制的可行性与稳定性有了更全面、深入的认识;同时在 Dubbo 3.0 的规划也在全面进行中,如何让应用级服务发现成为未来下一代服务框架 Dubbo 3.0 的基础服务模型,解决云原生、规模化微服务集群扩容与可伸缩性问题,也已经成为我们当前工作的重点。 既然这套新机制如此重要,那它到底是怎么工作的呢?今天我们就来详细解读一下。在最开始的社区版本,我们给这个机制取了一个神秘的名字 - 服务自省,下文将进一步解释这个名字的由来,并引用服务自省代指这套应用级服务发现机制。 熟悉 Dubbo 开发者应该都知道,一直以来都是面向 RPC 方法去定义服务的,并且这也是 Dubbo 开发友好性、治理功能强的基础。既然如此,那我们为什么还要定义个应用粒度的服务发现机制呢?这个机制到底是怎么工作的?它与当前机制的区别是什么?它能给我们带来哪些好处那?对适配云原生、性能提升又有哪些帮助? 带着所有的这些问题,我们开始本文的讲解。 服务自省是什么? 首先,我们先来解释文章开篇提到的问题: 应用粒度服务发现是到底是一种怎样的模型,它与当前的 Dubbo 服务发现模型的区别是什么? 我们为什么叫它服务自省? 所谓“应用/实例粒度” 或者“RPC 服务粒度”强调的是一种地址发现的数据组织格式。 以 Dubbo 当前的地址发现数据格式为例,它是“RPC 服务粒度”的,它是以 RPC 服务作为 key,以实例列表作为 value 来组织数据的: "RPC Service1": [ {"name":"instance1", "ip":"127.0.0.1", "metadata":{"timeout":1000}}, {"name":"instance2", "ip":"127.0.0.1", "metadata":{"timeout":2000}}, {"name":"instance3", "ip":"127.0.0.1", "metadata":{"timeout":3000}},]"RPC Service2": [Instance list of RPC Service2],"RPC ServiceN": [Instance list of RPC ServiceN] 而我们新引入的“应用粒度的服务发现”,它以应用名(Application)作为 key,以这个应用部署的一组实例(Instance)列表作为 value。这带来两点不同: 数据映射关系变了,从 RPC Service -> Instance 变为 Application -> Instance; 数据变少了,注册中心没有了 RPC Service 及其相关配置信息。 "application1": [ {"name":"instance1", "ip":"127.0.0.1", "metadata":{}}, {"name":"instance2", "ip":"127.0.0.1", "metadata":{}}, {"name":"instanceN", "ip":"127.0.0.1", "metadata":{}}] 要进一步理解新模型带来的变化,我们看一下应用与 RPC 服务间的关系,显而易见的,1 个应用内可能会定义 n 个 RPC Service。因此 Dubbo 之前的服务发现粒度更细,在注册中心产生的数据条目也会更多(与 RPC 服务成正比),同时也存在一定的数据冗余。 简单理解了应用级服务发现的基本机制,接着解释它为什么会被叫做“服务自省”? 其实这还是得从它的工作原理说起,上面我们提到,应用粒度服务发现的数据模型有几个以下明显变化:数据中心的数据量少了,RPC 服务相关的数据在注册中心没有了,现在只有 application - instance 这两个层级的数据。为了保证这部分缺少的 RPC 服务数据仍然能被 Consumer 端正确的感知,我们在 Consumer 和 Provider 间建立了一条单独的通信通道:Consumer 和 Provider 两两之间通过特定端口交换信息,我们把这种 Provider 自己主动暴露自身信息的行为认为是一种内省机制,因此从这个角度出发,我们把整个机制命名为:服务自省。 为什么需要服务自省? 上面讲服务自省的大概原理的时候也提到了它给注册中心带来的几点不同,这几点不同体现在 Dubbo 框架侧(甚至整个微服务体系中),有以下优势: 与业界主流微服务模型对齐,比如 SpringCloud、Kubernetes Native Service 等; 提升性能与可伸缩性。注册中心数据的重新组织(减少),能最大幅度的减轻注册中心的存储、推送压力,进而减少 Dubbo Consumer 侧的地址计算压力;集群规模也开始变得可预测、可评估(与 RPC 接口数量无关,只与实例部署规模相关)。 1. 对齐主流微服务模型 自动、透明的实例地址发现(负载均衡)是所有微服务框架需要解决的事情,这能让后端的部署结构对上游微服务透明,上游服务只需要从收到的地址列表中选取一个,发起调用就可以了。要实现以上目标,涉及两个关键点的自动同步: 实例地址,服务消费方需要知道地址以建立链接; RPC 方法定义,服务消费方需要知道 RPC 服务的具体定义,不论服务类型是 rest 或 rmi 等。 对于 RPC 实例间借助注册中心的数据同步,REST 定义了一套非常有意思的成熟度模型,感兴趣的朋友可以参考这里的链接 :https://www.martinfowler.com/articles/richardsonMaturityModel.html 按照文章中的 4 级成熟度定义,Dubbo 当前基于接口粒度的模型可以对应到 L4 级别。 接下来,我们看看 Dubbo、SpringCloud 以及 Kubernetes 分别是怎么围绕自动化的实例地址发现这个目标设计的。 2. Spring Cloud Spring Cloud 通过注册中心只同步了应用与实例地址,消费方可以基于实例地址与服务提供方建立链接,但是消费方对于如何发起 HTTP 调用(SpringCloud 基于 rest 通信)一无所知,比如对方有哪些 HTTP endpoint,需要传入哪些参数等。 RPC 服务这部分信息目前都是通过线下约定或离线的管理系统来协商的。这种架构的优缺点总结如下: 优势:部署结构清晰、地址推送量小; 缺点:地址订阅需要指定应用名, provider 应用变更(拆分)需消费端感知;RPC 调用无法全自动同步。 3. Dubbo Dubbo 通过注册中心同时同步了实例地址和 RPC 方法,因此其能实现 RPC 过程的自动同步,面向 RPC 编程、面向 RPC 治理,对后端应用的拆分消费端无感知,其缺点则是地址推送数量变大,和 RPC 方法成正比。 4. Dubbo + Kubernetes Dubbo 要支持 Kubernetes native service,相比之前自建注册中心的服务发现体系来说,在工作机制上主要有两点变化: 服务注册由平台接管,provider 不再需要关心服务注册; consumer 端服务发现将是 Dubbo 关注的重点,通过对接平台层的 API-Server、DNS 等,Dubbo client 可以通过一个 Service Name(通常对应到 Application Name)查询到一组 Endpoints(一组运行 provider 的 pod),通过将 Endpoints 映射到 Dubbo 内部地址列表,以驱动 Dubbo 内置的负载均衡机制工作。 Kubernetes Service 作为一个抽象概念,怎么映射到 Dubbo 是一个值得讨论的点 Service Name - > Application Name,Dubbo 应用和 Kubernetes 服务一一对应,对于微服务运维和建设环节透明,与开发阶段解耦。 apiVersion: v1kind: Servicemetadata: name: provider-app-namespec: selector: app: provider-app-name ports: - protocol: TCP port: targetPort: 9376 Service Name - > Dubbo RPC Service,Kubernetes 要维护调度的服务与应用内建 RPC 服务绑定,维护的服务数量变多。 apiVersion: v1kind: Servicemetadata: name: rpc-service-1spec: selector: app: provider-app-name ports: ## ... apiVersion: v1kind: Servicemetadata: name: rpc-service-2spec: selector: app: provider-app-name ports: ## ... apiVersion: v1kind: Servicemetadata: name: rpc-service-Nspec: selector: app: provider-app-name ports: ##... 结合以上几种不同微服务框架模型的分析,我们可以发现,Dubbo 与 SpringCloud、Kubernetes 等不同产品在微服务的抽象定义上还是存在很大不同的。SpringCloud 和 Kubernetes 在微服务的模型抽象上还是比较接近的,两者基本都只关心实例地址的同步,如果我们去关心其他的一些服务框架产品,会发现它们绝大多数也是这么设计的; 即 REST 成熟度模型中的 L3 级别。 对比起来 Dubbo 则相对是比较特殊的存在,更多的是从 RPC 服务的粒度去设计的。 对应 REST 成熟度模型中的 L4 级别。 如我们上面针对每种模型做了详细的分析,每种模型都有其优势和不足。而我们最初决定 Dubbo 要做出改变,往其他的微服务发现模型上的对齐,是我们最早在确定 Dubbo 的云原生方案时,我们发现要让 Dubbo 去支持 Kubernetes Native Service,模型对齐是一个基础条件;另一点是来自用户侧对 Dubbo 场景化的一些工程实践的需求,得益于 Dubbo 对多注册、多协议能力的支持,使得 Dubbo 联通不同的微服务体系成为可能,而服务发现模型的不一致成为其中的一个障碍,这部分的场景描述请参见这里。 5. 更大规模的微服务集群 - 解决性能瓶颈 这部分涉及到和注册中心、配置中心的交互,关于不同模型下注册中心数据的变化,之前原理部分我们简单分析过。为更直观的对比服务模型变更带来的推送效率提升,我们来通过一个示例看一下不同模型注册中心的对比: 图中左边是微服务框架的一个典型工作流程,Provider 和 Consumer 通过注册中心实现自动化的地址通知。其中,Provider 实例的信息如图中表格所示: 应用 DEMO 包含三个接口 DemoService 1 2 3,当前实例的 ip 地址为 10.210.134.30。 对于 Spring Cloud 和 Kubernetes 模型,注册中心只会存储一条 DEMO - 10.210.134.30+metadata 的数据; 对于老的 Dubbo 模型,注册中心存储了三条接口粒度的数据,分别对应三个接口 DemoService 1 2 3,并且很多的址数据都是重复的。 可以总结出,基于应用粒度的模型所存储和推送的数据量是和应用、实例数成正比的,只有当我们的应用数增多或应用的实例数增长时,地址推送压力才会上涨。 而对于基于接口粒度的模型,数据量是和接口数量正相关的,鉴于一个应用通常发布多个接口的现状,这个数量级本身比应用粒度是要乘以倍数的;另外一个关键点在于,接口粒度导致的集群规模评估的不透明,相对于实i例、应用增长都通常是在运维侧的规划之中,接口的定义更多的是业务侧的内部行为,往往可以绕过评估给集群带来压力。 以 Consumer 端服务订阅举例,根据我对社区部分 Dubbo 中大规模头部用户的粗略统计,根据受统计公司的实际场景,一个 Consumer 应用要消费(订阅)的 Provier 应用数量往往要超过 10 个,而具体到其要消费(订阅)的的接口数量则通常要达到 30 个,平均情况下 Consumer 订阅的 3 个接口来自同一个 Provider 应用,如此计算下来,如果以应用粒度为地址通知和选址基本单位,则平均地址推送和计算量将下降 60% 还要多。 而在极端情况下,也就是当 Consumer 端消费的接口更多的来自同一个应用时,这个地址推送与内存消耗的占用将会进一步得到降低,甚至可以超过 80% 以上。 一个典型的几段场景即是 Dubbo 体系中的网关型应用,有些网关应用消费(订阅)达 100+ 应用,而消费(订阅)的服务有 1000+ ,平均有 10 个接口来自同一个应用,如果我们把地址推送和计算的粒度改为应用,则地址推送量从原来的 n 1000 变为 n 100,地址数量降低可达近 90%。 工作原理 1. 设计原则 上面一节我们从服务模型及支撑大规模集群的角度分别给出了 Dubbo 往应用级服务发现靠拢的好处或原因,但这么做的同时接口粒度的服务治理能力还是要继续保留,这是 Dubbo 框架编程模型易用性、服务治理能力优势的基础。 以下是我认为我们做服务模型迁移仍要坚持的设计原则: 新的服务发现模型要实现对原有 Dubbo 消费端开发者的无感知迁移,即 Dubbo 继续面向 RPC 服务编程、面向 RPC 服务治理,做到对用户侧完全无感知; 建立 Consumer 与 Provider 间的自动化 RPC 服务元数据协调机制,解决传统微服务模型无法同步 RPC 级接口配置的缺点。 2. 基本原理详解 应用级服务发现作为一种新的服务发现机制,和以前 Dubbo 基于 RPC 服务粒度的服务发现在核心流程上基本上是一致的:即服务提供者往注册中心注册地址信息,服务消费者从注册中心拉取&订阅地址信息。 这里主要的不同有以下两点: 注册中心数据以“应用 - 实例列表”格式组织,不再包含 RPC 服务信息; 以下是每个 Instance metadata 的示例数据,总的原则是 metadata 只包含当前 instance 节点相关的信息,不涉及 RPC 服务粒度的信息。 总体信息概括如下:实例地址、实例各种环境标、metadata service 元数据、其他少量必要属性。 { "name": "provider-app-name", "id": "192.168.0.102:20880", "address": "192.168.0.102", "port": 20880, "sslPort": null, "payload": { "id": null, "name": "provider-app-name", "metadata": { "metadataService": "{\"dubbo\":{\"version\":\"1.0.0\",\"dubbo\":\"2.0.2\",\"release\":\"2.7.5\",\"port\":\"20881\"}}", "endpoints": "[{\"port\":20880,\"protocol\":\"dubbo\"}]", "storage-type": "local", "revision": "6785535733750099598", } }, "registrationTimeUTC": 1583461240877, "serviceType": "DYNAMIC", "uriSpec": null} Client – Server 自行协商 RPC 方法信息。 在注册中心不再同步 RPC 服务信息后,服务自省在服务消费端和提供端之间建立了一条内置的 RPC 服务信息协商机制,这也是“服务自省”这个名字的由来。服务端实例会暴露一个预定义的 MetadataService RPC 服务,消费端通过调用 MetadataService 获取每个实例 RPC 方法相关的配置信息。 当前 MetadataService 返回的数据格式如下: [ "dubbo://192.168.0.102:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=9585&release=2.7.5&side=provider&timestamp=1583469714314", "dubbo://192.168.0.102:20880/org.apache.dubbo.demo.HelloService?anyhost=true&application=demo-provider&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=9585&release=2.7.5&side=provider&timestamp=1583469714314", "dubbo://192.168.0.102:20880/org.apache.dubbo.demo.WorldService?anyhost=true&application=demo-provider&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=9585&release=2.7.5&side=provider&timestamp=1583469714314"] 熟悉 Dubbo 基于 RPC 服务粒度的服务发现模型的开发者应该能看出来,服务自省机制机制将以前注册中心传递的 URL 一拆为二: 一部分和实例相关的数据继续保留在注册中心,如 ip、port、机器标识等; 另一部分和 RPC 方法相关的数据从注册中心移除,转而通过 MetadataService 暴露给消费端。理想情况下是能达到数据按照实例、RPC 服务严格区分开来,但明显可以看到以上实现版本还存在一些数据冗余,有些也数据还未合理划分。尤其是 MetadataService 部分,其返回的数据还只是简单的 URL 列表组装,这些 URL其实是包含了全量的数据。 以下是服务自省的一个完整工作流程图,详细描述了服务注册、服务发现、MetadataService、RPC 调用间的协作流程。 服务提供者启动,首先解析应用定义的“普通服务”并依次注册为 RPC 服务,紧接着注册内建的 MetadataService 服务,最后打开 TCP 监听端口; 启动完成后,将实例信息注册到注册中心(仅限 ip、port 等实例相关数据),提供者启动完成; 服务消费者启动,首先依据其要“消费的 provider 应用名”到注册中心查询地址列表,并完成订阅(以实现后续地址变更自动通知); 消费端拿到地址列表后,紧接着对 MetadataService 发起调用,返回结果中包含了所有应用定义的“普通服务”及其相关配置信息; 至此,消费者可以接收外部流量,并对提供者发起 Dubbo RPC 调用。 在以上流程中,我们只考虑了一切顺利的情况,但在更详细的设计或编码实现中,我们还需要严格约定一些异常场景下的框架行为。比如,如果消费者 MetadataService 调用失败,则在重试知道成功之前,消费者将不可以接收外部流量。 服务自省中的关键机制 1. 元数据同步机制 Client 与 Server 间在收到地址推送后的配置同步是服务自省的关键环节,目前针对元数据同步有两种具体的可选方案,分别是:内建 MetadataService;独立的元数据中心,通过中细化的元数据集群协调数据。 内建 MetadataService:MetadataService 通过标准的 Dubbo 协议暴露,根据查询条件,会将内存中符合条件的“普通服务”配置返回给消费者。这一步发生在消费端选址和调用前; 元数据中心:复用 2.7 版本中引入的元数据中心,provider 实例启动后,会尝试将内部的 RPC 服务组织成元数据的格式到元数据中心,而 consumer 则在每次收到注册中心推送更新后,主动查询元数据中心。 注意 consumer 端查询元数据中心的时机,是等到注册中心的地址更新通知之后。也就是通过注册中心下发的数据,我们能明确的知道何时某个实例的元数据被更新了,此时才需要去查元数据中心。 2. RPC 服务 < - > 应用映射关系 回顾上文讲到的注册中心关于“应用 - 实例列表”结构的数据组织形式,这个变动目前对开发者并不是完全透明的,业务开发侧会感知到查询/订阅地址列表的机制的变化。具体来说,相比以往我们基于 RPC 服务来检索地址,现在 consumer 需要通过指定 provider 应用名才能实现地址查询或订阅。 老的 Consumer 开发与配置示例: 新的 Consumer 开发与配置示例: 以上指定 provider 应用名的方式是 Spring Cloud 当前的做法,需要 consumer 端的开发者显示指定其要消费的 provider 应用。 以上问题的根源在于注册中心不知道任何 RPC 服务相关的信息,因此只能通过应用名来查询。 为了使整个开发流程对老的 Dubbo 用户更透明,同时避免指定 provider 对可扩展性带来的影响(参见下方说明),我们设计了一套 RPC 服务到应用名的映射关系,以尝试在 consumer 自动完成 RPC 服务到 provider 应用名的转换。 Dubbo 之所以选择建立一套“接口-应用”的映射关系,主要是考虑到 service - app 映射关系的不确定性。一个典型的场景即是应用/服务拆分,如上面提到的配置 ,PC Service 2 是定义于 provider-app-x 中的一个服务,未来它随时可能会被开发者分拆到另外一个新的应用如 provider-app-x-1 中,这个拆分要被所有的 PC Service 2 消费方感知到,并对应用进行修改升级,如改为 ,这样的升级成本不可否认还是挺高的。 到底是 Dubbo 框架帮助开发者透明的解决这个问题,还是交由开发者自己去解决,当然这只是个策略选择问题,并且 Dubbo 2.7.5+ 版本目前是都提供了的。其实我个人更倾向于交由业务开发者通过组织上的约束来做,这样也可进一步降低 Dubbo 框架的复杂度,提升运行态的稳定性。 总结与展望 应用级服务发现机制是 Dubbo 面向云原生走出的重要一步,它帮 Dubbo 打通了与其他微服务体系之间在地址发现层面的鸿沟,也成为 Dubbo 适配 Kubernetes Native Service 等基础设施的基础。 我们期望 Dubbo 在新模型基础上,能继续保留在编程易用性、服务治理能力等方面强大的优势。但是我们也应该看到应用粒度的模型一方面带来了新的复杂性,需要我们继续去优化与增强;另一方面,除了地址存储与推送之外,应用粒度在帮助 Dubbo 选址层面也有进一步挖掘的潜力。 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
美国西部时间 2020 年 5 月 27 日,阿里云和微软云共同宣布,Open Application Model (OAM) 社区携手知名混合云管理项目 Crossplane,联合发布了 OAM 在 Kubernetes 平台上的标准实现与核心依赖库项目。新版的 OAM 核心依赖库以 Go 语言编写,由来自阿里、微软和 Crossplane 三方的工程师共同维护,能够以 Kubernetes 插件或者 Go 语言依赖库的方式被社区所使用。在内置了 OAM 核心依赖库之后,Crossplane 项目也实现了“华丽升级”,从原先的混合云管理项目一跃成为了一个能够面向所有云环境、提供“应用 + 云服务”一站式管理与交付体验的 OAM 标准实现平台。 OAM 核心依赖库项目:https://github.com/crossplane/oam-kubernetes-runtime Crossplane 项目:https://github.com/crossplane/crossplane OAM 是阿里云与微软云在 2019 年末联合推出的标准化云原生应用管理模型。相比于传统 PaaS 封闭、不能同“以 Operator 为基础的云原生生态”衔接的现状,基于 OAM 和 Kubernetes 构建的现代云原生应用管理平台,本质上是一个“以应用为中心”的 Kubernetes ,保证了这个应用平台在能够无缝接入整个云原生生态。同时,OAM 可以进一步屏蔽掉容器基础设施的复杂性和差异性,为平台的使用者带来低心智负担的、标准化的、一致的应用管理与交付体验。 OAM 项目:https://github.com/oam-dev/spec “Cloud 2.0”时代的应用定义模型 应用容器技术自诞生开始,就以“彻底改变了软件打包与分发方式”的魅力迅速征服了几乎所有的云厂商与数据中心。 不过,软件打包与分发方式的革新,并没有能够让软件本身的定义与描述发生本质的变化,基于 K8s 的应用管理体验,也没有让业务研发与运维的工作变得更简单。 实际上,Kubernetes 带来的云原生技术革命,在于实现了基础设施层的标准化和抽象,但这一层抽象距离业务研发与运维还是太过遥远了。一个最典型的例子,直到今天,Kubernetes 里面始终都没有“应用”这个概念,它提供的是更细粒度的“工作负载”原语,比如 Deployment 或者 DaemonSet。而在实际环境中,一个应用往往是由一系列独立组件的组合,比如一个“PHP 应用容器”和一个“数据库实例”组成的电商网站;一个“参数服务节点”和一个“工作节点”组成的机器学习训练任务;一个由“Deployment + StatefulSet + HPA + Service + Ingress”组成的 Kubernetes 应用。 而 OAM 项目,是一个基于 Kubernetes API 资源模型(Kubernetes Resource Model)的标准应用定义规范。在 OAM 中,它强调一个现代应用是多个组件的集合,而非一个简单的工作负载或者 K8s Operator。所以在 OAM 的语境中,一个 PHP 容器和它所依赖的数据库,以及它所需要使用的各种云服务,都是一个“电商网站”应用的组成部分。更进一步的,OAM 把这个应用所需的“运维策略”也认为是一个应用的一部分,比如这个 PHP 容器所需的 HPA(水平自动扩展策略): 与此同时,OAM 模型依据“关注点分离”的思想,对上述应用的组成部分进行了分类。其中,应用研发所关注的部分被称作“Component”,应用运维所关注的运维策略等被称作“Traits” 和 “Scope”,如下所示: 自发布 6 个月以来,OAM 模型正在迅速成为了阿里和微软内部进行应用定义的事实标准,并且已经成为了阿里云企业级分布式应用服务 EDAS 等云端应用管理产品的核心架构。在 2020 年 4 月,国外知名技术媒体 TheNewStack 提出了一个《为什么 Kubernetes 时代的应用如此与众不同》的疑问,引发了巨大的反响。TheNewStack 在文中把 OAM 称作“Cloud 2.0 时代的应用定义模型”,并在最后讲到: “OAM 的核心思想是,业务研发人员的工作以从编写源代码开始,到构建完容器镜像结束。而应用运维人员或者应用管理平台会负责将这个应用所需的所有组件配置和部署为一个完整的应用程序。而我们已经能够看到,在以 Kubernetes 为代表的 Cloud 2.0 时代,一个现代化应用程序是由许多对象组成的,其中一些对象已经超出了业务研发人员的认知范围。 这可能是互联网历史上第一次研发人员不再完全控制自己开发制品的完整生命周期。” 解读:OAM Kubernetes 核心依赖库 社区在落地 OAM 模型的过程中,提出了很多关于 OAM 统一实现库的诉求。一方面,一个统一的实现库能够更好的对规范进行诠释,增强复用性;另一方面,大量共性需求比如依赖管理、参数传递、冲突管理、编排等,也可以在这个核心依赖库构建。 所以在本次发布中,三方工程师使用 Go 语言开发了一个 OAM Kubernetes 核心依赖库。这个项目的名字叫做 oam-kubernetes-runtime ,它的主要功能包括: 1. 稳定且统一的 OAM 内核:所有基于 OAM 的应用交付平台的构建者都将基于这个依赖库开始构建,OAM 内核将会是统一的。同时该依赖库也由三方的顶级工程师共同维护,确保其具备生产级的稳定性。 可漂移的 Workload/Trait 能力:基于这个依赖库构建的 OAM 平台,上面新增的所有 Workload 和 Trait,都可以复用和漂移到其他同样基于该依赖库的平台,像插件一样可以轻松插拔,不需要做代码的变动。 通用逻辑内置:所有公共的逻辑,如依赖管理等,也将内置到这个依赖库中,使得大家使用 OAM 基于 K8s 构建以“应用为中心”的管理平台更加容易。 而 OAM 核心依赖库最大的使用场景,就是构建开放、用户友好、标准化的应用管理平台。这样一个管理平台的核心架构如下图所示。 在这样的平台中,平台构建者可以基于 OAM 模块化添加 Workload 和 Trait,而这些模块也可以在不同的 OAM 平台复用。比如,Workload 可以包括函数、容器、云资源、虚拟机等多种不同形态的工作负载;Trait 则可以包含流量管理、发布策略、弹性策略、可观测性策略等多种不同的运维能力。最终,平台本身可以通过不同 Workload 和 Trait 组合,来对最终用户提供差异化的场景。 oam-kubernetes-runtime 使用起来非常便利,可以直接按照样例代码中所描述的那样,通过一个 main 函数把这个依赖库运行起来。OAM Kubernetes 核心依赖库本身是一个 Kubernetes Controller ,通过响应 ApplicationConfiguration 的变化来创建和管理 Workload 和 Trait。具体来说,OAM Kubernetes 核心依赖库会提供两个非常重要的功能: 功能一:无缝对接现有 K8s API 资源 oam-kubernetes-runtime 支持将任何现有的 CRD 被声明为 Workload 或者 Trait 而不需要做任何改动。当然,这也意味着任何 K8s 原生的 API 资源也可以被声明为 Workload 或者 Trait。通过这种设计,现有 Kubernetes 集群里的所有能力进行 OAM 化变得就”易如反掌“了。这也使得 OAM 成为了结构化管理当前集群中的各种 Operator 的不二之选。 功能二:Workload 与 Trait 标准化交互机制 前面的例子告诉我们,OAM 可以模块式的接入、部署和管理任何 Kubernetes 工作负载和运维能力。而这些工作负载和运维能力之间的交互,则需要通过第二个功能来实现标准化和统一化。 这个交互关系在 Kubernetes 里非常常见,比如一个 Deployment 和 HPA(自动水平扩展控制器) 的协作关系。这里 Deployment 在 OAM 模型中就属于 Workload,而 HPA 则属于 Trait。所以说在 OAM 当中,一个 ApplicationConfiguration 里引用的 Workload 和 Trait 也必须通过协作的方式来操作具体的 k8s 资源。举个例子,一个 HPA Trait 该如何去水平扩展上述 OpenFaaS 的 Function Workload 呢?这个协作关系就得依靠 OAM 插件来去管理了。 在 OAM Kubernetes 核心依赖库中,它会通过一种叫做 DuckTyping (鸭子类型)的机制,在 Trait 对象上自动记录与之绑定的 Workload 关系,从而实现了工作负载(Workload)和运维能力(Trait)之间的双向记录关系: 给定任何一工作负载(Workload) ,系统可以直接获取到同它绑定的所有运维能力(Trait); 给定任何一个运维能力(Trait),系统可以直接获取到它所要作用于的所有工作负载(Workload)。 这种双向记录关系,对于在一个大规模的生产环境中保证运维能力的可管理性、可发现性和应用的稳定性,是至关重要的。 除此之外,OAM 社区还 Kubernetes 核心依赖库中目前还有几个非常重要的基础功能同大家见面,包括: Component 版本管理:对于任何一次 Component 的变更,OAM 平台都可以记录下来它对于的变更历史,从而允许运维通过 Trait 来进行回滚、蓝绿发布等运维操作。 Component 间依赖关系与参数传递:该功能将解决大家亟需的组件间依赖问题,包括 Component 之间的依赖和传输传递,以及 Trait 与 Component 之间的依赖和参数传递。 Component 运维策略:该功能将允许研发在 Component 中声明对运维能力的诉求,指导运维人员或者系统给这个 Component 绑定和配置合理的运维能力。 OAM + Crossplane:定义云原生应用的下一阶段 可能大家会有这样一个疑问,为什么这一次 OAM 会选择 Crossplane 社区来作为 OAM Kubernetes 核心依赖库的合作团队呢? 我们知道,OAM 的主要思想是以 Kubernetes API 资源模型为核心、以结构化和平台无关的方式,对应用进行定义和管理。这里的“应用”,既包括待运行的程序本身(比如一个容器),也包括它需要的所有其他依赖(比如云资源和运维能力的描述)。而如果你熟悉 Kubernetes 生态的话,就会知道这种通过 Kubernetes API 模型“定义一切”的思想,也正是 Crossplane 项目的设计理念。 只不过,作为 Kubernetes 混合云场景中的佼佼者,Crossplane 项目以前的关注点是以结构化和平台无关的方式对云服务进行定义和管理而已。而在经过 OAM 化之后,今天的 Crossplane 项目,已经成为了 OAM 的标准实现,使用 OAM 作为其应用定义的入口,并且直接通过 OAM Component 的方式来为使用者暴露出平台无关的云服务定义。这样,一个符合 OAM 规范的待运行程序、运维能力和它所依赖的云服务,就可以组成一个整体,在不同的平台之间无缝漂移了。 这种平台无关的应用定义范式,使得应用研发人员只需要通过 OAM 规范来描述他们的应用程序,那么该应用程序就可以在任何 Kubernetes 群集或者 Serverless 应用平台甚至边缘环境上运行而无需对应用描述做任何修改。 这种体验,一直是阿里云和微软云在努力的构建“云、边、端”一致性体验的核心思想。 而此次 OAM 与 Crossplane 的深度协作,也终于将标准应用定义和标准化的云服务管理能力统一起来,从而使“云端应用交付”的故事真正成为了现实。 在未来,这两个社区将进一步紧密协作,在 OAM Kubernetes 标准实现中提供更好的 Component 和 Trait 可移植性、互操作性,在 OAM 生态中上线更加丰富的应用运维能力,共同建立一个专注于标准应用程序与基础设施管理的开放社区。 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
Kubernetes 集群中,业务通常采用 Deployment + LoadBalancer 类型 Service 的方式对外提供服务,其典型部署架构如图 1 所示。这种架构部署和运维都十分简单方便,但是在应用更新或者升级时可能会存在服务中断,引发线上问题。今天我们来详细分析下这种架构为何在更新应用时会发生服务中断以及如何避免服务中断。 图1 业务部署图 为何会发生服务中断 Deployment 滚动更新时会先创建新 pod,等待新 pod running 后再删除旧 pod。 新建 Pod 图 2 服务中断示意图 中断原因:Pod running 后被加入到 Endpoint 后端,容器服务监控到 Endpoint 变更后将 Node 加入到 SLB 后端。此时请求从 SLB 转发到 Pod 中,但是 Pod 业务代码还未初始化完毕,无法处理请求,导致服务中断,如图 2 所示。 解决方法:为 pod 配置就绪检测,等待业务代码初始化完毕后后再将 node 加入到 SLB 后端。 删除 Pod 在删除旧 pod 过程中需要对多个对象(如 Endpoint、ipvs/iptables、SLB)进行状态同步,并且这些同步操作是异步执行的,整体同步流程如图 3 所示。 图 3 Deployment 更新时序图 Pod pod 状态变更:将 Pod 设置为 Terminating 状态,并从所有 Service 的 Endpoints 列表中删除。此时,Pod 停止获得新的流量,但在 Pod 中运行的容器不会受到影响; 执行 preStop Hook:Pod 删除时会触发 preStop Hook,preStop Hook 支持 bash 脚本、TCP 或 HTTP 请求; 发送 SIGTERM 信号:向 Pod 中的容器发送 SIGTERM 信号; 等待指定的时间:terminationGracePeriodSeconds 字段用于控制等待时间,默认值为 30 秒。该步骤与 preStop Hook 同时执行,因此 terminationGracePeriodSeconds 需要大于 preStop 的时间,否则会出现 preStop 未执行完毕,pod 就被 kill 的情况; 发送 SIGKILL 信号:等待指定时间后,向 pod 中的容器发送 SIGKILL 信号,删除 pod。 中断原因:上述 1、2、3、4步骤同时进行,因此有可能存在 Pod 收到 SIGTERM 信号并且停止工作后,还未从 Endpoints 中移除的情况。此时,请求从 slb 转发到 pod 中,而 Pod 已经停止工作,因此会出现服务中断,如图 4 所示。 图 4 服务中断示意图 解决方法:为 pod 配置 preStop Hook,使 Pod 收到 SIGTERM 时 sleep 一段时间而不是立刻停止工作,从而确保从 SLB 转发的流量还可以继续被 Pod 处理。 iptables/ipvs 中断原因:当 pod 变为 termintaing 状态时,会从所有 service 的 endpoint 中移除该 pod。kube-proxy 会清理对应的 iptables/ipvs 条目。而容器服务 watch 到 endpoint 变化后,会调用 slb openapi 移除后端,此操作会耗费几秒。由于这两个操作是同时进行,因此有可能存在节点上的 iptables/ipvs 条目已经被清理,但是节点还未从 slb 移除的情况。此时,流量从 slb 流入,而节点上已经没有对应的 iptables/ipvs 规则导致服务中断,如图 5 所示。 图 5 服务中断示意图 解决方法: Cluster 模式:Cluster 模式下 kube-proxy 会把所有业务 Pod 写入 Node 的 iptables/ipvs 中,如果当前 Node 没有业务 pod,则该请求会被转发给其他 Node,因此不会存在服务中断,如 6 所示; 图 6 Cluster 模式请求转发示意图 Local 模式:Local 模式下,kube-proxy 仅会把 Node 上的 pod 写入 iptables/ipvs。当 Node 上只有一个 pod 且状态变为 terminating 时,iptables/ipvs 会将该 pod 记录移除。此时请求转发到这个 node 时,无对应的 iptables/ipvs 记录,导致请求失败。这个问题可以通过原地升级来避免,即保证更新过程中 Node 上至少有一个 Running Pod。原地升级可以保障 Node 的 iptables/ipvs 中总会有一条业务 pod 记录,因此不会产生服务中断,如图 7 所示; 图 7 Local 模式原地升级时请求转发示意图 ENI 模式 Service:ENI 模式绕过 kube-proxy,将 Pod 直接挂载到 SLB 后端,因此不存在因为 iptables/ipvs 导致的服务中断。 图 8 ENI 模式请求转发示意图 SLB 图 9 服务中断示意图 中断原因:容器服务监控到 Endpoints 变化后,会将 Node 从 slb 后端移除。当节点从 slb 后端移除后,SLB 对于继续发往该节点的长连接会直接断开,导致服务中断。解决方法:为 SLB 设置长链接优雅中断(依赖具体云厂商)。 如何避免服务中断 避免服务中断可以从 Pod 和 Service 两类资源入手,接下来将针对上述中断原因介绍相应的配置方法。 Pod 配置 注意:需要合理设置就绪检测(readinessProbe)的探测频率、延时时间、不健康阈值等数据,部分应用启动时间本身较长,如果设置的时间过短,会导致 POD 反复重启。 livenessProbe 为存活检测,如果失败次数到达阈值(failureThreshold)后,pod 会重启,具体配置见官方文档; readinessProbe 为就绪检查,只有就绪检查通过后,pod 才会被加入到 Endpoint 中。容器服务监控到 Endpoint 变化后才会将 node 挂载到 slb 后端; preStop 时间建议设置为业务处理完所有剩余请求所需的时间,terminationGracePeriodSeconds 时间建议设置为 preStop 的时间再加 30 秒以上。 Service 配置 Cluster 模式(externalTrafficPolicy: Cluster) apiVersion: v1kind: Servicemetadata: name: nginx namespace: defaultspec: externalTrafficPolicy: Cluster ports: port: 80 protocol: TCP targetPort: 80selector: run: nginxtype: LoadBalancer 容器服务会将集群中所有节点挂载到 SLB 的后端(使用 BackendLabel 标签配置后端的除外),因此会快速消耗 SLB quota。SLB 限制了每个 ECS 上能够挂载的 SLB 的个数,默认值为 50,当 quota 消耗完后会导致无法创建新的监听及 SLB。 Cluster 模式下,如果当前节点没有业务 pod 会将请求转发给其他 Node。在跨节点转发时需要做 NAT,因此会丢失源 IP。 Local 模式(externalTrafficPolicy: Local) 容器服务默认会将 Service 对应的 Pod 所在的节点加入到 SLB 后端,因此 SLB quota 消耗较慢。Local 模式下请求直接转发到 pod 所在 node,不存在跨节点转发,因此可以保留源 IP 地址。Local 模式下可以通过原地升级的方式避免服务中断,yaml 文件如上。 ENI 模式(阿里云特有模式) apiVersion: v1kind: Servicemetadata: annotations: service.beta.kubernetes.io/backend-type: "eni" name: nginxspec: ports: name: http port: 30080 protocol: TCP targetPort: 80selector: app: nginxtype: LoadBalancer Terway 网络模式下,通过设置 service.beta.kubernetes.io/backend-type:"eni" annotation 可以创建 ENI 模式的 SLB。ENI 模式下,pod会直接挂载到 SLB 后端,不经过 kube-proxy,因此不存在服务中断的问题。请求直接转发到 pod,因此可以保留源 IP 地址。 三种 svc 模式对比如下表所示。 图 10 Service 对比 结论 Terway 网络模式 (推荐方式)选用 ENI 模式的 svc + 设定 Pod 优雅终止 + 就绪检测。 Flannel 网络模式 如果集群中 slb 数量不多且不需要保留源 ip:选用 cluster 模式 + 设定 Pod 优雅终止 + 就绪检测; 如果集群中 slb 数量较多或需要保留源 ip:选用 local 模式 + 设定 Pod 优雅终止 + 就绪检测 + 原地升级(保证更新过程中每个节点上至少有一个 Running Pod)。 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
背景 OpenKruise 是阿里云开源的大规模应用自动化管理引擎,在功能上对标了 Kubernetes 原生的 Deployment / StatefulSet 等控制器,但 OpenKruise 提供了更多的增强功能如:优雅原地升级、发布优先级/打散策略、多可用区workload抽象管理、统一 sidecar 容器注入管理等,都是经历了阿里巴巴超大规模应用场景打磨出的核心能力。这些 feature 帮助我们应对更加多样化的部署环境和需求、为集群维护者和应用开发者带来更加灵活的部署发布组合策略。 目前在阿里巴巴内部云原生环境中,绝大部分应用都统一使用 OpenKruise 的能力做 Pod 部署、发布管理,而不少业界公司和阿里云上客户由于 K8s 原生 Deployment 等负载不能完全满足需求,也转而采用 OpenKruise 作为应用部署载体。 今天的分享文章就从一个阿里云上客户对接 OpenKruise 的疑问开始。这里还原一下这位同学的用法(以下 YAML 数据仅为 demo): 准备一份 Advanced StatefulSet 的 YAML 文件,并提交创建。如: 然后,修改了 YAML 中的 image 镜像版本,然后调用 K8s api 接口做更新。结果收到报错如下: metadata.resourceVersion: Invalid value: 0x0: must be specified for an update 而如果使用 kubectl apply 命令做更新,则返回成功: statefulset.apps.kruise.io/sample configured 问题在于,为什么同一份修改后的 YAML 文件,调用 api 接口更新是失败的,而用 kubectl apply 更新是成功的呢?这其实并不是 OpenKruise 有什么特殊校验,而是由 K8s 自身的更新机制所决定的。 从我们的接触来看,绝大多数用户都有通过 kubectl 命令或是 sdk 来更新 K8s 资源的经验,但真正理解这些更新操作背后原理的人却并不多。本文将着重介绍 K8s 的资源更新机制,以及一些我们常用的更新方式是如何实现的。 更新原理 不知道你有没有想过一个问题:对于一个 K8s 资源对象比如 Deployment,我们尝试在修改其中 image 镜像时,如果有其他人同时也在对这个 Deployment 做修改,会发生什么? 当然,这里还可以引申出两个问题: 如果双方修改的是同一个字段,比如 image 字段,结果会怎样? 如果双方修改的是不同字段,比如一个修改 image,另一个修改 replicas,又会怎么样? 其实,对一个 Kubernetes 资源对象做“更新”操作,简单来说就是通知 kube-apiserver 组件我们希望如何修改这个对象。而 K8s 为这类需求定义了两种“通知”方式,分别是 update 和 patch。在 update 请求中,我们需要将整个修改后的对象提交给 K8s;而对于 patch 请求,我们只需要将对象中某些字段的修改提交给 K8s。 那么回到背景问题,为什么用户提交修改后的 YAML 文件做 update 会失败呢?这其实是被 K8s 对 update 请求的版本控制机制所限制的。 Update 机制 Kubernetes 中的所有资源对象,都有一个全局唯一的版本号(metadata.resourceVersion)。每个资源对象从创建开始就会有一个版本号,而后每次被修改(不管是 update 还是 patch 修改),版本号都会发生变化。 官方文档告诉我们,这个版本号是一个 K8s 的内部机制,用户不应该假设它是一个数字或者通过比较两个版本号大小来确定资源对象的新旧,唯一能做的就是通过比较版本号相等来确定对象是否是同一个版本(即是否发生了变化)。而 resourceVersion 一个重要的用处,就是来做 update 请求的版本控制。 K8s 要求用户 update 请求中提交的对象必须带有 resourceVersion,也就是说我们提交 update 的数据必须先来源于 K8s 中已经存在的对象。因此,一次完整的 update 操作流程是: 首先,从 K8s 中拿到一个已经存在的对象(可以选择直接从 K8s 中查询;如果在客户端做了 list watch,推荐从本地 informer 中获取); 然后,基于这个取出来的对象做一些修改,比如将 Deployment 中的 replicas 做增减,或是将 image 字段修改为一个新版本的镜像; 最后,将修改后的对象通过 update 请求提交给 K8s; 此时,kube-apiserver 会校验用户 update 请求提交对象中的 resourceVersion 一定要和当前 K8s 中这个对象最新的 resourceVersion 一致,才能接受本次 update。否则,K8s 会拒绝请求,并告诉用户发生了版本冲突(Conflict)。 上图展示了多个用户同时 update 某一个资源对象时会发生的事情。而如果如果发生了 Conflict 冲突,对于 User A 而言应该做的就是做一次重试,再次获取到最新版本的对象,修改后重新提交 update。 因此,我们上面的两个问题也都得到了解答: 用户修改 YAML 后提交 update 失败,是因为 YAML 文件中没有包含 resourceVersion 字段。对于 update 请求而言,应该取出当前 K8s 中的对象做修改后提交; 如果两个用户同时对一个资源对象做 update,不管操作的是对象中同一个字段还是不同字段,都存在版本控制的机制确保两个用户的 update 请求不会发生覆盖。 Patch 机制 相比于 update 的版本控制,K8s 的 patch 机制则显得更加简单。 当用户对某个资源对象提交一个 patch 请求时,kube-apiserver 不会考虑版本问题,而是“无脑”地接受用户的请求(只要请求发送的 patch 内容合法),也就是将 patch 打到对象上、同时更新版本号。 不过,patch 的复杂点在于,目前 K8s 提供了 4 种 patch 策略:json patch、merge patch、strategic merge patch、apply patch(从 K8s 1.14 支持 server-side apply 开始)。通过 kubectl patch -h 命令我们也可以看到这个策略选项(默认采用 strategic): 篇幅限制这里暂不对每个策略做详细的介绍了,我们就以一个简单的例子来看一下它们的差异性。如果针对一个已有的 Deployment 对象,假设 template 中已经有了一个名为 app 的容器: 如果要在其中新增一个 nginx 容器,如何 patch 更新? 如果要修改 app 容器的镜像,如何 patch 更新? json patch([RFC 6902]()) 新增容器: kubectl patch deployment/foo --type='json' -p \ '[{"op":"add","path":"/spec/template/spec/containers/1","value":{"name":"nginx","image":"nginx:alpine"}}]' 修改已有容器 image: kubectl patch deployment/foo --type='json' -p \ '[{"op":"replace","path":"/spec/template/spec/containers/0/image","value":"app-image:v2"}]' 可以看到,在 json patch 中我们要指定操作类型,比如 add 新增还是 replace 替换,另外在修改 containers 列表时要通过元素序号来指定容器。 这样一来,如果我们 patch 之前这个对象已经被其他人修改了,那么我们的 patch 有可能产生非预期的后果。比如在执行 app 容器镜像更新时,我们指定的序号是 0,但此时 containers 列表中第一个位置被插入了另一个容器,则更新的镜像就被错误地插入到这个非预期的容器中。 merge patch(RFC 7386) merge patch 无法单独更新一个列表中的某个元素,因此不管我们是要在 containers 里新增容器、还是修改已有容器的 image、env 等字段,都要用整个 containers 列表来提交 patch: kubectl patch deployment/foo --type='merge' -p \ '{"spec":{"template":{"spec":{"containers":[{"name":"app","image":"app-image:v2"},{"name":"nginx","image":"nginx:alpline"}]}}}}' 显然,这个策略并不适合我们对一些列表深层的字段做更新,更适用于大片段的覆盖更新。 不过对于 labels/annotations 这些 map 类型的元素更新,merge patch 是可以单独指定 key-value 操作的,相比于 json patch 方便一些,写起来也更加直观: kubectl patch deployment/foo --type='merge' -p '{"metadata":{"labels":{"test-key":"foo"}}}' strategic merge patch 这种 patch 策略并没有一个通用的 RFC 标准,而是 K8s 独有的,不过相比前两种而言却更为强大的。 我们先从 K8s 源码看起,在 K8s 原生资源的数据结构定义中额外定义了一些的策略注解。比如以下这个截取了 podSpec 中针对 containers 列表的定义,参考 Github: // ...// +patchMergeKey=name// +patchStrategy=mergeContainers []Container json:"containers" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,2,rep,name=containers" 可以看到其中有两个关键信息:patchStrategy:"merge" patchMergeKey:"name" 。这就代表了,containers 列表使用 strategic merge patch 策略更新时,会把下面每个元素中的 name 字段看作 key。 简单来说,在我们 patch 更新 containers 不再需要指定下标序号了,而是指定 name 来修改,K8s 会把 name 作为 key 来计算 merge。比如针对以下的 patch 操作: kubectl patch deployment/foo -p \ '{"spec":{"template":{"spec":{"containers":[{"name":"nginx","image":"nginx:mainline"}]}}}}' 如果 K8s 发现当前 containers 中已经有名字为 nginx 的容器,则只会把 image 更新上去;而如果当前 containers 中没有 nginx 容器,K8s 会把这个容器插入 containers 列表。 此外还要说明的是,目前 strategic 策略只能用于原生 K8s 资源以及 Aggregated API 方式的自定义资源,对于 CRD 定义的资源对象,是无法使用的。这很好理解,因为 kube-apiserver 无法得知 CRD 资源的结构和 merge 策略。如果用 kubectl patch 命令更新一个 CR,则默认会采用 merge patch 的策略来操作。 kubectl 封装 了解完了 K8s 的基础更新机制,我们再次回到最初的问题上。为什么用户修改 YAML 文件后无法直接调用 update 接口更新,却可以通过 kubectl apply 命令更新呢? 其实 kubectl 为了给命令行用户提供良好的交互体感,设计了较为复杂的内部执行逻辑,诸如 apply、edit 这些常用操作其实背后并非对应一次简单的 update 请求。毕竟 update 是有版本控制的,如果发生了更新冲突对于普通用户并不友好。以下简略介绍下 kubectl 几种更新操作的逻辑,有兴趣可以看一下 kubectl 封装的源码。 apply 在使用默认参数执行 apply 时,触发的是 client-side apply。kubectl 逻辑如下: 首先解析用户提交的数据(YAML/JSON)为一个对象 A;然后调用 Get 接口从 K8s 中查询这个资源对象: 如果查询结果不存在,kubectl 将本次用户提交的数据记录到对象 A 的 annotation 中(key 为 kubectl.kubernetes.io/last-applied-configuration),最后将对象 A提交给 K8s 创建; 如果查询到 K8s 中已有这个资源,假设为对象 B:1. kubectl 尝试从对象 B 的 annotation 中取出 kubectl.kubernetes.io/last-applied-configuration 的值(对应了上一次 apply 提交的内容);2. kubectl 根据前一次 apply 的内容和本次 apply 的内容计算出 diff(默认为 strategic merge patch 格式,如果非原生资源则采用 merge patch);3. 将 diff 中添加本次的 kubectl.kubernetes.io/last-applied-configuration annotation,最后用 patch 请求提交给 K8s 做更新。 这里只是一个大致的流程梳理,真实的逻辑会更复杂一些,而从 K8s 1.14 之后也支持了 server-side apply,有兴趣的同学可以看一下源码实现。 edit kubectl edit 逻辑上更简单一些。在用户执行命令之后,kubectl 从 K8s 中查到当前的资源对象,并打开一个命令行编辑器(默认用 vi)为用户提供编辑界面。 当用户修改完成、保存退出时,kubectl 并非直接把修改后的对象提交 update(避免 Conflict,如果用户修改的过程中资源对象又被更新),而是会把修改后的对象和初始拿到的对象计算 diff,最后将 diff 内容用 patch 请求提交给 K8s。 总结 看了上述的介绍,大家应该对 K8s 更新机制有了一个初步的了解了。接下来想一想,既然 K8s 提供了两种更新方式,我们在不同的场景下怎么选择 update 或 patch 来使用呢?这里我们的建议是: 如果要更新的字段只有我们自己会修改(比如我们有一些自定义标签,并写了 operator 来管理),则使用 patch 是最简单的方式; 如果要更新的字段可能会被其他方修改(比如我们修改的 replicas 字段,可能有一些其他组件比如 HPA 也会做修改),则建议使用 update 来更新,避免出现互相覆盖。 最终我们的客户改为基于 get 到的对象做修改后提交 update,终于成功触发了 Advanced StatefulSet 的原地升级。 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
边缘云计算是基于云计算技术的核心和边缘计算的能力,构筑在边缘基础设施之上的新型计算平台,并正在成为行业的新焦点。OpenYurt 作为阿里巴巴首个边缘计算云原生开源项目,汇聚了阿里巴巴众多边缘计算业务团队的深厚技术积累,深度挖掘了边缘计算 + 云原生落地实施诉求。 两年前,OpenYurt 作为公共云服务 ACK@Edge 的核心框架,就已经应用于 CDN、音视频直播、物联网、物流、工业大脑、城市大脑等实际应用场景中,并服务于阿里云 LinkEdge、盒马、优酷、视频云(视频点播、视频直播、实时通信、视频监控、智能视觉)等多个业务或项目中。 阿里巴巴云原生开源负责人、云原生应用平台资深技术专家李响表示:“随着边缘计算的场景和需求不断增加,‘云边协同’、‘边缘云原生’正在逐渐成为新的技术焦点。OpenYurt 开源项目实践‘云边一体化’概念,依托原生 Kubernetes 强大的容器编排、调度能力,实现完全边缘计算云原生基础设施架构,帮助开发者轻松完成在海量边、端资源上的大规模应用的交付、运维、管控。我们希望 OpenYurt 开源能推动社区在云原生和边缘计算交叉领域的协同发展。” 什么是 OpenYurt 使用 OpenYurt(Yurt,/jɜːrt/,蒙古包)作为本次开源项目名称,期望以其“形”来表示边缘计算侧重于创建一个集中管理但物理分布的基础设施,并支持自动/自治运行操作的含义。 OpenYurt 主打“云边一体化”概念,依托原生 Kubernetes 强大的容器编排、调度能力,通过众多边缘计算应用场景锤炼,实现了一整套对原生 Kubernetes“零”侵入的边缘云原生方案,提供诸如边缘自治、高效运维通道、边缘单元化管理、边缘流量拓扑管理,安全容器、边缘 Serverless/FaaS、异构资源支持等能力。OpenYurt 能帮用户解决在海量边、端资源上完成大规模应用交付、运维、管控的问题,并提供中心服务下沉通道,实现和边缘计算应用的无缝对接。 1. OpenYurt 诞生背景 时间倒回两年前,伴随当时的行业发展,边缘计算正在成为云计算的新焦点,而规模和复杂度的日益提升对边缘计算的效率、可靠性及资源利用率等一系列能力提出了更高的要求。从 2017 年底开始,阿里云物联网(IoT)和 CDN 服务作为典型的边缘计算业务正面临着产品规模的爆发式增长、运维复杂度急剧攀升、运维效率不高的“三难”境地,因此引入云原生理念、全面转型边缘应用的运维管理模式成为亟需解决的问题。 正是在这样的背景下,OpenYurt 诞生于阿里云容器服务团队,并在接下来的两年多时间内,作为公共云服务 ACK@Edge 的核心框架被广泛应用于 CDN、音视频直播、物联网、物流、工业大脑、城市大脑等实际应用场景中,并正在服务于阿里云 LinkEdge、盒马、优酷、视频云(视频点播,视频直播,实时通信,视频监控,智能视觉)等多个业务或项目中。 2. OpenYurt 技术特点 OpenYurt 沿用了目前业界流行的“中心管控、边缘自治”的边缘应用管理架构,将“云边端一体化协同”作为目标,赋能云原生能力向边缘端拓展。在技术实现上,OpenYurt 贯彻了“Extending your native Kubernetes to Edge”的核心设计理念,其技术方案有如下特点: 对原生 Kubernetes“零”侵入,保证对原生 K8s API 的完全兼容。不改动 Kubernetes 核心组件,并不意味着 OpenYurt 是一个简单的 Kubernetes Addon。OpenYurt 通过 proxy node network traffic,对 Kubernetes 节点应用生命周期管理加了一层新的封装,提供边缘计算所需要的核心管控能力; 无缝转换,OpenYurt 提供了工具将原生 Kubernetes“一键式”转换成支持边缘计算能力的 Kubernetes 集群; 低 Overhead,OpenYurt 参考了大量边缘计算场景的实际需求,在保证功能和可靠性的基础上,本着最小化,最简化的设计理念,严格限制新增组件的资源诉求。 以上技术特点使得 OpenYurt 能够: 最大程度保证用户在管理边缘应用时获得和管理云端应用一致的体验; 兼容所有云厂商的 Kubernetes 服务,易于集成; 保持极低的运维成本。 3. OpenYurt 核心能力 OpenYurt 开源的核心能力包括: 边缘自治能力:YurtHub 作为节点上的临时配置中心,在网络连接中断的情况下,持续为节点上所有设备和客户业务提供数据配置服务。YurtHub 提供了对大量原生 Kubernetes API 的支持,可以在节点和边缘单元维度提供“Shadow Apiserver”的能力,在边缘计算弱网络链接场景的价值尤为突出; 边缘运维通道:在边缘场景,由于大多数边缘节点没有暴露在公网之上,中心管控无缝和边缘节点主动建立网络链接,所有的 Kubernetes 原生应用运维 APIs(logs/exec/metrics)会失去效力;YurtTunnel 通过在管控与边缘节点之间建立反向通道,并和节点的生命周期完整联动,承载原生运维 APIs 的流量; 集群转换能力:Yurtctl 作为 OpenYurt 官方命令行工具,提供原生 Kubernetes 集群支持边缘计算 infrastructure 的一键式切换。 其它更高级的功能比如边缘流量管理、单元化管理,部署、区域自治等将会逐步开源。 4. OpenYurt Roadmap 作为阿里云容器服务 ACK@Edge 的开源版本,OpenYurt 将采用全开源社区开发模式,每季度发布新版本更新,包含社区上游安全/关键 bug 修复和新特性、新能力,并逐步将产品完整能力开源,预计到 2021 年一季度正式发布 OpenYurt 1.0 版本。大致的 RoadMap 如下: 主导这次开源的阿里巴巴云原生应用平台团队,目前已经开源 OAM、OpenKruise、Dragonfly、Apache RocketMQ、Apache Dubbo 等众多明星项目,是国内最资深的云原生开源贡献团队。OpenYurt 项目的开源,本着“Extending your native Kubernetes to Edge”的设计理念,让云原生技术在边缘计算领域的生态建设与普及前进了一大步,也为全球开发者拓展云原生边界贡献了一份力量。 OpenYurt 项目地址:https://github.com/alibaba/openyurt 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
随着微服务的流行,应用更加轻量和开发效率不断提升,但是带来的困境是线上问题排查越来越复杂困难。传统的Java排查问题,需要重启应用再进行调试,但是重启应用之后现场会丢失,问题难以复现。 因此自2018年9月,阿里巴巴开源了久经考验,深受开发者喜爱的应用诊断利器Arthas。在阿里巴巴内部Arthas每年诊断数百万次,服务10000+应用,总共节约9000人日。 Arthas通过创新的字节码织入技术,可以在应用无需重启时,查看调用上下文,高效排查问题;结合火焰图,可以直接定位热点,发现性能瓶颈;通过redefine技术,实现在线热更新代码;同时支持黑屏化和白屏化诊断,可以连接诊断大规模的集群。 目前Arthas在 GitHub 上星标数达到 21000+,多次登顶github趋势榜首,并收到120+公司的登记支持,月下载量7000+,连续获得开源中国GVP和最受欢迎软件奖,是目前最流行的Java应用诊断工具。 直播重点简述:一、Arthas如何秒杀类冲突问题?二、少年,想线上热更新代码不?三、怎样快速定位定位应用的热点? 7月9日(周四)14:00 ,阿里云技术专家陈志轩(断岭),讲解《Arthas-线上应用诊断利器》。 欢迎大家钉钉扫描二维码,加入直播群内免费看直播!
随着近年来 Kubernetes 逐渐成为事实标准和大量应用的云原生化,我们往往发现 Kubernetes 的原生 workload 对大规模化应用的支持并不十分“友好”。如何在 Kubernetes 上为应用提供更加完善、高效、灵活的部署发布能力,成为了我们探索的目标。 阿里应用场景与原生 workloads 阿里巴巴容器化道路的起步在国内外都是比较领先的。容器这个技术概念虽然出现得很早,但一直到 2013 年 Docker 产品出现后才逐渐为人所熟知。而阿里巴巴早在 2011 年就开始发展了基于 LXC 的容器技术,经过了几代的系统演进,如今阿里巴巴有着超过百万的容器体量,这个规模在世界范围内都是顶尖的。 随着云技术发展和云原生应用的兴起,我们近两年间逐步将过去的容器迁到了基于 Kubernetes 的云原生环境中。而在这其中,我们遇到了不少应用部署方面的问题。首先对于应用开发者来说,他们对迁移到云原生环境的期望是: 面向丰富业务场景的策略功能 极致的部署发布效率 运行时的稳定性和容错能力 阿里的应用场景非常复杂,基于 Kubernetes 之上生长着很多不同的 PaaS 二层,比如服务于电商业务的运维中台、规模化运维、中间件、Serverless、函数计算等,而每个平台都对部署、发布要求各有不同。 我们再来看一下 Kubernete 原生所提供的两种常用 workload 的能力: 简单来说,Deployment 和 StatefulSet 在一些小规模的场景下是可以 work 的;但到了阿里巴巴这种应用和容器的规模下,如果全量使用原生 workload 则是完全不现实的。目前阿里内部容器集群上的应用数量超过十万、容器数量达到百万,有部分重点核心应用甚至单个应用下就有上万的容器。再结合上图的问题,我们会发现不仅针对单个应用的发布功能不足,而且当发布高峰期大量应用同时在升级时,超大规模的 Pod 重建也成为一种“灾难”。 阿里自研的扩展 workloads 针对原生 workload 远远无法满足应用场景的问题,我们从各种复杂的业务场景中抽象出共通的应用部署需求,据此开发了多种扩展 workload。在这些 workload 中我们做了大幅的增强和改进,但同时也会严格保证功能的通用化、不允许将业务逻辑耦合进来。 这里我们重点介绍一下 CloneSet 与 Advanced StatefulSet。在阿里内部云原生环境下,几乎全量的电商相关应用都统一采用 CloneSet 做部署发布,而中间件等有状态应用则使用了 Advanced StatefulSet 管理。 Advanced StatefulSet 顾名思义,是原生 StatefulSet 的增强版,默认行为与原生完全一致,在此之外提供了原地升级、并行发布(最大不可用)、发布暂停等功能。而 CloneSet 则对标原生 Deployment,主要服务于无状态应用,提供了最为全面丰富的部署发布策略。 原地升级 CloneSet、Advanced StatefulSet 均支持指定 Pod 升级方式: ReCreate:重建 Pod 升级,和原生 Deployment/StatefulSet 一致; InPlaceIfPossible:如果只修改 image 和 metadata 中的 labels/annotations 等字段,则触发 Pod 原地升级;如果修改了其他 template spec 中的字段,则退化到 Pod 重建升级; InPlaceOnly:只允许修改 image 和 metadata 中的 labels/annotations 等字段,只会使用原地升级。 所谓原地升级,就是在升级 template 模板的时候,workload 不会把原 Pod 删除、新建,而是直接在原 Pod 对象上更新对应的 image 等数据。 如上图所示,在原地升级的时候 CloneSet 只会更新 Pod spec 中对应容器的 image,而后 kubelet 看到 Pod 中这个容器的定义发生了变化,则会把对应的容器停掉、拉取新的镜像、并使用新镜像创建启动容器。另外可以看到在过程中,这个 Pod 的 sandbox 容器以及其他本次未升级的容器还一直处于正常运行状态,只有需要升级的容器会受到影响。 原地升级给我们带来的好处实在太多了: 首先就是发布效率大大提升了,根据非完全统计数据,在阿里环境下原地升级至少比完全重建升级提升了 80% 以上的发布速度:不仅省去了调度、分配网络、分配远程盘的耗时,连拉取新镜像的时候都得益于 node 上已有旧镜像、只需要拉取较少的增量 layer); IP 不变、升级过程 Pod 网络不断,除本次升级外的其他容器保持正常运行; Volume 不变,完全复用原容器的挂载设备; 保障了集群确定性,使排布拓扑能通过大促验证。 后续我们将会有专文讲解阿里在 Kubernetes 之上做的原地升级,意义非常重大。如果没有了原地升级,阿里巴巴内部超大规模的应用场景几乎是无法在原生 Kubernetes 环境上完美落地的,我们也鼓励每一位 Kubernetes 用户都应该“体验”一下原地升级,它给我们带来了不同于 Kubernetes 传统发布模式的变革。 流式+分批发布 前一章我们提到了,目前 Deployment 支持 maxUnavailable/maxSurge 的流式升级,而 StatefulSet 支持 partition 的分批升级。但问题在于,Deployment 无法灰度分批,而 StatefulSet 则只能一个一个 Pod 串行发布,没办法并行的流式升级。 首先要说的是,我们将 maxUnavailable 引入了 Advanced StatefulSet。原生 StatefulSet 的 one by one 发布,大家其实可以理解为一个强制 maxUnavailable=1 的过程,而 Advanced StatefulSet 中如果我们配置了更大的 maxUnavailable,那么就支持并行发布更多的 Pod 了。 然后我们再来看一下 CloneSet,它支持原生 Deployment 和 StatefulSet 的全部发布策略,包括 maxUnavailable、maxSurge、partition。那么 CloneSet 是如何把它们结合在一起的呢?我们来看一个例子: 针对这个副本数为 5 的 CloneSet,如果我们修改了 template 中的 image,同时配置:maxSurge=20% maxUnavailable=0 partition=3。当开始发布后: 先扩出来 1 个新版本的 Pod,5 个存量 Pod 保持不动; 新 Pod ready 后,逐步把旧版本 Pod 做原地升级; 直到剩余 3 个旧版本 Pod 时,因为满足了 partition 终态,会把新版本 Pod 再删除 1 个; 此时 Pod 总数仍然为 5,其中 3 个旧版本、1 个新版本。 如果我们接下来把 partition 调整为 0,则 CloneSet 还是会先扩出 1 个额外的新版 Pod,随后逐渐将所有 Pod 升级到新版,最终再次删除一个 Pod,达到 5 个副本全量升级的终态。 发布顺序可配置 对于原生的 Deployment 和 StatefulSet,用户是无法配置发布顺序的。Deployment 下的 Pod 发布顺序完全依赖于它修改 ReplicaSet 后的扩缩顺序,而 StatefulSet 则严格按照 order 的反序来做一一升级。 但在 CloneSet 和 Advanced StatefulSet 中,我们增加了发布顺序的可配置能力,使用户可以定制自己的发布顺序。目前可以通过以下两种发布优先级和一种发布打散策略来定义顺序: 优先级(1):按给定 label key,在发布时根据 Pod labels 中这个 key 对应的 value 值作为权重: 优先级(2):按 selector 匹配计算权重,发布时根据 Pod 对多个 weight selector 的匹配情况计算权重总和: 打散:将匹配 key-value 的 Pod 打散到不同批次中发布: 可能有同学会问为什么要配置发布顺序呢?比如 zookeeper 这类应用在发布时,需要先把所有非主节点升级,最后再升级主节点,这样才能保证在整个发布过程中只会发生一次切主。这时用户就可以通过流程打标、或者写一个 operator 自动为 zookeeper 的 Pod 打上节点职责的标签,而后配置非主节点的发布权重较大,使得发布时能够尽量减少切主的次数。 sidecar 容器管理 轻量化容器也是阿里巴巴在云原生阶段的一次重大改革,过去阿里的容器绝大多数都是以“富容器”的方式运行的,所谓“富容器”即在一个容器中既运行业务、也跑着各种各样的插件和守护进程。而在云原生时代,我们在逐渐把原先“富容器”中的旁路插件拆分到独立的 sidecar 容器中,使主容器逐渐回归业务自身。 这里对于拆分的好处就不赘述了,我们来看下另一个问题,就是拆分之后这些 sidecar 容器如何做管理呢?最直观的方式是在每个应用的 workload 中显示去定义 Pod 中需要的 sidecar,但这样带来的问题很多: 当应用和 workload 数量众多时,我们很难统一的 sidecar 增减管理; 应用开发者不知道(甚至也不关心)自己的应用需要配置哪些 sidecar 容器; 当 sidecar 镜像需要升级时,要把所有应用的 workload 全部升级一遍,很不现实。 因此,我们设计了 SidecarSet,将 sidecar 容器的定义与应用 workload 解耦。应用开发者们不再需要再关心自己的 workload 中需要写哪些 sidecar 容器,而通过原地升级, sidecar 维护者们也可以自主地管理和升级 sidecar 容器。 开放能力应用 到了这里,大家是不是对阿里巴巴的应用部署模式有了一个基本的了解呢?其实上述的能力都已经开源到了社区,我们的项目就叫做 OpenKruise,目前它已经提供了 5 种扩展 workload: CloneSet:提供了更加高效、确定可控的应用管理和部署能力,支持优雅原地升级、指定删除、发布顺序可配置、并行/灰度发布等丰富的策略,可以满足更多样化的应用场景; Advanced StatefulSet:基于原生 StatefulSet 之上的增强版本,默认行为与原生完全一致,在此之外提供了原地升级、并行发布(最大不可用)、发布暂停等功能; SidecarSet:对 sidecar 容器做统一管理,在满足 selector 条件的 Pod 中注入指定的 sidecar 容器; UnitedDeployment:通过多个 subset workload 将应用部署到多个可用区; BroadcastJob:配置一个 job,在集群中所有满足条件的 Node 上都跑一个 Pod 任务。 此外,我们还有更多的扩展能力还在开源的路上!近期,我们会将内部的 Advanced DaemonSet 开放到 OpenKruise 中,它在原生 DaemonSet 的 maxUnavailable 之上,额外提供了如分批、selector 等发布策略,分批的功能使 DaemonSet 在发布的时候能够只升级其中部分 Pod,而 selector 更是允许发布的时候指定先在符合某些标签的 node 上升级,这为我们在大规模集群中升级 DaemonSet 带来了灰度能力和稳定性的保障。 而后续,我们还计划将阿里巴巴内部扩展的 HPA、调度插件等通用化能力开放出来,让每一位 Kubernetes 开发者和阿里云上的用户都能很便捷地使用上阿里内部开发应用的云原生增强能力。 Q & A Q1:目前阿里最大规模的业务 pod 数量有多少,发布一次需要多少时间?A1:这个只能透露数量目前最大规模的单个应用下数量是以万为单位的,一次发布时间要看具体分批灰度的时长了。如果分批较多、观察时间较长的话,可能是会持续一两周的。 Q2:pod 的资源 request 和 limit 是怎么配置的?request 和 limit 是什么比例来配置?过多的 request 造成浪费,过少可能会导致热点 node 负载超高。A2:这个主要还是根据应用的需求来定的,目前大部分在线应用都是 1:1 的关系,部分离线和job 类型的会配置 request>limit。 Q3:kruise 升级问题,升级 kurise apiversion 版本的情况下,原有的版本的部署如何升级?A3:目前 kruise 中资源的 apiVersion 还都是统一的。我们计划在今年下半年将部分较为成熟的 workload 进行版本升级,用户在自己的 K8s 集群内升级后,存量的旧版本资源会自动通过 conversion 升级到新版本。 Q4:OpenKruise 有提供 go-client 吗?A4:目前提供两个方式:1. 引入 github.com/openkruise/kruise/pkg/client 包,下面有生成好的 clientset / informer / lister 等工具;2. 使用 controller-runtime 的用户(包括 kubebuilder、operator-sdk),直接引入 github.com/openkruise/kruise-api 轻量化依赖,然后加到 scheme 里就能直接用了。 Q5:阿里 K8s 版本升级是如何做的?A5:阿里集团内部使用 Kube-On-Kube 的架构进行大规模的 Kubernetes 集群管理,用一个元 K8s 集群管理成百上千个业务 K8s 集群。其中元集群版本较为稳定,业务集群会进行频繁升级,业务集群的升级流程事实上就是对元集群中的 workloads(原生 workloads 以及 kruise workloads) 进行版本或配置升级,与正常情况我们对业务 workloads 的升级流程相似。 Q6:这个灰度之后,流量是怎么切的?A6:在原地升级前,kruise 会先通过 readinessGate 将 Pod 置为 not-ready,此时 endpoint 等控制器会感知到并把 Pod 从端点摘掉。然后 kruise 更新 pod image 触发容器重建,完成后再把 Pod 改为 ready。 Q7:daemonset 的分批是通过类似 deployment 的暂停功能实现的么?统计已经发布数量然后暂停,然后继续,然后再暂停。A7:总体过程上类似,升级过程中对新旧版本进行统计并判断是否已达到指定终态。但相比 deployment,daemonset 需要处理比较复杂的边界情况(例如初次发布时集群中并没有指定的 Pod),具体细节可以持续关注我们即将开源的代码。 Q8:多集群发布页面上怎么开始发布的?A8:直播中演示的是一个 demo 的发布系统结合 Kruise Workloads 的例子,从交互上是通过用户选择对应的集群,点击开始执行进行发布;从实现上实际是对新版本的 YAML 与集群中的 YAML 计算 diff 后 Patch 进集群,再操作 DaemonSet 的控制字段(partition / paused 等),控制灰度进程。 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
传统架构的数据中心由于无法适应资源的快速伸缩,已经无法很好的服务在线化的消费者,企业开始意识到只有借助云技术才能实现低成本,敏捷的服务海量用户。但是迁移上云,特别是数据库的上云,会觉得工作量巨大且风险很高,往往望而却步。 通过本课程,将让开发者意识到数据库上云不是洪水猛兽,通过合理的评估,选型,迁移,测试和验证,无缝平滑上云是完全有可能的,上云后业务既能享受云数据库的技术红利,也能充分利用到云灵活,可扩展的弹性优势。 直播重点简述:一、如何解决传统架构数据中心无法适应资源快速伸缩的问题?二、企业如何借助云技术实现低成本?三、数据库如何无缝平滑安全上云? 7月7日(周二)14:00 ,阿里云高级解决方案架构师 时慢 ,讲解《数据库如何快速上云》。 欢迎大家钉钉扫描二维码,加入直播群内免费看直播!
前言 Dubbo 线程池满异常应该是大多数 Dubbo 用户都遇到过的一个问题,本文以 Arthas 3.1.7 版本为例,介绍如何针对该异常进行诊断,主要使用到 dashboard / thread 两个指令。 推荐使用 Arthas 方式一:推荐使用 IDEA 插件下载 Cloud Toolkit 来使用 ArthasCloud Toolkit 是阿里云发布的免费本地 IDE 插件,帮助开发者更高效地开发、测试、诊断并部署应用。通过插件,可以将本地应用一键部署到任意服务器,甚至云端(ECS、EDAS、ACK、ACR 和 小程序云等);并且还内置了 Arthas 诊断、Dubbo工具、Terminal 终端、文件上传、函数计算 和 MySQL 执行器等工具。不仅仅有 IntelliJ IDEA 主流版本,还有 Eclipse、Pycharm、Maven 等其他版本。 方式二:直接下载 Dubbo 线程池满异常介绍 理解线程池满异常需要首先了解 Dubbo 线程模型,官方文档:http://dubbo.apache.org/zh-cn/docs/user/demos/thread-model.html。 简单概括下 Dubbo 默认的线程模型:Dubbo 服务端每次接收到一个 Dubbo 请求,便交给一个线程池处理,该线程池默认有 200 个线程,如果 200 个线程都不处于空闲状态,则客户端会报出如下异常: Caused by: java.util.concurrent.ExecutionException: org.apache.dubbo.remoting.RemotingException: Server side(192.168.1.101,20880) threadpool is exhausted ... 服务端会打印 WARN 级别的日志: [DUBBO] Thread pool is EXHAUSTED! 引发该异常的原因主要有以下几点: 客户端/服务端超时时间设置不合理,导致请求无限等待,耗尽了线程数; 客户端请求量过大,服务端无法及时处理,耗尽了线程数; 服务端由于 fullgc 等原因导致处理请求较慢,耗尽了线程数; 服务端由于数据库、Redis、网络 IO 阻塞问题,耗尽了线程数; … 原因可能很多,但究其根本,都是因为业务上出了问题,导致 Dubbo 线程池资源耗尽了。所以出现该问题,首先要做的是:排查业务异常。 紧接着针对自己的业务场景对 Dubbo 进行调优: 调整 Provider 端的 dubbo.provider.threads 参数大小,默认 200,可以适当提高。多大算合适?至少 700 不算大;不建议调的太小,容易出现上述问题; 调整 Consumer 端的 dubbo.consumer.actives 参数,控制消费者调用的速率。这个实践中很少使用,仅仅一提; 客户端限流; 服务端扩容; Dubbo 目前不支持给某个 Service 单独配置一个隔离的线程池,用于保护服务,可能在以后的版本中会增加这个特性。 另外,不止 Dubbo 如此设计线程模型,绝大多数服务治理框架、 HTTP 服务器都有业务线程池的概念,所以理论上它们都会有线程池满异常的可能,解决方案也类似。 那既然问题都解释清楚了,我们还需要排查什么呢? 一般在线上,有很多运行中的服务,这些服务都是共享一个 Dubbo 服务端线程池,可能因为某个服务的问题,导致整个应用被拖垮,所以需要排查是不是集中出现在某个服务上,再针对排查这个服务的业务逻辑;需要定位到线程堆栈,揪出导致线程池满的元凶。 定位该问题,我的习惯一般是使用 Arthas 的 dashboard 和 thread 命令,而在介绍这两个命令之前,我们先人为构造一个 Dubbo 线程池满异常的例子。 复现 Dubbo 线程池满异常 配置服务端线程池大小 dubbo.protocol.threads=10 默认大小是 200,不利于重现该异常。 模拟服务端阻塞 @Service(version = "1.0.0")public class DemoServiceImpl implements DemoService { @Override public String sayHello(String name) { sleep(); return "Hello " + name; } private void sleep() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } } sleep 方法模拟了一个耗时操作,主要是为了让服务端线程池耗尽。 客户端多线程访问 for (int i = 0; i < 20; i++) { new Thread(() -> { while (true){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } try { demoService.sayHello("Provider"); } catch (Exception e) { e.printStackTrace(); } } }).start(); } 问题复现 客户端 (客户端异常) 服务端 (服务端异常) 问题得以复现,保留该现场,并假设我们并不知晓 sleep 的耗时逻辑,使用 Arthas 来进行排查。 dashboard 命令介绍 $ dashboard 执行效果:(dashboard) 可以看到如上所示的面板,显示了一些系统的运行信息,这里主要关注 THREAD 面板,介绍一下各列的含义: ID: Java 级别的线程 ID,注意这个 ID 不能跟 jstack 中的 nativeID 一一对应; NAME: 线程名; GROUP: 线程组名; PRIORITY: 线程优先级, 1~10 之间的数字,越大表示优先级越高; STATE: 线程的状态; CPU%: 线程消耗的 CPU 占比,采样 100ms,将所有线程在这 100ms 内的 CPU 使用量求和,再算出每个线程的 CPU 使用占比; TIME: 线程运行总时间,数据格式为分:秒 INTERRUPTED: 线程当前的中断位状态; DAEMON: 是否是 daemon 线程。 在空闲状态下线程应该是处于 WAITING 状态,而因为 sleep 的缘故,现在所有的线程均处于 TIME_WAITING 状态,导致后来的请求被处理时,抛出了线程池满的异常。 在实际排查中,需要抽查一定数量的 Dubbo 线程,记录他们的线程编号,看看它们到底在处理什么服务请求。使用如下命令可以根据线程池名筛选出 Dubbo 服务端线程: dashboard | grep "DubboServerHandler" thread 命令介绍 使用 dashboard 筛选出个别线程 id 后,它的使命就完成了,剩下的操作交给 thread 命令来完成。其实,dashboard 中的 thread 模块,就是整合了 thread 命令,但是 dashboard 还可以观察内存和 GC 状态,视角更加全面,所以我个人建议,在排查问题时,先使用 dashboard 纵观全局信息。 thread 使用示例: 查看当前最忙的前 n 个线程 $ thread -n 3 (thread -n) 显示所有线程信息 $ thread 和 dashboard 中显示一致。 显示当前阻塞其他线程的线程 $ thread -bNo most blocking thread found!Affect(row-cnt:0) cost in 22 ms. 这个命令还有待完善,目前只支持找出 synchronized 关键字阻塞住的线程, 如果是 java.util.concurrent.Lock, 目前还不支持。 显示指定状态的线程 $ thread --state TIMED_WAITING (thread --state) 线程状态一共有 [RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, NEW, TERMINATED] 6 种。 查看指定线程的运行堆栈 $ thread 46 (thread ${thread_id}) 介绍了几种常见的用法,在实际排查中需要针对我们的现场做针对性的分析,也同时考察了我们对线程状态的了解程度。我这里列举了几种常见的线程状态: 初始(NEW) 新创建了一个线程对象,但还没有调用 start() 方法。 运行(RUNNABLE) Java 线程将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。 阻塞(BLOCKED) 线程阻塞于锁。 等待(WAITING) 进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断): Object#wait() 且不加超时参数 Thread#join() 且不加超时参数 LockSupport#park() 超时等待(TIMED_WAITING) 该状态不同于 WAITING,它可以在指定的时间后自行返回。 Thread#sleep() Object#wait() 且加了超时参数 Thread#join() 且加了超时参数 LockSupport#parkNanos() LockSupport#parkUntil()‘ 终止(TERMINATED) 标识线程执行完毕。 状态流转图 (线程状态) 问题分析 分析线程池满异常并没有通法,需要灵活变通,我们对下面这些 case 一个个分析: 阻塞类问题。例如数据库连接不上导致卡死,运行中的线程基本都应该处于 BLOCKED 或者 TIMED_WAITING 状态,我们可以借助 thread --state 定位到; 繁忙类问题。例如 CPU 密集型运算,运行中的线程基本都处于 RUNNABLE 状态,可以借助于 thread -n 来定位出最繁忙的线程; GC 类问题。很多外部因素会导致该异常,例如 GC 就是其中一个因素,这里就不能仅仅借助于 thread 命令来排查了; 定点爆破。还记得在前面我们通过 grep 筛选出了一批 Dubbo 线程,可以通过 thread ${thread_id} 定向的查看堆栈,如果统计到大量的堆栈都是一个服务时,基本可以断定是该服务出了问题,至于说是该服务请求量突然激增,还是该服务依赖的某个下游服务突然出了问题,还是该服务访问的数据库断了,那就得根据堆栈去判断了。 总结 本文以 Dubbo 线程池满异常作为引子,介绍了线程类问题该如何分析,以及如何通过 Arthas 快速诊断线程问题。有了 Arthas,基本不再需要 jstack 将 16 进制转来转去了,大大提升了诊断速度。 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
Sentinel 是阿里巴巴开源的,面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统自适应保护等多个维度来帮助开发者保障微服务的稳定性。Sentinel 承接了阿里巴巴近 10 年的 双11 大促流量的核心场景,例如秒杀、冷启动、消息削峰填谷、集群流量控制、实时熔断下游不可用服务等,是保障微服务高可用的利器,原生支持 Java/Go/C++ 等多种语言,并且提供 Istio/Envoy 全局流控支持来为 Service Mesh 提供高可用防护的能力。 近期,Sentinel Go 0.3.0 正式发布,带来了熔断降级特性支持,可以针对 Go 服务中的不稳定调用进行自动熔断,避免出现级联错误/雪崩,是保障服务高可用重要的一环。结合 Sentinel Go 已经提供的 gRPC、Gin、Dubbo 等框架组件的适配模块,开发者可以快速在 Web、RPC 调用层面配置熔断降级规则来保护自身服务的稳定性。同时 0.3.0 版本也带来了 etcd 动态数据源模块,开发者可以方便地通过 etcd 来动态调整熔断降级策略。 Sentinel Go 项目地址:https://github.com/alibaba/sentinel-golang 为什么需要熔断降级 一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。 现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的服务进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。 Sentinel Go 熔断降级特性基于熔断器模式的思想,在服务出现不稳定因素(如响应时间变长,错误率上升)的时候暂时切断服务的调用,等待一段时间再进行尝试。一方面防止给不稳定服务“雪上加霜”,另一方面保护服务的调用方不被拖垮。Sentinel 支持两种熔断策略:基于响应时间(慢调用比例)和基于错误(错误比例/错误数),可以有效地针对各种不稳定的场景进行防护。 下面我们介绍一下 Sentinel 流控降级的一些最佳实践。 流控降级最佳实践 在服务提供方(Service Provider)的场景下,我们需要保护服务提供方不被流量洪峰打垮。我们通常根据服务提供方的服务能力进行流量控制,或针对特定的服务调用方进行限制。为了保护服务提供方不被激增的流量拖垮影响稳定性,我们可以结合前期的容量评估,通过 Sentinel 配置 QPS 模式的流控规则,当每秒的请求量超过设定的阈值时,会自动拒绝多余的请求。 在服务调用端(Service Consumer)的场景下,我们需要保护服务调用方不被不稳定的依赖服务拖垮。借助 Sentinel 的信号量隔离策略(并发数流控规则),限制某个服务调用的并发量,防止大量慢调用挤占正常请求的资源;同时,借助熔断降级规则,当异常比率或业务慢调用比例超过某个阈值后将调用自动熔断,直到一段时间过后再尝试恢复。熔断期间我们可以提供默认的处理逻辑(fallback),熔断期间的调用都会返回 fallback 的结果,而不会再去尝试本已非常不稳定的服务。需要注意的是,即使服务调用方引入了熔断降级机制,我们还是需要在 HTTP 或 RPC 客户端配置请求超时时间,来做一个兜底的保护。 同时 Sentinel 还提供全局维度的系统自适应保护能力,结合系统的 Load、CPU 使用率以及服务的入口 QPS、响应时间和并发量等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。系统规则可以作为整个服务的一个兜底防护策略,保障服务不挂。 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
Istio is the future!基本上,我相信对云原生技术趋势有些微判断的同学,都会有这个觉悟。其背后的逻辑其实是比较简单的:当容器集群,特别是 Kubernetes 成为事实上的标准之后,应用必然会不断的复杂化,服务治理肯定会成为强需求。 Istio 的现状是,聊的人很多,用的人其实很少。所以导致我们能看到的文章,讲道理的很多,讲实际踩坑经验的极少。阿里云售后团队作为一线踩坑团队,分享问题排查经验,我们责无旁贷。这篇文章,我就跟大家聊一个简单 Istio 问题的排查过程,权当抛砖。 二分之一活的微服务 问题是这样的,用户在自己的测试集群里安装了 Istio,并依照官方文档部署 bookinfo 应用来上手 Istio。部署之后,用户执行 kubectl get pods 命令,发现所有的 Pod 都只有二分之一个容器是 READY 的。 如果从来都没有注意过 READY 这一列的话,我们大概会有两个疑惑:2 在这里是什么意思,以及 1/2 到底意味着什么。 简单来讲,这里的 READY 列,给出的是每个 Pod 内部容器的 Readiness,即就绪状态。每个集群节点上的 kubelet 会根据容器本身 Readiness 规则的定义,分别是 tcp、http 或 exec 的方式,来确认对应容器的 Readiness 情况。 更具体一点,kubelet 作为运行在每个节点上的进程,以 tcp/http 的方式(节点网络命名空间到 Pod 网络命名空间)访问容器定义的接口,或者在容器的 namespace 里执行 exec 定义的命令,来确定容器是否就绪。 这里的 2 说明这些 Pod 里都有两个容器,1/2 则表示,每个 Pod 里只有一个容器是就绪的,即通过 Readiness 测试的。关于 2 这一点,我们下一节会深入讲,这里我们先看一下,为什么所有的 Pod 里,都有一个容器没有就绪。 使用 kubectl 工具拉取第一个 details pod 的编排模板,可以看到这个 Pod 里两个容器,只有一个定义了 readiness probe。对于未定义 readiness probe 的容器, kubelet 认为,只要容器里的进程开始运行,容器就进入就绪状态了。所以 1/2 个就绪 Pod,意味着,有定义 readiness probe 的容器,没有通过 kubelet 的测试。 没有通过 readiness probe 测试的是 istio-proxy 这个容器。它的 readiness probe 规则定义如下: readinessProbe: failureThreshold: 30 httpGet: path: /healthz/ready port: 15020 scheme: HTTP initialDelaySeconds: 1 periodSeconds: 2 successThreshold: 1 timeoutSeconds: 1 我们登录这个 Pod 所在的节点,用 curl 工具来模拟 kubelet 访问下边的 uri,测试 istio-proxy 的就绪状态。 绕不过去的大图 上一节我们描述了问题现象,但是留下一个问题,就是 Pod 里的容器个数为什么是 2。虽然每个 Pod 本质上至少有两个容器:一个是占位符容器 pause,另一个是真正的工作容器,但是我们在使用 kubectl 命令获取 Pod 列表的时候,READY 列是不包括 pause 容器的。 这里的另外一个容器,其实就是服务网格的核心概念 sidercar。其实把这个容器叫做 sidecar,某种意义上是不能反映这个容器的本质的。Sidecar 容器本质上是反向代理,它本来是一个 Pod 访问其他服务后端 Pod 的负载均衡。 然而,当我们为集群中的每一个 Pod,都“随身”携带一个反向代理的时候,Pod 和反向代理就变成了服务网格。正如下边这张经典大图所示。这张图实在有点难画,所以只能借用,绕不过去。 所以 sidecar 模式,其实是“自带通信员”模式。这里比较有趣的是,在我们把 sidecar 和 Pod 绑定在一块的时候,sidecar 在出流量转发时扮演着反向代理的角色,而在入流量接收的时候,可以做超过反向代理职责的一些事情。这点我们会在其他文章里讨论。 Istio 在 Kubernetes 基础上实现了服务网格,Isito 使用的 sidecar 容器就是第一节提到的,没有就绪的容器。所以这个问题,其实就是服务网格内部,所有的 sidecar 容器都没有就绪。 代理与代理的生命周期管理 上一节我们看到,Istio 中的每个 Pod,都自带了反向代理 sidecar。我们遇到的问题是,所有的 sidecar 都没有就绪。我们也看到 readiness probe 定义的,判断 sidecar 容器就绪的方式就是访问下边这个接口: 接下来,我们深入看下 Pod,以及其 sidecar 的组成及原理。在服务网格里,一个 Pod 内部除了本身处理业务的容器之外,还有 istio-proxy 这个 sidecar 容器。正常情况下,istio-proxy 会启动两个进程:pilot-agent 和 Envoy。 如下图,Envoy 是实际上负责流量管理等功能的代理,从业务容器出、入的数据流,都必须要经过 Envoy;而 pilot-agent 负责维护 Envoy 的静态配置,以及管理 Envoy 的生命周期。这里的动态配置部分,我们在下一节会展开来讲。 我们可以使用下边的命令进入 Pod 的 istio-proxy 容器做进一步排查。这里的一个小技巧,是我们可以以用户 1337,使用特权模式进入 istio-proxy 容器,如此就可以使用 iptables 等只能在特权模式下运行的命令。 docker exec -ti -u 1337 --privileged bash 这里的 1337 用户,其实是 sidecar 镜像里定义的一个同名用户 istio-proxy,默认 sidecar 容器使用这个用户。如果我们在以上命令中,不使用用户选项 u,则特权模式实际上是赋予 root 用户的,所以我们在进入容器之后,需切换到 root 用户执行特权命令。 进入容器之后,我们使用 netstat 命令查看监听,我们会发现,监听 readiness probe 端口 15020 的,其实是 pilot-agent 进程。 istio-proxy@details-v1-68868454f5-94hzd:/$ netstat -lnptActive Internet connections (only servers)Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program nametcp 0 0 0.0.0.0:15090 0.0.0.0:* LISTEN 19/envoytcp 0 0 127.0.0.1:15000 0.0.0.0:* LISTEN 19/envoytcp 0 0 0.0.0.0:9080 0.0.0.0:* LISTEN -tcp6 0 0 :::15020 :::* LISTEN 1/pilot-agent 我们在istio-proxy内部访问readiness probe接口,一样会得到503的错误。 就绪检查的实现 了解了 sidecar 的代理,以及管理代理生命周期的 pilot-agent 进程,我们可以稍微思考一下 pilot-agent 应该怎么去实现 healthz/ready 这个接口。显然,如果这个接口返回 OK 的话,那不仅意味着 pilot-agent 是就绪的,而必须确保代理是工作的。 实际上 pilot-agent 就绪检查接口的实现正是如此。这个接口在收到请求之后,会去调用代理 Envoy 的 server_info 接口。调用所使用的 IP 是 Localhost。这个非常好理解,因为这是同一个 Pod 内部进程通信。使用的端口是 Envoy 的 proxyAdminPort,即 15000。 有了以上的知识准备之后,我们来看下 istio-proxy 这个容器的日志。实际上,在容器日志里,一直在重复输出一个报错,这句报错分为两部分,其中 Envoy proxy is NOT ready 这部分是 pilot agent 在响应 healthz/ready 接口的时候输出的信息,即 Envoy 代理没有就绪;而剩下的 config not received from Pilot (is Pilot running?): cds updates: 0 successful, 0 rejected; lds updates: 0 successful, 0 rejected 这部分,是 pilot-agent 通过 proxyAdminPort 访问 server_info 的时候带回的信息,看起来是 Envoy 没有办法从 Pilot 获取配置。 Envoy proxy is NOT ready: config not received from Pilot (is Pilot running?): cds updates: 0 successful, 0 rejected; lds updates: 0 successful, 0 rejected. 到这里,建议大家回退看下上一节的插图,在上一节我们选择性的忽略是 Pilot 到 Envoy 这条虚线,即动态配置。这里的报错,实际上是 Envoy 从控制面 Pilot 获取动态配置失败。 控制面和数据面 目前为止,这个问题其实已经很清楚了。在进一步分析问题之前,我聊一下我对控制面和数据面的理解。控制面数据面模式,可以说无处不在。我们这里举两个极端的例子。 第一个例子,是 DHCP 服务器。我们都知道,在局域网中的电脑,可以通过配置 DHCP 来获取 IP 地址,这个例子中,DHCP 服务器统一管理,动态分配 IP 地址给网络中的电脑,这里的 DHCP 服务器就是控制面,而每个动态获取 IP 的电脑就是数据面。 第二个例子,是电影剧本,和电影的演出。剧本可以认为是控制面,而电影的演出,包括演员的每一句对白,电影场景布置等,都可以看做是数据面。 我之所以认为这是两个极端,是因为在第一个例子中,控制面仅仅影响了电脑的一个属性,而第二个例子,控制面几乎是数据面的一个完整的抽象和拷贝,影响数据面的方方面面。Istio 服务网格的控制面是比较靠近第二个例子的情况,如下图: Istio 的控制面 Pilot 使用 gRPC 协议对外暴露接口 istio-pilot.istio-system:15010,而 Envoy 无法从 Pilot 处获取动态配置的原因,是在所有的 Pod 中,集群 DNS 都无法使用。 简单的原因 这个问题的原因其实比较简单,在 sidecar 容器 istio-proxy 里,Envoy 不能访问 Pilot 的原因是集群 DNS 无法解析 istio-pilot.istio-system 这个服务名字。在容器里看到 resolv.conf 配置的 DNS 服务器是 172.19.0.10,这个是集群默认的 kube-dns 服务地址。 istio-proxy@details-v1-68868454f5-94hzd:/$ cat /etc/resolv.confnameserver 172.19.0.10search default.svc.cluster.local svc.cluster.local cluster.local localdomain 但是客户删除重建了 kube-dns 服务,且没有指定服务 IP,这导致,实际上集群 DNS 的地址改变了,这也是为什么所有的 sidecar 都无法访问 Pilot。 svc -n kube-systemNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEkube-dns ClusterIP 172.19.9.54 53/UDP,53/TCP 5d 最后,通过修改 kube-dns 服务,指定 IP 地址,sidecar 恢复正常。 结论 这其实是一个比较简单的问题,排查过程其实也就几分钟。但是写这篇文章,有点感觉是在看长安十二时辰,短短几分钟的排查过程,写完整背后的原理,前因后果,却花了几个小时。这是 Istio 文章的第一篇,希望在大家排查问题的时候,有所帮助。 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
在多控制平面拓扑的配置中,每个 Kubernetes 集群都会安装相同的 Istio 控制平面,并且每个控制平面只会管理自己集群内的服务端点。通过使用 Istio 网关、公共根证书颁发机构(CA)以及服务条目 ServiceEntry,可以将多个集群配置组成一个逻辑上的单一服务网格。这种方法没有特殊的网络要求,因此通常被认为是在 Kubernetes 集群之间没有通用网络连接时的一种最简单方法。 在这种拓扑配置下,Kubernetes 跨集群通信需要服务之间的双向 TLS 连接,要在集群之间启用双向 TLS 通信,每个集群的 Citadel 将配置由共享的根 CA 生成的中间 CA 证书,如图所示。 (多控制平面) 部署控制平面 从共享的根 CA 为每个集群的 Citadel 生成中间 CA 证书,共享的根 CA 启用跨不同集群的双向 TLS 通信。为了便于说明,我们将 samples/certs 目录下 Istio 安装中提供的示例根 CA 证书用于两个集群。在实际部署中,你可能会为每个集群使用不同的 CA 证书,所有 CA 证书都由公共根 CA 签名。 在每个 Kubernetes 集群中实施以下步骤,以在所有集群中部署相同的 Istio 控制平面配置。 1. 使用以下的命令为生成的 CA 证书创建 Kubernetes 密钥,如下所示: kubectlcreate namespace istio-systemkubectlcreate secret generic cacerts -n istio-system \ --from-file=samples/certs/ca-cert.pem \ --from-file=samples/certs/ca-key.pem \ --from-file=samples/certs/root-cert.pem \ --from-file=samples/certs/cert-chain.pem 1. 安装 Istio 的 CRD 并等待几秒钟,以便将它们提交给 Kubernetes API 服务器,如下所示: fori in install/kubernetes/helm/istio-init/files/crd*yaml; do kubectl apply -f $i;done 部署 Istio 控制平面:如果 helm 依赖项缺失或者不是最新的,可以通过 helm dep update 来更新这些依赖项。注意因为没有使用 istio-cni,可以暂时将其从依赖项 requirements.yaml 中去掉再执行更新操作。具体执行命令如下: helmtemplate install/kubernetes/helm/istio --name istio --namespace istio-system \ -finstall/kubernetes/helm/istio/values-istio-multicluster-gateways.yaml >./istio.yamlkubectlapply -f ./istio.yaml 确保上述步骤在每个 Kubernetes 集群中都执行成功。当然,通过 helm 生成 istio.yaml 的命令执行一次即可。 设置 DNS 为远程集群中的服务提供 DNS 解析,则现有应用程序不需要做修改就可以运行,因为应用程序通常期望通过其 DNS 名称来解析服务并访问所得到的 IP 地址。Istio 本身不使用 DNS 在服务之间路由请求,同一个 Kubernetes 集群下的服务会共享一个相同的 DNS 后缀(例如 svc.cluster.local)。Kubernetes DNS 为这些服务提供 DNS 解析能力。为了给远程集群中的服务提供相似的设置,将远程集群中的服务以 ..global 的格式命名。 Istio 安装包中附带了一个 CoreDNS 服务器,该服务器将为这些服务提供DNS解析能力。为了利用这个 DNS 解析能力,需要配置 Kubernetes 的 DNS 服务指向该 CoreDNS 服务。该 CoreDNS 服务将作为 .global DNS 域的 DNS 服务器。 对于使用 kube-dns 的集群,请创建以下配置项或更新现有的配置项: kubectlapply -f - <apiVersion:v1kind:ConfigMapmetadata: name: kube-dns namespace: kube-systemdata: stubDomains: | {"global": ["$(kubectl get svc -n istio-system istiocoredns -o jsonpath={.spec.clusterIP})"]}EOF 对于使用 CoreDNS 的集群,请创建以下配置项或更新现有的配置项: kubectlapply -f - <apiVersion:v1kind:ConfigMapmetadata: name: coredns namespace: kube-systemdata: Corefile: | .:53 { errors health kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure upstream fallthrough in-addr.arpa ip6.arpa } prometheus :9153 proxy . /etc/resolv.conf cache 30 reload loadbalance } global:53 { errors cache 30 proxy . $(kubectl get svc -n istio-system istiocoredns -o jsonpath={. spec.clusterIP}) } EOF 部署示例应用 为了演示跨集群访问,在一个 Kubernetes 集群中部署 sleep 应用服务,在第二个集群中部署 httpbin 应用服务,然后验证 sleep 应用是否可以调用远程集群的 httpbin 服务。 1. 部署 sleep 服务到第一个集群 cluster1 中,执行如下命令: kubectlcreate namespace app1kubectllabel namespace app1 istio-injection=enabledkubectlapply -n app1 -f samples/sleep/sleep.yamlexportSLEEP_POD=$(kubectl get -n app1 pod -l app=sleep -ojsonpath={.items..metadata.name}) 1. 部署 httpbin 服务到第二个集群 cluster2 中,执行如下命令: kubectlcreate namespace app2kubectllabel namespace app2 istio-injection=enabledkubectlapply -n app2 -f samples/httpbin/httpbin.yaml 1. 获取集群 cluster2 的入口网关地址,如下所示: exportCLUSTER2_GW_ADDR=$(kubectl get svc --selector=app=istio-ingressgateway \ -n istio-system -ojsonpath="{.items[0].status.loadBalancer.ingress[0].ip}") 1. 为了让在集群 cluster1 中的服务 sleep 能够访问集群 cluster2 中的服务 httpbin,我们需要在集群 cluster1 中为服务 httpbin 创建一个服务条目 ServiceEntry 资源。服务条目 ServiceEntry 的主机名应该是..globalname,其中 name 和 namespace 分别对应于集群 cluster2 中的远程服务的名称和命名空间。 对于 *.global 域下服务的 DNS 解析,需要为这些服务分配一个 IP 地址,并且保证 .globalDNS 域中的每个服务在集群中必须具有唯一的 IP 地址。这些 IP 地址在 pod 之外是不可路由的。在这个例子中,我们将使用网段 127.255.0.0/16 来避免与其他的IP冲突。这些 IP 的应用流量将由 Sidecar 代理捕获并路由到适当的其他远程服务。 在集群 cluster1 中创建该 httpbin 服务对应的 ServiceEntry,执行如下命令: 上面的配置将会使集群 cluster1 中访问 httpbin.app2.global 的所有流量,包括访问它的任何端口的流量,都会被路由到启用了双向 TLS 连接的端点 :15443 上。 端口 15443 的网关是一个特殊的 SNI 感知的 Envoy 代理,它是在前面开始部分中作为多集群 Istio 安装步骤的一部分预先配置和安装的。进入端口 15443 的流量将在目标集群的适当内部服务的 pod 中进行负载均衡。 在集群 cluster1 下执行如下命令查看容器 istiocoredns,可以看到上述 ServiceEntry 的域名映射关系已经被加载: exportISTIO_COREDNS=$(kubectl get -n istio-system po -l app=istiocoredns -ojsonpath={.items..metadata.name})kubectllogs --tail 2 -n istio-system ${ISTIO_COREDNS} -c istio-coredns-plugin 执行结果如下所示: 1. 验证在集群 cluster1 中的 sleep 服务是否可以正常调用位于集群 cluster2 中的 httpbin 服务,在集群 cluster1 执行如下命令: kubectlexec $SLEEP_POD -n app1 -c sleep -- curl httpbin.app2.global:8000/headers 执行结果如下所示: 至此,集群 cluster1 与 cluster2 在多控制平面配置下完成了连通。 跨集群的版本路由 通过前面的文章,我们已经了解了 Istio 的很多功能,例如基本版本的路由等,可以在单个 Kubernetes 集群上很容易地实现。而很多真实的业务场景中,基于微服务的应用程序并非那么简单,而是需要在多个位置跨集群去分配和运行服务。那么问题就来了,是否 Istio 的这些功能同样可以很简单地运行在这些真实的复杂环境中呢? 下面我们将会通过一个示例来了解 Istio 的流量管理功能如何在具有多个控制平面拓扑的多集群网格中正常运行。 1. 首先,部署版本 v1 的 helloworld 服务到第一个集群 cluster1 中,执行如下命令: kubectlcreate namespace hellokubectllabel namespace hello istio-injection=enabledkubectlapply -n hello -f samples/sleep/sleep.yamlkubectlapply -n hello -f samples/helloworld/service.yamlkubectlapply -n hello -f samples/helloworld/helloworld.yaml -l version=v1 1. 部署版本 v2 与 v3 的 helloworld 服务到第二个集群 cluster2 中,执行如下命令: kubectlcreate namespace hellokubectllabel namespace hello istio-injection=enabledkubectlapply -n hello -f samples/helloworld/service.yamlkubectlapply -n hello -f samples/helloworld/helloworld.yaml -l version=v2kubectlapply -n hello -f samples/helloworld/helloworld.yaml -l version=v3 1. 如前面章节中所述,多控制平面下,需要使用以 .global 为后缀的 DNS 名称访问远程服务。 在我们的例子中,它是 helloworld.hello.global,所以我们需要在集群 cluster1 中创建服务条目 ServiceEntry 和目标规则 DestinationRule。服务条目 ServiceEntry 将使用集群 cluster2 的入口网关作为端点地址来访问服务。 通过使用以下命令在集群 cluster1 中创建 helloworld 服务对应的服务条目 ServiceEntry 和目标规则DestinationRule: 1. 在两个集群上创建目标规则。在集群 cluster1 中创建子集 v1 对应的目标规则,执行如下命令: 而在集群 cluster2 中创建子集 v2 和 v3 对应的目标规则,执行如下命令: 1. 创建虚拟服务以路由流量。 应用下面的虚拟服务将会使得来自用户 jason 对 helloworld 的流量请求指向位于集群 cluster2 中的版本 v2 和 v3,其中 v2 比例为 70%,v3 比例为 30%;来自任何其他用户对 helloworld 的流量请求都将转到位于集群 cluster1 中的版本 v1: 执行多次调用,可以从下面的执行结果中看出,上述流量路由的规则生效,这也说明了在多控制平面拓扑下,用于路由的规则定义与在本地集群的使用方式是一样的: 设置多集群网格的最简单方法是使用多控制平面拓扑,因为它没有特殊的网络要求。通过上述示例可以看出,在单个 Kubernetes 集群上运行的路由功能同样很容易地在多个集群中使用运行。 本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手
2020年09月
2020年08月
2020年07月