今天的分享主要是结合精灵云在Docker和微服务领域的实践进行分享。精灵云(Ghostcloud——云端的Ghost),是国内第一批从事容器虚拟化研发的创业型公司,主要提供容器云管理平台、容器私有云建设及咨询服务。秉承Eat Your Own Dogfood的思想,他们的整体架构就是全部运行在容器中的微服务架构,他们拥有国内领先的PaaS/CaaS研发能力,Ghostcloud平台也是国内唯一全自主研发的PaaS/CaaS平台。
产生微服务的背景
首先我们从微服务出现的背景说起,我觉得首先是移动互联网这个时代促使微服务的出现。过去10年是移动互联网飞速发展的10年,我们的生活受到了翻天覆地的变化,我们每时每刻都在享受着移动互联网带来的福利。整个系统对组件复用,水平扩展性,高可用性都提出了新的要求。这就要求我们用敏捷的思想来进行开发,持续的集成,持续交互,精益方法论都逐步深入人心。然后是虚拟化技术的发展,从传统的硬件级虚拟化逐步过渡到操作系统级虚拟化,也就是我们所说的容器虚拟化。而Docker无疑是这里面的杰出代表。同时,随着AWS的兴起,公有云, IaaS, PaaS, 等都纷纷出现。总的来说,都是要突出一个“快”字,凡是能加快我们交互的途径都值得去改进。微服务架构下,将功能相对独立的模块进行了封装,模块间的交互取而代之的是接口,极大的实现了“高内聚”,“低耦合”,同时模块与模块间没有了硬性的语言要求,而更关注于功能。
架构的转变
与Microservice相对立的就是Monolithic模式,翻译过来就是单体模式。单体模式把很多功能或者组件柔和在一起,在开发和部署方面是有很大的优势。但是其缺点也很明显,首先测试成本很高,之前我曾经供职的一家公司,要测试一个系统需要完整编译一次系统,至少需要1个小时,这种情况下就根本没办法持续交付。维护成本由于整个系统的耦合性,也会增加。同时,系统要进行扩展时,大多只能垂直扩展,即使是水平扩展也需要高配置的硬件。而在微服务模式下,模块独立,低耦合,每一个模块都可以单独测试,并发测试,并发部署。同时,具有很强的扩展性及更高的资源利用率。但是,微服务也存在诸多缺点,比如:设计的要求增加,系统的复杂度增加,模块的增多以后管理和运维的要求也会增加。
如何应对转变
这其实是我深有体会的。要实行微服务架构,肯定需要一个人来进行推动,而这个推动势必会影响活着损害部分人的利益,这不仅是技术层面的,还会上升到管理层面。因此,推动的人必须首先对微服务有深刻的认识和理解,同时需要得到公司管理层的支持,而这个角色可以是老板或者CTO。你需要说服管理层,同时也需要说服中层。这些中层包括架构师、高级工程师,这些人往往是公司的骨干也是经验最丰富的,这类人如果不与时俱进,在具体实施过程中要么遇到很大的阻力,要么成为形式上的微服务。中国的IT企业普遍落后于国外,同时对IT基础架构也不够重视,很多老板根本不管你什么架构,搞出来就好,这就导致后期维护成本非常高。因此,我觉得思想的转变比什么都重要,真正从上到下意识到我们需要进行转变。从物理机到虚拟机是转变,从虚拟机到容器是转变,从私有云向公有云到混合云同样是转变,只有团队具有这种持续改进的传统和颠覆的魄力,微服务才可能落到实处。
架构师的欠缺
现在的技术发展光JavaScript就有几十种框架,各个领域都有层出不穷的技术。一个合格的架构师,需要时刻有空杯的学习心态,不停的快速学习。我一个很好的朋友是唯品会的资深架构师,他到现在每年都会看20多本的技术书籍。尽管如此,他在很多时候都会感觉不能跟上时代的步伐。这一页是我罗列的一些需要的技能,从中你可以看出沟通能力和影响力也被我加了进来,很多高级技术人员都不注重非技术方面的提升,这一步是迈向架构师的一个坎。我自己是一个纯粹的技术人员,在创业之后我深刻认识到,技术对我们都不是多大的问题,性格、沟通、情商、大局的把控才是真正需要修炼的地方,所谓人生无处不修行。
微服务生存周期
对于微服务架构,我大致整理了上面几个步骤。首先从用户需求开始,然后是模块拆分和技术选型,接下来就是通过敏捷的思想进行持续集成,然后是部署,之后是运维,然后进行下一次迭代。有的时候我们可能会将持续集成、测试和部署合并在一起,有的时候可能又会根据实际情况分拆开来。
语言的选择
需求分析方面我觉得没什么多讲的,我直接进入模块拆分阶段。模块拆分就涉及到到底拆分的多细,我觉得这个没什么定论,总体准则就是功能尽量单一,框架尽量单一,开发测试方便,然后最重要的是团队觉得好。只有团队觉得好才是真的好。过细过粗都不是很好的方式。我首先从语言选择方面来讲一下我们的实践。我们团队之前是做内核开发的对c/c++非常熟悉,但是确实不大适合当今的开发,c语言的包管理和语言内部的易错性绝对是最高的。后来我们集体转到了Java,对于我们来说Java基本没任何语言层面的难度,两三个星期就上手了。但是,我们没有选择重量级的框架,而是选择了Spring Boot, Spring Data, Spring Data REST等一系列最新的框架技术,选择这些框架主要还是方便容器化的封装。整体来看开发速度确实很快,也没什么过多的问题,性能也都还可以,当然不能和c比。但是一个比较麻烦的事情就是Java的外部包太多,同时JRE也很大,对于我们这种拥有几十个微服务的设计,明显是不合适的。后来,我们切换到了Go, 基本上Go和c大同小异,我们很快就上手了。我们招人都是招c语言和数据结构好的人,然后让他转,基本都是2周就能做事了。Go与Java比除了空间小,一个比较大的优势就是原生跨平台。这一点跟Python和Ruby都不一样,Python和Ruby, nodejs,其实都有一个类似虚拟机的机制,并不是运行的原生二进制程序。而Go是把程序编译成了原生二进制,这一点就在性能上有非常大的提升,你运行的c语言编译的程序没有什么区别。但是Go也不是完美的,Go目前的反射相对偏弱;Go的开发工具目前也还不是很完善,调试和MVC框架都还不是很完善,我们在开发过程中还自己写了一些调试器和一个REST框架。但是相比于性能和空间,我们觉得都是值得的。我们现在最小的rest服务只有几十k大小。
持续集成-镜像型
说到微服务肯定就离不开容器,我们使用的是Docker。我们团队基本所有的服务都是运行在容器中的,我们带代码管理和bug跟踪系统都是gitlab来管理的,通过注入代码提交的hook,通知我们的编译型控制容器,之后会push到我们的私有镜像仓库中,紧接着我们会启动测试型容器拉取镜像,并运行测试用例,最后是发布。这种模式我们称为镜像型持续集成,如果你的业务偏简单,没有多少服务是很不错的,但是如果你的镜像多,服务多,在公网上的拉取就会非常慢。我们那个时候更新一次版本要差不多10分钟,后来我们就过渡到了代码型持续集成。
持续集成-代码型
这种改进型的持续集成是将容器预先做好放在那儿,当有变化时去取代码,然后编译,测试和运行,同时异步push到仓库,在完成好内部测试以后,启动公网的容器进行编译、测试和发布。整个过程都是以代码为线索,不会出现大镜像的拉取动作。
持续集成-容器-代码库
如上图,我们采用的是一个容器对应一个代码库的方式来的,这样可以并行的测试、构建和部署。同时容器和容器之间是通过无状态协议交互的,可以自动构建相互间的联系,这样做主要是为了方便的更新和扩展。
容器的网络访问方式
这里是几种可能会用到的容器网络模式,
第一种是默认的桥接方式,容器可以通过主机ip+端口来进行访问,这种方式是可以跨集群访问的;
第二种是直接使用容器的内部ip,因此只能在内部访问
第三种是link的方式将容器连接到一起,这种方式只能在主机层面访问;
第四种是使用主机的网络,容器使用主机的网络通讯,因此也可以跨主机访问;
第五种是给容器赋予可路由的ip,这种方式需要借助MacVlan等技术来实现,也是可跨主机访问的;
第六种是Overlay+link方式,Overlay是在docker1.9以后加入的虚拟网络访问方式,它可以在多主机间建立虚拟网络,因此也是可以跨主机访问的;
我们在实际使用过程中,会根据业务场景来选择。
服务发现
作为微服务架构,一个比较核心的问题就是服务发现。目前服务发现一般可以通过zookeeper, consul, 或etcd来做。这些都是高可用中间件,每一种都有特有的栈。拿consul来说,它的注册可以通过Registrator,服务仓库用Consul,然后配置更新用consul-template,然后负载均衡用Nginx/HAProxy。
案例分析
这是我们其中一个用户的案例,这个用户主要是做基于微信的开发和运营。他们的业务有一个特点,闲的时候会没有多大的用户访问量,一旦做活动(比如:抢红包,做游戏)并发量就会暴增。由于他们的主要精力是集中在不同场景的活动,所以非常适合微服务架构。但是,他们目前基本都是手动来调配资源,并且对容器和主机也没有系统的监控。基于此,他们和我们平台一起研发了动态的负载均衡及弹性伸缩功能,可以根据各种资源使用情况自动伸缩服务。而Ghostcloud的平台,则为他们提供了一系列的相关服务。