2016年伊始,Docker无比兴盛,现在Kubernetes万人瞩目。在这个无比须要创新与速度的时代,由容器、微服务、DevOps构成的云原生席卷整个IT界。在近期举办的QCon全球软件开发大会上,个推应用平台基础架构高级研发工程师王志豪,基于他在基础架构方面多年的经验,分享了《个推基于Docker和Kubernetes的微服务实践》。sql
个推应用平台基础架构高级研发工程师王志豪数据库
1、微服务化编程
微服务架构后端
微服务是将单一的应用程序拆分红多个微小的服务,各个小服务之间松耦合,高内聚,每一个小的服务能够单独进行开发,不依赖于具体的编程语言,也可使用不一样的数据存储技术,各个服务能够独立部署,拥有各自的进程,相互之间经过轻量化的机制进行通讯(如基于HTTP的API接口),全部的服务共同实现具体的业务功能。缓存
客户端与服务端通讯有2种方式,第一种是客户端直接与各个微服务进行通讯,这样的架构有4个缺点:网络
(1)屡次服务请求,效率低;架构
(2)对外暴露服务接口;并发
(3)接口协议没法统一;框架
(4)客户端代码复杂,服务端升级困难。运维
第二种方式是由API网关统一代理各个服务,对外提供统一的接口协议,该架构有3 个优点:
(1)封装服务接口细节,减小通讯次数;
(2)统一通讯协议,减小客户端代码耦合;
(3)统一鉴权,流控,防攻击;
在该架构下,网关也有可能成为系统瓶颈。
相应地,这2种架构也带来了2种服务注册发现的方式,第一种是客户端经过向服务的注册中心查询微服务的地址与其通讯,第二种是增长统一的API网关来查询。前者会增长客户端的复杂度,开发成本高,第二种操做会显得更加简洁,所以咱们在实践的时候选择了第二种架构方式。
微服务数量增长之后,服务之间的调用关系易产生耦合,甚至出现循环调用的状况,最好的应对方法是对服务进行分层,即将相互依赖的服务经过消息队列等技术进行异步解耦,减小服务间的依赖。
服务分层
微服务的具体实践
1.技术选型
在实践中,咱们的API Gateway使用的是OpenResty, OpenResty基于Nginx并扩展了对Lua的支持,可构建高并发的Web服务。咱们经过HTTP接口实现客户端通讯,数据基本封装成JSON格式,服务间的通讯接口也是基于HTTP,并利用消息队列进行异步解耦;至于服务注册发现,咱们使用的是Consul;咱们选择了Lua(扩展API Gateway的功能),Node.js(用于开发后端服务),Java(用于密集计算和与大数据通讯的场景)做为主要的开发语言。
2.具体实现过程
在实践过程当中,咱们使用Lua开发了本身的微服务框架——WebLua,其封装服务之间的通讯协议和访问外部资源(如Mysql、Rdis等)的方法和依赖,同时提供了应用插槽。咱们能够将每个APP当作一个功能模块,每一个APP都须要插到WebLua中才能运行。WebLua能够方便地将模块进行组合,既能够一个APP运行一个微服务,也能够多个APP一块儿对外提供服务。如此,开发者只需关注业务APP开发,很大程度上提升了开发效率。上图右侧是具体的代码目录结构,每一个APP可分为Actions,Page,Data三层,Action层在请求处理先后进行拦截,可作某些特殊处理,如请求前进行权限校验等;Page层主要对请求的参数进行解析和校验;Data层负责具体业务处理,同时提供了Shell脚本,可实现APP打包和部署安装。
2、 API网关
在架构中一个重要角色就是API网关,下面来作一个介绍。
从上面的对比图中能够看到,左侧是没有API Gateway的,不少的模块如Auth,Logging等,这些代码都须要本身去实现,形成了模块的重复建设,同时侵入了服务,功能扩展比较困难;右侧的图是使用了API Gateway以后的架构图,全部通用模块均在API Gateway实现,维护简单,一处建设,各处受益。在这种状况下,对API Gateway也提出了更高要求——其功能必须能够很方便地扩展。
为了实现这样的API网关,咱们基于 OpenResty,借鉴了Kong和Orange的插件机制,经过插件来扩展API网关功能。
从上面的API Gateway架构图中能够看到,网关安装诸多插件,每一个插件会在请求的一个或多个阶段发挥做用。插件配置会在Consul上更新,实时生效,插件规则可灵活配置。在操做中,咱们为插件开发者提供了更多自由选择,开发者能够本身定义格式。
3、容器化
在微服务落地实践时咱们选择了Docker,下面将详细介绍个推基于Docker的实践。
首先网络组件选择的是Calico,服务注册发现和配置管理选择的是Consul。Consul-Template可实时监测Consul配置和服务的变化。
个推镜像体系是以Centos为基础系统镜像,安装OpenResty,Nodejs,Jdk,由此获得环境镜像,再在这个基础上安装微服务框架,得到Gorp镜像。再在这个基础上安装具体应用服务,获得应用服务镜像。
服务注册发现和配置更新流程
在API网关中,服务注册经过Consul-Agent来实现,配置更新经过Consul-Template实现。Consul-Template主要更新3类配置,包括:Services:代理的全部微服务的服务地址;Products:简言之即请求到微服务的映射表,如左上所示,全部请求都有统一个规范,从Host中能够获取Prod,从URI中能够获取APP,这 2个信息可将请求动态路由到具体服务;Nging-Conf:产品的Nginx配置。
应用服务容器,服务注册的方式跟API网关一致。首先,服务经过容器内部运行的Consul Agent将服务注册到Consul上,其次经过Consul-Template来监测观察 Consul上配置的变化,并更新配置文件。OpenResty或者WebNode配置的更新是直接覆盖相应的配置文件,而后重启对应的服务。
上图是个推基于Docker的集群架构,从中可看到,Docker集群包括3个节点,整个微服务分为3层,最上层是API Gateway,中间是业务层,最下层是一些多产品公用的基础的微服务。
4、Kubernetes实践
微服务虽然有不少好处,但也带来了不少问题,其中一个就是运维复杂。之前运维只须要面对一个单体应用便可,如今可能面临的是几十甚至上百的微服务。在这种状况下,咱们须要借助Kubernetes来解决问题。Kubernetes是Google开源的一个容器编排工具,可用于协助管理容器。
一开始,咱们将容器向Kubernetes集群迁移时,没作任何改变,只是采用Pod将全部的服务体系在Kubernetes集群运行。但随着深刻使用Kubernetes,咱们对微服务作了一些改变。
1.首先咱们换成用Deployment的方式来部署服务,Deployment会保证服务时刻有必定的副本存活,提升了服务稳定性。
2.其次,咱们使用了Service,它能够代理Pod实现负载的均衡。
3. Kube-DNS能够将Service名解析成具体的ClusterIP,而且当Service没有删除重建时,其clusterIP不变,如此DNS解析的缓存就不存在失效问题。基于Kube-DNS和Service的特性,后续咱们改造了服务注册发现体系。
上图是咱们当前的服务部署方式,Pod用Deployment的方式建立,用Service来进行代理。
在实践过程当中,咱们还遇到了另外一个问题,即配置管理问题。
(1)微服务化后配置文件多而分散;
(2)不一样环境之间有不少没必要要的差别,如数据库名;
(3)在不少不一样环境中,相同的配置项暴露给测试和运维;
(4)没有版本控制,回滚比较麻烦;
(5)基于Consul的Web UI没法对非法的输入进行校验。
针对这些问题咱们作了如下调整:
(1) 统一不一样环境间没必要要的差别;
(2) 对配置文件进行模板化,只暴露差别部分,同时可实现不一样配置文件集中配置;
(3)基于Consul开发配置中心,对产品配置集中管理;对输入进行合法性校验;增长版本控制,方便回滚。
配置中心流程图
关于日志服务,咱们在应用容器中集成了Fluent-Bit,配置了2个输入源,TCP和tail, 输出也有2个,一个是Elasticsearch,全部的日志都会上传到ES经过Kibana展现查询,另外一个是日志审计服务,有些须要进行审计的操做日志会发送到日志审计服务进行进一步的分析处理。
微服务数量增长之后,请求链路可能延长,开发者在追踪问题和排查性能瓶颈时会很不方便,所以咱们引入了Zipkin,其主要用于分布式链路追踪,在API Gateway实现了一个插件进行Span收集,后端服务则经过开源的中间件来实现。
上图是个推目前的总体架构图,最底层是K8S集群,上面部署了Kube-DNS,Consul用于服务注册发现和配置管理,再者是咱们分层的微服务体系,右侧是一些辅助的管理系统。
5、总结
上述是个推基于Docker和Kubernetes的整个微服务实践过程,咱们在实践微服务过程当中作了9件重要的事情,简化了操做流程,提升了工做效率。个推设计实现了本身的微服务框架,完成微服务的容器化部署,自研API网关,并基于Consul的服务注册和配置管理,使用Kubernetes对容器进行编排,基于Service和Kube-DNS对服务注册和发现体系进行改造,搭建了本身的配置中心,优化了日志服务,实现了Zipkin链路追踪。