美好的五一假期结束啦,你们玩得怎么样?今天小数给你们带来的是数人云架构师周伟涛在CSDN作Mesos生态圈系列技术分享的第二期,讨论他在微服务架构方面的经验心得以及一些具体问题的解决。
Mesos生态圈系列第一期传送门: 畅谈 Mesos 生态圈系列前端
今天是 Mesos 生态圈系列的第二次分享,此次分享的内容是微服务架构软件的持续交付,我首先解释下标题与 Mesos 生态圈的关系。微服务架构是近几年讨论比较多或者说比较受欢迎的一种软件架构风格,可是该架构有一些缺点,譬如运维复杂度提升,交付次数频繁等,咱们能够利用 Mesos 配合一些工具搭建持续交付平台来弥补这些缺点。这里我将以具体实践为例,讨论微服务架构的一些经验心得,而后着重介绍如何利用 Docker,Jenkins 来解决一些具体问题,实现微服务软件的持续交付。git
首先介绍下微服务架构的优点与劣势。相较于单体应用来讲,微服务架构有这么几个优势:github
易于开发、理解。 因为每一个服务只负责单一功能,开发者能够聚焦于本身负责的几个服务模块,对于其余服务,只须要理解接口便可。固然,单体应用通过良好设计也能够达到这个效果,可是,与单体应用的进程内通讯或单机内的进程间通讯不一样的是,微服务的各服务之间通常采用 RESTfulAPI或者异步消息队列进行通讯,不管 RESTful接口仍是异步消息队列都是开发语言无关的,极易理解的通讯方式。web
全局稳定性提升。因为每一个服务负责的功能单一,各服务的资源需求也相对更低。从而能够选择将服务分散的部署到多台中低配的服务器上,而不是一台高配的机器上。若是某个机器上的服务故障,譬如说内存泄漏,故障只会影响该机器上的某一个或几个服务,对全局影响不大。docker
不受限于任何技术栈,极大的提升团队搭建的速度。这一点对初创公司尤其重要,组建开发团队对初创公司来讲原本就是个头疼的问题,如何还要求团队的技术栈一致,招聘难度可想而知。可是,若是产品架构采用微服务架构,那么咱们能够容许不一样的服务模块采用不一样的技术栈,只须要定义好对外接口便可。设计模式
局部的修改很容易部署,从而大大的提升了功能的交付效率。浏览器
说完了微服务架构的优势,咱们再来讨论下其缺点或者说复杂的地方:缓存
如何肯定软件功能切分的粒度,边界。太多的微服务模块会致使服务间通讯成本和运维成本的增长,过犹不及;可是若粒度过大,又违背了微服务的初衷。服务器
多种技术栈(譬如 C,Java,Python,Scala 等)咱们须要为每种语言准备编译环境,运行环境等,增长了维护成本。这个能够经过Docker 隔离来解决,咱们后面会详细展开。微信
微服务模块多了,会致使全局的上线次数变多,从而须要更复杂的版本管理和 Bug跟踪等,间接致使项目管理成本增长。
持续集成和交付(CI/CD)是一种软件开发实践,使用得当,它会极大的提升软件开发效率并保障软件开发质量;持续集成和交付分为持续集成和持续交付两部分,这里咱们再也不具体探讨这二者的区别,统一按持续交付来处理。Jenkins是一个开源项目,它提供了一种易于使用的持续集成系统;除 Jenkins 外,常见的持续集成系统还有:
Travis: https://travis-ci.com/
Codeship: https://codeship.com/
Stridercd: http://stridercd.com/
另外,常见的交付方式通常有:
源代码交付: 源代码交付须要将源代码以 tar 包等方式 download 到服务器,而后在服务器上借助程序的构建脚本去构建可执行程序,显然这种方式会常常因服务器环境差别,构建环境初始化失败等问题致使没法构建可执行程序。严重依赖于构建脚本的完备程度。
Linux 标准包交付: 将项目的依赖经过 Linux deb 或者 rpm 来管理,因为这种方式更符合 Linux 规范,间接的提升了项目在服务器上部署的成功率,可是有些时候仍然须要解决包冲突问题。
虚拟镜像交付: 虚拟镜像交付指的是咱们将项目在虚拟机里测试成功后直接将该虚拟镜像部署到服务器上。显然,这种方式部署成功率接近100%并且隔离性好。可是随之而来的问题就是虚拟镜像自己对服务器资源的消耗。
docker image 交付: docker image 交付是虚拟镜像交付的进一步演进,在保证系统隔离的同时,docker image 对服务器的资源消耗更低。固然,docker 的隔离机制是进程级别的,可能不适合一些强隔离场景。咱们团队目前正在使用这种方式进行交付。
上图(图片来自于网络)展现了围绕 Docker 镜像仓库的持续交付流程:
首先开发者将代码推送到代码仓库,譬如github
代码仓库的更新会触发新的代码构建,生成新的 docker 镜像并推送到 docker 镜像仓库
接下来会基于新的 docker 镜像进行集成测试
测试经过后,docker 镜像被交付到公有或者私有云上
经过上述持续交付的方式交付微服务架构的软件,可以很好的解决前面提到的第二与第三个问题,即
结合 Docker 解决多技术栈的环境维护问题;
按微服务模块交付来提升软件的交付效率
引入“版本服务”来可视化各微服务的版本信息
引入“ReleaseNote服务”来发布各微服务的 feature 更新
微服务架构有多种,数人云的微服务架构有以下特色:
RESTAPI 与 消息队列 结合使用。微服务与外部用户经过 RESTAPI 通讯,内部微服务之间经过消息队列通讯。
所有 Docker 交付,这解决了多技术栈的环境维护问题。
一个微服务对应于一个持续交付的 Job,这保证了各服务在交付环节无相互依赖,单独触发。同时能够利用不一样的 Docker 镜像为不一样的 Job 提供相应环境。
版本信息自动更新
ReleaseNote 自动发布
构建环境 Docker 化,与底层隔离,保证宿主机环境的一致性,下降运维成本
利用 Docker-compose 维护本地开发环境,从而实现开发环境与生产环境的逻辑一致性
数人云的架构设计模式如图所示。用户经过浏览器或者直接经过 RESTAPI 与后台通讯,后台是一个微服务集合,对外服务的接口一概采用 RESTAPI, 内部服务之间的接口采用消息队列。各微服务负责维护自身的状态集,有本身独立的缓存和 DB (若有必要)。微服务自己尽可能无状态化,以保证横向扩展能力。
上图是咱们目前采用的持续交付架构图。A, B, C, D 四个 github 代码库分别存储着四个微服务的源代码,相应的咱们为每个代码库建立了独立的构建做业,代码更新触发构建时,构建做业将执行以下的大体步骤:
从 Docker 私有镜像仓库拉取相应的构建镜像
从 github 源码库拉取相应代码并挂载到构建容器里,触发特定脚原本执行构建
若构建成功,马上从 Docker 私有镜像仓库拉取相应的 runtime 镜像,将构建成功的可执行程序 Docker 化成新一版的微服务镜像
将上述微服务镜像推送到 Docker 私有镜像仓库
若已经设置了自动交付
则经过 Marathon 的 RESTful API 接口触发线上微服务的更新
更新版本服务里面对应微服务的版本信息
自动更新 ReleaseNote 服务里面对应微服务的 ReleaseNote。 开发者在实现了相应功能时已经将对应的 ReleaseNote 放在了代码库特定目录下面(这个后面会详细提到)
另外,从架构图中咱们能够看出,程序的构建环境和运行环境正在共享同一个 Mesos 资源池,提升了资源利用率。
把 Jenkins 运行在 Mesos 上有以下几个考虑:
把Jenkins运行到 Apache Mesos上,或者说利用 Apache Mesos 向 Jenkins 提供 slave 资源,最主要的目的是利用 Mesos 的弹性资源分配来提升资源利用率。
术栈的软件状况下尤为重要,能够极大下降运维成本。
Marathon会对发布到它之上的应用程序进行健康检查,从而在应用程序因为某些缘由意外崩溃后自动重启该应用。这样,选择利用Marathon管理 Jenkins Master 保证了该构建系统的全局高可用。并且,Jenkins Master 自己也经过 Marathon 部署运行在 Mesos 资源池内,进一步实现了资源共享,提升了资源利用率。
关于怎样将 jenkins 运行在 mesos 上,你们能够参考我之前在csdn发布的一篇文章http://www.csdn.net/article/2015-06-18/2824998
Docker 在整个体系中承担了以下几个角色:
各代码库的编译载体: 咱们已经提早将各代码库的编译环境制做成了 docker 镜像
交付介质: 编译成功的可执行程序将被打包成 docker 镜像, 镜像的 tag 对应于程序版本信息
Runtime 环境: 运行环境已经被打包到 docker 镜像中了,启动的 docker 容器将做为微服务的 runtime 环境
资源隔离: docker 自己支持进程级别隔离,已经知足内部应用需求
为了保证单机开发环境与线上环境的配置/架构一致,咱们在单机开发环境利用 docker-compose 来编排整个微服务环境,以便于调试
在实践微服务架构时,咱们碰到了这么一个问题:各微服务模块频繁交付,如何确认线上各微服务的版本, 即咱们须要对各微服务进行版本控制。
目前团队迭代出了以下解决方案:交付成功后,交付 job会截取 docker 镜像里相应的 tag(表明着版本信息),并把该信息推送到一个 NginxServer 的静态文件里面, 前端页面访问该静态文件来获取相应微服务的版本信息。另外,同一个微服务会部署到开发,测试和生产三个环境上,因此基于不一样的环境,咱们会维护三个不一样的静态文件 。
在多人协做的微服务项目开发中,因为多人频繁的 merge 代码,ReleaseNote 的管理也会成为团队的负担。我这里推荐的作法是这样:
文档规约:团队达成一个 agreement: 对重大 feature 或 bug-fix 的提交都须要在目录 pending-release 里面建立相应的 markdown 文件,并将改动添加到里面。这样,咱们能够控制交付 Job 在交付时扫描 pending-release 目录并将其中的文本 merge 到一块儿生成此次交付的 ReleaseNote。
git pre-commit-hook:同时,为了不团队成员忘记这个agreement,咱们还能够在本地的 git-precommit-hook 中添加相应的扫描提醒。
目前网络上关于配置管理的解决方案已经很是成熟,我这里就不过多解释了。惟一须要提到的就是, 咱们的配置中心服务尚未彻底融入到整个交付过程当中来,微服务的配置文件仍然须要手工介入。
显然,团队但愿 CI 服务器在执行了持续集成后可以及时的将集成结果通知团队成员, Jenkins 自己是有 irc notification 插件的,可是国内开发者可能使用 IRC 的并很少;微信是小团队使用比较多的沟通工具,咱们可使用微信公众号进行消息通知;或者使用国内的 LessChart 等交流工具,它们自己支持 webhook 调用。
微服务架构致使软件模块增多,增长了开发环境搭建的难度,同时也致使了团队新成员上手门槛的提升。咱们目前是利用 docker-compose 维护本地开发环境来解决这个问题的。它不只实现了开发环境与生产环境的逻辑一致性,同时也可让相应模块的开发者聚焦于本身的业务,没必要纠结于如何启停其它微服务。下面是咱们的 docker-compose 文件的一部分。
在具体实践中,还有以下几个难题。
微服务间的消息传递须要根据业务变更而频繁改动,尤为是前期,这带来了极大的沟通成本,目前没有好办法解决。
如何界定各微服务承担的功能,每一个微服务的功能粒度切分,微服务间的边界定义,这也是咱们团队一直在摸索的问题。
参考连接: