云原生

tech2022-08-14  140

目录

1 云原生简介

2 云原生系统应该具备三大特征

3 云原生的四大核心要素

3.1 微服务

3.2 DevOps

3.3 持续交付

3.4 容器化

4 云原生应用的12要素

4.1 基准代码

4.2 依赖

4.3 配置

4.4 后端服务

4.5 构建,发布,运行

4.6 进程

4.7 端口绑定

4.8 并发

4.9 易处理

4.10 开发环境与线上环境等价

4.11 日志

4.12 管理进程


1 云原生简介

        自进入云计算时代后,大量的新概念、新技术如雨后春笋般的涌现出来,从早期的openstack、IAAS平台,到中期的容器技术、微服务架构,再到现在的servicemesh服务网格技术、serverless无服务器架构、云原生技术,可谓在云计算的时代,我们从未停下前进的步伐。

        云原生(Cloud Native)的概念,由来自Pivotal的MattStine于2013年首次提出,被一直延续使用至今。这个概念是Matt Stine根据其多年的架构和咨询经验总结出来的一个思想集合,并得到了社区的不断完善,内容非常多,包括DevOps、持续交付(Continuous Delivery)、微服务(MicroServices)、敏捷基础设施(Agile Infrastructure)和12要素(The Twelve-Factor App)等几大主题,不但包括根据业务能力对公司进行文化、组织架构的重组与建设,也包括方法论与原则,还有具体的操作工具。采用基于云原生的技术和管理方法,可以更好地把业务生于“云”或迁移到云平台,从而享受“云”的高效和持续的服务能力。

        那么什么是云原生呢?我们将名词拆成两部分—云、原生,这些是相对于本地应用来的,云是相对于本地而言的,传统的应用都是运行在本地机房的服务器上,而云的应用则是运行在云端(如IAAS、PAAS、SAAS)。

        原生就是亲生的、土生土长的意思,即应用一诞生就是基于云的,可以直接在云平台上运行或非常轻松的迁移到云平台。

        我们可以这么来定义云原生:一套新的技术体系、一种新的工作方法论、云计算发生的必然导向。

        云原生应用要运行在云平台,那么就必须要有云的特点,比如弹性伸缩、分布式、快速部署、快速迭代、高效、持续。这可不止是简单的把原先在物理服务器上的应用迁移到虚拟机里,不止是基础设施和运行平台在云上,应用架构、应用开发方式、应用部署方式、应用维护方式全都要做出改变。

2 云原生系统应该具备三大特征

容器化封装:以容器为基础,提高整体开发水平,形成代码和组件重用,简化云原生应用程序的维护。在容器中运行应用程序和进程,并作为应用程序部署的独立单元,实现高水平资源隔离。自动化管理:统一调度和管理中心,从根本上提高系统和资源利用率,同时降低运维成本。面向微服务:通过松耦合方式,提升应用程序的整体敏捷性和可维护性。

3 云原生的四大核心要素

        云原生的四大核心要素便是微服务技术、DevOps、持续交付、容器化。

        

微服务技术使得应用原子化,所有的应用都可以独立的部署、迭代。DevOps使得应用可以快速编译、自动化测试、部署、发布、回滚,让开发和运维一体化。持续交付让应用可以频繁发布、快速交付、快速反馈、降低发布风险。容器使得应用整体开发以容器为基础,形成代码组件复用、资源隔离。

3.1 微服务

        微服务,改变产品开发方式。

        微服务是什么?重点在“微”。它的核心是将单个应用程序作为一组小型服务来开发。原来一个产品的开发可能是拆成几个大的模块,然后由几个团队来做,然后再合,微服务的理念是把一个产品拆的更细,可能一个人、几个人负责一个服务的开发,每个服务之间都是独立的。

        每个服务都在自己的进程中运行,并使用轻量级机制(通常是基于HTTP的API)进行通信。 这些服务是围绕业务功能构建,可以通过全自动部署机制独立部署,不需要集中管理,可以用不同的编程语言编写,并使用不同的数据存储技术。

        所以,微服务核心就是服务粒度要小,每个服务是针对一个单一职责的业务能力的封装,专注做好一件事情。但是又不能太小,否则易发生“服务爆炸”。通常在工程实践中,如果一个功能被两个或两个以上的服务调用,就可以被封装为服务。

        所以,微服务的优点很明显,小而美、松耦合、灵活、易集成,但是挑战也很明显,最大的问题在于服务如何切分。其实,早在1968年康威就提出了——康威定律,系统的服务划分应该是根据组织架构的功能来划分。这一点用在微服务领域也非常合适。

        这样按照组织架构划分的优势在于:

内聚更强,所有遵循同一种业务准则的人内聚在一起,就容易解决问题。服务解耦,变更容易,更加敏捷。

3.2 DevOps

        DevOps,内部协作更紧密。

        DevOps,如果从字面上来理解,是Dev(开发)+ Ops(运维)。DevOps的定义是研发运维一体化,通过自动化流程使得软件过程更加快捷和可靠。它不是一个产品,而是一种新的团队工作方式、新的技术理念。  

        一个软件从0到1的最终交付包含如下阶段:市场规划、产品规划、编码设计、编译构建、部署测试、发布上线、后期维护。

        早期的时候全由一个人完成了,这个人一般都是CEO了,他根据对市场的洞察感知有了好的idea,自己开发编码,编译打包,进行测试之后在云厂商上买一两台服务器部署上应用就对外发布了,这就是瀑布式开发模型,确认好需求后就进入开发阶段,直到完成上线。

        而随着使用人群的增加,应用的整体维护开始变得艰难,因为CEO对外要去扩展业务、对内要继续开发、继续维护应用,一个人实在干不过来了。

        慢慢的团队里有了产品经理、开发人员、测试人员、运维人员的划分,由产品经理负责需求的规划、产品交互设计,研发人员负责编码、构建包,测试人员负责功能测试和自动化测试、上线发布,运维人员负责维护线上服务的正常运行、扩容缩容,这就是敏捷开发模型,在开发过程阶段测试介入,快速验证修改问题直到基本无误后上线部署。这一切所带来的问题是整体的交付周期变长了,团队之间沟通合作成本变高了,因此DevOps应运而生。它将整个软件开发测试运维过程变为一体化,每完成一个小的需求点便测试上线部署,快速验证需求,捕获用户,占领市场。

         

        因此DevOps的出现是一种组织架构的变革,一种开发模式的变化,团队人员在需求规划、代码设计、编译构建、测试部署、上线发布、后期维护的过程全程参与,每个人都对整体的方案了解清晰,可制定合适的系统架构、技术架构、运维部署方案。

       

        云计算时代的到来带来了虚拟化、容器、微服务等新的技术理念,强调的是服务的拆分、精细化的分工,奠定了DevOps落地的基础条件,只有当服务拆分的原子化了,整个团队密切合作的成本才会降低,才能实现云上应用的快速迭代,符合云计算时代的特色,确定是云原生的特性之二了。

3.3 持续交付

        持续交付的定义就是一直在交付,敏捷开发和DevOps要求随时都有一个合适的版本部署在生产环节上,频繁发布、快速部署、快速验证,所以必须要持续交付。

        持续交付解决的情况是需求迟迟不能确定从而缩短了开发时间,需求不能确定所带来的问题是:在确定的过程中整个市场或用户已经发生了变化,开发出来的内容早已不符合当下用户的需求了。举一个例子,有些公司非常喜欢谈需求,谈很久,可是开发只剩1/3时间就开发完成,然后交付,再上线运营。这就会碰到一个问题,就是你开始谈需求到最后交付产品的时间,短则三月,长则半年,这中间市场已经变化了,需求也随之变化了。因此市场上出现了新的想法,即是不是能够小步快跑,把交付的周期缩短一点,我可以实现快速交付,每次交付都可以重新确认方向,这样尽量避免与未来期待的落差。

         为了快速的验证需求,往往在生产环境上会部署多个版本,从而也产生了不同的发布部署方式,比如灰度发布、蓝绿发布。

         所谓灰度发布便是当新的需求开发完成后,将线上的版本只升级部分服务,让一部分用户继续使用老版本,一部分使用新版本,如果用户对新版本没有意见,再迁移到新版本来。整个过程是运维人员从负载均衡上去掉灰度服务器,待服务升级成功后再加入负载均衡服务器列表,这时候有少量用户访问业务时流量到新版本,如果这小部分用户使用没有反对,逐渐扩大灰度范围,最后升级剩余服务器。

        

        所谓蓝绿发布则是将应用从逻辑上分为A、B两组,升级时将A从负载均衡组里删除,进行新版本的部署,同时B组仍然继续提供服务。当A组升级完成后,负载均衡重新接入A组,再把B组从负载列表摘除,进行新版本的部署。A组重新提供服务。最后B组升级完成,负载均衡重新接入B组。此时AB组版本都升级完成,并且都对外提供服务。保障整个过程对用户无影响,出现问题及时回退上一个版本。

        

        通过灰度发布和蓝绿发布的方式,可以快速的验证用户需求,频繁的发布,根据用户情况规划产品演变方向,实现了云计算时代的快速迭代,符合云计算时代的特色,确定是云原生的特性之三了。

3.4 容器化

        容器,云原生的基石。

        很多人会将Docker与容器划等号,其实不然,Docker只是容器理念最普及的一种应用技术。容器的英文单词是Container,有集装箱的含义,而借用集装箱技术会很好理解容器的优势。集装箱的特点,在于标准化,这样可以大量堆叠,装卸也很方便。容器也是这样。

        容器技术的定义就是一个单独的应用程序进程、运行资源的高度隔离。早期的时候应用全运行在物理机上,这导致资源分配不均匀,即使是一个小的应用也要耗费同样的计算存储资源,中期的时候有了虚拟化技术将物理机划分为多个虚拟机,这样在一台物理服务器上可以运行多个虚拟服务器,实现了资源利用率的较大提升,而云计算时代的到来,带来了微服务、DevOps、持续集成持续交付等内容,要求应用要原子化、快速的开发迭代、快速的上线部署,划分为虚拟机的方式不能保障应用在每个环境(Dev、Test、Pre、Prod)都一致,容易引起应用因环境的问题而产生Bug,容器的出现极好的解决了这个问题。

        在容器出现之后,整个的流程变成了研发人员在将代码开发完成后,会将代码、相关运行环境构建镜像,测试人员在宿主机上下载服务的镜像,使用容器启动镜像后即可运行服务进行测试;测试无误后运维人员申请机器,拉取服务器的镜像,在一台或多台宿主机上可以同时运行多个容器,对用户提供服务。在这个过程中每个服务都在独立的容器里运行,每台机器上都运行着相互不关联的容器,所有容器共享宿主机的cpu、磁盘、网络、内存等,即实现了进程隔离(每个服务独立运行)、文件系统隔离(容器目录修改不影响主机目录)、资源隔离(CPU内存磁盘网络资源独立)。

        使用容器,研发团队可以将微服务及其所需的所有配置、依赖关系和环境变量移动到全新的服务器节点上,而无需重新配置环境,这样就实现了强大的可移植性,实现了云计算时代的资源最大化利用,符合云计算时代的特色,确定是云原生的特性之四了。

        

        综上所述,云原生的DevOps、容器化平台、持续交付、微服务都是云原生不可缺少的一部分,而云原生也必然是云计算发展的必定趋势,我们需要以全局的眼光看待问题,对四个核心元素加以整合后才能见到云原生的全局风貌。

4 云原生应用的12要素

        如今,软件通常会作为一种服务来交付,它们被称为网络应用程序,或软件即服务(SaaS)。12-Factor 为构建如下的 SaaS 应用提供了方法论:

使用标准化流程自动配置,从而使新的开发者花费最少的学习成本加入这个项目。和操作系统之间尽可能的划清界限,在各个系统中提供最大的可移植性。适合部署在现代的云计算平台,从而在服务器和系统管理方面节省资源。将开发环境和生产环境的差异降至最低,并使用持续交付实施敏捷开发。可以在工具、架构和开发流程不发生明显变化的前提下实现扩展。这套理论适用于任意语言和后端服务(数据库、消息队列、缓存等)开发的应用程序。

        注:SaaS,是Software-as-a-Service的缩写名称,意思为软件即服务,即通过网络提供软件服务。 SaaS平台供应商将应用软件统一部署在自己的服务器上,客户可以根据工作实际需求,通过互联网向厂商定购所需的应用软件服务,按定购的服务多少和时间长短向厂商支付费用,并通过互联网获得Saas平台供应商提供的服务。

4.1 基准代码

        一份基准代码(Codebase),多份部署(deploy)。

        12-Factor应用(译者注:应该是说一个使用本文概念来设计的应用,下同)通常会使用版本控制系统加以管理,如Git, Mercurial, Subversion。一份用来跟踪代码所有修订版本的数据库被称作 代码库(code repository, code repo, repo)。

        在类似 SVN 这样的集中式版本控制系统中,基准代码 就是指控制系统中的这一份代码库;而在 Git 那样的分布式版本控制系统中,基准代码 则是指最上游的那份代码库。

        

        基准代码和应用之间总是保持一一对应的关系:

一旦有多个基准代码,就不能称为一个应用,而是一个分布式系统。分布式系统中的每一个组件都是一个应用,每一个应用可以分别使用 12-Factor 进行开发。多个应用共享一份基准代码是有悖于 12-Factor 原则的。解决方案是将共享的代码拆分为独立的类库,然后使用 依赖管理 策略去加载它们。

        尽管每个应用只对应一份基准代码,但可以同时存在多份部署。每份 部署 相当于运行了一个应用的实例。通常会有一个生产环境,一个或多个预发布环境。此外,每个开发人员都会在自己本地环境运行一个应用实例,这些都相当于一份部署。

        所有部署的基准代码相同,但每份部署可以使用其不同的版本。比如,开发人员可能有一些提交还没有同步至预发布环境;预发布环境也有一些提交没有同步至生产环境。但它们都共享一份基准代码,我们就认为它们只是相同应用的不同部署而已。

4.2 依赖

        显式声明依赖关系( dependency )。

        大多数编程语言都会提供一个打包系统,用来为各个类库提供打包服务,就像 Perl 的 CPAN 或是 Ruby 的 Rubygems 。通过打包系统安装的类库可以是系统级的(称之为 “site packages”),或仅供某个应用程序使用,部署在相应的目录中(称之为 “vendoring” 或 “bunding”)。

        12-Factor规则下的应用程序不会隐式依赖系统级的类库。 它一定通过 依赖清单 ,确切地声明所有依赖项。此外,在运行过程中通过 依赖隔离 工具来确保程序不会调用系统中存在但清单中未声明的依赖项。这一做法会统一应用到生产和开发环境。

        例如, Ruby 的 Bundler 使用 Gemfile 作为依赖项声明清单,使用 bundle exec 来进行依赖隔离。Python 中则可分别使用两种工具 – Pip 用作依赖声明, Virtualenv 用作依赖隔离。甚至 C 语言也有类似工具, Autoconf 用作依赖声明,静态链接库用作依赖隔离。无论用什么工具,依赖声明和依赖隔离必须一起使用,否则无法满足 12-Factor 规范。

        显式声明依赖的优点之一是为新进开发者简化了环境配置流程。新进开发者可以检出应用程序的基准代码,安装编程语言环境和它对应的依赖管理工具,只需通过一个 构建命令 来安装所有的依赖项,即可开始工作。例如,Ruby/Bundler 下使用 bundle install,而 Clojure/Leiningen 则是 lein deps。

        12-Factor 应用同样不会隐式依赖某些系统工具,如 ImageMagick 或是curl。即使这些工具存在于几乎所有系统,但终究无法保证所有未来的系统都能支持应用顺利运行,或是能够和应用兼容。如果应用必须使用到某些系统工具,那么这些工具应该被包含在应用之中。

4.3 配置

        在环境中存储配置。

        通常,应用的 配置 在不同 部署 (预发布、生产环境、开发环境等等)间会有很大差异。这其中包括:

数据库,Memcached,以及其他 后端服务 的配置第三方服务的证书,如 Amazon S3、Twitter 等每份部署特有的配置,如域名等

        有些应用在代码中使用常量保存配置,这与 12-Factor 所要求的代码和配置严格分离显然大相径庭。配置文件在各部署间存在大幅差异,代码却完全一致。

        判断一个应用是否正确地将配置排除在代码之外,一个简单的方法是看该应用的基准代码是否可以立刻开源,而不用担心会暴露任何敏感的信息。

        需要指出的是,这里定义的“配置”并不包括应用的内部配置,比如 Rails 的 config/routes.rb,或是使用 Spring 时 代码模块间的依赖注入关系 。这类配置在不同部署间不存在差异,所以应该写入代码。

        另外一个解决方法是使用配置文件,但不把它们纳入版本控制系统,就像 Rails 的 config/database.yml 。这相对于在代码中使用常量已经是长足进步,但仍然有缺点:总是会不小心将配置文件签入了代码库;配置文件的可能会分散在不同的目录,并有着不同的格式,这让找出一个地方来统一管理所有配置变的不太现实。更糟的是,这些格式通常是语言或框架特定的。

        12-Factor推荐将应用的配置存储于 环境变量 中( env vars, env )。环境变量可以非常方便地在不同的部署间做修改,却不动一行代码;与配置文件不同,不小心把它们签入代码库的概率微乎其微;与一些传统的解决配置问题的机制(比如 Java 的属性配置文件)相比,环境变量与语言和系统无关。

        配置管理的另一个方面是分组。有时应用会将配置按照特定部署进行分组(或叫做“环境”),例如Rails中的 development,test, 和 production 环境。这种方法无法轻易扩展:更多部署意味着更多新的环境,例如 staging 或 qa。 随着项目的不断深入,开发人员可能还会添加他们自己的环境,比如 joes-staging ,这将导致各种配置组合的激增,从而给管理部署增加了很多不确定因素。

        12-Factor 应用中,环境变量的粒度要足够小,且相对独立。它们永远也不会组合成一个所谓的“环境”,而是独立存在于每个部署之中。当应用程序不断扩展,需要更多种类的部署时,这种配置管理方式能够做到平滑过渡。

4.4 后端服务

        把后端服务(backing services)当作附加资源。

        后端服务是指程序运行所需要的通过网络调用的各种服务,如数据库(MySQL,CouchDB),消息/队列系统(RabbitMQ,Beanstalkd),SMTP 邮件发送服务(Postfix),以及缓存系统(Memcached)。

        类似数据库的后端服务,通常由部署应用程序的系统管理员一起管理。除了本地服务之外,应用程序有可能使用了第三方发布和管理的服务。示例包括 SMTP(例如 Postmark),数据收集服务(例如 New Relic 或 Loggly),数据存储服务(如 Amazon S3),以及使用 API 访问的服务(例如 Twitter, Google Maps, Last.fm)。

        12-Factor 应用不会区别对待本地或第三方服务。 对应用程序而言,两种都是附加资源,通过一个 url 或是其他存储在 配置中的服务定位/服务证书来获取数据。12-Factor 应用的任意 部署 ,都应该可以在不进行任何代码改动的情况下,将本地 MySQL 数据库换成第三方服务(例如 Amazon RDS)。类似的,本地 SMTP 服务应该也可以和第三方 SMTP 服务(例如 Postmark )互换。上述 2 个例子中,仅需修改配置中的资源地址。

        每个不同的后端服务是一份 资源 。例如,一个 MySQL 数据库是一个资源,两个 MySQL 数据库(用来数据分区)就被当作是 2 个不同的资源。12-Factor 应用将这些数据库都视作 附加资源 ,这些资源和它们附属的部署保持松耦合。

        部署可以按需加载或卸载资源。例如,如果应用的数据库服务由于硬件问题出现异常,管理员可以从最近的备份中恢复一个数据库,卸载当前的数据库,然后加载新的数据库 – 整个过程都不需要修改代码。

4.5 构建,发布,运行

        严格分离构建和运行。

        基准代码 转化为一份部署(非开发环境)需要以下三个阶段:

构建阶段 是指将代码仓库转化为可执行包的过程。构建时会使用指定版本的代码,获取和打包 依赖项,编译成二进制文件和资源文件。发布阶段 会将构建的结果和当前部署所需 配置 相结合,并能够立刻在运行环境中投入使用。运行阶段 (或者说“运行时”)是指针对选定的发布版本,在执行环境中启动一系列应用程序 进程。

        12-facfor 应用严格区分构建,发布,运行这三个步骤。 举例来说,直接修改处于运行状态的代码是非常不可取的做法,因为这些修改很难再同步回构建步骤。

        部署工具通常都提供了发布管理工具,最引人注目的功能是退回至较旧的发布版本。比如, Capistrano 将所有发布版本都存储在一个叫 releases 的子目录中,当前的在线版本只需映射至对应的目录即可。该工具的 rollback 命令可以很容易地实现回退版本的功能。

        每一个发布版本必须对应一个唯一的发布 ID,例如可以使用发布时的时间戳(2011-04-06-20:32:17),亦或是一个增长的数字(v100)。发布的版本就像一本只能追加的账本,一旦发布就不可修改,任何的变动都应该产生一个新的发布版本。

        新的代码在部署之前,需要开发人员触发构建操作。但是,运行阶段不一定需要人为触发,而是可以自动进行。如服务器重启,或是进程管理器重启了一个崩溃的进程。因此,运行阶段应该保持尽可能少的模块,这样假设半夜发生系统故障而开发人员又捉襟见肘也不会引起太大问题。构建阶段是可以相对复杂一些的,因为错误信息能够立刻展示在开发人员面前,从而得到妥善处理。

4.6 进程

        以一个或多个无状态进程运行应用。

         运行环境中,应用程序通常是以一个和多个 进程 运行的。

        最简单的场景中,代码是一个独立的脚本,运行环境是开发人员自己的笔记本电脑,进程由一条命令行(例如python my_script.py)。另外一个极端情况是,复杂的应用可能会使用很多 进程类型 ,也就是零个或多个进程实例。

        12-Factor 应用的进程必须无状态且 无共享 。 任何需要持久化的数据都要存储在 后端服务 内,比如数据库。

        内存区域或磁盘空间可以作为进程在做某种事务型操作时的缓存,例如下载一个很大的文件,对其操作并将结果写入数据库的过程。12-Factor应用根本不用考虑这些缓存的内容是不是可以保留给之后的请求来使用,这是因为应用启动了多种类型的进程,将来的请求多半会由其他进程来服务。即使在只有一个进程的情形下,先前保存的数据(内存或文件系统中)也会因为重启(如代码部署、配置更改、或运行环境将进程调度至另一个物理区域执行)而丢失。

        源文件打包工具(Jammit, django-compressor) 使用文件系统来缓存编译过的源文件。12-Factor 应用更倾向于在 构建步骤做此动作——正如 Rails资源管道 ,而不是在运行阶段。

        一些互联网系统依赖于 “粘性 session”, 这是指将用户 session 中的数据缓存至某进程的内存中,并将同一用户的后续请求路由到同一个进程。粘性 session 是 12-Factor 极力反对的。Session 中的数据应该保存在诸如 Memcached 或 Redis 这样的带有过期时间的缓存中。

4.7 端口绑定

        通过端口绑定(Port binding)来提供服务。

         互联网应用有时会运行于服务器的容器之中。例如 PHP 经常作为 Apache HTTPD 的一个模块来运行,正如 Java 运行于 Tomcat。

        12-Factor 应用完全自我加载 而不依赖于任何网络服务器就可以创建一个面向网络的服务。互联网应用 通过端口绑定来提供服务,并监听发送至该端口的请求。

        本地环境中,开发人员通过类似http://localhost:5000/的地址来访问服务。在线上环境中,请求统一发送至公共域名而后路由至绑定了端口的网络进程。

        通常的实现思路是,将网络服务器类库通过 依赖声明 载入应用。例如,Python 的 Tornado, Ruby 的Thin , Java 以及其他基于 JVM 语言的 Jetty。完全由 用户端 ,确切的说应该是应用的代码,发起请求。和运行环境约定好绑定的端口即可处理这些请求。

        HTTP 并不是唯一一个可以由端口绑定提供的服务。其实几乎所有服务器软件都可以通过进程绑定端口来等待请求。例如,使用 XMPP 的 ejabberd , 以及使用 Redis 协议 的 Redis 。

        还要指出的是,端口绑定这种方式也意味着一个应用可以成为另外一个应用的 后端服务 ,调用方将服务方提供的相应 URL 当作资源存入 配置 以备将来调用。

4.8 并发

        通过进程模型进行扩展。

        任何计算机程序,一旦启动,就会生成一个或多个进程。互联网应用采用多种进程运行方式。例如,PHP 进程作为 Apache 的子进程存在,随请求按需启动。Java 进程则采取了相反的方式,在程序启动之初 JVM 就提供了一个超级进程储备了大量的系统资源(CPU 和内存),并通过多线程实现内部的并发管理。上述 2 个例子中,进程是开发人员可以操作的最小单位。

        

        在 12-factor 应用中,进程是一等公民。12-Factor 应用的进程主要借鉴于 unix 守护进程模型 。开发人员可以运用这个模型去设计应用架构,将不同的工作分配给不同的 进程类型 。例如,HTTP 请求可以交给 web 进程来处理,而常驻的后台工作则交由 worker 进程负责。

        这并不包括个别较为特殊的进程,例如通过虚拟机的线程处理并发的内部运算,或是使用诸如 EventMachine,  Twisted,  Node.js 的异步/事件触发模型。但一台独立的虚拟机的扩展有瓶颈(垂直扩展),所以应用程序必须可以在多台物理机器间跨进程工作。

        上述进程模型会在系统急需扩展时大放异彩。 12-Factor 应用的进程所具备的无共享,水平分区的特性 意味着添加并发会变得简单而稳妥。这些进程的类型以及每个类型中进程的数量就被称作 进程构成 。

        12-Factor 应用的进程 不需要守护进程 或是写入 PID 文件。相反的,应该借助操作系统的进程管理器(例如 Upstart ,分布式的进程管理云平台,或是类似 Foreman 的工具),来管理 输出流 ,响应崩溃的进程,以及处理用户触发的重启和关闭超级进程的请求。

4.9 易处理

        快速启动和优雅终止可最大化健壮性。

        12-Factor 应用的 进程 是 易处理(disposable)的,意思是说它们可以瞬间开启或停止。 这有利于快速、弹性的伸缩应用,迅速部署变化的 代码 或 配置 ,稳健的部署应用。

        进程应当追求 最小启动时间 。 理想状态下,进程从敲下命令到真正启动并等待请求的时间应该只需很短的时间。更少的启动时间提供了更敏捷的 发布 以及扩展过程,此外还增加了健壮性,因为进程管理器可以在授权情形下容易的将进程搬到新的物理机器上。

        进程 一旦接收 终止信号(SIGTERM) 就会优雅的终止 。就网络进程而言,优雅终止是指停止监听服务的端口,即拒绝所有新的请求,并继续执行当前已接收的请求,然后退出。此类型的进程所隐含的要求是HTTP请求大多都很短(不会超过几秒钟),而在长时间轮询中,客户端在丢失连接后应该马上尝试重连。

        对于 worker 进程来说,优雅终止是指将当前任务退回队列。例如,RabbitMQ 中,worker 可以发送一个NACK信号。 Beanstalkd 中,任务终止并退回队列会在worker断开时自动触发。有锁机制的系统诸如 Delayed Job 则需要确定释放了系统资源。此类型的进程所隐含的要求是,任务都应该 可重复执行 , 这主要由将结果包装进事务或是使重复操作 幂等 来实现。

        进程还应当在面对突然死亡时保持健壮,例如底层硬件故障。虽然这种情况比起优雅终止来说少之又少,但终究有可能发生。一种推荐的方式是使用一个健壮的后端队列,例如 Beanstalkd ,它可以在客户端断开或超时后自动退回任务。无论如何,12-Factor 应用都应该可以设计能够应对意外的、不优雅的终结。Crash-only design 将这种概念转化为 合乎逻辑的理论。

4.10 开发环境与线上环境等价

        尽可能的保持开发,预发布,线上环境相同。

        从以往经验来看,开发环境(即开发人员的本地 部署)和线上环境(外部用户访问的真实部署)之间存在着很多差异。这些差异表现在以下三个方面:

时间差异: 开发人员正在编写的代码可能需要几天,几周,甚至几个月才会上线。人员差异: 开发人员编写代码,运维人员部署代码。工具差异: 开发人员或许使用 Nginx,SQLite,OS X,而线上环境使用 Apache,MySQL 以及 Linux。

12-Factor 应用想要做到 持续部署 就必须缩小本地与线上差异。 再回头看上面所描述的三个差异:

缩小时间差异:开发人员可以几小时,甚至几分钟就部署代码。缩小人员差异:开发人员不只要编写代码,更应该密切参与部署过程以及代码在线上的表现。缩小工具差异:尽量保证开发环境以及线上环境的一致性。

将上述总结变为一个表格如下:

        后端服务 是保持开发与线上等价的重要部分,例如数据库,队列系统,以及缓存。许多语言都提供了简化获取后端服务的类库,例如不同类型服务的 适配器 。下列表格提供了一些例子。

        开发人员有时会觉得在本地环境中使用轻量的后端服务具有很强的吸引力,而那些更重量级的健壮的后端服务应该使用在生产环境。例如,本地使用 SQLite 线上使用 PostgreSQL;又如本地缓存在进程内存中而线上存入 Memcached。

        12-Factor 应用的开发人员应该反对在不同环境间使用不同的后端服务 ,即使适配器已经可以几乎消除使用上的差异。这是因为,不同的后端服务意味着会突然出现的不兼容,从而导致测试、预发布都正常的代码在线上出现问题。这些错误会给持续部署带来阻力。从应用程序的生命周期来看,消除这种阻力需要花费很大的代价。

        与此同时,轻量的本地服务也不像以前那样引人注目。借助于Homebrew,apt-get等现代的打包系统,诸如Memcached、PostgreSQL、RabbitMQ 等后端服务的安装与运行也并不复杂。此外,使用类似 Chef 和 Puppet 的声明式配置工具,结合像 Vagrant 这样轻量的虚拟环境就可以使得开发人员的本地环境与线上环境无限接近。与同步环境和持续部署所带来的益处相比,安装这些系统显然是值得的。

        不同后端服务的适配器仍然是有用的,因为它们可以使移植后端服务变得简单。但应用的所有部署,这其中包括开发、预发布以及线上环境,都应该使用同一个后端服务的相同版本。

4.11 日志

        把日志当作事件流。

        日志 使得应用程序运行的动作变得透明。在基于服务器的环境中,日志通常被写在硬盘的一个文件里,但这只是一种输出格式。

        日志应该是 事件流 的汇总,将所有运行中进程和后端服务的输出流按照时间顺序收集起来。尽管在回溯问题时可能需要看很多行,日志最原始的格式确实是一个事件一行。日志没有确定开始和结束,但随着应用在运行会持续的增加。

        12-factor应用本身从不考虑存储自己的输出流。 不应该试图去写或者管理日志文件。相反,每一个运行的进程都会直接的标准输出(stdout)事件流。开发环境中,开发人员可以通过这些数据流,实时在终端看到应用的活动。

        在预发布或线上部署中,每个进程的输出流由运行环境截获,并将其他输出流整理在一起,然后一并发送给一个或多个最终的处理程序,用于查看或是长期存档。这些存档路径对于应用来说不可见也不可配置,而是完全交给程序的运行环境管理。类似 Logplex 和 Fluent 的开源工具可以达到这个目的。

        这些事件流可以输出至文件,或者在终端实时观察。最重要的,输出流可以发送到 Splunk 这样的日志索引及分析系统,或 Hadoop/Hive 这样的通用数据存储系统。这些系统为查看应用的历史活动提供了强大而灵活的功能,包括:

找出过去一段时间特殊的事件。图形化一个大规模的趋势,比如每分钟的请求量。根据用户定义的条件实时触发警报,比如每分钟的报错超过某个警戒线。

4.12 管理进程

        后台管理任务当作一次性进程运行。

        进程构成(process formation)是指用来处理应用的常规业务(比如处理 web 请求)的一组进程。与此不同,开发人员经常希望执行一些管理或维护应用的一次性任务,例如:

运行数据移植(Django 中的 manage.py migrate, Rails 中的 rake db:migrate)。运行一个控制台(也被称为 REPL shell),来执行一些代码或是针对线上数据库做一些检查。大多数语言都通过解释器提供了一个 REPL 工具(python 或 perl) ,或是其他命令(Ruby 使用 irb, Rails 使用 rails console)。运行一些提交到代码仓库的一次性脚本。

        一次性管理进程应该和正常的 常驻进程 使用同样的环境。这些管理进程和任何其他的进程一样使用相同的 代码 和 配置 ,基于某个 发布版本 运行。后台管理代码应该随其他应用程序代码一起发布,从而避免同步问题。

        所有进程类型应该使用同样的 依赖隔离 技术。例如,如果Ruby的web进程使用了命令 bundle exec thin start ,那么数据库移植应使用 bundle exec rake db:migrate 。同样的,如果一个 Python 程序使用了 Virtualenv,则需要在运行 Tornado Web 服务器和任何 manage.py 管理进程时引入 bin/python 。

        12-factor 尤其青睐那些提供了 REPL shell 的语言,因为那会让运行一次性脚本变得简单。在本地部署中,开发人员直接在命令行使用 shell 命令调用一次性管理进程。在线上部署中,开发人员依旧可以使用ssh或是运行环境提供的其他机制来运行这样的进程。

5 云原生技术体系

https://www.processon.com/view/5ec6afcd5653bb6f2a23d134#map

 

 

 

 

 

 

 

云原生应用的12要素:http://blog.didispace.com/12factor-zh-cn/

一文看懂云原生,正确迎接云时代的到来:https://zhuanlan.zhihu.com/p/82003941

 

最新回复(0)