【编者按】 Shopify是一家提供电商网店解决方案的公司,目前服务的网店数有10万家以上(Tesla 也是它的用户)。网站主要的框架是Ruby on Rails,1700个内核和6TB RAM,每秒可以响应8000个用户请求。为了更易扩展和管理业务,Shopify开始使用Docker和CoreOS技术,Shopify软件工程师Graeme Johnson将撰写一系列文章分享其经验,本文是系列中的第二篇,重点介绍了Shopify在生产环境中是如何使用容器技术。
这是第一篇召集群里(230365411)的同学们一起翻译的文章,谢谢大家的义务帮助,本次参与翻译的同学包括王大隆、孙宏亮、吴京润、吴方洲、周敬滨、赵文举。我们也欢迎你的加入!
以下为翻译原文: 
为什么使用容器技术? 在我们深入到构建容器的机制之前,首先讨论一下这么做的动机。容器在数据中心拥有的潜能就像游戏机之于游戏。在PC游戏的初期,通常在你开始玩一款游戏之前,都需要安装视频或音频驱动。然而,游戏机提供了不同的体验,与Docker很类似: - 可预见性:游戏带它自带游戏,随时可运行,不需要下载或更新;
- 迅速:游戏带采用只读存储器,从而获得了如同闪电的速度;
- 简单:游戏带是健壮的,并且很大程度上拥有儿童防护措施,它们是真正的即插即用;
- 可预见性、迅速、简单,在扩展规模方面都是好东西。 Docker容器提供了构建模块,使我们的数据中心更容易运行、更适合使应用成为独立模块,随时就绪的单元就像是游戏机上的游戏带。
引导程序 为了努力实现容器化,你需要同时具备开发和运维技能。首先,与你的运维团队交流,你需要确信你的容器能够完全复制于你的生产环境。如果你运行在OSX(或windows)桌面操作系统,但部署在Linux上,使用一个虚拟机(比如Vagrant)作为本地测试环境。第一步,更新你的操作系统和安装支持包。挑选一个基础镜像匹配你的生产环境(我们使用Ubuntu14.01),不能出差错——你不会想处理容器化和操作系统/包在同一时间升级所带来的麻烦。
选择容器类型 在容器类型方面Docker为你提供了足够的选择空间,从一个单进程的“瘦”容器到一个让你觉得类似于传统虚拟机的“胖”容器(例如,Phusion)。 我们选择去遵循“廋”容器方式,从容器内部去除一些无关的组件。虽然从两种方式作出选择是困难的,但是我们更青睐于小的那种,因为容器简单化会消耗更少的CPU和内存。这种方式被详细的说明在 Docker blog中。
环境配置 在生产环境中,我们使用Chef这一部署工具来管理系统的各个节点。这样的话,我们可以轻松做到在一个容器之中运行Chef,然而我们并不希望某些服务在每一个容器中都运行,比如:日志的索引服务,运行状态采集服务等。而Chef的使用无疑使得很多服务都会重复安装在不必要的容器中,由于无法忍受以上徒劳的重复工作,我们选择在每一台运行Docker的宿主机上共享同一份这些服务的副本。 如何将容器做得轻量级,关键是:将Chef部署工具的运行脚本转换为一个Dockerfile(这部分内容,我们后来将其替换为一个自定义的Docker build流程,之后的文章会涉及)。Docker的诞生,可以说是天赐良机,使运维人员评估内部的生产环境,并回顾以及整理整个系统生命周期中到底需要什么。在这一环节中,对于系统的的割舍请尽量无情,同时也要保证在code review过程中尽量做到谨慎。 其实,整个过程,并没有听起来那么艰难。最终,我们团队以一个125行Dockerfile的形式告终,而该Dockerfile则是定义了在Shopify上所有容器需要共享的基础镜像。该基础镜像包含了25 个包,这些包中包括跨度较大的编程语言运行时(Ruby、Python和Node),还有多种开发工具(Git、Vim、build-essential和Go),也有一些需要共享使用的库文件。同时,基础镜像中还包含了一系列工具脚本用以任务的运行,比如通过调整参数来启动Ruby,或者向Datadog发送监控数据等。 在以上环境下,我们的应用可以很随意在这个基础镜像上添加自身的特定需求。尽管如此,我们最大的应用也仅仅是添加了一些操作系统的依赖包,所以总体来讲,我们的基础镜像还是相对简洁精干的。
容器的100定律 在选择将何种服务容器化时,可以首先假设你有100个小容器运行在同一个host上,然后想一下是否真的有必要运行100个服务的副本,还是大家共享一个单独的host更好。 以下是一些实例说明我们如何根据100定律来决定如何使用容器的: 日志索引:日志对于诊断错误至关重要,尤其在容器退出,文件系统消失后显得更为重要。我们特意避免了修改应用程序的日志行为(比如强制它们重新定向到系统日志),并且允许它们继续写日志到文件系统。运行100个日志代理似乎是错误的做法,所以我们创建一个后台程序去处理以下重要任务: - 运行在host端,并订阅Docker event;
- 在容器启动时,配置日志索引来监视容器;
- 容器销毁时,删除索引指令。
- 为了确保容器退出时,所有的日志都被编入索引,你可能需要稍微延迟容器的销毁。
统计:Shopify在几个级别(系统,中间件和应用程序)上都生成了运行时统计,得到的结果通过代理或应用程序代码转述。 - 许多我们的统计结果可以通过StasD传输,幸运的是我们可以配置Datadog的host端去接收容器传来的流量,通过适当的配置,就可以将StasD接收的地址传给容器;
- 由于容器从本质上来说就是进程树,所以一个host端的系统监控代理可以看到容器边界共享一个单一的系统监控也是自由的;
- 从一个更加以容器为中心的角度出发,来考虑Docker和Datadog的集成,会添加Docker metrics到host端的监控代理;
- 应用级别的metrics大多数运行得还可以,它们或者通过StasD发送事件,或者直接和其它服务对话。为一个容器指定名字很重要,这样一来metrics也更容易读。
Kafka:我们使用Kafka来实时处理从Shopify堆栈到合作伙伴的事件。我们使用Ruby on Rail代码来发布Kafka事件,生成信息,然后放到SysV消息队列。一个Go语言写的后台程序会在队列中取出消息发送给Kafka。这减少了Ruby进程的时间,我们也能更好地应对Kafka服务器的事故。有一点不好的是,SysV消息队列是IPC namespace的一部分,所以我们不能用来连接container:host。我们的解决方案是在host上添加一个socket端,用来将消息放到SysV队列。 100定律的使用需要一定的灵活性。一些情况下,仅仅需要写一下组件的“黏合器”,也可以通过配置来达到目的。最终,你应该获得一个容器,内含你的应用程序运行所需的东西,以及一个提供了Docker托管和共享服务的主机环境。
|