设为首页收藏本站

LUPA开源社区

 找回密码
 注册
文章 帖子 博客
LUPA开源社区 首页 业界资讯 技术文摘 查看内容

有“容”乃大:Docker容器,十万网店轻松托管

2014-11-27 16:17| 发布者: joejoe0332| 查看: 2117| 评论: 0|原作者: CSDN|来自: CSDN

摘要: Shopify是一家提供电商网店解决方案的公司,目前服务的网店数有10万家以上(Tesla 也是它的用户)。网站主要的框架是Ruby on Rails,1700个内核和6TB RAM,每秒可以响应8000个用户请求。 ...


将你的应用容器化

  随着环境的就绪,我们可以把注意力转到程序的容器化上来。

  我们更倾向于thin container能准确地做一件事。例如一个unicorn(Unicorn是一个Unix和局域网/本地主机优化的HTTP服务器)master和工作线程服务web请求,或者一个Resque(Resque使用Redis创建后台任务,存储进队列,并随后执行。它是Rails下最常用的后台任务管理工具之一)工作线程服务一个特定的队列。thin container允许细粒度的缩放比例(一般是指远程方法调用的接口的颗粒大小),以满足需求。例如,我们可以增加检查垃圾邮件攻击响应的 Resque工作线程的数目。

  我们发现采取一些标准约定对于在容器中的代码布局会有用处:

  • 应用程序总是root在容器内部的/app下;
  • 应用程序通常通过单个端口发布服务;

  我们还确立了一些容器化git repo(repo封装了对git的操作)的约定:

  • /container/files 拥有一个在其建立时就被直接复制进容器的文件树,举个例子,请求 Splunk 索引的应用程序日志,它添加/container/files/etc/splunk.d/inputs.conf 文件进你的git repo就足够了(控制日志索引的责任转移到开发者,这是一个微妙而重大的转变,过去这都是运维管理员的工作);
  • /container/compile是一个 shell 脚本,编译您的应用程序,并生成一个随时可运行的容器。建立此文件并适应您的应用程序,是最复杂的地方;

  • /container/roles.json保存命令行以机器可读的形式用来运行该工作负载。很多我们的应用程序以多个角色,运行相同代码库,一些处理 web 流量同时处理后台任务。这部分的灵感来自 Heroku 的 procfile。以下是一个示例的roles.json文件:

  我们用一个简单的Makefile驱动建立,也可以本地运行。我们的 Dockerfile 看起来像这样:取消编译阶段的目标是产生一个是即刻准备运行的容器。Docker关键的优势之一就是在容器启动时超级快速启动而不被损坏因为额外的工作。为了实现这一目标您需要了解您的整个部署过程。 举几个例子:

  • 我们正在使用Capistrano(Capistrano是一种在多台服务器上运行脚本的开源工具,它主要用于部署web应用)将代码部署到机器,并且asset编译以前发生的事情作为部署的一部分。通过移动资产编译到容器里生成,部署新代码由更简单、快几分钟。
  • 我们的unicorn 主机启动来从table 模型中获取数据(Table 对象代表一个 HTML 表格。)不只这是缓慢的,而且我们更小的容器大小意味着将需要更多的数据库连接。另一方面,它是可能做到这一点(获取数据)的在容器建立的时候,以加速启动。

  在本例中,编译阶段包括以下的逻辑步骤:

  • bundle install
  • asset compile
  • database setup

  为了保持这一公告(邮件)的大小合理,我们简化了一些细节。密钥管理是一个主要的细节,我们还没有在这里讨论。不要将其登记入源码管理。我们已经获取了用来加密密钥的代码,致力于该主题的博客帖子很快就会来了。


调试和细节

  运行在容器中的应用程序和容器之外的表现绝大多数情况是相同的。此外,你的大多数调试工具(例如:strace,gdb,/proc/filesystem)是运行在Docker所在的host上。还有大家熟悉的工具nsenter或nsinit,可以用他们挂载到一个运行中的容器里进行调试。

  docker exec,作为docker 1.3.0版本中提供的新工具,能够被用来向运行的容器中注入进程。但是,不幸的是,如果你注入进程需要root权限,你还是需要通过nsenter,有些地方的情况可能不会像人们预料的那样。


进程分层

  尽管我们运行轻量级的容器,仍然需要初始化进程(pid=1)从而与监控工具,后台管理,服务发现等做到紧密集成,也能给我们细粒度的健康监测。

  除了初始化进程,我们在每个容器中添加了一个ppidshim进程(pid=2),应用程序进程的pid=3。由于ppdishim进程的存在,应用程序没有直接继承自初始化进程,避免它们以为自己是后台守护进程,造成错误的后果。

  最终的进程层次是这样的:


Signals

  如果你在使用容器技术,你很可能会修改现有的运行脚本或写一套新的脚本,其中包含了docker run的调用。默认的情况下,docker run会代理signal到你的应用程序,所以你必须理解应用程序是如何解读signal的。

  标准的UNIX做法是发送SIGTERM去请求有序地关闭一个进程。为了确保应用程序遵守这个惯例,Resque使用SIGQUIT来有序地关闭进程,SIGTERM来紧急关闭进程。幸运的是,Resque可以被配置使用SIGTERM来优雅地关闭进程。


Hostnames

  我们选择容器名称来描述工作负载(例如 unicorn-1, resque-2),并结合这些主机名来进行简单追溯。最终的结果会是这样:unicorn-1.server2.shopify.com.我们使用Docker的主机名标签将结果传入容器中,这使大多数应用程序报告出正确的值。一些程序(Ruby)询问主机名来得到缩略名(unicorn-1),而不用询问预期的FQDN。由于Docker管理着/etc/resolv.conf 文件,我们当前版本不允许任意变更,所以我们通过LD_PRELOAD,重写了gethostname()和uname()的方法,并注入到library中。最终结果会是,监控工具发布我们想要得到的主机名却不用去更改应用程序代码。


注册和部署

  我们发现构建容器的过程中复制‘bare metal’的行为相当于一个不断调试的过程。如果你是理智的的,你肯定想自动化的去构建容器。

  为了每个人都能push,我们使用github commit hook去触发一个容器的构建,构建过程中我们会提交状态日志来判定是否构建成功。我们使用git提交SHA到容器的“docker tag”,所以你能准确的看到什么样的代码版本被包含在容器中。为了更容易被脚本调试和使用,我们同样也把SHA放到容器里的一个文件中(/app/REVISION)。

  一旦你的构建是正常的,你会想把容器push到一个中央的注册表仓库。为了提高部署速度和减少外部依赖我们选择运行数据中心中自己的库。 我们在Nginx反向代理(其中缓存了GET请求的内容)背后运行多个标准Python注册表的副本,如下图所示:


  当多个Docker主机请求相同的镜像时,我们发现大型网络接口(10 Gbps)和反向代理,在解决”thundering herd“这样的代码部署相关的问题是很有效的。代理方法还允许我们运行多种类型的库,并在发生故障的情况下,提供自动故障转移。

  如果遵循这个蓝图,你可以自动化去构建容器,并且安全地把容器存储在一个中央仓库注册表中,这些都可以融入你的部署过程。

  在本系列的下一篇文章,作者将描述如何管理应用程序的秘密。


原文链接:Docker at Shopify: How we built containers that power over 100,000 online shops(编译/ CSDN-Docker字幕菌群(王大隆、孙宏亮、吴京润、吴方洲、周敬滨、赵文举)

酷毙

雷人

鲜花

鸡蛋

漂亮
  • 快毕业了,没工作经验,
    找份工作好难啊?
    赶紧去人才芯片公司磨练吧!!

最新评论

关于LUPA|人才芯片工程|人才招聘|LUPA认证|LUPA教育|LUPA开源社区 ( 浙B2-20090187 浙公网安备 33010602006705号   

返回顶部