28.8 标准与规范

随着Docker带来的容器技术的爆发,社区在不断增强容器技术易用性的同时,也在思考如何更长远地发展容器技术,如兼容不同的容器标准,适应更多类型的操作系统平台以及设计应用等。目前沿着这个方向努力,已经有了一些组织(如开放容器倡议OCI)在倡导成立一些推荐大家都遵守的容器标准和规范,同时也总结了一些面向云应用的设计实践经验。

1.runC标准

runC标准最早由Docker公司在2014年2月左右推出,项目地址为https://github.com/opencontainers/runc,它的目标是打造一套轻量级的、标准化的容器运行环境。

通过它,容器可以在多种平台上得到统一的运行时环境以及更好的资源隔离。目前,runC已经贡献给了开放容器倡议,得到了包括Docker、Google、IBM在内的众多厂家的支持。

目前,Docker 1.11+版本中已经默认集成了runC机制的支持。

2.appC标准

appC来自于另外一家容器领域的积极贡献者CoreOS公司,最早在2014年11月左右提出,项目地址为https://github.com/appc

除了对运行时环境进行了一些定义,appC还对容器如何进行打包、如何保持对环境的配置(挂载点、环境变量)、如何验证镜像、如何传输镜像等尝试进行规定。

遵循appC标准,CoreOS公司实现了rkt容器机制。

目前,appC也已经贡献给了开放容器倡议组织,尝试推出更开放规范的标准。

3.Nulecule

Nulecule来自于ReDHat公司的贡献,最早在2015年3月左右开源,项目地址为https://github.com/projectatomic/nulecule/。Nulecule面向的应用场景是描述由多个容器组成的复合应用栈。

目前,支持多容器应用的平台。如Docker Swarm采用了Docker Compose模板文件,而Kubernetes则采用了自定义的描述文件,它们彼此之间无法通用,那么Nulecule则都支持,它定义一套标准的、通用的、可移植的格式,定义了多容器应用的部署规范,包括应用的元数据、依赖、参数配置等。

为了支持Nulecule,RedHat公司还推出了Atomic项目,提供快速使用支持。

4.开放容器格式

为了推动容器标准化,2015年6月22日,AWS、EMC、IBM、谷歌、Docker、CoreOS、redhat等数十家公司共同牵头成立了开放容器倡议组织(Open Container Initiative,OCI),旨在建立一套通用的容器规范OCF。该组织现在受到Linux基金会的支持,其官方网站为https://www.opencontainers.org

目前,OCI正在推动所提出的开放容器规范(Open Container Format,OCF),融合了来自runC、appC等多家容器规范,试图打造一套移植性好、开放统一的容器标准。目前已经有了对容器运行时、镜像格式等方面的规范草案。

OCF对标准容器运行时规范制定了5条原则:

·标准化操作(standard operations):创建、删除、打包容器等操作都必须标准化;

·内容无关性(contentagnostic):操作应该跟内容无关,保持行为上的一致性;

·平台无关性(infrastructureagnostic):在任何支持OCI的平台上,操作都必须能同等执行;

·设计考虑自动化(designed for automation):标准容器是为自动化而生,其规范必须考虑自动化条件;

·企业级交付(industrialgrade delivery):标准容器需要适用于企业级流水线的交付任务。

5.云应用12要素

在云计算时代,应用的整个生命周期将在数据中心里渡过,这跟传统软件模式极大不同。

云应用实际上意味着:代码+配置+运行时环境。

什么样的软件才是可用性和可维护性好的软件?

什么样的代码才能避免后续开发的上手障碍?

什么样的实施才能可靠地运行在分布式的环境中?

Heroku(PaaS服务提供商,2010年被Salesforce收购)平台创始人Adam Winggins提出了云应用12要素(12factor),对开发者设计和实现云时代(特别是PaaS和SaaS上)高效的应用都有很好的参考意义。

(1)Codebase——代码仓库

One codebase tracked in revision control,many deploys。

每个子系统都用独立代码库管理,使用版本管理,实现独立的部署。即将系统拆分为多个分布式应用,每个应用使用自己的代码库进行管理。多个应用之间共享的代码用依赖库的形式提供。

(2)Dependencies——依赖

Explicitly declare and isolate dependencies。

显式声明依赖,通过环境来严格隔离不同依赖。所依赖的与所声明的要保持一致,并且声明要包括依赖库的版本信息。

(3)config——配置

Store config in the environment。

在环境变量中保存配置信息,避免放在源码或配置文件中。

(4)Backing Services——后端服务

Treat backing services as attached resources

后端服务(数据库、消息队列、缓存等)作为可挂载资源来使用,这样系统跟外部依赖尽量松耦合。

(5)Build,release,run——生命周期管理

Strictly separate build and run stages。

区分不同生命周期的运行环境,包括创建(代码编译为运行包)、发布(多个运行包和配置放一起打包,打包是一次性的,每次修改都是新的release)、运行,各个步骤的任务都很明确,要相互隔离。例如,绝对不允许在运行时去改代码和配置信息(见过太多工程师直接SSH到生产环境修bug了)。

(6)Processes——进程

Execute the app as one or more stateless processes。

以一个或多个无状态的进程来运行应用,即尽量实现无状态,不要在进程中保存数据。尽量通过数据库来共享数据。

(7)Port binding——端口

Export services via port binding。

通过端口绑定来对外提供服务。可以是HTTP、XMPP、Redis等协议。多个应用之间通过URL来使用彼此的服务。

(8)Concurrency——并发模型

Scale out via the process model。

通过进程控制来扩展,即尽量以多进程模型进行扩展。

(9)Disposability——任意存活

Maximize robustness with fast startup and graceful shutdown。

快速启动(秒级响应),优雅关闭(收到SIGTERM信号后结束正在处理请求,然后退出),并尽量鲁棒(随时kill,随时crash都不应该导致问题)。

(10)Dev/prod parity——减少开发与生产环境的差异性

Keep development,staging,and production as similar as possible。

尽量保持从开发、演练到生产部署环境的相似性。这点很不容易,真正要求工程师懂研发,还得懂运维。

(11)Logs——日志

Treat logs as event streams。

将日志当作事件流来进行统一的管理和维护(使用Logstash等工具)。应用只需要将事件写出来,例如到标准输出stdout,剩下的由采集系统处理。

(12)Admin processes——管理

Run admin/management tasks as one-off processes。

将管理(迁移数据库、查看状态等)作为一次性的系统服务来使用。管理代码跟业务代码要放在一起进行代码管理。