云原生(Cloud Native)起源与发展
云原生的概念最早开始于 2010 年,在当时 Paul Fremantle 的一篇博客中被提及,他一直想用一个词表达一种架构,这种架构能描述应用程序和中间件在云环境中的良好运行状态。
2013 年 Matt Stine 在推特上迅速推广云原生概念,并在 2015 年《迁移到云原生架构》一书中定义了符合云原生架构的特征:12 因素、微服务、自服务、基于 API 协作、扛脆弱性。
2015 年由 Linux 基金会发起了一个 The Cloud Native Computing Foundation(CNCF) 基金组织,CNCF基金会的成立标志着云原生正式进入高速发展轨道
到了 2017 年, 云原生应用提出者之一的 Pivotal 在其官网上将云原生的定义概括为 DevOps、持续交付、微服务、容器四大特征,这也成了很多人对 Cloud Native 的基础印象。
而到 2018 年,随着 Service Mesh 的加入。
解读:概念随着新的技术发展而演化
- 第一阶段:容器化封装+自动化管理+面向微服务
- 第二阶段:DevOps、持续交付、微服务、容器
- 第三阶段:DevOps、持续交付、容器、服务网格、微服务、声明式API
Cloud Native,从词面上拆解其实就是 Cloud 和 Native,也就是云计算和土著的意思——云计算上的原生居民,即天生具备云计算的亲和力。
首先从 Cloud 来理解,云可以看作是一种提供稳定计算存储资源的对象。为了实现这一点,云提供了像虚拟化、弹性扩展、高可用、高容错性、自恢复等基本属性,这是云原生作为一种云计算所具备的第一层含义。第二层要从 Native 来看,云原生和在云上跑的传统应用不同。一些基于公有云搭建的应用是基于传统的 SOA 架构来搭建的,然后再移植到云上去运行,那么这些应用和云的整合非常低。
为什么低呢?云作为一种分布式架构,其“土著居民”也应该是基于分布式架构设计出来的,而微服务或 Serverless 这种将服务或函数拆分成一个个模块的松耦合系统,天然具备分布式设计的属性。这是 Native 的第一种表现。
其次云作为一种 PaaS 服务,这位“土著居民”从出生(设计)到成长(开发),再到生活(部署)都应该是基于云的理念来实现的,那么就需要一套自动化的开发流程 CI/CD 来实现。这是 Native 的第二种表现。
而最后“土著居民”的特点希望做到能够适应所有云端,都能做到无缝的运行和连接。
关键点技术
下面介绍云原生架构的一些关键技术点。涉及内容由微服务、分布式常见架构设计(性能、数据一致性、可扩展性、高可用)、研发流程、DevOps、组织文化等,可以根据目录选择性的看看,基本上都是一些介绍,详细的设计可以查看相关文档进一步了解。
1. 微服务
Martin Fowler 与 James Lewis 共同提出了微服务的概念,定义了微服务架构是以开发一组小型服务的方式来开发一个独立的应用系统,每个服务都以一个独立进程的方式运行,每个服务与其他服务使用轻量级(通常是 HTTP API)通信机制。这些服务是围绕业务功能构建的,可以通过全自动部署机制独立部署,同时服务会使用最小规模的集中管理(例如 Docker)能力,也可以采用不同的编程语言和数据库。
1)优势
- 敏捷开发帮助我们减少浪费、快速反馈,以用户体验为目标。
- 持续交付促使我们更快、更可靠、更频繁地改进软件;基础设施即代码(Infrastructure As Code)帮助我们简化环境的管理。
2)什么时候开始微服务架构
- 几乎所有成功的微服务架构都是从一个巨大的单体架构开始的,并且都是由于单体架构太大而被拆分为微服务架构。
- 在所一开始就构建微服务架构的故事中,往往都有人遇到了巨大的麻烦。
3)如何决定微服务架构的拆分粒度
微服务架构中的“微”字,并不代表足够小,应该解释为合适。
4)单体架构 VS 微服务架构对比
流行的微服务框架:spring-cloud/dubbo。
2. 敏捷基础设施及公共基础服务
敏捷基础设施及公共基础服务是微服务架构成败的关键因素之一,能够简化业务开发。
1)敏捷基础设施的目标
- 标准化:所有的基础设施最好都是标准的。
- 可替换:任意节点都能够被轻易地创建、销毁、替换。
- 自动化:所有的操作都通过工具自动化完成,无须人工干预。
- 可视化:当前环境要做到可控,就需要对当前的环境状况可视。
- 可追溯:所有的配置统一作为代码进行版本化管理,所有的操作都可以追溯。
- 快速:资源申请及释放要求秒级完成,以适应弹性伸缩和故障切换的要求。
2)基于公共基础服务的平台化
- 平台化是指利用公共基础服务提升整体架构能力。
- 公共基础服务是指与业务无关的、通用的服务,包括监控服务、缓存服务、消息服务、数据库服务、负载均衡、分布式协调、分布式任务调度等。
3)常见的平台服务
- 监控告警服务
- 分布式消息中间件服务
- 分布式缓存服务
- 分布式任务调度服务
3. 分布式架构 – 可用性设计
可用性(Availability)是关于系统可以被使用的时间的描述,以丢失的时间为驱动(Be Driven by Lost Time)。
可用性公式:A=Uptime /(Uptime+Downtime)。其中,Uptime 是可用时间,Downtime 是不可用时间。
1)什么降低了可用性
- 发布
- 故障
- 压力
- 外部依赖
2)设计阶段考虑如下几个比较重要的方法
- 20/10/5,设计系统的时候,以实际流量的 20 倍来设计;开发系统的时候,以实际流量的 10 倍来开发系统;发布系统的时候,以实际流量的 5 倍来部署。这只是一个通用的原则,可以根据实际情况来确定,不需要严格按照倍数来执行。
- Design for failure,预测可能发生的问题,做好预案。
3)容错设计
如果说错误是不可避免或者难以避免的,那么我们应该换一个思路,保证错误发生时,我们可以从容应对。
- 消除单点
- 特性开关
- 服务分级
- 降级设计
- 超时重试
4)隔离策略
隔离是为了在系统发生故障时,限制传播范围和影响范围,特别要注意非核心系统的故障对核心系统的影响。
- 线程池隔离
- 进程隔离
- 集群隔离
- 用户隔离
- 租户隔离
- 逻辑隔离
- 物理隔离
- 混合隔离
5)熔断器
熔断器模式(Circuit Breaker Patten)的原理类似于家里的电路熔断器的原理。当发生短路或者超负荷时,熔断器能够主动熔断电路,以避免灾难发生。
Spring Cloud Hystrix 提供了熔断器、线程隔离等一系列服务保护的能力,使用起来非常简单,引入依赖的 JAR 包,通过简单的注解即可实现。
6)流控设计
- 限流算法。限流也就是调节数据流的平均速率,通过限制速率保护自己,常见的算法有:
- 固定窗口算法(fixed window)。
- 漏桶算法(Leaky Bucket):漏桶算法主要目的是控制数据注入网络的速率,平滑网络上的突发流量。
- 令牌桶算法(token bucket):令牌桶控制的是一个时间窗口内通过的数据量,通常我们会以 QPS、TPS 来衡量。
- 流控策略
- 请求入口处。
- 业务服务入口处。
- 公共基础服务处。
- 基于 Guava 限流:Guava 是 Google 提供的 Java 扩展类库,其中的限流工具类 RateLimiter 采用的就是令牌桶算法,使用起来非常简单。
- 基于 Nginx 限流。
7)容量预估
互联网公司普遍采用全链路压测的方式,来进一步预估容量。
8)故障演练
- 随机关闭生产环境中的实例。
- 让某台机器的请求或返回变慢,观察系统的表现,可以用来测试上游服务是否有服务降级能力,当然如果响应时间特别长,也就相当于服务不可用。
- 模拟 AZ 故障,中断一个机房,验证是否跨可用区部署,业务容灾和恢复的能力。
- 查找不符合最佳实践的实例,并将其关闭。
9)数据迁移
- 逻辑分离,物理不分离。
- 物理分离 。
4. 分布式架构 – 可扩展设计
- 水平扩展,指用更多的节点支撑更大量的请求。
- 横向扩展通常是为了提升吞吐量,响应时间一般要求不受吞吐量影响即可。
1)AKF 扩展立方体
2)如何扩展数据库
- X 轴扩展——主从复制集群
- Y 轴扩展——分库、垂直分表
- Z 轴扩展——分片(sharding)
5. 分布式架构 – 性能设计
1)性能指标
- 响应时间(Latency),就是发送请求和返回结果的耗时。
- 吞吐量(Throughput),就是单位时间内的响应次数。
- 负载敏感度,是指响应时间随时间变化的程度。例如,当用户增加时,系统响应时间的衰减速度。
- 可伸缩性,是指向系统增加资源对性能的影响。例如,要使吞吐量增加一倍,需要增加多少服务器。
2)如何树立目标
- 通过缓存提升读性能。
- 通过消息中间件提升写性能。
6. 分布式架构 – 一致性设计
1)事务的四大特征
- 原子性(Atomicity)。
- 一致性(Consistency)是指通过事务保证数据从一种状态变化到另一种状态。
- 隔离性(Isolation)是指事务内的操作不受其他操作影响,当多个事务同时处理同一个数据的时候,多个事务之间是互不影响的。
- 持久性(Durability)是指事务被提交后,应该持久化,永久保存下来。
2)CPA 定理
该定理认为对于一个分布式计算系统来说,不可能同时满足以下三点:
- 一致性(Consistence)
- 可用性(Availability)
- 分区容错性(Partition tolerance)
分布式意味着必须满足分区容错性,也就是 P,因此一般只能是 AP 或 CP。
3)BASE 理论
BASE 理论的核心思想是:如果无法做到强一致性,或者做到强一致性要付出很大的代价,那么应用可以根据自身业务特点,采用适当方式来使系统达到最终一致性,只要对最终用户没有影响,或者影响是可接受的即可。
- BA:Basically Available,基本可用。
- S:Soft state,软状态。
- E:Eventually consistent,最终一致。
4)Quorum 机制(NWR 模型)
如果多个服务分别向三个节点写数据,为了保证强一致,就必须要求三个节点全部写成功才返回;同步写三个节点的性能较低,如果换一个思路,一致性并不一定要在写数据的时候完成,可以在读的阶段再决策,只要每次能读到最新版本即可。
Quorum 机制就是要满足公式 W+R>N,式中 N 代表备份个数,W 代表要写入至少 W 份才认为成功,R 表示至少读取 R 个备份。
5)租约机制(Lease)
如果现在我们有三个节点,为了实现一致性,要确保有且只有一个是 Leader,另外两个为 Follower,只有 Leader 是可写的,Follower 只能读。管理节点 M 通过心跳判断各个节点的状态,用 M 去指定 Leader,一旦 Leader 死掉,就可以重新指定一个 Leader。
6)脑裂问题
- 一种是采用投票机制(Paxos 算法)。
- 一种是采用租约机制——Lease,租约机制的核心就是在一定时间内将权力下放。
7)分布式系统的一致性分类
- 建立多个副本。可以把副本放到不同的物理机、机架、机房、地域,当一个副本失效时,可以让请求转到其他副本。
- 对数据进行分区。复制多个副本解决了读的性能问题,但是无法解决写的性能问题。
8)以数据为中心的一致性模型
从数据存储的角度出发的,包括数据库、文件等。
- 严格一致性(Strict Consistency)
- 顺序一致性(Sequential Consistency)
- 因果一致性(Causal Consistency)
9)以用户为中心的一致性模型
以下一致性模型适应的场景为不会同时发生更新操作,或者同时发生更新操作时能够比较容易地化解。因为这里的数据更新默认有一个与之关联的所有者,此所有者拥有唯一被允许修改数据的权限,可以按照用户 ID 进行路由。
- 单调读一致性(Monotonic-read Consistency)
- 单调写一致性(Monotonic-write Consistency)
- 写后读一致性(Read-your-writes Consistency)
- 读后写一致性(Writes-follow-reads Consistency)
10)业界常用的一致性模型
- 弱一致性:写入一个数据 a 成功后,在数据副本上可能读出来,也可能读不出来。不能保证每个副本的数据一定是一致的。
- 最终一致性(Eventual Consistency):写入一个数据 a 成功后,在其他副本有可能读不到 a 的最新值,但在某个时间窗口之后保证最终能读到。
- 强一致性(Strong Consistency):数据 a 一旦写入成功,在任意副本任意时刻都能读到 a 的最新值。
11)如何实现强一致性
- 两阶段提交
- 三阶段提交(3PC)
12)如何实现最终一致性
- 重试机制:超时时间,重试的次数,重试的间隔时间,重试间隔时间的衰减度。
- 本地记录日志。
- 可靠事件模式。
- Saga 事务模型:又叫 Long-running-transaction,核心思想是把一个长事务拆分为多个本地事务来实现,由一个 Process manager 统一协调。
- TCC 事务模型:两阶段提交是依赖于数据库提供的事务机制,再配合外部的资源协调器来实现分布式事务。TCC(Try Confirm Cancel)事务模型的思想和两阶段提交虽然类似,但是却把相关的操作从数据库提到业务中,以此降低数据库的压力,并且不需要加锁,性能也得到了提升。
7. 十二因素
12 因素应用是一系列云原生应用架构的模式集合。这些模式可以用来说明什么样的应用才是云原生应用,关注速度、安全、通过声明式配置扩展、可横向扩展的无状态/无共享进程以及部署环境的整体松耦合。
在 12 因素的背景下,应用指的是独立可部署单元。组织中经常把一些互相协作的可部署单元称作一个应用。
- 基准代码,一份基准代码,多份部署,使用 GIT 或者 SVN 管理代码,并且有明确的版本信息。
- 依赖,显示声明依赖。
- 配置:环境中存储配置。
- 后端服务:把后端服务当作附加资源。后端服务是指程序运行所需要的通过网络调用的各种服务,如数据库(MySQL、CouchDB)、消息/队列系统(RabbitMQ、Beanstalkd)、SMTP 邮件发送服务(Postfix),以及缓存系统(Memcached)。
- 构建、发布、运行:严格分离构建和运行。
- 进程,以一个或多个无状态进程运行应用,如果存在状态,应该将状态外置到后端服务中,例如数据库、缓存等。
- 端口绑定,通过端口绑定提供服务,应用通过端口绑定来提供服务,并监听发送至该端口的请求。
- 并发,通过进程模型进行扩展,扩展方式有进程和线程两种。进程的方式使扩展性更好,架构更简单,隔离性更好。线程扩展使编程更复杂,但是更节省资源。
- 易处理,快速启动和优雅终止可最大化健壮性,只有满足快速启动和优雅终止,才能使服务更健壮。
- 开发环境与线上环境等价,尽可能保持开发、预发布、线上环境相同。
- 日志,把日志当作事件流,微服务架构中服务数量的爆发需要具备调用链分析能力,快速定位故障。
- 管理进程,把后台管理任务当作一次性进程运行,一些工具类在生产环境上的操作可能是一次性的,因此最好把它们放在生产环境中执行,而不是本地。
8. 研发流程
1)为什么选择 DevOps
能提高交付速度、更新频率,这两点是衡量一个公司能力的重要指标。
2)Gartner 提出的 DevOps 模型
文化、技术、过程和人,其中团队文化才是最难改变的,技术方面包括基础设施即代码、全局监控、持续监控。
3)自动化测试
- 自动化测试可以代替人工测试。
- 测试成了全栈工程师的工作,因为不沟通才是最有效率的沟通。
4)Code Review
- 提升代码易读性。
- 统一规范、标准。
- 技术交流,提升能力。
- Code Review 原则:以发现问题为目标,团队开放、透明,整个 Code Review 的过程对事不对人,不设置惩罚。
- 线上线下接合的方式,长期线上,定期线下。
5)流水线
持续交付:降低交付周期,通过自动化工具实现设计、开发、测试、发布、运维各个阶段的重复性工作,通过工具实现标准化、规范化,降低出错概率。
6)开发人员自服务
对于开发过程来说,少交流、少沟通、少开会就是最高效的。
- 高覆盖率的自动化测试
- 全面的监控
- 持续交付流水线
- 敏捷基础设施
- 自动化/智能化运维
- 好的架构
- 全栈工程师
- 服务型管理
- 工程师文化
- 信任文化
- 分享文化
7)代码即设计
- 模糊敏捷研发流程阶段性:业务需求太多和技术变化速度太快。
- 整个进化设计需要简单的架构+持续集成+重构+整个研发流程设计。
9. 团队文化
团队文化就好比土壤,要培养什么样的员工,就要有适合他的土壤。
1)团队规模导致的问题
- 缺乏信任。由于人数众多,难于管理,只能通过制度、流程、规范、绩效约束。
- 没有责任感。高层管理者忙着开各种决策会议。
- 部门墙。跨部门协调还不如与第三方合作。
- 不尊重专业人士。当所有的生杀大权都掌握在少数人手中的时候。
- 管理层级太深。管理层级太深导致的问题很多。
2)组织结构 – 康威定律
设计系统的组织,其产生的设计和架构等价于组织间的沟通结构。通俗来讲,就是什么样的团队结构,就会设计出什么样的系统架构。如果将团队拆分为前端、后端、平台、数据库,那么系统也会按照前端、后端、平台、数据库结构隔离。
- 第一定律:Communication dictates design,即组织沟通方式会通过系统设计呈现。
- 第二定律:There is never enough time to do something right,but there is always enough time to do it over,即时间再多,一件事情也不可能做得完美,但总有时间做完一件事情。
- 第三定律:There is a homomorphism from the linear graph of a system to the linear graph of its design organization,即线型系统和线型组织架构间有潜在的异质同态特性。
- 第四定律:The structures of large systems tend to disintegrate during development,qualitatively more so than with small systems,即大的系统组织总是比小系统更倾向于分解。
3)“沟通漏斗”是指工作中团队沟通效率下降的一种现象
如果一个人心里想表述事项目标的 100%,当你在众人面前、在开会的场合用语言表达时,你说出来的只剩下 80%。而进入别人的耳朵时,由于文化水平、知识背景等关系,只留存了 60%。实际上,真正被别人理解了大概只有 40%。等到这些人遵照领悟的 40% 具体行动时,只具备了当初事项目标的 20% 了。三个月后信息只剩下 5% 了。
4)环境氛围
- 公开透明的工作环境.
- 学习型组织:让团队拥有共同愿景、目标,并持续学习。
- 减少无效的正式汇报。
- 高效的会议:缩小会议范围,常规会议不应该超过 45 分钟;限制“意见领袖”的发言时长;会议中不允许开小差;会议中的分歧不应该延伸到会议之外。
10. Serverless
随着以 Kubernetes 为代表的云原生技术成为云计算的容器界面,Kubernetes 成为云计算的新一代操作系统。面向特定领域的后端云服务 (BaaS) 则是这个操作系统上的服务 API,存储、数据库、中间件、大数据、 AI 等领域的大量产品与技术都开始提供全托管的云形态服务,如今越来越多用户已习惯使用云服务,而不是自己搭建存储系统、部署数据库软件。
当这些 BaaS 云服务日趋完善时,Serverless 因为屏蔽了底层设施的运维复杂度,让开发人员可以将更多精力用于业务逻辑设计与实现,而逐渐成为云原生主流技术之一。Serverless 计算包含以下特征:
- 全托管的计算服务,客户只需要编写代码构建应用,无需关注同质化的、负担繁重的基础设施开发、运维、安全、高可用等工作。
- 通用性,结合云 BaaS API 的能力,能够支撑云上所有重要类型的应用。
- 自动的弹性伸缩,让用户无需为资源使用提前进行容量规划。
- 按量计费,让企业使用成本得有效降低,无需为闲置资源付费。
函数计算 (Function as a Service) 是 Serverless 中最具代表性的产品形态。通过把应用逻辑拆分多个函数,每个函数都通过事件驱动方式触发执行,例如当对象存储 (OSS) 中产生的上传 / 删除对象等事件, 能够自动、可靠地触发 FaaS 函数处理且每个环节都是弹性和高可用的,客户能够快速实现大规模数据的实时并行处理。同样的,通过消息中间件和函数计算的集成,客户可以快速实现大规模消息的实时处理。
Serverless 不足的地方:
- 成功案例太少
- 很难满足个性化
- 缺乏行业标准
- 初次访问性能差
- 缺乏开发调试工具
11. Service Mesh 技术
Service Mesh 是分布式应用在微服务软件架构之上发展起来的新技术,旨在将那些微服务间的连接、安全、流量控制和可观测等通用功能下沉为平台基础设施,实现应用与平台基础设施的解耦。这个解耦意味着开发者无需关注 微服务相关治理问题而聚焦于业务逻辑本身,提升应用开发效率并加速业务探索和创新。换句话说,因为大量非功能性从业务进程剥离到另外进程中,Service Mesh 以无侵入的方式实现了应用轻量化,下图展示了 Service Mesh 的 典型架构:
在这张架构图中,Service A 调用 Service B 的所有请求,都被其下的 Proxy(在 Envoy 中是 Sidecar) 截获, 代理 Service A 完成到 Service B 的服务发现、熔断、限流等策略,而这些策略的总控是在 Control Plane 上配置。
服务网格的技术发展上数据平面与控制平面间的协议标准化是必然趋势。控制平面可以认为是注册中心及管理配置面板;数据平面可以认为是由服务化框架依赖的组件独立而成的一个进程,数据平面代理业务服务的注册发现、负载均衡、容错等能力。 为什么需要 Service Mesh:
- 在微服务架构中,让开发人员感觉不到微服务之间的通信。
- 当服务数量越来越多,升级微服务框架变得越来越复杂的时候,微服务框架不可能一直不变且没有 bug。
- Service Mesh 则从业务进程集成客户端的方式演进为独立进程的方式,客户端变成了一个独立进程。
- 对这个独立进程升级、运维要比绑在一起强得多。
- 微服务架构更强调去中心化、独立自治、跨语言。Service Mesh 通过独立进程的方式进行隔离,低成本实现跨语言。
- 每个服务独立占用一个容器,将服务、依赖包、操作系统、监控运维所需的代理打包成一个镜像。这种模式促成了 Service Mesh 的发展,让 Service Mesh 实现起来更容易。
12. 云原生架构成熟度模型
由于云原生架构包含了 6 个关键架构维度(简写为 SESORA,Service + Elasticity + Serverless + Observability + Resilience + Automation),因此我们先定义关键维度的成熟度级别:
参考自 快速了解云原生架构