Docker 技术在 ruby 社区是有影响力的,我所知道的一些创业团队很早就在运用它来解决环境管理、持续集成以及部署的问题了。但是,也有一些同学尚未注意到这个技术,或者了解过后认为它不是很重要,所以我想讨论一下 Docker 对 Ruby 系技术的帮助。
我一直很喜欢 Vagrant 这个工具,两三年前就用它来进行自己项目的环境维护,那时候主要是做测试,由于 Vagrant 将操作系统环境进行了标准化,我很容易就能让自己的应用系统以及相关的测试结果保持稳定。
Vagrant 还有一个好处,Ruby 社区比较偏爱 Mac,但是线上的系统基本都是 Linux,所以开发环境所做的测试是有疑问的,特别是遇到一些有 so 依赖的 gem,这时一个和线上完全一样的环境就特别重要。
其实上面的表述不太准确,vagrant 也有各种 provider,我所说的场景,基本上都是 virtualbox 的 provider,所以这些地方正确的说法是 vagrant/virtualbox。
和 Docker 相比,vagrant/virtualbox 组合的成本还是很高的,无论是 setup 一个环境还是 reset 一个环境,都需要一段时间的等待,vagrant 只是把 virtualbox 的操作 DSL 了而已,底层的做法没有变化。而 Docker 由于本质上就是一个进程,因此天生就是轻量级的。对于运行时间在分钟级别的自动化测试工作,docker 显然有很大的优势。
当然,也有人会认为 docker 不能模拟完整的操作系统,不过这恐怕是一个优点而不是缺点。我在以前的文章中已经说过了,这里概述一下主要观点——
docker 简化了操作系统这个基础设施,让应用精简为其最核心的形态——携带有限资源的进程,在此基础上更有利于架构上的最佳实践
而对 Ruby 工程师而言,这个“最佳实践”中肯定少不了的一条就是——微服务
Ruby 工程师中有很多就是 Rails 工程师,而 Rails 实际上更倾向于单体架构,因此后来社区的工程师们才需要在实际工作中总结1 to 30
这样的实践。
其实微服务本身不是个教条,即使没有人教,我们也常常自发的去进行服务化改造,但是这个工作并不容易,主要是会受到一些问题的掣肘,比如运维复杂度和系统测试成本会大幅度上升等等。
处理这些困难,首先当然是看是否必要,一些简单场景我们也可以用单体架构直接搞定,但是我们很容易会注意到,这两年大家越来越多的提到了微服务或者服务化,这背后其实是有趋势的——各种业务形态都在朝着互联网级的用户规模推进,同时大家都在努力从每一个用户的各种维度上挖掘价值(这导致了大数据的需求),这些场景变得越来越常见,单体架构是难以支持的。
既然微服务或者服务化不可避免,那么就要有相应的对策,虽然 Ruby 社区也有很多人在不同问题点上针对微服务进行改进(比如完善异步化框架,以及对服务协议的探索等),但是在基础设施层面,Docker 是最重要的武器,没有之一!
对 Ruby 工程师来说,Docker 能做两件事:约束边界和建立通用基础服务。
Ruby 项目 Docker 化,并不是简单换个虚拟机那么简单,我们会面对拆分的压力,相信很多人尝试用 Dockerfile 来描述自己的项目的时候都会觉得束手束脚,但这些地方其实是促使我们想清楚——这个应用到底要做什么?它和外界是什么关系?对于外界的变化它如何响应?失败后怎样恢复?
这类的问题对系统架构非常重要。比如应用到底要做什么,这是让工程师去思考系统的目标,无论是提供 web 服务,管理调度后台任务,还是提供实时分析,它们都应该有一个尽可能单一的目标,在这个基础之上,我们建立的服务才有可能是易测试、易扩展和易维护的。
其它问题也类似,这些地方以前如果没有留意,很可能不是没问题,而是没意识到,使用 Docker 有助于我们意识到这些问题。
另外补充一点,由于 Ruby 项目不能完全脱离动态库依赖(java 大都可以),本身的打包机制又没有自包含结构(gem+bundle 不包括动态库,相比之下,Golang 是静态联编的),在分布式环境中的交付和软件包分发其实是有着先天不足的,Docker 的 Image 恰好补上了这一块,简直是睡觉时候有人送枕头了。
当我们将应用系统分裂为各种服务并明确其边界以后,就出现了“分久必合”的问题,这很自然,服务化改造并不是各行其是,应用之间还是要协作,而对应用的运维——服务发现、水平扩展、容错等等——都需要基础设施的支持。
以前,对于这种运维基础设施,各公司甚至同一个公司的各个团队的做法都千差万别, 但是借助 Docker 以及周边的生态圈,我们可以很容易的得到通用的服务发现框架,享受自动的部署和弹性扩展。
更好的消息是,这些基础服务是通用的——不但不关心是 rails 还是 sinatra,甚至根本不关心是不是 ruby。
这也很好理解,Docker 是对进程这个操作系统工作单元进行了简化约束,而进程的概念本来就是与语言和框架无关的
这使得 Ruby 工程师以及 Ruby 项目可以更为自由的选择合适的技术去扩展公司的产品线。
Ruby 刚出来的时候,有很多来自 Java 社区的工程师加入其中(我也算是其中之一吧),很多人最大的感受是——视野被打开了。曾经象口号一样的“all in java”变成了落后的标志,大家意识到,一把钥匙开一把锁,用最合适的技术针对性的解决问题才是聪明的做法,单纯排斥某种技术或者语言框架并不明智。
这个道理在 Ruby/RoR 应用开发中也不例外,但是不少人在使用了几年 ruby 以后都会遇到一个问题——“ruby 确实很适合开发 Web,但是现在有些问题需要使用 XX 技术,而我们的系统严重依赖 ruby 环境,这该怎么办呢?”
我认为问题就出在“系统严重依赖 ruby 环境”上,研发的基础设施,比如配管、自动化测试、打包、部署,不应该仅满足一种技术或是语言,它一开始就要考虑到通用性,否则我们就只能“手里拿着锤子,看谁都像钉子”。
Docker 本身和语言无关,它唯一的约束大概就是要运行在 Linux 上,这个对互联网服务端系统来说也算是标准了,问题不大。所以,我们应该以 Docker 为核心打造研发的基础设施,这将是未来的一笔重要投资。
当然,为未来画饼是危险的,不过还好,Docker 领域的创业很活跃,有很多团队和公司已经做了相当多的基础工作,对于 Ruby 工程师和 Ruby 创业团队,去用现成的基础设施其实更方便。