
大家好,我是人月聊IT,今天接着聊云原生技术实践和管理实践。
1. 云原生基本概念阐述
云原生(Cloud Native)这个概念如今在企业 IT 领域中备受关注,它是一系列云计算技术、企业管理方法的集合。
其中涵盖了 DevOps和持续交付、微服务(MicroServices)、敏捷基础设施(Agile Infrastructure)、康威定律(Conways Law)等内容,还包括根据商业能力对公司进行重组。云原生既包含了技术层面的微服务、敏捷基础设施等,也包含管理层面的持续交付、DevOps 等方面。
在传统的 IaaS 云阶段,主要是以服务器、存储、网络等基础设施形成虚拟化资源池,重点在于提供弹性计算和存储服务,这本质上是以资源为中心的模式。然而,到了云原生阶段,我们需要从以资源为中心转变为以应用为中心。这意味着要将业务的通用能力下沉到平台侧,从而助力企业实现应用的自动化,所以云原生阶段也常被称为应用云化。
但是大家看了上面内容,可能还是很难理解云原生核心底层逻辑。因此在这里我结合一张图进一步说明。
对于原生这个概念,经常听到的就是原生家庭这个说法。也就是说你家小孩不是中途你从福利院抱养来的,而是一出生就在你家里,或者再朝前面追溯下,从家庭开始有了备孕的想法,开始备孕后就在你们家里面。覆盖了从有了备孕的想法到生产,再到后续养育全过程。
那么对应到企业上云和云原生也是同样的道理。
对于传统的云计算和云数据中心,不管是阿里,华为的公有云服务,还是企业自己搭建的云数据中心。基本都是以资源为中心,将服务器,存储等物理资源转变为一种弹性计算和存储服务提供给开发团队使用。
那么开发团队什么用?
一般来说都是整个应用程序开发完成,已经编译构建为一个完整的部署包后才会对接到云平台。应用程序前面采用的开发框架,开发语言,架构开发团队都可以自由选择,底层云平台也不关心。这个和前面小孩抱养完全是一个道理,应用程序都已经做好了你才想到如何用云平台能力。
在这种情况下自然就容易出现问题,即我们期望云平台提供的按需使用,基于应用并发灵活扩展调度等能力实际你根本没有办法用起来的。因为你应用程序在设计阶段就没有参考云平台的标准要求进行设计和开发。真正系统出现类似性能问题了,往往还需要自己新购买虚拟机服务,再自己完成集群扩展。
这样显然是没有将云计算各种优势发挥出来的。
因此在云原生阶段我们更加强调云平台的能力要朝前面延伸,应用程序的设计和开发也需要面向云平台而设计。也就是说一个应用程序有了需求这个想法开始,云平台能力就应该介入,其中就包括了开发框架的搭建,开发组件的选择,开发和测试,部署和交付等各个环节。
简单来说就是应用面向云而生,在开发态就做好云能力的协同,自然到了运行态你才能够更好的云平台能力进行融合,做到灵活调度,自有扩展,充分享受云平台带来的安全,可靠,灵活扩展等优势。
简单来说,云原生让应用从长在云上变到了生在云上的过程。
抽象化:关注重心不断上移,从资源到服务,彻底不再关注资源层和底层技术细节。过程化:不只是管结果,而且要管理过程,管理整个软件开发全生命周期过程。
2. 微服务
首先,我们来谈谈微服务。微服务的基本概念是什么呢?
简单来说,微服务是一种架构风格,它将一个大型的单体系统拆分成多个小型的、高度独立自治的服务模块。
这里要注意,这个拆分不仅仅是在开发阶段,在运行阶段也是如此。这与传统单体架构有着关键的区别。传统单体架构是一个整体,就像一个大的整体建筑,各个部分紧密相连,难以单独进行维护和扩展。而微服务则像是由许多独立的小房子组成的社区,每个小房子(微服务)都能独立存在并且功能完整。
对于微服务拆分要注意到开发态和运行态都需要拆分。在运行态每个微服务都独立部署和运行。由于拆分后每个微服务独立小,一般微服务部署包都在200M以内,因此就更加容易托管和部署到容器里面。再通过kubernetes容器编排来实现微服务的灵活扩展调度。
拆分后的每个微服务模块都高度独立自治,每个微服务间只能通过轻量 http rest API 接口交互。我们要明白,这个拆分本身也包括数据库拆分,如果数据库没有拆分,那么仍然是单体架构,就类似于传统单体的组件化架构。
例如,一个电商系统,如果按照微服务架构来设计,可能会把用户管理、商品管理、订单管理等功能都拆分成独立的微服务。用户管理微服务负责用户的注册、登录、信息修改等功能;商品管理微服务负责商品的添加、删除、查询等操作;订单管理微服务则处理订单的创建、支付、发货等流程。
这样,每个微服务都可以独立开发、部署和扩展,不会因为一个模块的问题而影响整个系统的运行。
在微服务架构中,Spring Cloud 是一个非常流行的开源微服务开发框架。它为构建分布式系统提供了一系列的工具和组件,方便开发人员快速构建和部署微服务应用。里面一般包括了服务注册发现,限流熔断,安全,微服务网关,链路监控等关键技术组件。
当然对于微服务开发框架的选择可以参考下图。
3. 服务网格
服务网格也是云原生架构中的重要组成部分。那么,什么是服务网格呢?服务网格是一种专门为微服务架构设计的基础设施层,它的主要目标是在微服务架构下实现去中心化的微服务治理。
为什么服务网格可以去中心化呢?
其核心逻辑在于它将微服务之间的通信和治理逻辑从微服务本身中分离出来,放到一个独立的代理层(也就是服务网格)中进行处理。这样,微服务就不需要再关心诸如服务发现、负载均衡、熔断、限流等复杂的治理功能,只需要专注于自身的业务逻辑即可。
其次,原来的微服务治理涉及到安全,日志,流量管控,往往都需要类似微服务网关或API网关来实现。这种网关本身又是一个中心化的架构,所有的流量都需要通过网关,网关拦截流量后自然容易对流量进行安全,日志,限流等各种管控措施。但是带来的问题也很明显,就是如果这个集中化网关出现故障或性能问题如何解决?
因此简单来理解就是服务网格通过sidecar边车这张方式,将流量的拦截和管控下移到了各个微服务容器实例内部。从而实现了流量的去中心化架构。当然我原来也讲过,这种去中心化基本是针对微服务间的东西流量来谈的,如果一个微服务应用本身还涉及到前端访问请求或对外开放能力接口,这个本身也是南北流量,南北流量的问题服务网格本身也解决不了。
下图是API网关和ServiceMesh架构的一个对比。
可以看到API网关的大部分能力都可以被SericeMesh来替代。
唯一的就是上图提到的南北流量和对外统一接口暴露问题,这个仍然需要处理,即实现最基本的Proxy和南北流量分发的能力。
在服务网格的开源技术实现方面,比较著名的有 Istio 等。Istio 通过在微服务之间插入代理(Envoy)来实现服务网格的功能。这些代理可以拦截微服务之间的所有网络流量,从而实现对流量的管理和监控。
从这个图可以看到微服务之间虽然通过下发的Sidecar边车模式来实现了点对点的交互,但是所有的微服务模块本身仍然会接入到上层的控制中心或控制面板。
微服务之间交互和协同,其核心的消息和数据并不需要通过控制中心进行中转和协同,但是当有统一的规则形成的时候,仍然需要通过控制中心进行下发。同时控制中心也需要采集各个微服务中心的核心数据进行分析,优化自身的控制能力。
包括类似redis,hdfs, kurbernetes等所有的分布式集群,实际都可以看到一个大的分布式集群中仍然会有管理节点,实现基本的管理和控制能力。
控制流和数据流分离,既实现点对点网状连接,又实现基础的管控能力,是分布式技术发展过程中给出的一个重要启示。也就是说节点和节点之间的交互和协同并不会因为多了控制中心而变成多层级下的复杂信息协同链。
例如,在一个大型的微服务架构的企业应用中,有很多个微服务相互交互,如果没有服务网格,每个微服务都要自己实现服务发现、流量控制等功能,这会使微服务变得非常复杂。而有了服务网格,就像是给这些微服务之间的通信建立了一个统一的交通管理系统,让通信变得更加有序和高效。
4. 无服务器化(Serverless)
接下来,我们了解一下无服务器 (Serverless)。无服务器化的基础概念和逻辑是什么呢?无服务器并不是说真的没有服务器,而是指开发人员在开发应用时不需要再关心服务器的管理和运维。
无服务器的分层架构体系,底层是 BaaS 层(Backend as a Service,后端即服务),上面是 FaaS 层(Function as a Service,函数即服务)等。
在传统的应用开发部署交付过程中,开发完成后的部署包要部署到一个应用中间件容器里面。然而,到了 Serverless 阶段,这个中间件容器就不再需要了。
比如说,一个简单的文件处理应用,在传统架构下,我们需要搭建服务器,安装中间件,然后部署应用。而在 Serverless 架构下,我们只需要编写处理文件的函数,然后将其上传到 Serverless 平台,平台会根据函数的调用情况自动分配资源来执行这个函数,大大简化了开发和部署的流程。
而随着云原生技术的发展,特别是云原生中的微服务,容器技术也在不断发展。公有云PaaS平台发展到了围绕容器+技术服务为核心的云原生PaaS平台。在这个过程中传统的单体应用为了获得更好的性能和可扩展性,也转变从单体拆分为更小的微服务。
这个发展阶段可以参考下图:
我们可以将整个演进过程分为三个阶段。
在传统单体架构阶段,往往只会使用到云平台提供的虚拟化资源池,提供弹性计算和弹性存储能力。应用自己申请虚拟机,然后安装环境,管理环境。同时应用在开发完成后也是人工来完成将测试通过的版本部署到虚拟机环境中去。
而到了云原生PaaS平台阶段,底层的资源池变成了更加轻量化的容器,同时上层的单体已经拆分为了多个独立松耦合的微服务,中间件PaaS层实现两个能力。
其一是类似K8s实现的容器资源编排和调度;其二是实现共性的技术服务能力提供,其中包括了数据库,消息,缓存等各种技术服务能力。
为了更好地衔接上层微服务和底层容器云资源,可以通过DevOps持续集成和交付最佳实践和工具集的整合,来完成整个从需求,开发,测试,集成,交付过程的全面自动化。也就是说整个编译,构建,打包,部署的动作全部由DevOps过程自动完成。
到了ServerLess阶段,实际看到又带来如下变化。
其一是底层的容器变成无状态化容器,更加轻量,也更加容易快速创建和销毁;其二是上层的微服务能力进一步拆分为各个独立,无状态的云函数或服务;其三是PaaS层的技术服务能力进一步增强,构建完整的BaaS层。
5. 不可变基础设施
什么叫不可变基础设施呢?
我们之前讲过,就是一个容器镜像变成实例运行后,就不能再做任何修改。如果要修改,应该是销毁老的容器实例,再生成一个新的实例挂接到容器集群。这就好比是一个已经做好的蛋糕(容器镜像实例),如果想要改变它的口味或者外观(进行修改),不能直接在原来的蛋糕上动手脚,而是要重新做一个新的蛋糕(生成新的实例)。
我在前面的文章对这个技术要素谈得比较少。不可变基础设施里的“不可变”非常类似于程序设计中的“不可变”概念。程序设计中不可变变量(Immutable Variable)就是在完成赋值后就不能发生更改,只能创建新的来整体替换旧的。由于具有这样的特性这种变量可以在并发环境下安全地使用。对于基础设施的不可变性,最基本的就是指运行服务的服务器在完成部署后,就不再进行更改。
我们还是举个容器云的例子来说明下。
比如一个软件程序V1.0版本,我们制作了一个容器镜像,并将其通过kurbernetes部署下去形成容器实例。那么到了V1.1版本,我们是否对这个容器实例进行变更和修改?
实际情况并不是如此。
对于V1.1版本的部署,并部署对V1.0版本容器实例进行修改,而是新部署了一个新的V1.1版本的容器实例,在部署完成后将用户访问切换到新的V1.1版本上面。
也就是说V1.0版本的容器实例一旦生成并使用,其本质就是不可变的,只能是创建的新的容器来替代旧的,而不是直接对旧的容器进行修改。
这种方式可以保证基础设施的一致性和稳定性,避免因为对运行中的实例进行修改而可能带来的各种风险,比如配置冲突、环境不一致等问题。
还有一个类似不可变基础设施的概念,在这里也做一个专门的说明。即在云原生12要素和原则里面也提到。在进行持续集成和交付的时候,有一个重要原则就是一次构建,多处运行。
大家都知道,我们的应用程序在完成了编译,构建和打包和部署后,往往需要经过单元测试,集成测试,UAT测试后才会最终部署到生产环境。而一次构建,多处运行的要求就是程序最终构建成镜像后交付给测试环境,那么最终部署到生产环境的也应该是当初的镜像版本。而不是到了生产部署的时候再重新去编译和构建。
这就确保了测试的版本就是生产环境最终部署的版本。对于环境参数,数据库连接等都不应该剥离在部署包外面单独管理。
6. 声明式API
在云原生架构中,声明式API 是一种重要的编程范式,它允许开发者通过声明期望的最终状态,而不是详细描述实现该状态的具体步骤。与命令式API不同,声明式API关注的是“要做什么”,而不是“如何做”,从而简化了系统的配置和管理。
现在我们进一步解释一下什么是声明式 API。
在声明式 API 中,我们的部署操作不是直接命令形式的,而是所有操作内容都在配置文件里面。例如,在 kubernetes 的部署配置文件中,我们可以定义容器的镜像、资源分配、端口映射等各种信息。然后 kubernetes 拿到这个配置文件后,会根据文件中的内容进行相关操作。
以下是一个典型的 Kubernetes Deployment 文件示例,展示了如何定义一个部署的容器、副本数量(节点数)、资源限制(CPU 和内存)等关键信息。这个示例使用 YAML 格式编写,适用于部署一个简单的 Web 应用。
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-web-app
labels:
app: my-web-app
spec:
replicas: 3 # 定义副本数量(节点数)
selector:
matchLabels:
app: my-web-app
template:
metadata:
labels:
app: my-web-app
spec:
containers:
- name: web-container
image: nginx:1.19.0 # 使用的容器镜像
ports:
- containerPort: 80 # 容器暴露的端口
resources: # 定义资源限制
requests: # 请求的资源量
memory: "64Mi"
cpu: "250m"
limits: # 资源上限
memory: "128Mi"
cpu: "500m"
env: # 定义环境变量
- name: ENVIRONMENT
value: "production"这样做有什么好处呢?
首先,命令操作也是可以纳入配置管理和版本管理的。这就意味着我们可以像管理代码一样管理我们的部署操作,方便进行回滚、审计等操作。而且,通过配置文件来定义操作,更加清晰明了,便于团队成员之间的协作和理解。
7. 前后端分离
我们再来看看应用开发中的前后端分离。前后端分离的基本概念是将前端和后端的开发工作分开,让前端和后端各自独立进行开发、部署和维护。
前后端分离有很多优点。我们可以看到,它更加容易贯彻 SOA 服务重用和领域建模思想。后端提供能力,就像一个强大的后台支持中心,提供各种数据和业务逻辑处理能力;前端组装能力,根据用户的需求和交互场景,灵活地组合后端提供的能力。
前后端分离虽然没有出现在标准的微服务架构定义中,但是却应该作为微服务设计开发的一个最佳实践。前后端分离实际和SOA思想里面的服务分层思路相对一致,至少SOA跨系统间的服务分层,服务组织思想进入到单个微服务内部的功能实现机制上。
前后端分离实际上当前是包括了技术和团队组织两个方面,在技术上前端和后端完全拆分,都可以独立编译打包,在团队上形成前端和后端角色,不再是一个人前后贯通实现。
为何前后端分离设计如此重要,总结如下:
更加容易贯彻SOA服务重用和领域建模思想,后端提供能力,前端组装能力 对需求变化更加敏捷和优化,后端相对稳定,前端灵活调整
除了这两点,可以看到前后端分离方便进行可复用的领域服务和API接口积累,这个能力可以作为企业核心的中台服务层能力。
例如,在一个新闻资讯类的应用中,后端负责管理新闻的采集、存储、分类等功能,前端则负责将新闻以不同的形式(列表、详情页等)展示给用户,并且根据用户的浏览习惯提供个性化的推荐等功能。
7.面向持续集成和持续交付而设计
持续集成和持续交付本身也是云原生里面的关键技术要素。在业务需求不断变化的今天,如何快速地响应业务需求变化并快速交付可用的版本是一个重要的问题。这里面包含了大量的实践。
例如,敏捷开发让开发过程更加灵活和高效,微服务拆分使得各个功能模块可以独立开发和部署,可复用的服务库积累则可以减少重复开发的工作量。到了云原生阶段,由于传统单体拆分为更小的微服务,微服务数量大,交付频度高,涉及到的底层容器数量也灵活多变。
DevOps最佳实践-处理好敏捷研发,持续集成和容器云三者集成
这就需要一个自动化的实现应用持续部署交付的能力,这也就是我们经常谈到的 Devops 能力。我们要明白,Devops 首先应该理解为一个过程实践,其次才是技术实践。它贯穿了从代码开发、构建、测试、部署到运维的整个生命周期,通过自动化的流程和工具链,实现快速、高效、稳定的软件交付。
流水线设计属于持续交付过程域中的一个关键内容,其核心还是为了持续集成服务。那么我们首先回顾下流水线作业解决什么问题?
简单来说就是解决自动化的持续集成问题,那么持续集成本身又包括哪些关键内容?即我们常说的自动化编译构建,自动化部署,自动化测试。而在和容器技术结合后可以看到在编译构建完成后自动化部署进行了拆分,即部署动作变化为了首先是自动化的打包即制作容器镜像,然后才是自动化部署,部署部署基于部署包的,而是基于制品库中的容器镜像的。
说这么多,可以看到,在DevOps中的流水线设计更多的是解决开发人员的自动化问题,即开发只需要check in自测通过的代码到配置管理库,那么后续的所有事情都自动化完成,最终部署到测试环境供测试人员测试。开发人员不用关心编译构建过程,不用关心测试服务器包括测试环境数据库,中间件的安装部署等一系列的问题。
同时由于一系列的自动化操作,也让代码从检入到交付测试的周期缩短了,在周期缩短后交付频度也可以进一步提升以加快迭代速度。
在云原生架构下,这些技术和管理实践相互配合、相互促进,共同为企业构建更加灵活、高效、可靠的应用系统提供了有力的支持。我们要根据企业自身的业务需求和发展战略,合理地运用这些云原生的技术和管理实践,才能在数字化转型的浪潮中取得优势。