Awesome Docker@IT·互联网技术

云原生时代下的12-Factor应用与实践

2017-02-21  本文已影响2670人  43ce3d72fadb

作者简介:黄庆兵,网易蜂巢首席技术布道师,浙大硕士毕业,从事云计算、Docker、Go等相关开发及技术布道工作;喜欢开源,乐于分享,勤于布道,折腾过开源小工具,制作过Docker课程,分享过 Gopher Meetup。欢迎一起来探讨技术!个人主页:http://bingohuang.com/

以下为正文:

1. 简介

在云的时代,应用会更多的迁移到云端,基于云的架构设计和开发模式需要一套全新的理念去承载,于是云原生思想应运而生,而针对云原生应用开发的最佳实践原则,12-Factor脱颖而出,同时也带来了新的解读。本文将介绍在Cloud Native时代下,结合Docker等技术,如何一一实践12-Factor原则。

2. 云原生

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

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

1.png

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

3. 12-Factor

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

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

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

2.png

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

注:虽然 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

  1. 应用与实践

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

0. 应用准备

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

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

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

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

安装 Sails 框架最新版:
<code>npm install sails -g</code>

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

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

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

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

<code>
$ 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
</code>

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

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

5.PNG

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

1.增加一个新用户
<code>
$ 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
}
</code>

2.获取用户列表
<code>
$ curl http://localhost:1337/user
[
{
"name": "bingo",
"createdAt": "2017-02-13T06:13:53.791Z",
"updatedAt": "2017-02-13T06:13:53.791Z",
"id": 58a41d952f53291200b9e065
}
]
</code>

3.修改一个用户
<code>
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
}
</code>

4.删除一个用户

<code>
curl -XDELETE http://localhost:1337/user/58a41d952f53291200b9e065
</code>

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

<code>
注册你自己
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
</code>

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

I. 基准代码

Factor解说:

12-Factor应用只有一份基准代码(Codebase),可以多份部署(deploy)。意思就是说一个应用只有一份用来跟踪所有修订版本的代码仓库,基准代码和应用之间总是保持一一对应的关系,因为:

11.png

Factor实践:

使用Git作为应用的版本管理系统,使用GitHub我们的在线仓库
在刚刚创建好的应用目录下执行:

<code>
$ 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
</code>

II. 依赖

Factor解说:

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

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

Factor实践:

package.json 就是我们的 依赖清单,所有应用程序的依赖都声明在此。
<code>
{
"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": ""
}
</code>

接下来我们加入 mongodb 的库依赖(后续会用到),只需要执行:
<code>
npm install sails-mongo --save
</code>

同时 package.json 中会有相应的变更
<code>
{
...
"dependencies": {
...
"sails-mongo": "^0.12.2" //最新加入的依赖
}
...
}
</code>
应用程序需要用到的依赖库都安装在node_modules文件夹下,该文件夹就是作为应用的 依赖隔离,并且和系统的库是隔离的

III. 配置

Factor解说:

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

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

Factor实践:

在应用程序的 config/connections.js 文件中,我们使用 MONGO_URL 这个环境变量来定义 mongo 的连接方式
<code>
module.exports.connections = {
mongo: {
adapter: 'sails-mongo',
url: process.env.MONGO_URL
}
};
</code>
在 文件中,指定module所使用的连接
<code>
module.exports.models = {
connection: mongo,
migrate: 'safe'
};
</code>

如果你在本地起了一个mongodb测试服务,就可以用这个命令验证应用是否正常配置
<code>
MONGO_URL=mongodb://localhost:27017/12factor-app npm start
</code>

IV. 后端服务

Factor解说:

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

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

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

15.PNG

Factor实践:

V. 构建,发布,运行

Factor解说:

12-facfor 应用严格区分构建,发布,运行这三个步骤:

15.PNG

Factor实践:

针对这条原则,强烈推荐使用Docker及其组件(Compose),它的核心理念正是:Build, Ship and Run,将适合在整个构建、发布和运行流程,我们也将从这三个方面进行讲解。

构建:

书写构建脚本:Dockerfile
<code>
FROM hub.c.163.com/library/node:5.12.0
MAINTAINER bingohuang me@bingohuang.com

拷贝依赖清单

COPY package.json /tmp/package.json

安装依赖包

RUN cd /tmp && npm install --registry=https://registry.npm.taobao.org

将依赖包拷贝到应用程序目录下

RUN mkdir /app && cp -a /tmp/node_modules /app/

更改工作目录

WORKDIR /app

拷贝应用程序代码

COPY . /app

设置应用启动端口

ENV PORT 1337

暴露应用程序端口

EXPOSE 1337

启动应用

CMD ["npm","start"]
</code>

Docker 构建
<code>$ docker build -t message-app:v1.0 .
</code>

Docker 镜像 推送:可以将其 push 到指定的镜像仓库,比如网易蜂巢的镜像中仓库

<code>docker push hub.c.163.com/bingohuang/12factor-app:1.0
</code>

发布:

书写发布脚本:docker-compose.yml

<code>version: '2'
services:
mongo:
image: hub.c.163.com/library/mongo:3.2
volumes:
- mongo-data:/data/db
ports:
- "27017:27017"
app:
image: hub.c.163.com/bingohuang/12factor-app:1.0
ports:
-"1337:1337"
links:
- mongo
depends_on:
- mongo
environment:
- MONGO_URL=mongodb://mongo/12factor-app
volumes:
mongo-data:
</code>

以上在构建好的镜像基础上,定义了一个发布过程,并将配置(MONGO_URL)通过环境变量注入进去
<code>
MONGO_URL=mongodb://mongo/12factor-app
</code>

运行:

可以通过Docker Compose在本地运行,也可以通过云平台来在线编排(网易蜂巢即将支持服务编排功能)
<code>
docker-compose up -d
</code>
继而查看日志
<code>
docker-compose logs -f
</code>
注:为了方便不熟悉docker和docker-compose命令的人快速运行程序和本地调试,我在源代码中还提供了 docker.sh 脚本,方便构建、发布和运行应用(源码请看后续资料链接)。

VI. 进程

Factor解说:

Factor实践:

虽然这是一个简单的demo应用,但查看docker容器中的运行进程,发现也有4个进程在运行,其中 npm 也就是我们的启动进程,node app.js 是实际运行应用的进程
<code>
$ docker exec eaaa922abf08 ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.2 2.0 1076204 42024 ? Ssl 18:22 0:00 npm
root 17 0.0 0.0 4340 724 ? S 18:22 0:00 sh -c node app.js
root 18 0.9 4.5 1253808 93808 ? Sl 18:22 0:01 node app.js
root 27 1.1 3.7 962884 77076 ? Sl 18:22 0:01 grunt
</code>
在这里,我们的应用进程是无状态的,持久化的数据都存储在了后端服务 MongoDB 当中
到此,我们已经分享完了6个Factor,并且都成功实践在了我们的应用上,Yes!

接下来,我们继续分享和实践后续的6个Factor。

VII. 端口绑定

Factor解说:

12-Factor 应用通过自我加载而不依赖于任何网络服务器就可以创建一个面向网络的服务。

Factor实践:

docker-compose文件为我们很好的定义了端口绑定
<code>
ports:
- "1337:1337" // 应用容器暴露1337端口在容器中,宿主机将其映射到1337端口
</code>
需要注意的是,如果在一个宿主机中部署多个应用实例,就不能将一个宿主机端口映射到多个容器端口(端口冲突),解决方法是在这之上加一个负载均衡,负载宿主机的不同端口服务所对应的不同容器。

VIII. 并发

Factor解说:

12-factor 应用通过进程模型进行扩展,把进程看作是一等公民,并且具备具备的无共享,水平分区的特性

举例来说,HTTP 请求可以交给 web 进程来处理,而常驻的后台工作则交由 worker 进程负责,定时任务交由 clock 来处理,这样扩展每一类的进程就非常方便,如下图所示:

18.png

Factor实践:

如第六个原则所描述,我们的应用拥有多个进程,最主要的是 Node.js 的http server进程,进程都是无状态并无共享,所以我们可以非常容易的水平扩展应用。

IX. 易处理

Factor解说:

12-Factor 应用的进程是易处理(disposable)的,意思是说任何进程都可以快速启动和优雅终止,这样做的好处是:

如下图所示,就是一个优雅的应用启动和终止流程

19.png

Factor实践:

Docker 先天的轻量级和隔离性,就非常适合来做快速启动和优雅终止,Docker非常适合实践这条原则,在我们的应用中,就加入了 Docker和Compose实践

针对线上环境,推荐构建在容器云平台之上(比如网易蜂巢平台),可以更优雅的处理进程的启动和停止。

X. 环境等价

Factor解说:

12-Factor 应用想要做到持续部署就必须缩小本地与线上差异,包括以下三种差异:

12-Factor 应用的开发人员应该反对在不同环境间使用不同的后端服务

这是因为,不同的后端服务意味着会突然出现的不兼容,从而导致测试、预发布都正常的代码在线上出现问题。

Factor实践:

我们的应用程序中,使用了docker-compose作为我们的发布脚本,它使得应用既可以在本地运行,也可以在任何支持 Docker 的云平台上运行,应用无需变化,只需修改配置文件,很好的解除了不同环境的差异化
从以往经验来看,传统应用和12-Factor应用会存在如下差异:

20.png

XI. 日志

Factor解说:

Factor实践:

21.png

XII. 管理进程

Factor解说:

开发人员经常希望执行一些管理或维护应用的一次性任务,例如:

Factor实践:

5. 总结

至此,12-Factor一一实践完毕,从中可以看出,12-Factor并非相互独立,而是一个整体,有的涉及代码和框架(Node和Rails),有的涉及工具(Docker和Compose)有的涉及架构和平台。在云原生时代,12-Factor仍然具有强大的生命力,每一条原则都是应用开发的珠玑,而且每一个原则也不是一成不变的,随着新的理念出现,原有的Factor会得到延伸和发展,也会出现新的原则,有兴趣的同学,不妨读一读《Beyond the 12 Factor App》这本书,还会有更大的收获。最后,希望此次分享对你理解云原生应用、实践12-Factor有所帮助。

6. 参考链接:

上一篇 下一篇

猜你喜欢

热点阅读