云原生时代下的12-factor应用与实践(上)

社区编辑2018-05-18 14:25


本次分享将结合 Docker等技术,介绍在 Cloud Native时代下,如何一一实践 12-Factor原则。

云原生

云原生(Cloud Native)是由 Pivotal 的 Matt Stine在 2013年提出的一个概念,是他多年的架构和咨询总结出来的一个思想的集合。

那怎么去理解云原生应用?我觉得可以从三个角度来说明,这和云计算平台的三个层次不谋而合,如下图:



○ IaaS看做基础云设施,用来提供各种基础资源 (Infrastructure)

○ PaaS作为开发平台,用来提供各种平台服务 (Platform)

○ SaaS交付应用或服务,直面用户,提供应用价值 (Application)

云原生应用,正好契合了云、平台和服务,一层层建构,所以我通常就把它理解为面向云(平台)来设计我们的应用。网易三拾众筹的架构师陈晓辉还为它起了一个小清新的名字——向云而生,我觉得非常贴切,再通俗一点讲,也可以叫做云平台应用。


12-Factor

12-Factor,是由 Heroku创始人 Adam Wiggins首次提出并开源,并由众多经验丰富的开发者共同完善,这综合了他们关于 SaaS应用几乎所有的经验和智慧,是开发此类应用的理想实践标准。

12-Factor 全称叫 The Twelve-Factor App,它定义了一个优雅的互联网应用在设计过程中,需要遵循的一些基本原则,和 Cloud-Native 有异曲同工之处。其中文翻译不少,我觉得“十二要素”或“十二原则”比较贴切。

那具体有哪十二原则了,见下图:



在接下来的应用和实践当中,我们会一一实践每条原则。

注:虽然 12-Factor 的原文书籍都是发布在其官网上,但因为网络问题和格式问题,不是很方便阅读,我将其转化为了 GitBook 格式,并架设在网易云基础服务平台上,同时开源在 GitHub 上,方便大家阅读和下载:


在线阅读地址:
http://12.bingohuang.com/zh_cn/index.html
GitHub 开源地址:
https://github.com/bingohuang/12factor-gitbook
pdf/epub下载地址:
https://github.com/bingohuang/12factor-gitbook/download
GitBook 地址:
https://www.gitbook.com/book/bingohuang/12factor/details

应用与实践

既然 12-factor作为 SaaS开发的最佳实践原则,当然脱离不了实践,接下来我们就来设计一款云原生应用,并依照 12-factor,一步步验证和升级我们的应用。从中,我们将讲解每个 Factor的要点,以及如何在我们的应用中实践 Factor。


应用准备

这是一个面向云平台设计的简单 Web应用,它将暴露一个 HTTP REST风格的接口,可以实现对 user 的增删改查功能,将用到以下技术栈:

1. 基于 Node.js,用 Node.js 写 Web应用非常方便,而且是当今最火的编程平台之一。

下载安装 Node.js (包含 npm):https://nodejs.org/zh-cn/download/

注:Node 版本只要 v4.4 以上就够用,当前最新的稳定版是 v6.9.5, 我本地的版本是 v5.12.0

2. 基于 Sails,类似 Rails 框架,用于快速开发 Node.js 应用:http://sailsjs.com/

安装 Sails 框架最新版:npm install sails -g

3. 基于 mongo 3.2 :https://docs.mongodb.org/manual/installation/

4. 基于 Docker,非常契合 12-Factor理念,作为我们打包、发布、运行的工具。

安装 Docker:https://docs.docker.com/engine/installation/

5. 以上环境安装好之后,就开始初始化我们的应用并运行,应用的名称就叫:12factor-app。

$ sails new 12factor-app
info: Created a new Sails app `12factor-app`!
$ cd 12factor-app
$ sails generate api user
info: Created a new api!
$ npm start


注:本文源代码放在 GitHub上,请参考文后参考链接

仅需 4条命令就搞定了应用的框架代码,并自动生成了基于 user的 CRUD接口,我们已经将应用启动起来,可以通过以下方式本地调试接口:

控制台输出正常,在浏览器中访问下面链接,即可看到 Sails应用的首页:http://localhost:1337



接着,就可以通过本地 curl命令或者 http工具来做接口调试,这里以常规的增删改查为例:

1. 增加一个新用户

$ curl -XPOST http://localhost:1337/user?name=bingo
{
"name": "bingo",
"createdAt": "2017-02-13T06:13:53.791Z",
"updatedAt": "2017-02-13T06:13:53.791Z",
"id": 58a41d952f53291200b9e065
}

2. 获取用户列表

$ curl http://localhost:1337/user
[
{
"name": "bingo",
"createdAt": "2017-02-13T06:13:53.791Z",
"updatedAt": "2017-02-13T06:13:53.791Z",
"id": 58a41d952f53291200b9e065
}
]

3. 修改一个用户

curl -XPUT http://localhost:1337/user/58a41d952f53291200b9e065?name=bingohuang
{
"name": "bingohuang",
"createdAt": "2017-02-13T06:13:53.791Z",
"updatedAt": "2017-02-13T06:14:13.460Z",
"id": 58a41d952f53291200b9e065
}

4. 删除一个用户

curl -XDELETE http://localhost:1337/user/58a41d952f53291200b9e065

我已经将该应用部署到了网易云基础服务在线平台,如果您对这个应用感兴趣,直接将 localhost替换为 59.111.110.95,一样可以体验 CRUD操作,如下所示,只要把 yourname换成您的名字即可(建议在 PC端操作):

# 注册你自己
curl -XPOST http://59.111.110.95:1337/user?name=yourname
# 查看所有注册过的用户
curl http://59.111.110.95:1337/user
# 或者 PC浏览器直接访问 http://59.111.110.95:1337/user

接下来开始就让我们开始一一实践 12-Factor中的每条原则吧,每个原则中我们将分为 Factor解说和 Factor实践两块。



1 基准代码

Factor解说:

12-Factor应用只有一份基准代码(Codebase),可以多份部署(deploy)。

意思就是说一个应用只有一份用来跟踪所有修订版本的代码仓库,基准代码和应用之间总是保持一一对应的关系,因为:

○ 一旦有多个基准代码,就不能称为一个应用,而是一个分布式系统。分布式系统中的每一个组件都是一个应用,每一个应用可以分别使用 12-Factor 进行开发。
○ 多个应用共享一份基准代码是有悖于 12-Factor 原则的。解决方案是将共享的代码拆分为独立的类库,然后使用依赖管理(第二个原则) 策略去加载它们。
○ 多份部署相当于是运行了该应用的多个实例,比如开发环境一个实例,测试环境、生产环境都有一个实例。
○ 一个代码仓库,确保了单一的信任源,从而保证了更少的配置错误和更强的容错和复原能力。



Factor实践:

使用 Git作为应用的版本管理系统,使用 GitHub我们的在线仓库。

在刚刚创建好的应用目录下执行:

$ echo "# 12factor-app" >> README.md
$ git init
$ git add .
$ git commit -m "first commit"
$ git remote add origin git@github.com:bingohuang/12factor-app.git
$ git push -u origin master


2 依赖

Factor解说:

12-Factor规则下的应用程序不会隐式依赖系统级的类库。

意思就是说:通过依赖清单声明所有依赖项,通过依赖隔离工具确保程序不会调用系统中存在但清单中未声明的依赖项。并统一应用到生产和开发环境。

云平台根据这些声明管理依赖,确保云应用所需的库和服务。

Factor实践:

package.json 就是我们的依赖清单,所有应用程序的依赖都声明在此。

{
"name": "12factor-app",
"private": true,
"version": "0.0.0",
"description": "a Sails application",
"keywords": [],
"dependencies": {
"ejs": "2.3.4",
"grunt": "1.0.1",
"grunt-contrib-clean": "1.0.0",
"grunt-contrib-coffee": "1.0.0",
"grunt-contrib-concat": "1.0.1",
"grunt-contrib-copy": "1.0.0",
"grunt-contrib-cssmin": "1.0.1",
"grunt-contrib-jst": "1.0.0",
"grunt-contrib-less": "1.3.0",
"grunt-contrib-uglify": "1.0.1",
"grunt-contrib-watch": "1.0.0",
"grunt-sails-linker": "~0.10.1",
"grunt-sync": "0.5.2",
"include-all": "^1.0.0",
"rc": "1.0.1",
"sails": "~0.12.11",
"sails-disk": "~0.10.9"
},
"scripts": {
"debug": "node debug app.js",
"start": "node app.js"
},
"main": "app.js",
"repository": {
"type": "git",
"url": "git://github.com/bingo/12factor-app.git"
},
"author": "bingo",
"license": ""
}

# 接下来我们加入 mongodb的库依赖(后续会用到),只需要执行:

npm install sails-mongo --save

# 同时 package.json 中会有相应的变更

{
...
"dependencies": {
...
"sails-mongo": "^0.12.2" //最新加入的依赖
}
...
}

应用程序需要用到的依赖库都安装在 node_modules文件夹下,该文件夹就是作为应用的依赖隔离,并且和系统的库是隔离的。

3 配置

Factor解说:

12-Factor推荐将应用的配置存储于环境变量中,保证配置排除在代码之外,有如下好处:

○ 环境变量是一种清楚、容易理解和标准化的配置方法;
○ 环境变量可以非常方便地在不同的部署间做修改,却不动一行代码;
○ 与配置文件不同,不小心把它们签入代码库的概率微乎其微;
○ 与一些传统的解决配置问题的机制(比如 Java 的属性配置文件)相比,环境变量与语言和系统无关;
○ 存储在环境变量中的另一个好处是,方便和 Docker配合使用。

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

Factor实践:

在应用程序的 config/connections.js 文件中,我们使用 MONGO_URL 这个环境变量来定义 mongo 的连接方式。

module.exports.connections = {
mongo: {
adapter: 'sails-mongo',
url: process.env.MONGO_URL
}
};

在文件中,指定 module所使用的连接。

module.exports.models = {
connection: mongo,
migrate: 'safe'
};

如果你在本地起了一个 mongodb测试服务,就可以用这个命令验证应用是否正常配置。

MONGO_URL=mongodb://localhost:27017/12factor-app npm start


4 后端服务

Factor解说:

12-Factor 应用不会区别对待本地或第三方服务,统一把后端服务 (backing services)当作附加资源或者说是远程的资源。

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

除了本地服务之外,应用程序有可能使用了第三方发布和管理的服务,如 SMTP(例如 Postmark),数据收集服务,数据存储服务(如 Amazon S3),以及使用 API 访问的服务(例如 Twitter)等。

对应用程序而言,本地或第三方服务都是附加资源,通过一个 url 或是其他存储在 配置中的设置来获取数据,仅需修改配置中的资源地址即可。

应用也因此具有容错和复原能力,因为它一方面要求编码时就要考虑资源不可用的情况,另外一方面也增强微服务方法的好处。



本文未结束,敬请期待下篇。