Docker 自发布以来,它的影响力不容小觑,目前已经在整个行业甚至于许多大企业都得到实际的应用案例以及支持。Coding.net 作为一个创业公司,大量采用了微服务架构解耦系统,在提高开发效率的同时也引入了不少新的问题。今天在这里跟大家分享一下我们是如何采用 Docker 技术在内部推行生产环境容器化,代码化,自动化的。
微服务架构
从2014年上线到如今,Coding.net 已经由早期的一个 Java war 发展成为一个系统结构复杂,独立模块很多的大型分布式程序。网站的每个小功能,比如说 GIT 协议处理、Repo 信息读取(网页展示)、Push 动态、邮件通知等等都作为一个个微型服务在云上运行,互相通过 API / RPC 调用充分解耦,互不侵入,接口明确。 采用微服务架构设计的原因很简单:解放生产力。传统 Java 基于 Application Server 的开发模型下,AS确实提供了很多运行时服务,很多功能都可以在其中实现,但是这也带来了AS负担过重,组件隔离性不强的问题。AS 运行时服务缺少标准,各类实现区别很大,调试困难程度不一,集成测试更是难上加难。
在这种环境下,松耦合的实际是非常可行的提高团队生产力的方案。放开手脚让小朋友们自己选择合适的程序包装方式,实现语言。小伙伴们更进一步的互相提供 Mock/Stub 帮助集成测试及单元测试,极大的释放了团队生产力。
容器化
经历了早期野蛮式、爆发式的服务拆分后,Coding.net 不可避免的面临着生产环境中越来越高的复杂度问题。早期 Coding.net 的部署方式简单,一个 War 包拖入合适位置,网站就完全更新了。拆分后,变成了三五个小服务跑在十几台虚拟机上的问题。同时这三五个小服务又分别使用不同技术实现,有的是 Jar 文件运行,有的采用 Jetty+war 或者 Tomcat +war 。一批小伙伴用 Go 重写了一部分服务,还有几个小伙伴采用 Ruby 重构了另外一个核心功能。如此一来,生产环境的压力可想而知。每个组件的编译方式各不相同,启动方式和配置方式更是天差地别,甚至有好几个组件就是源代码直接运行。运维同学不光要记住所有组件的运行位置、编译方法,更要记住配置方式、依赖模型,压力山大!同时,某一天大家突然发现,我们再也不能把整个 Coding.net 在机器上跑起来了,因为谁也不知道如何才能正确安装配置各种依赖服务。
为了解决这些问题, 我们最终采用了 Docker container 技术来降低生产环境的复杂度。有个比喻很形象,Docker 把程序装进了盒子里,每个 Docker 镜像包含了完整的程序代码,运行时状态,而且有一个标准的启动接口。不管小伙伴设计的牛X程序多复杂,一个 docker run 也能把他正常运行起来。同时 Docker 还带来了版本化的集成,从此告别了不知道运行什么版本代码的问题。
代码化
应用程序装进盒子后,我们还面临着盒子在哪里运行的问题。非常实在的说十几个 Container 和十几台VM 存在着比较复杂的对应关系,原因是很现实的:设计之初程序之间的 RPC 是依靠主机 DNS 地址互相通信的,就算装进了盒子里,盒子也不能轻易挪动。如果挪动盒子A,可能涉及到更改盒子B、C、D的配置。所以实际上 Explicit knowledge 在小伙伴的日常工作里变成了非常严重的问题。 这个问题的解决之道就是将依赖关系代码化。服务之间关联复杂不要紧,只要我们把各种依赖关系变成代码,计算机可以帮助自动处理相当一部分的工作。
代码化分为两大块:第一块是用一个脚本从云服务提供商那里抓去了所有机器的 host/IP 配置。 第二块是定义了一个 protobuf 文件将 Coding.net 生产环境的服务主机的对应关系记录下来。我们进一步设计了一个 Service IP 体制,自动为生产环境中的每个服务创建一个DNS CNAME 记录,指向服务实际使用的机器 DNS A 记录。于是当有服务挪动操作的时候,DNS记录会刷新,所有的下游依赖都会重新解析DNS 到新的地址。同时,我们还自己开发了一个工具,根据记录的信息检查生产环境是否与记录存在差异,进一步可以将常用操作(如启动停止,更新,移动主机)等等包装起来,为操作人员提供一个标准的 CLI 工具,将复杂的生产环境操作变成程序。
同样道理,我们也可以按代码生成一个开发环境的定义,用一套工具同时操作开发环境和生产环境。小伙伴们一键可以在自己电脑上起停整个 Coding.net 服务云,也可以同样的命令一键起停生产环境(前提当然是有足够权限)。这样环境的统一所带来的生产力变革是非常深刻的,对公司研发的组织架构也有很深远的影响,有机会的话可以在另一篇文章里详细再说。
自动化
装进盒子,能挪动,只描述了 Coding.net 宏伟蓝图的一小部分,而管理大规模生产环境的关键在于自动化。做好代码化之后,我们马上上马 Continuous Deployment ,让每一行更改过的代码一分钟内进入Staging, 再结合自动化的 Regresssion testing 工具,自动汇报未预见的错误更改。 同时我们还有一个一键发布和回退服务,让更新变得更简单,容易。自动化的服务系统还包括Log的转发存储,metrics的自动收集, Application healthcheck 等等,这些都极大的降低了小组件的运维难度,鼓励大家开发更小更独立的组件,而不是又笨又大的单体程序。
以上就是 Coding.net 服务架构的演进, 希望能对大家有所启发。在 4月23 日 QCon 全球软件开发大会“挑战全栈开发”专题 上,我也将会给大家分享一下如何利用 Docker等的相关技术为创业公司的全栈开发加分,提高产品质量,创造一个可持续的全栈开发环境。敬请关注4月23日下午 15:50 《Coding.net 全栈开发实践分享》。欢迎大家和我交流,邮件 : sunyucong@coding.net 。
感谢郭蕾对本文的审校。
给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ,@丁晓昀),微信(微信号: InfoQChina )关注我们,并与我们的编辑和其他读者朋友交流。