我叫鄢晶,来自众安科技,这个生僻字念 yan,跟北京旧称“燕京”发音一样。我加入众安,一开始接触的是应用层面的开发,完整经历了保险核心系统从单体架构到微服务架构、容器化架构的转型。后来由于对云原生方向比较感兴趣,加入基础架构团队,主导了众安最新一代运维平台的建设,平台发展至今已是第四代。
下面,正式进入分享内容,第一部分介绍众安基础架构的演进路线。
基础架构演进路线
互联网保险行业,从刚孵化到现在,业务特性越来越复杂,现在已慢慢成为保险行业未来的发展方向。众安的运维体系可以说就像是一部运维编年史,众安成立之初也是“人肉运维”,业务部署在云服务之上,运维手段是通过“堆人肉”来做的。随着业务越来越复杂,团队规模不断壮大,慢慢发现业务增长瓶颈来自于研发效能和交付速率,而运维体系和基础架构体系则是研发效能提升的关键。我们也在慢慢思考,基于已经沉淀的工具、脚本建立了一整套研发一体化体系。
随着容器化技术的兴起,我们也开始拥抱容器技术,基于 Docker Swarm 建设了第二代运维平台,随着 Kubernetes 慢慢成为容器编排的事实标准,强大的调度能力以及开放标准的 API 接口让我们转向 Kubernetes 建设了第三代平台,现在则是更多将重心放在监控及运维数据沉淀上,形成智能运维体系。同时,应用架构也伴随着运维体系的转变在慢慢发生变化,从单体架构到现在,当前众安全部业务都已实现微服务化与容器化。
众安第四代运维体系,基于云基础设施、Kubernetes 建设容器服务,这个容器服务作为整个运维体系的核心,通过监控平台、安全平台实现数据的输入、分析和决策。通过运维控制管道来实现异构化资源的调度,包含容器、数据库、Git 等方向。平台覆盖整个研发生命周期,在开发侧基于 Kubernetes 和 docker 将异构化的运行环境真正实现不可变的基础设施,同时做到开发语言不受限。众安内部有各种技术栈,包括 GO、Python、JAVA,这些都是在我们体系之内的技术栈。线上运维侧主要是基于 Kubernetes 带来的能力,提供自动扩缩容、自动上下线的能力。
刚才是运维体系的视角,接下来看看运维体系之下的应用视角。Kubernetes 给我们提供了不可变的基础设施能力,我们的应用环境可以在各种异构化的物理基础设施上,包括公有云甚至自建的 IDC。通过 DevOps 平台提供代码管理、流水线管理以及发布管理、持续交付的能力。
应用方面,着重讲一下微服务架构服务治理,我们在服务治理方面也会产生一些思考,比如负载均衡、服务注册发现,现在主流的 consul、Eureka 这种客户端服务发现的机制在大规模的微服务集群规模下是不是真的非常合适?在应用规模越来越庞大的今天,我们会慢慢发现客户端维护消费实例数据,以及准实时的服务实例信息变更可能会给客户端带来非常大的负担。服务发现机制是为了解决什么问题呢?是在微服务多实例的情况下,我们消费端需要实时感知到服务实例的变化。Kubernetes 的资源 service 天生具备这个能力,我们在服务启动时就可以在 etcd 中存储所有实例信息,可以将 service 作为整个消费端的服务单元,客户端的消费直接面向这个单元就可以了。又比如负载均衡,负载均衡也有两种,比如以 FeignClient 或 Ribbon 为代表的客户端负载均衡,service 也天生具备负载均衡的能力。在这个考量之下,微服务体系更多会拥抱 Kubernetes 已有的 service 组件来实现负载均衡以及注册发现逻辑。
微服务应用体系的支撑层面建设有全链路监控,所谓全链路,是指监控覆盖从前端、网络、应用、安全到数据、基础设施的完整链路。同时在系统调用方面以 Opentracing 作为链路规范标准,实现从前端到后端、到中间件全链路的追踪。在 C 端,我们会通过埋点的形式来进行远端数据收集,实现 C 端 APP 或者 H5 的性能监控,比如可能会有图片加载不出来或者 APP 卡顿的问题,这些问题一般情况下业务方是无法感知到的。在这之上,我们同时建设有 Aiops 体系,主要关注的点是异常检测、容量规划、归因分析、故障自愈。比较典型的场景,比如归因分析,我们可以根据链路数据来形成事件传播链分析,达到根因分析的效果。
运维 1+N:基础运维建设
刚才大致介绍了众安基础架构的演进路线,第二部分主要介绍 DevOps 建设的理念,叫做运维 1+N 模式。运维 1+N 简单的说就是指基础运维+应用运维的模式,1 个基础运维平台,N 个应用运维场景,第二部分我将介绍基础运维平台的建设思路。
基础运维平台的核心组件是 CMDB,CMDB 传统的叫法是配置管理数据库,这里简单介绍一下新一代 CMDB 应该具备的核心能力是什么。
首先,CMDB 应该是灵活面向抽象的设计,为什么需要这个能力?企业的运维场景是丰富多变、复杂多样的,在这个前提下,CMDB 维护的数据就不仅限于传统的人、组织或者资源、应用,CMDB 应该是面向抽象的设计,如何设计一款抽象的 CMDB?我们应该从 CMDB 的模型层出发,一个模型有哪些东西,首先模型有模型项,比如主机有 IP 或者区域属性。另外,模型具备模型之间的关联关系,比如人和应用的关系,应用和主机的关系。我们会对接一些数据源,有数据进来,模型可能会有校验、清洗规则。同时,对于审计的要求,模型会有可变更、可追踪的机制。
第二个设计要点是 CMDB 应该具备数据发现的能力,为什么这么说?运维成功的关键要素是 CMDB,CMDB 成功的关键则是 CMDB 数据的一致性、实时性和准确性。我们发现传统 CMDB 数据一般都是通过某个组织,比如运维,或者通过 ITSM 流程来管控的,数据的一致性以及准确性受到的最大挑战是来自于人本身,所以对于 CMDB 而言,应该能自动发现数据。发现数据有很多手段,比如使用云服务的场景,发现数据可以来自于 CMP 系统、也就是多云管理系统,通过云 API 多云管理系统可以实现对 CMDB 数据的上报。对于自建 IDC 场景,通过 Agent 手段,比如 Osquery 来实现数据采集。数据具备自动发现的能力,才能保证 CMDB 中维护的数据是准确的。
可能大家会有疑问,CMDB 中为什么会存在状态数据?CMDB 为什么要打通监控?这里举两个例子。有一个大家比较熟知的概念叫客户画像,某短视频平台可能会基于我们平时的浏览行为、关注的内容,通过历史行为来沉淀用户画像,比如某些用户比较喜欢小动物,有些用户比较喜欢看搞笑视频,有些用户看美女比较多,这就是用户画像,通过用户画像能做到内容的精准推送,同时形成用户对产品的粘性。相同的,我们可以通过应用历史的运行情况,通过监控对接,形成应用画像。比如调度场景,在调度层面可以对应用历史运行的情况进行分析,看它到底是什么应用,是 CPU 密集型、内存密集型还是 IO 密集型,基于这些打通监控平台的数据,状态数据、标签化数据,来达到精准调度的效果,这是第一个场景。另外一个场景,比如资源编排场景,我们会发现在容器编排上可能不知道要给一个应用或者一个服务分配多少资源,分配多少 CPU,分配多少内存,更多情况可能是基于人的经验拍一个编排的资源,刚才我们说到最不可控的就是人,基于人的经验设的资源编排很有可能会造成资源极大浪费。我们也可以基于容量规划产生应用的水位基线,或者是基于应用历史的运行情况来得到一个当前应用最适合的 CPU 或者内存是多少,去提供编排推荐的能力,达到资源最大使用效果。有越来越多的场景,要求 CMDB 应该能集成监控系统,以标签化的形式将状态数据放在 CMDB 当中。
接下来介绍 CMDB 设计思路中的第三点,应该存在以应用中心和以人为中心的两种视图。为什么这么说?CMDB 比较典型的两个场景,一个场景是以人为中心的,面向管控层的 ITSM 流程,也就是就是 ITSM 场景。另外一个比较典型的场景则是以应用为中心的面向执行层的持续交付场景。在这两个场景之下,我们应该具备以应用为中心和以人为中心的两种视图。
刚才说到 CMDB 的建设思路,有 CMDB 这个平台还不够,有 CMDB 平台之后怎么给 CMDB 数据建模呢?我们觉得 CMDB 的数据建模应该是自上而下面向场景的建模。为什么这么说?我们认为 CMDB 的数据有没有价值其实还是看它能不能支撑上层的场景。比如刚才说到的 ITSM 流程场景以及持续交付场景,以持续交付场景为例,怎么进行数据建模?持续交付的过程有创建发布,创建发布之后会执行构建动作,CI 之后有 CD,CD 之后有线上运维,每个场景都会有需要的对应 API,API 下面需要模型支撑,比如选择人和应用,在这个场景之下,人和应用需要哪些模型项以及人的模型跟应用模型之间应该存在什么关系。面向场景的建模,才能保证我们在 CMDB 中的数据能服务于场景,而不是存在那儿的死数据,死数据除了给数据维护带来成本之外没有任何作用。
刚才说到基础运维平台第一个比较重要的组件 CMDB,CMDB 能给基础运维平台提供数据基础。第二个部分则是基础运维平台另外一个比较重要的概念,我们称之为运维的生命线,叫做运维控制管道,它能给基础运维平台提供调度的基础,运维过程中可能涉及很多资源的调度,包括服务器、Redis,面向 GitFlow 的运维流程可能还会有 Git 的调度。我们可以基于 Kubernetes 的 CRD 来建设调度执行层,对于主机的调度则可能会选用 saltstack 或者 Ansible 作为调度介质。对于数据库调度,比如发布过程中、应用的交付流程中可能涉及到要发布一个 SQL,这个时候我们会选用开源组件,像 Liquibase 可以很好的作为数据库调度的组件。兑入容器的调度可以使用 k8s 的 apiServer 提供的能力,当然不应该仅仅面向单 Kubernetes 集群来设计,我们可能会有容器服务,通过容器服务来做整体 Kubernetes 集群的管控,提供统一的容器调度服务。
运维控制管道,简单来说它就是一个工作流引擎。展开来说,工作流引擎可能涉及到选型,目前众安内部的选型是通过云原生生态上的工作流引擎,使用 Tekton 来建设。一个 Pipeline 应该具备哪些能力?仅仅是工作流还不够,更重要的能力是各个工作流的执行以及原子调度任务的执行应该是可被观测的,我们应该有一个状态机来观测它的状态。同时,应该具备分布式任务调度机制来保证我们可以实现调度任务的故障转移。运维控制管道设计可以类比微服务,微服务有一个服务编排的概念,目的是将运维涉及到的所有调度动作通过 Kubernetes 的 CRD 能力抽象成一个个原子调度任务,基于工作流的能力来编排实际运维场景,这就是基础运维的概念。有了编排的能力,我们可以很快构建上层应用运维,比如发布场景,可能是一层,或者两层、三层流水线模型,基于我们的编排,可以快速构建 CI/CD 场景。
刚才说到 CMDB 和 Pipeline,下面回到运维 1+N 模式。什么是运维 1+N 模式?我们对 1+N 模式的定义是在云基础设施之上,云基础设施包含云原生运行时、云原生存储和云原生网络,在云基础设施之上建设“1”,这个“1”是指基础运维平台,CMDB 提供了运维需要的数据能力,Pipeline 提供了整个运维过程中的需要的调度能力。“N”是基于一个基础运维平台来建设 N 个应用运维场景,比如故障处理场景,我们接触更多的是发布,发布是典型的变更场景。另外,我们也有 ChatOps 的概念,会有一些客服场景,还有一块是容量基础场景。
运维 1+N 模式能带来哪些收益?刚才我们强调了在这个设计思路下可以很快建设新的运维场景,比如数据能力跟调度能力我们这边已经有了,可能以低代码甚至零代码的形式做到新场景的建设,这是第一点。第二点是我们相当于将基础运维平台作为整个运维体系的实施标准,这样就能避免系统烟囱式建设,什么叫烟囱式建设?比如各项目管理领域或者发布的领域,各自有各自的平台,但这些数据我们没办法打通,流程是割裂的,这就叫烟囱式系统建设。我们将 CMDB 作为数据标准,实现需求管理与发布管理的流程串联是非常简单的。这是第二点,避免烟囱式建设。第三点是避免企业内的重复建设,比如各自团队有各自的运维场景,比如要用到容器服务,操作 Git 或者操作主机,Pipeline 的存在避免了各个团队对调度、流程做重复性的建设。
应用运维实践
刚才说到的是运维 1+N 模式的“1”的部分,接下来介绍一下“N”的部分,在应用运维上的实践。
第一个案例是持续交付场景,这是一个经典的 CI 流程,整个 CI 流程可能是通过开发者提交一次代码来触发的,比如通过 Tekton CI 任务来执行 Git 代码拉取,基于代码构建镜像。同时可能会执行自动化测试或者安全扫描。成功之后,会推送到 Harbor 中,推送到镜像仓库,Kubernetes 要做的事情就是同时执行一次开发环境的部署动作,拉取镜像,启动镜像。整个过程通过之后,CI 流程结束,通知开发者或者合并代码或者触发 CD。这是一个典型的 CI 流程。
接下来是在经典 CI 流程上 GitOps 设计。刚才的 CI 流程其实是典型的推式流水线,什么是推式流水线?就是平台会对 Kubernetes 集群下发一个指令,要么通过 API service,要么通过 kubectl,将指令推到 Kubernetes 集群当中。GitOps 在最佳实践上有一点比较重要的概念叫做拉式流水线,区别于推式流水线,我们会在 Kubernetes 集群中部署一个服务,叫 Seaman,GitOps 遵循 IAC 设计,即基础设施及代码,我们认为应用服务它的状态是可以在 Git 中声明的。Seaman 这个组件的职责是实时获取当前集群的状态,对比 Git 中的声明来产生差异,通过差异分析,在集群内执行调度,自动将整个集群状态达到 Git 声明的状态。这跟 Kubernetes 的设计理念非常相似,命令式编程跟声明式编程的差异,声明式编程能给我们带来什么优势呢?命令式可能会产生命令丢失的问题,需要辅助手段来保证最终一致性。
GitOps 在拉式流水线的场景之下能带来哪些收益呢?一个很典型的收益,比如推式流水线自然而然要将集群的密钥暴露到平台当中,这对安全性是一个挑战。对于拉式流水线来说不需要,因为是通过事件驱动,集群自感知,通过集群的自动差异分析达到部署动作,集群无需暴露证书。这是第一点。
第二点收益,来自于 Git 天然具备一些能力,一个是版本控制的能力,另外一个是操作审计、操作留痕,分别从这些能力介绍一下 GitOps 给我们带来的优势。
首先是版本,运维或多或少会面临回滚场景,GitOps 将整个应用声明性的架构存放到 Git 当中,相当于 Git 作为应用期望状态的事实标准,通过 git 版本的回滚,我们能非常简单的做到整个环境的全回滚,这是对于版本控制带来的优势。另外一块是操作审计,刚才也提到了,金融行业监管部门可能会对我们有非常严格的要求,服务的任何变更应该都是可被记录、可被追溯的,我们会天然用到 Git 的这些能力来实现操作审计。接下来是用 Git 天然的 rbac 策略来实现角色和权限的控制。
刚才说到的是服务变更的上的实践,第二点分享的是基础设施及代码的实践,服务部署以及变更流程的设计。
这张图就是基于运维控制管道来设计的整个服务部署以及变更流程,跟传统服务变更的差异是什么?我们认为服务的变更不仅仅是软件制品或者代码的变更,这只是服务的一部分。一个服务由什么组成?包括应用本身以及围绕应用相关的配置,以及初始化数据库脚本或者服务依赖的中间件。再延伸,甚至是服务依赖的 IaaS 层基础资源。我们可以基于基础设施及代码的理念将它做到可声明,我们这边叫做“工程”。基于工程的声明,怎么设计一键部署的逻辑?刚才也看到了整个运维控制管道集成了所有运维相关的调度,对于工程声明的要素而言,比如 APP,我们声明它之后可能就要触发 Kubernetes deployment 的调度。对于配置,比如微服务有配置中心,它的调度可以设计成调用配置中心的抽象层来实现配置中心的推送。对于 SQL,我们声明它之后,可能会基于事件自动触发 Liquibase 的执行。对于中间件,可能是容器化中间件或者部署在虚拟机中的中间件。不同的点是我们会同时声明它是什么中间件,触发不一样的调度动作,对于虚拟机部署的中间件而言,会触发 ansible 下发中间件部署启动命令,对于容器化而言,可以是 apply 一个 statefulSet,也就是创建一个容器的调度。
在这个理念之下,我们发现通过声明式的设计,将整个应用环境通过工程的定义来进行声明,同时通过声明、通过事件来驱动,触发运维控制管道的工作流去执行各个组件的调度。在系统设计上也就比较简单了,调度是触发工作流 API,有 API 我们就知道可能需要哪些参数,有参数就可以基于参数来定义 DSL,可能是基于 JSON 的 DSL,也可能是基于 YML 的 DSL,DSL 给到开发者一个视角,我们可以基于 DSL 字段快速创建用户界面,通过用户界面,开发者可以基于一些编排配置动作来实现对于环境的定义以及环境的一键拉起。
基于工程来声明应用的环境,服务变更流程就可以变得很简单了,所谓的发布,就可以设计为通过交付流水线来修改各个环境工程的定义。
最后一块要分享的应用运维实践则是 DevSecOps 体系的建设,一开始也提到金融行业对公司安全性的要求,对于安全跟 DevOps 的集成,我们也有一些思考,我们觉得对于安全的扫描也好、管控也好,应该尽量左移,而不是将安全问题暴露在生产运行时。左移会有一些策略,比如在 CI/CD 层,我们可以集成自动化安全扫描或者代码安全扫描,同时基于质量门禁体系,比如每次变更不能有高危漏洞,同时不能有超过多少个中危漏洞,通过这种机制来达到在 CI/CD 过程中控制安全漏洞,减少安全事件产生的目的。在系统层,运行环境层有多个层面,一个是主机层面,有主机安全扫描的手段,在容器层面有镜像安全扫描手段以及引用安全容器。在应用层可能会通过一些反欺诈行为或者加密存储行为来做到应用层的安全管控。在业务层,C 端业务比较典型的是有一些登录场景,这个时候就会引入多因素认证服务。
DevSecOps 体系就是将安全管控纳入到运维体系当中,不管是从运行环境这一层也好,还是到变更流程也好,都要进行深度集成。
云先行,智未来。我们现正在云原生的道路上深耕,基于云原生建设的运维体系是我们正在进行的事情,同时我们认为,运维体系的未来一定是智能化的,我们希望未来可以更多使用人工智能的技术代替现有的一些规则化的场景。
评论