Spring Cloud + Kubernetes 微服务框架原理和实践

早在半年前,公司开始推行容器化部署方案 AppOS,虽然发布界面过于极客,十分晦涩,不过仔细研究起来真的以为十分强大,容器化推行后,计算资源(CPU、内存)的利用率能够极大提升,下降服务器数量,从而节约技术成本。html

恰巧,若干个朋友所在创业公司最近也在尝试作微服务、容器化。架构上摒弃 SOA 的 dubbo,加入Spring Cloud阵营;部署方案上从过去的云服务器直接部署,升级到基于Kubernetes集群的容器化部署。docker

Spring Cloud

微服务这个概念从开发者的视角理解和SOA的差别不大,按照业务领域细粒度的拆分系统为若干服务,服务仅访问对应的数据库,按照项目组,服务独立开发,部署和迭代。服务之间的调用,经过RPC完成。数据库

用几张图,直观、简明扼要的阐述一下在Spring Cloud中相关的概念~api

上图中,展现了一个简单的系统,该系统有几个组件。bash

  1. 服务提供方,Demo Service,从开发者的视角看,它是一个独立的项目(或者是子项目),它只提供接口声明给外部,它运行起来是一个独立的进程,好像一个 Web Server同样,在此等候远端的调用。图中画了三个一摸同样的用来描述它的部署状况,三个服务进程分别位于三台主机上,高可用(一个挂了,不影响全部),可伸缩(能够增长到十台),它访问本身对应的数据库。
  2. 服务消费方,Demo Consumer,为了简化只画了一个实例,和服务提供方同样,它也能够高可用,可伸缩。由于服务提供方和消费方,部署在不一样的主机上,因此他们之间的调用使用RPC(远程服务调用)
  3. 注册中心,Eureka-Server,既然有服务提供者和服务消费者,而他们都是运行在不一样主机上,那如何让服务消费者发现,并按照相应的协议调用服务提供者呢,这就引入了注册中心的概念。若是读者有dubbo使用经验,很容易想到 zookeeper 集群对吧,他们提供的功能是相似的。
    固然它的部署也是支持高可用的(多个实例注册组成集群),三个核心组件已经浮出水面了,
  4. 路由,Zuul,在图的最上侧。也是总体架构开发在外网的入口。经过 url 规则配置,能够把请求转发到合适的服务,例如请求 GET api.dummy.com/demo_consumer/user/1 经过Zuul,能够把请求转发到 demo-consumer:GET /user/1。固然Zuul还能够支持更多,包括通用的鉴权,过滤器等。

核心组件中涉及到服务消费方和服务提供方是经过RPC调用实现的,经过注册中心,服务消费方发现服务提供方,顺其天然就引入了客户端负载均衡和熔断相关的概念。消费方手持若干个提供方的实例,最简单的方式就是轮流调用,这就是客户端负载均衡了;若是一个服务提供方在过去一段时间内,故障比例达到阈值,那么能够暂时设置它为不可用,这就是熔断了。在Spring Cloud里提供了相关的内置组件,Ribbon和Hystrix。服务器

固然一切都不绝对,Spring Cloud的一个优点就是社区里有不少兼容性良好的备选方案,在 musical.ly 的Spring Cloud架构实践中提到:团队对框架自己作了较多的改造,替换了更友好的注册中心 Consul,采用了 gRPC 做为远程调用框架,用 Protobuf 做为序列化框架,替换了熔断和限流方案,集成了故障诊断和追踪功能等等,这些改造对业务是透明的。网络

微服务的部署

采用Spring Cloud后,不一样业务能够拆分红不一样的项目,均可以单独部署。可使用Jenkins搭建一套简单的持续集成和持续交付方案。开发人员推代码到Git仓库后,会触发Jenkins的构建动做,进一步的还能够用Jenkins执行不一样环境下的发布脚本,固然脚本还能够执行备份,以及回滚的动做。架构

执行到这里,该体系方案就能够支持一个公司走很远了,那Kubernetes又有什么勇武之地。app

假设公司进一步发展,流量和业务都极具增多,会出现两个比较常见的问题负载均衡

  1. 扩容动做依然有些麻烦,能够经过预先准备好的操做系统镜像(包括各类线上运行环境),把新的实例快速准备好,可是依然须要更新发布脚本。固然若是有强大的运维团队,是能够作到几乎自动化的。
  2. 大量的资源浪费,由于有不少服务,访问量很小,大量的机器可能CPU使用率不足5%这样的case时有发生(来自腾讯的同事分享说,他们的优化目标是CPU利用率平均30%),形成技术成本巨高不下。

理想的情况下,若是把运维手里的机器,都经过一个入口、统一管理起来,统一掌握集群的资源使用,须要对集群扩容或缩容的状况,只要增长或者回收服务器;须要对某个服务扩容缩容,只要简单的设置一下 replicas 数量。那该多好。(固然Kubernetes远远比这个功能多的多)

Kubernetes(K8S)

若是有过Docker的使用经验,就很容易理解K8S,最初使用docker的用途可能仅仅是用它搭建CI/CD,一条命令启动Gitlab,再一条命令启动一个Jenkins,一切超级简单。不少教程里,都会把若干微服务,放在一台服务器中的docker里运行,你会发现服务注册,服务发现都很简单。

可是,当容器运行在不一样的服务器上的时候,问题就来了,你甚至发现跨主机都容器之间都不能通讯。

K8S来源于谷歌,高富帅的出身,决定了刚出道就自带各类光环。市场占有率已经超过70%,已经成为了容器管理的主流工具。在实践中,由于大量的资料实践的背景都是GPE上的K8S集群,网络、存储等基础设施都由平台提供,一切都以为好简单,可是一旦尝试自建私有的K8S集群,却发现世界却充满敌意,甚至基本的网络插件都须要本身安装。

谷歌虽好,而且提供 $300 的代金券和一年试用期很厚道,可是谷歌不是你想访问就能够访问。幸亏阿里云也提供了Kubernetes集群服务,虽然价格比买ECS贵,不过相比一个运维团队的开销和各类不断踩坑感受也是划算的。

本文先介绍一些基础概念,而后介绍若是在阿里云的K8S集群上,部署Spring Cloud的微服务的实践。

集群

集群是一组节点,这些节点能够是物理服务器或者虚拟机,之上安装了Kubernetes平台。下图展现这样的集群。注意该图为了强调核心概念有所简化。这里能够看到一个典型的Kubernetes架构图。

Pod

K8S中最基础的调度单位是Pod,它有网络,有存储。Pod里面运行着一个或者若干个docker容器。同一个Pod里的容器共享同一个网络命名空间,可使用localhost互相通讯。能够理解成Pod就是一台主机,docker容器是运行在主机上的进程。

Replication Controller

咱们通常不会手动本身建立Pod,这样很难管理。利用Replication Controller,能够定义Pod运行内容,副本的个数等信息,它的升级版本是 ReplicaSet。如今已经建立了Pod的一些副本,那么在这些副本上如何均衡负载呢?咱们须要的是Service。

Service

能够把一组Pod组成服务 Service,Service有一个虚拟的ClusterIP,服务访问能够经过ClusterIP做为统一请求入口,由于一个 Service 对应一组Pod,因此能够作到负载均衡。服务能够经过 NodePort,LoadBalancer的方式暴露对外服务。注意 type = LoadBalancer须要云服务平台提供基础的服务,自建的K8S集群默认是没有这个东西的。若是在阿里云上定义服务 type = LoadBalancer 后,你会发现,在管理后台的负载均衡页面,会增长一个负载均衡器

kubectl get service 执行结果,注意External-IP
自动建立负载均衡器对外提供统一入口,backend对应容器Pod

实践

为了下降成本,笔者从阿里云采购了最低配置的K8S集群,包含 3个Master节点,还有2个Node节点。基本都是最低配置,天天成本30块钱。预先准备好了一份手脚架代码,包括几个基本项目

  • demo-service 服务提供方
  • demo-provider 服务消费方
  • eureka-server 注册中心
  • api-gateway 网关

须要首先部署注册中心 eureka-server, 而后部署服务提供方 demo-service 和 消费方demo-provider,最后部署 api-gateway。

那么手里是代码,对面是K8S集群,怎么部署上去呢,答案是 镜像服务。
阿里的镜像服务是一个选择,固然也能够选择其它的,能够经过CI/CD方案,自动把构建后的镜像,Tag后,推到镜像服务提供的Registry中,而后就可使用了。

例如在镜像仓库中,有以下镜像:registry.cn-beijing.aliyuncs.com,经过书写YAML文件,定义RC

apiVersion: v1
kind: ReplicationController
metadata:
 name: demo-service
spec:
 replicas: 2
 selector:
  app: demo-service
 template:
  metadata:
   labels:
    app: demo-service
  spec:
   containers:
    - name: demo-service
      image: registry.cn-beijing.aliyuncs.com/tianming/demo-service:latest
      ports:
      - containerPort: 8081

黑色字体部分,将要发布服务的镜像了,这里设置了副本数是 2,经过执行下面的bash命令就能够建立 RC了

kubectl create -f demo-service-rc.yaml

而后能够执行

kubectl get pods

查看容器是否被正确建立,若是pod有状态异常,例如 Error 等能够经过describe命令查看建立失败的缘由,这个命令颇有用,能够帮咱们搞定不少问题。

kubectl describe pod demo-service-xxx

固然这还不够,咱们还须要定义服务,以及服务暴露的接口:

apiVersion: v1
kind: Service
metadata:
 name: demo-service
spec:
 type: LoadBalancer
 ports:
  - port: 8081
 selector:
  app: demo-service

这样服务就创建好,由于设置了 LoadBalancer,因此能够经过 external ip 在外部网络访问到。在 Prod 环境中,咱们不会这样作,通常只有 api-gateway 项目才会对外暴露访问端口。

按照这样的方式,依次部署其它服务,若是有一套可行的 CI/CD 方案,那么后续的发布,扩容缩容,都将易如反掌。

碎碎念

若是你是一个初创公司的CTO,没什么人手搭建集群,本身也没有精力学习K8S。在架构选型上,能够只用Spring Cloud的微服务组件,而后在云主机上部署;若是你有能力学习K8S,可是没有精力和人力搭建自建K8S集群,能够购买云厂商的集群服务,有了这套东西,至少不再用担忧将来扩容的痛苦了,而且做为架构的发展的相对终极形态,短时间内也不会有重构的需求,等将来有人力财力,再迁回自建的K8S集群,也是易如反掌。

整体来说,文章仍是有些浅了,实践中的坑和优化的选项,仍是要远远大于此文的。

传送门:https://zhuanlan.zhihu.com/p/31670782