欢迎访问网易云社区,了解更多网易技术产品运营经验。
12 要素
“12 要素”英文全称是 The Twelve-Factor App,最初由 Heroku 的工程师整理起步,是 集体贡献总结的智慧,如图 1-9 所示。根据基于云的软件开发模式,12 要素比较贴切地描 述了软件应用的原型,并诠释了使用原生云应用架构的原因。比如,一个优雅的互联网应 用在设计过程中,需要遵循的一些基本原则和云原生有异曲同工之处。通过强化详细配置 和规范,类似 Rails 的基于“约定优于配置” (convention over configuration)的原则,特别 在大规模的软件生产实践中,这些约定非常重要,从无状态共享到水平扩展的过程,从松 耦合架构关系到部署环境。基于 12 要素的上下文关联,软件生产就变成了一个个单一的部 署单元;多个联合部署的单元组成一个应用,多个应用之间的关系就可以组成一个复杂的 分布式系统应用。
图 1-9 12 要素
下面简要介绍图 1-9 中的这些原则。相信很多开发者在实际开发工作中已经很好地应 用了其中的一些原则,只是没有意识到概念本身。对这些原则比较陌生的开发者,如果想 了解更多的操作过程,请在互联网上搜索《云原生时代下的 12 要素(12-Factor)应用与实 践》一文作为参考。
基准代码
每一个部署的应用都在版本控制代码库中被追踪。在多个部署环境中,会有多种部署 实例,单个应用只有一份代码库,多份部署相当于运行了该应用的多个实例,比如开发环 境一个实例,测试环境、生产环境都有一个实例。
实际上,在云计算架构中,所有的基础设施都是代码配置,即 Infrastructure as Code (IaC),整个应用通过配置文件就可以编排出来,而不再需要手工的干预,做到基础服务也 是可以追踪的。
依赖
应用程序不会隐式依赖系统级的类库,通过依赖清单声明所有依赖项,通过依赖隔离 工具确保程序不会存在调用系统,但清单中未声明依赖项,清单统一应用到生产和开发环 境。比如通过合适的工具(例如 Maven、Bundler、NPM),应用可以很清晰地对部署环境 公开和隔绝依赖性,而不是模糊地对部署环境产生依赖性。
在容器应用中,所有应用的依赖和安装都是通过 DockerFile 来完成声明的,通过配置 能明确把依赖关系,包括版本都明确地图形化展示出来,不存在黑盒。
配置
环境变量是一种清楚、容易理解和标准化的配置方法,将应用的配置存储于环境变量 中,保证配置排除在代码之外,或者其他可能在部署环境(例如研发、展示、生产)之间 区别的任何代码,可以通过操作系统级的环境变量来注入。
实例根据不同的环境配置运行在不同的环境中,此外,实现配置即代码,在云环境中, 无论是统一的配置中心还是分布式的配置中心都有好的实践方式,比如 Docker 的环境变量 使用。
后端服务
不用区别对待本地或第三方服务,统一把依赖的后端作为一种服务来对待,例如数据 库或者消息代理,作为附加资源,同等地在各种环境中被消耗。比如在云架构的基础服务 中,计算、网络、存储资源都可以看作是一种服务去对待使用即可,不用区分是远程还是 本地的。
构建、发布、运行
应用严格区分构建、发布、运行这 3 个阶段。3 个阶段是严格分开的,一个阶段对应 做一件事情,每个阶段有很明确的实现功能。云原生应用的构建流程可以把发布配置挪到 开发阶段,包括实际的代码构建和运行应用所需的生产环境配置。在云原生应用中,基于 容器的 Build-Ship-Run 和这 3 个阶段完全吻合,也是 Docker 对本原则的最佳实践。
进程
进程必须无状态且无共享,即云应用以一个或多个无状态不共享的程序运行。任何必 要状态都被服务化到后端服务中(缓存、对象存储等)。 所有的应用在设计时就认为随时随地会失败,面向失败而设计,因此进程可能会被随 时拉起或消失,特别是在弹性扩容的阶段。
端口绑定
不依赖于任何网络服务器就可以创建一个面向网络的服务,每个应用的功能都很齐全, 通过端口绑定对外提供所有服务,比如 Web 应用通过端口绑定(Port binding)来提供服务, 并监听发送至该端口的请求(包括 HTTP)。
在容器应用中,应用统一通过暴露端口来服务,尽量避免通过本地文件或进程来通信, 每种服务通过服务发现而服务。
并发
进程可以看作一等公民,并发性即可以依靠水平扩展应用程序来实现,通过进程模型 进行扩展,并且具备无共享、水平分区的特性。
在互联网的服务中,业务的爆发性随时可能发生,因此不太可能通过硬件扩容来随时提供扩容服务,需要依赖横向扩展能力进行扩容。
易处理
所有应用的架构设计都需要支持能随时销毁的特点,和状态的无关性保持一致,允许 系统快速弹性扩展、改变部署及故障恢复等。
在云环境中,由于业务的高低峰值经常需要能实现快速灵活、弹性的伸缩应用,以及 不可控的硬件因素等,应用可能随时会发生故障,因此应用在架构设计上需要尽可能无状 态,应用能随时随地拉起,也能随时随地销毁,同时保证进程最小启动时间和架构的可弃 性,也可以提供更敏捷的发布及扩展过程。
环境等价
必须缩小本地与线上差异,确保环境的一致性,保持研发、测试和生产环境尽可能相 似,这样可以提供应用的持续交付和部署服务。
在容器化应用中,通过文件构建的环境运行能做到版本化,因此保证各个不同环境的 差异性,同时还能大大减少环境不同带来的排错等成本沟通问题。
日志
每一个运行的进程都会直接标准输出(stdout)和错误输出(stderr)事件流,还可以 将日志当作事件流作为数据源,通过集中服务,执行环境收集、聚合、索引和分析这些 事件。
日志是系统运行状态的部分体现,无论在系统诊断、业务跟踪还是后续大数据服务的 必要条件中,Docker 提供标准的日志服务,用户可以根据需求做自定义的插件开发来处理 日志。
管理进程
管理或维护应用的运行状态是软件维护的基础部分,比如数据库迁移、健康检查、安 全巡检等,在与应用长期运行的程序相同环境中,作为一次性程序运行。
在应用架构模式中,比如 Kubernetes 里面的 Pod 资源或者 docker exec,可以随着其他 的应用程序一起发布或在出现异常诊断时能通过相关的程序去管理其状态。 云原生的内容非常广泛,目前没有系统的说明和完整的定义,上文介绍了云原生应用 的基础组件和相关特点,可能读者对云原生应用的逻辑还存在一些困惑。为了更清楚地进 行说明,我们总结了其依赖关系,如图 1-10 所示。
图 1-10 云原生内容的依赖关系
首先,为了抓住商业机会,业务需要快速迭代,不断试错,因此,企业需要依赖拥有 持续交付的能力,这些不仅包括技术需求还包括产品的需求,如何能拥有持续交付的能力, 大而全的架构因为效率低下,显然是不合适的。于是演变出微服务架构来满足需求,通过 把系统划分出一个个独立的个体,每个个体服务的设计依赖需要通过 12 要素的原则来规范 完成。同样,如果系统被分成了几十个甚至几百个服务组件,则需要借助 DevOps 才能很 好地满足业务协作和发布等流程。最后,DevOps 的有效实施需要依赖一定的土壤,即敏捷 的基础设施服务,现实只有云计算的模式才能满足整体要求。通过上述梳理,我们总结出 面向云原生应用的 3 个不同层次的特点。
高可用设计(Design for Availability), 依据应用业务需求,高可用分为不同级别, 比如不同区域、不同机房(跨城或同城)、不同机柜、不同服务器和不同进程的高 可用,云原生应用应该根据业务的可用性要求设计不同级别的架构支持。
可扩展设计(Design for Scale),所有应用的设计是无状态的,使得业务天生具有扩 展性,在业务流量高峰和低峰时期,依赖云的特性自动弹性扩容,满足业务需求。
快速失败设计(Design for Failure),即包括系统间依赖的调用随时可能会失败, 也包括硬件基础设施服务随时可能宕机,还有后端有状态服务的系统能力可能有 瓶颈,总之在发生异常时能够快速失败,然后快速恢复,以保证业务永远在线, 不能让业务半死不活地僵持着。
通过上面的基本描述及云原生应用的组成或特点,与容器技术(第 2 章将详细介绍) 相比可以得知,容器的特性天生就是按这些原则进行设计的。随着互联网业务的架构不断 演进,从单体应用到分布式应用,甚至微服务架构应用中,12 要素较好地为构建互联网化 应用提供了统一的方法论和标准化,具有强大的生命力,每一条原则都是应用开发的珠 玑。当然,在实践过程中,每一个原则也不是一成不变的,随着新的理念和技术出现, 原有的因素会得到延伸和发展,会出现新的原则和应用,这套理论也适用于任意语言和后 端服务(数据库、消息队列、缓存等)开发的应用程序,因此也作为云原生架构应用的基 本指导原则之一。
文章节选自《云原生应用架构实践》 网易云基础服务架构团队 著
网易云计算基础服务深度整合了 IaaS、PaaS 及容器技术,提供弹性计算、DevOps 工具链及微服务基础设施等服务,帮助企业解决 IT、架构及运维等问题,使企业更聚焦于业务,是新一代的云计算平台。点击可免费试用。