1.2 十二要素应用
在基础架构即服务(Infrastructure as a Service, IaaS)和平台即服务(Platform as a Service, PaaS)发展的早期阶段,人们就发现需要一种新的方式来开发更适用于云计算的应用程序。举个例子,传统数据中心在需要扩容时经常采用纵向扩容的方式,即通过增加单台物理服务器的计算资源来进行扩容。而在云端,通常采用的是横向扩容的方式,即通过增加虚拟服务器的数量来分担负载。这种扩容方式要求应用程序是无状态的,而这个特点也是十二要素(12-factor)应用的宣言之一。十二要素应用这一方法论是由Heroku的工程师从云端应用开发的最佳实践中总结出来的,可以被认为是云原生应用的基础。尽管云计算一直在不停地发展,但是十二要素宣言中的这些原则仍然适用。以下是十二要素的内容及其对于云原生应用的意义:
1.基准代码
一份基准代码,多份部署。
一个应用只有一份基准代码,但是这份代码可以部署到多个环境中,如开发环境、测试环境和生产环境。在云原生架构中,这个原则可以解释成一个服务或者函数只有一份基准代码,它们各自拥有自己的持续集成(Continuous Integration, CI)和持续部署(Continuous Deployment, CD)工作流。
2.依赖
显式地声明依赖关系并隔离依赖。
声明和隔离依赖在云原生应用的开发中很重要。很多问题是由缺少依赖项或依赖项的版本不同造成的,其根源在于内部部署环境和云端环境是有差异的。通常,你应该用类似Maven或npm这样针对不同语言的管理工具来管理依赖项。现在,容器技术已经大大减少了由依赖产生的问题,因为依赖项都会在Dockerfile中进行声明然后被打包进容器中。对系统依赖而言,Chef、Puppet、Ansible和Terraform是很好的管理和安装工具。
3.配置
在环境中存储配置。
配置和代码应该严格分开,这样你才能够轻松地配置不同的环境。例如,在测试环境中,你可以有一个测试配置文件,这个文件中包含的连接字符串和其他信息都是指向这个测试环境的。如果你想把这个应用部署到生产环境中,那么你只需要替换掉这个配置文件就可以了。现在很多平台都支持外部配置,比如使用Kubernetes的配置管理或者使用云端环境中的托管配置服务。
4.后端服务
把后端服务当作附加资源。
后端服务指的是程序运行所需要的通过网络调用的各种服务。举两个在云原生应用领域的例子,比如像缓存服务和数据库服务(Database as a Service, DbaaS),这些都是后端服务。在访问这些后端服务时,一个比较推荐的做法是通过外部配置系统来获取这些服务的配置信息。这样做的好处是降低耦合度,这也是云原生应用的一个基本原则。
5.构建、发布和运行
严格分离构建和运行阶段。
正如你将在第5章中看到的DevOps相关内容,建议使用CI/CD的一些实践来实现全自动地构建和发布应用。
6.进程
用一个或多个无状态的进程来运行应用。
如前所述,在云端的应用应该是无状态的,任何需要持久化的数据都应该存储在外部。这样做才能实现弹性,而弹性是云计算的目的之一。
7.数据隔离
每个服务管理自己的数据。
这是微服务架构的一个关键原则,同时也是云原生应用的一个常见模式。每个服务管理自己的数据,这些数据只有通过该服务的API才能获取。这意味着即使属于同一个应用,一个服务也无法直接访问其他服务中的数据。
8.并发
通过进程模型进行扩展。
云原生应用的两大优势是可扩展性和更有效的资源利用。你可以通过独立横向扩展单个服务或者函数来实现更高的资源利用率。
9.易处理
通过快速启动和优雅退出来最大化应用的健壮性。
容器技术和函数已经能够满足第一点了,因为它们的启动速度都很快。但后一点常常被忽略,在设计一个服务时就应该考虑到程序崩溃和规模收缩问题,这种情况下容器或者函数的实例数量会减少,我们要特别注意这一点。
10.开发环境与线上环境等价
尽可能地保持开发环境、预发布环境和生产环境相同。
你可以利用容器技术来打包服务所需的依赖项,这样可以减少环境的不一致带来的问题。但还是会有一些比较棘手的情况,比如在你的开发环境下,有些受托管的服务是不可用的。第5章会介绍一些技术和方法来使得你的环境尽量保持一致。
11.日志
把日志当作事件流。
在分布式系统中,记录日志是一件很重要的事。因为有那么多的服务在同时运行,又是跑在不同的节点上的,如果你没有一个很好办法来记录日志,一旦你的应用出问题了,那你就抓瞎了。十二要素的这一条就是告诉你可以把日志当作事件流来处理,把这些事件流输出到一个外部系统中统一记录。
12.管理进程
把后台管理任务当作一次性进程来运行。
这一点的意思是你应该把管理任务当作是一个短期进程来执行。函数和容器都是执行这些任务的好工具。
你会在本书中再次了解上述要素,因为它们与云原生应用息息相关。