前言

无论什么语言,什么框架,它们容器化的原理都是一样的三步:编译生成的产物,或者不用编译,再通过dockerfile生成业务的镜像

前端调用后端:通过nginx upsteam 服务B的IP:端口 ---> xxx.com/servciceB 服务之间的调用,serviceA 通过nginx反代到servceB,通过域名xxx.com/servciceB 到serviceB

如果serviceA有多个实例,就是在nginx的upstream再加一个IP地址和端口 nginx不能有一个,否则会出现单点故障,前端有多个。

那前端有多个,如果后端加个实例的话,是件非常麻烦的事情,前端就要改多个。nginx配置文件可以做共享,怎么做共享呢?

方案: 不能用:nfs(单点故障)、ansible(配置文件同步到其他服务器上,如果一旦一个配置文件改错了,同步的是所有的nginx,这样会造成服务不可用)

在没有用springCloud或者服务发现之前,都是这种架构的。

服务A,访问服务B,不需要nginx,只需要服务发现。

consul实现太麻烦:

加了consul: serviceA、B、C 注册在consul,告诉consul我是谁,我的IP、端口是啥 serviceA调用serviceB,就问consul,consul就把B的信息都返回给A。A就在返回的B的实例列表里选一条连接B

A需要实现: 1、选哪条B的实例,这是A的代码里实现的) 2、如果通知了A,B有2个实例b1,b2,但此时b1挂了,A就要用轮询或其他算法,连接B的另一个实例) 还有: 问题:B注册到了consul,B1挂了,怎么在consul同步删除,这也是一个问题

一、SpringCloud

1.1 Eureka: 服务注册发现

服务间的访问:Eureka -- 东西流量

微服务的框架 Eureka:服务注册发现的中心(保存了其他应用的地址:serviceA、B)

A要调用B,Eureka就提供B的注册信息,这种和consul没啥区别,但是SpringCloud不仅仅实现了服务发现注册的机制。它有很多其他的组件,帮我们实现了负载均衡或容灾机制。(上面说的consul问题)

对于运维人员,不需要关心内部的逻辑代码,只需要知道:

Eureka是服务注册中心、serviceA、B就是java应用,A、B把信息注册在Eureka。 Eureka把注册信息缓存在一个注册表里. 【拉的形式】:B要访问A,B就会把Eureka的注册信息缓存到本地。 【推的形式】:Eureka更新了以后,会主动推到其他服务上。serviceA,有A1实例,现在又多了A2地址,Eureka不会立马同步到其他服务上的,等serviceB拉Eureka注册表的时候,Eureka才会把A2地址加进来。 【容错机制】serviceA的A1实例下线以后,Eureka会把A1实例的ip端口在注册表中删除,如果ServiceB没有及时同步Eureka注册表,也就是B还会连接A的实例A1。springCloud组件是有容错机制的,会自动轮询到下一个serviceA的地址A2。

【好处】:

比如公司有个项目,设计到40个后端应用,每个应用有3个副本,这相当于有120个实例,相当于有120个IP地址+端口。维护nginx配置会很恐怖的。 比如公司有40个后端应用,每个应用有3个副本,这120个微服务都把信息注册到Eureka里面,服务会用拉或者推的方式,获取其他服务的地址。 SpringCloud对开发而言,大大减少工作量,提高工作效率,对运维而言,也简化了部署工作。 不需要维护consul和很多nginx配置。

服务间的访问,serviceA 调用serviceB,被称为:东西流量

1.2  Zuul网关:提供动态路由、监控、安全,相当于一个网站所有后端的前门。

Zuul:提供动态路由、监控后端应用是否正常。及时的上线或下线,可以用Zuul实现灰度发布、蓝绿发布、访问控制。 相当于一个网站所有后端的前门。就是你访问我们网站,都是通过Zuul管理,可以在Zuul上做访问控制、路由、监控、灰度发布,都可以通过Zuul实现

南北流量 --Zuul 网关

链接访问过来,经过流量入口(外网网关),访问:根路径是到前端的,做前端页面展示, 访问:xxx.com/serviceA、serviceB是到我们后端的。这叫东西流量。 40个微服务,每个微服务有3个实例,就要nginx 配120个ip地址,如果serviceB要下线一个,那要把所有的nginx配置都要删掉一次,这就很麻烦。 SpringCloud用一个组件:

Zuul 网关

Zuul,也可以注册到Eureka信息里面,来获取其他服务的IP地址和端口。 Zuul 网关 内部维护了一张路由表,知道到serviceA、B,应该是哪个IP地址和端口。 /api是指向Zuul 如果你访问: /api/serviceA (Zuul维护的) 就访问到serviceA(A1,A2)服务上

zuul提供api接口 流量入口,无论访问什么样的服务,serviceA、serviceB,直接请求/api就可以了。后面zuul帮你路由,不用你去管了。 现在上线或下线一个服务,就不用更改nginx的配置,很大程度减少了我们的工作量。

1.3 ConfigServer: 配置管理,统一管理我们的配置

1、【解决配置文件重复】:通过配置文件中心解决 比如有两个微服务:serviceA、B,要连数据库,之前传统的是把配置写在代码里的,构建的时候,把配置打到jar包里。这种方式有什么缺点呢? 可能有30个微服务,调用的数据库、redis是同一个,需要在每个微服务下都加上配置文件。如果有3套环境,那30个微服务要配90个文件,那89个是重复的,我们需要一个统一管理配置文件的地方。 serviceA、B,如果都连接mysql、rabbitmq,redis等中间件都是一样的,我们写一份配置文件就可以。serviceA去请求配置中心,把配置拉走,再启动我们的服务。 如果用的SpringCloud。用的肯定是:ConfigServer,如果用的是PHP、go其他的语言,用的通用的是携程开发的“阿波罗”管理中心。

2、【不用改代码、重新编译】如果我们把配置文件写在代码里面,配置文件是打到jar包里的。我们要改配置的话,就要改文件,又要重新编译,然后再重新打包,重新上线。这就是重复多余的过程。 如果我们在配置中心管理的话,我们就只用在配置中心的文件改一改,再进行发布,然后其他服务,重启、或热加载就可以了。 配置中心改了之后,会通知serviceA。或者serviceA定期去拉配置。把最新的配置缓存到自己本地,自己去reload一下。 比如说prometheus就是这样的逻辑。prometheus监听到自己配置文件变化之后,会自动重载本身的服务,prometheus会reload一下,像nginx reload一样热加载。如果没有做热加载, 如果没有做热加载,就需要重启服务,比如serviceA做重启操作,就能载入最新的配置。就不需要重新编译了。 所以,springCloud引用了configServer叫配置管理。

二、如何在K8S上部署SpringCloud

2.1 在K8S上部署Eureka

Eureka至少部署三个实例,这三个实例启动的时候,指定了defautZone,defaultZone为三个eureka的地址,他们三个就能组建一个集群。 这三个地址,可以通过配置文件或者环境变量注入或者ConfigServer的方式注入,都是可以的。

2.1.1 Eureka不适合deployment

如果我们用deployment部署它,得用三个不同的deployment,不能是一个deployment配置三个副本。我们要部署三个deployment,然后部署三个域名,解析到不同的实例上面。

defaultZone:http://eureka-1:8761/eureka,http://eureka-2:8761/eureka,http://eureka-3:8761/eureka

1(1.1,1)、2、3 这样做滚动更新会比较麻烦,启新杀旧。会先启来一个新的1.1,那这个1域名后面挂了两个实例(1.1, 1),此时组成集群的可能是: 1、2、3 或者1.1、2、3, 或者1.1、1、2、3

服务注册也会出现问题,本来连接1的,结果service可能会连接1.1。可能会出现服务找不到的情况。

2.1.2 Eureka用statefulset部署

headless service Eureka用statefulset部署,会启三个副本,都有标识符:

Eureka用statefulset部署实例defaultZoneeureka-0.eureka1http://eureka-0.eureka:8761/eurekaeureka-1.eureka2http://eureka-1.eureka:8761/eurekaeureka-2.eureka3http://eureka-2.eureka:8761/eurekastatefulset名称-序号.headless service名称(无头service名称)

Eureka用statefulset部署 会生成三个无头service,就是fqdn,eureka-0.eureka会跑到第一个实例上,eureka-1.eureka会跑到第二个实例上,eureka-2.eureka会跑到第三个实例上。

这样defaultZone,就可以用无头service去配置

如果Eureka要部署在容器里面的话,让开发defaultZone写成这种格式。 我们只需要用statefulset部署Eureka。 发版的时候是倒序发版: 先把第三个容器删掉,再更新。如果它有问题,就不会再往下更新了。这样就不会出现刚刚讲的那种,它会注册到另一个实例上面,其他实例会找不到这个实例。所以要用statefulset部署。我们公司很多springCloud项目,都是用statefulset部署的。作为运维工程师,只需要告诉开发, 1、Eureka组成集群的的defaultZone要写成这种格式。 2、我的服务注册到Eureka的时候,Eureka地址也写成这三个地址: 这样地址就固定了,而且无论什么环境(生产、测试、开发环境),eureka的地址都是固定的。 不同的项目,是按namespace去隔离的。每个项目的defaultZone可以用同一个,也可以用三个地址的格式,这样就实现了不同环境的统一,不同项目的统一。这样就大大减少了工作量。 所以Eureka一定要使用statefulset部署,会生成固定的标识符,就是这三个,我们可以用ingress给我们的Eureka部署域名。这样开发就可以访问Eureka看到其他服务的注册信息,这个域名可以直接指到headless service里的。

defaultZone:  http://eureka-0.eureka:8761/eureka, http://eureka-1.eureka:8761/eureka, http://eureka-2.eureka:8761/eureka

2.1.3 到底用Eureka?还是K8S的服务发现?

K8s有自己的服务发现机制:

基于DNS的服务发现

        a)    CoreDNS(service服务发现)

     2. 基于环境变量的服务发现

        a)    Env(用的少)

    3.Istio(服务网格) – 服务发现

service 服务发现:(用的多) ServiceA -- > ServiceB http://service-b:8080

容器里执行env:(用的少) SERVICE_B_IP = service-b的ClusterIP SERVICE_B_PORT = Service-b的Port

K8S已经有服务发现了,还用Eureka吗? 新项目容器化了,已经用K8S了,就不用了。 老项目本身已经用Eureka,如果老大不想往K8S转,不想改动。那就不用了。但是就很难接入Istio服务网格。

如果用Eureka进行服务发现的,你的springCloud项目部署到K8S之后,是没法实现Istio服务网格这种功能的。流量控制、容灾机制、灰度发布等高级功能是很难实现的。

如果你们想实现Istio服务网格的功能的话,要把Eureka去掉。用K8S的服务发现。

对于新项目,先和开发人员确认到底使用K8S的服务发现,还是Eureka的服务发现,用K8S的coredns服务发现很简单,只需要在配置文件里写对端的服务地址,例如:http://service-b:8080。服务发现不需要它动手去写。很多开发愿意不用Eureka的。如果不用Eureka就不是用的SpringCloud架构了,只是单纯用的springBoot逻辑,以单个应用去部署的。

老项目已经用了Eureka,老大不想改,也没有用Istio服务网格的需求,就不用k8s的服务发现coreDNS。 新项目用springBoot逻辑,创建一堆service,让它去连就可以。

2.2 如何正确部署Zuul和ConfigServer到k8s

Zuul:提供动态路由、监控、安全,相当于一个网站所有后端的前门。 ConfigServer:配置管理,用来统一管理我们的配置

Zuul、ConfigServer这两个编译完成之后,就是jar包,就是java应用。 Zuul,我们用deployment把它起来,就可以了。配个service,前端配个api接口。

Zuul  zuul:zuul-svc(自定义的zuul的服务名) /  -->  前端 /api --> zuul /api/servicea  -->  zuul     Ingress

ConfigServer: 本身是无状态 Git、svc、MySQL(配置存在这些上,这些服务要做高可用,不能挂,否则configServer是读不到配置的) ConfigServer和zuul 只是一个简单的Java应用。 ConfigServer:http://config-svc (跟开发说:任何环境,configServer的地址就这一个)     Ingress--->给configserver配置一个域名,给开发查看configServer的配置 一共三个域名:Eureka(给研发看注册信息)、Zuul、configServer

2.3 弃用ConfigServer,把工作量分给谁:Zuul(开发),ingress(运维)

ConfigServer 本身是无状态 配置存在后端存储 Git、svc、MySQL: 调用链很长,如果网络出现波动的话,服务的启动会变得非常非常长。 此时,如果健康检查做的不对,你的服务会启不来

Redis、rabbit、MySQL 、 第三方服务的地址是什么?

环境变量Service — mysql-svc  service-a-mysql-svc

       开发环境:无selector --> 192.168.1.18:3306        生产环境创建一个同名的Service --> 172.16.1.20:3306

Redis、rabbit、MySQL,不同环境的配置是不一样的 引用configServer可以解决:不同环境下调用的配置是不一样的,用configServer来区分它们的配置。 如果用service,可以用统一的service名,通过更改service endpoint地址,就实现了不同环境调用不同的中间件。

如果用service,不需要维护configServer,不需要调用第三方服务,不需要调用配置中心,就能把服务起来。这样调用链特别短。不会因为某个地方的故障导致服务起不来。

用service虽然好,但是经过了一层coreDNS的解析,一般会出现网络延迟,但是延迟很小,而且coreDNS是高可用的,不会出现服务宕机的情况,比configServer好一点。因为用configServer还要维护git、mysql,肯定不能维护单点的mysql。

3、ConfigMap

把配置写在configMap里,把configMap挂载到容器里面,也是以文件形式挂载的,这样就能读到我们的配置。和configServer相比,少了调用configServer的过程,这样速度还是比较快的。但是这种情况下,需要维护很多个configMap,因为有可能serviceA、serviceB调用的数据库是不一样的。也不是两个configMap,因为configMap是K,V形式的,要在configMap里维护两个地址:service-a-mysql-svc、service-b-mysql-svc。 这样运维的工作量变大,开发工作量变小。

这个是配置文件方面的。也有用阿波罗去配置的,道理是一样的。 configServer只能给java做,阿波罗能给几乎任何语言都可以用的。

如果项目比较小的话,用k8s:service比较好,这样做服务网格的时候,可以把流控管理的更细。

**结论:** 弃用configServer,可用可不用:Zuul

如果不用Zuul的话,Zuul实现的路由,都要在ingress上配置一遍。当然ingress配置路由比较简单,只需要申明yaml文件就可以部署,不像之前nginx要改配置文件。但是如果我们后端比较多的话,就要配置很多条ingress。这样对于运维而言,工作量变多了。但是对于开发人员,工作量变少了。这个跟configServer是一样的。 主要取决于把工作量分给谁,来决定是用:Zuul(工作量:开发),ingress(工作量:运维)

弃用configServer: 因为你的配置没那么复杂,你用环境变量、或service,或configMap就足够了。 而且这样管理方便,不用调用第三方服务才能启动我的应用。当然和Eureka一样,不是一锤子敲死的。到底用不用,取决于自己的公司。

好文推荐

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。