想当初还处于风华正茂的年纪时,我总想当个酷炫的小朋友。之所以想找份跟计算机打交道的工作,是因为这样我就能接触到最新的窗口管理器并使用最前沿的软件版本。然而现实总是残酷的,相信大家都经历过那段“一周七天、每天多次重新编译内核”的黑暗时光。
幸运的是我最终坚持了下来(除了有一次遇上120GB Raid0设置丢失,那一回我确实受到了沉重的打击),而且步入成熟期后、我跟每个老头子一样都开始对变化充满恐惧。
出于这个理由,我逡巡了一段时间才真正下决心上手Docker。每个走在时代最前沿的年轻人都在讨论容器技术,但我已经不再身处那种急于彰显自己的岁数,如今的我只是个平均、内敛而且年纪还不算太大的家伙。
单单通过标题,大家可能已经猜到了故事的结局,不过Docker确实让我有了眼前一亮的感觉。它的出现以一种便捷的方式解决了大量常见于开发及部署流程中的难题。
在经过一段时间的学习后,我对自己的应用程序进行重构以保证其顺畅运行在Docker容器当中。而在随后的几周内,我把注意力集中在了Docker的部署方面。我们的应用程序运行在AWS云环境之下,而在众多Amazon服务中、我们选择了Opsworks——理由很简单,易于设置、易于维护、易于扩展。
遗憾的是,Opsworks并不支持Docker,而Amazon自家的容器运行方案ECS目前尚未正式推出生产版本。我需要把自己的复杂应用迁移到Docker当中,而且希望现时现下马上开始进行。要实现这一目标,惟一的办法就是将Docker支持能力引入Opsworks。
在谷歌了一圈之后,我发现一篇教程以及几套GitHub repo,但没有一样能真正满足我的需要。
我的应用程序需要多套容器环境才能实现运作,而我希望找到一种能够实现以下编排需求的方案:
除此之外,我希望一切都能以负载水平为基础实现横向规模扩展。这乍看起来似乎是个极为艰巨的任务,但随着我开始编写自己的第一套解决方案,一切开始变得明朗起来。我的灵感源自fig,我喜欢fig那种允许用户在不同容器之间指定彼此关系的设计,我也希望能将这种优势引入到Opsworks当中。
大家可以 点击此处 查看我的工作成果。届时本文截稿时,其中的README.md文件仍然一片空白,不过我承诺会在未来为大家提供说明文档……就目前而言,请各位先将本文作为仅有的参考资料:)
在对自己的Docker化应用进行部署时,我们首先要做的就是登录AWS、访问Opsworks并点击创建一套堆栈。请如下填写相关表彰:
在层创建完成之后,对其配置进行编辑并点击EBS分卷标签。我们需要为每个添加到该堆栈中的实例分配120GB分卷存储空间。大家可能会问为什么。很遗憾,在Amazon Linux/EC2中,Docker只能使用devicemapper实现容器管理,而devicemapper所创建的文件会在正常使用过程中增长到高达100GB。额外的20GB则用于存放我们的镜像。大家可以将存储空间调小一些,甚至干脆不分配EBS分卷,但请注意我们早晚会面临这样的问题。
在此之后,我们对层进行编辑以添加自己的自定义配置方案:
设置
docker::install, docker::registries, logrotate::default, docker::logrotate
部署
docker::data_volumes, docker::deploy
我们的这套自定义配置方案能做什么?
现在让我们添加一款应用程序。在左侧的Opsworks菜单中点击Apps而后选择“add a new one”。
现在我们只需要向应用程序当中添加一项环境变量:
其它一切都将通过(庞大的)Stack JSON完成配置。检查我们的堆栈设置并对内容进行编辑。大家还需要为自己的容器编译一套堆栈json。以下为相关示例代码:
{ "logrotate": { "forwarder": "logspout0" }, "deploy": { "amazing application": { "data_volumes": [ { "socks": { "volumes": ["/var/run", "/var/lib/haproxy/socks"] }, "static": { "volumes": ["/var/static"] } } ], "containers": [ { "app": { "deploy": "auto", "image": "quay.io/mikamai/awesome-app", "database": true, "containers": 2, "volumes_from": ["socks", "static"], "entrypoint": "/app/bin/entrypoint.sh", "command": "bundle exec unicorn -c config/unicorn.rb -l /var/lib/haproxy/socks/%{app_name}.sock", "migration": "bundle exec rake db:migrate", "startup_time": 60, "env": { "RANDOM_VAR": "foo" }, "notifications": { "rollbar" : { "access_token": "", "env_var": "RACK_ENV", "rev_var": "GIT_REVISION" } } } }, { "cron": { "deploy": "cron", "image": "quay.io/mikamai/awesome-app", "database": true, "containers": 1, "env_from": "app", "command": "bundle exec rake cron:run", "cron": {"minute": 59} } }, { "sidekiq": { "deploy": "auto", "image": "quay.io/mikamai/awesome-app", "database": true, "containers": 1, "env_from": "app", "command": "bundle exec sidekiq" } }, { "haproxy": { "deploy": "manual", "hostname": "opsworks", "image": "quay.io/mikamai/configured-haproxy", "volumes_from": ["socks"], "env": { "REMOTE_SYSLOG": "logs.papertrailapp.com:1234" } } }, { "nginx": { "deploy": "manual", "image": "quay.io/mikamai/configured-nginx", "ports": [ "80:80", "443:443" ], "volumes_from": [ "socks", "static" ] } }, { "logspout": { "deploy": "manual", "hostname": "%{opsworks}", "image": "progrium/logspout", "volumes": [ "/var/run/docker.sock:/tmp/docker.sock" ], "command": "syslog://logs.papertrailapp.com:1234" } } ] } }, "docker": { "registries": { "quay.io": { "password": "", "username": "" } } } }
哇哦!这些内容可够大家消化一阵子了,对吧?在下一篇文章中,我们将一同走入Stack JSON的世界并了解其中各组成部分的实际意义。
感谢大家前来捧场,让咱们下次再见!
原文链接: http://dev.mikamai.com/post/110066104864/zero-downtime-deployments-with-docker-on-opsworks