购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.2 微服务与容器云的边界

微服务与Kubernetes容器云的边界的考量,其实就是思考微服务要不要跨多个Kubernetes集群的问题。比较理想的情况是微服务和Kubernetes完全对齐,也就是一套微服务运行在一套Kubernetes集群上。在这种情况下,微服务、配置中心+注册中心都在相同的Kubernetes集群中。当微服务指向配置中心时,写配置中心的ServiceName即可,网络I/O路径较短。否则,需要通过Kubernetes Ingress访问注册中心(如果容器云的SDN采用overlay模式),网络延迟将会较大。这在微服务数量较多、变更较频繁的时候更为明显。

但是,如果微服务跨多个Kubernetes,会有什么问题呢?

我们先看两种实现方式。

1)配置+注册中心在一个Kubernetes集群上:如果Kubernetes集群的SDN用的是underlay网络,那么其他Kubernetes集群注册的时候,由于其Pod IP和宿主机IP在同一个网络平面,使得注册中心能够准确识别到Pod的IP。

这种方式的弊端体现在如下三个方面。

2)配置+注册中心不在一个Kubernetes集群上:如果Kubernetes集群的SDN方案用的是overlay网络,那么其他Kubernetes集群注册的时候,由于Pod IP和宿主机IP不在同一个网络平面,导致注册中心不能准确识别Pod的IP,只能识别到Pod所在Kubernetes宿主机的IP(Pod以SNAT的方式访问集群外部)。想要解决这个问题,可以考虑使用Pod的多网络平面,也就是给Pod增加第二个虚拟网卡,挂载数据中心到同一个网络平面的IP。这种方式类似macvlan、ipvlan,不用再单独配置DNS,但弊端是当宿主机上启动的macvlan数量较多时,网卡性能会下降。

以上两种实现方式各有优劣势。笔者看来,如果Spring Cloud的边界远大于一个Kubernetes边界,想让一套Spring Cloud分布在很多个Kubernetes集群时,最好把微服务配置中心和注册中心从Kubernetes集群中独立出来,放在虚拟机或者物理机上。这样做的好处是让这个配置+注册中心离所有Kubernetes集群网络都比较近。而且,在虚拟机或者物理机上部署配置+注册中心,当需要注册微服务的时候,也不必再经过类似Ingress的环节,性能也会得到提升。此外,我们可以针对独立的配置+注册中心做高可用或者容灾方案。

接下来,我们介绍如何选择微服务注册中心和配置中心。

2.2.1 微服务注册中心的选择

注册中心本质就是一个Query函数,即Si=F(ServiceName)。ServiceName为查询服务参数,Si为服务可用的列表(IP:Port)。

为了方便读者理解服务注册,我们参照图2-25进行介绍,将ServiceA的三个实例注册到注册中心。

054-1

图2-25 三个实例注册到注册中心

1)服务提供方将三个实例注册到注册中心。

2)服务调用方想要调用ServiceA,通过ServiceName去注册中心查询。然后注册中心通过Si=F(ServiceName)查询出服务的IP:Port列表。

3)服务调用方通过IP:Port列表去调用服务。

接下来,我们考虑一个问题:对于ServiceA来说,如果在注册中心注册的时候,只成功注册两个实例,那么是否应该允许服务调用方访问Service的两个实例?在真实的微服务中,我们一定希望服务调用方能访问ServiceA注册成功的两个实例,而不是必须等三个实例都注册成功后才能被访问。

此外,注册中心不能因为自身的任何原因破坏服务之间本身的可连通性。

如图2-26所示,我们将服务注册中心集群中的三个实例分别部署到三个机房。每个机房各有两个微服务。如果机房3的网络出现问题,不能与机房1和机房2进行通信,结果会怎样?

055-1

图2-26 机房3的网络出现问题

如果是强一致性的注册中心(CAP模型中的CP模型),那么机房3中的实例3由于是少数节点,将会被终止运行。结果是,不仅ServerE和ServiceF不能访问机房1和机房2的服务,这两个服务之间的访问也会出问题。

那么,针对微服务的注册中心,我们如何选择?有3个思路。

关于应用级注册中心,我们选取几个主流的开源方案进行对比,如表2-12所示。整体而言,针对Java类应用,Nacos作为应用级注册中心具有很大的优势。

表2-12 注册中心方案对比

056-1

从表2-12可以看到,ZooKeeper、etcd、Consul都是CP模型。而etcd和ZooKeeper都不支持跨数据中心部署。因此,我们在选择微服务的服务注册中心时,可以选择Nacos。

使用应用级注册中心的优缺点如下。

具体的,使用应用级注册中心,需要考虑Pod的服务注册实现。

接下来我们看看平台侧的服务注册中心。

如果程序员决定用Kubernetes做服务发现,实现不同服务之间的调用,那么就需要使用Kubernetes的Service名称。Service名称是可以固定的。

Kubernetes/OpenShift中Service有短名和长名两种。以图2-27为例,jws-app就是Service的短名,Service的长名的格式是<sevrvice_name>.<namespace>.svc.cluser.local,例如jws-app.web.svc.cluser.local。Service短名可以自动补充成长名,由OpenShift中的DNS实现,具体将在后面介绍。

057-1

图2-27 Service名称

如果在两个不同的Namespace中有两个相同的Service短名,微服务调用是否会出现混乱?程序员的代码里是否要写Service全名?

首先,从容器云集群管理员的角度来看,对于所有项目,例如几十个或者更多,会觉得在不同Namespace中存在相同的Service短名是可能的(比如Namespace A中有名为acat的Service,Namespace B中也有名为acat的Service)。但从程序员的角度来看,他只是容器云的使用者,只拥有自己负责的Namespace的管理权,不能访问其他Namespace。而且绝大多数情况下,同一个业务项目的微服务一般会运行在同一个Namespace中,如果使用短名称(只写Service名称),则默认会自动补全成当前Namespace的FQDN。只有在跨Namespace调用的时候才必须写全名。

所以,如果程序员写的程序用到了Service名称,那么,真正进行应用的Pod之间的通信时,也必然会以Service名称去查找。通过Service名称解析为Service ClusterIP,然后经过Kube-proxy(默认为iptables模式)的负载均衡设备最终选择一个实际的Pod IP。找到Pod IP之后,接下来就会进行实际的数据交换,与Service并无关联。

使用平台注册中心的优缺点如下。

注册中心的整体选择思路主要从三个维度考量:应用是否跨开发语言,微服务的边界是否大于Kubernetes集群,以及是否限定应用的Service名称。

下面我们看5种情况。

1)应用跨语言,微服务边界不大于一个Kubernetes集群,不限定应用的Service名称:使用Kubernetes平台的etcd。

2)应用跨语言,微服务边界大于一个Kubernetes集群,不限定应用的Service名称:使用应用级注册中心,而且每种语言都需要设置自己的注册中心。

3)应用不跨开发语言,微服务不大于一个Kubernetes边界,不限定应用的Service名称:使用Kubernetes平台的etcd。

4)应用不跨开发语言,微服务大于一个Kubernetes边界,不限定应用的Service名称:使用一个应用级注册中心。

5)限定应用的Service名称:使用应用级注册中心。

2.2.2 微服务配置中心的选择

配置中心存储的是独立于应用的只读变量。除此之外,配置中心还需要有权限控制,并且可以进行多个不同集群的配置管理。

与注册中心一样,配置中心同样有以下3个选择思路。

对于平台侧的配置中心,Kubernetes/OpenShift默认的配置管理是ConfigMap,即通过ConfigMap方式给应用注册配置。ConfigMap的访问权限由Kubernetes/OpenShift自身的RBAC提供。

应用级配置中心如表2-13所示。整体而言,针对Java类应用,Apollo作为应用级别配置中心具有很大的优势。

表2-13 注册中心方案选择

058-1

配置中心的整体选择思路主要从两个维度考量:应用是否跨开发语言以及微服务的边界是否大于Kubernetes集群。

下面我们看4种情况。

1)应用跨语言,微服务边界不大于一个Kubernetes集群:使用Kubernetes平台的ConfigMap。

2)应用跨语言,微服务边界大于一个Kubernetes集群:使用应用级配置中心,而且每种语言都需要设置自己的配置中心。

3)应用不跨开发语言,微服务不大于一个Kubernetes边界:使用Kubernetes平台的ConfigMap。

4)应用不跨开发语言,微服务大于一个Kubernetes边界,不限定应用的Service名称:使用应用级配置中心。

2.2.3 平台与应用级相结合的注册和配置中心

在本节中,我们介绍平台与应用级相结合的注册和配置中心的实现。需要指出的是,这种方式只适合Spring Cloud部署在单个Kubernetes集群的情况,此前这种方式被大量使用,但绝不是一个好的方法。笔者之所以展开介绍,是想让读者直观了解配置中心和注册中心的实际效果。

以图2-28为例,我们将整套微服务部署到一个Namespace中,从图中可以看到配置中心(service-config)和注册中心(service-registry)的SVC和端口号(https://github.com/davidsajare/spring-cloud-on-openshift.git)。

059-1

图2-28 配置中心和注册中心

微服务部署完后,业务微服务在指定配置中心的地址是service-config:8888。而service-config到IP地址的解析,由Kubernetes中的CoreDNS完成。例如,我们查看card-service部署中的环境变量,configure server指向http://service-config:8888/,如图2-29所示。

060-1

图2-29 configure server的配置

在微服务注册时,先指定访问配置中心(service-config:8888),然后配置中心(service-config)的Profile定义了注册中心的地址和端口号(service-registry:8761),以便微服务能够在注册中心进行注册,效果如图2-30所示。

060-2

图2-30 微服务访问配置中心和注册中心

我们查看配置中心(service-config:8888)名为openshift的配置文件,如图2-31所示。

060-3

图2-31 名为openshift的配置文件

图2-31所示的配置指定了注册中心的地址和端口号(service-registry:8761),即Eureka的地址和端口号:

"eureka.instance.instance-id": "${POD_NAME:${spring.application.name}}:${server.port}",
"eureka.instance.hostname": "${HOSTNAME:${spring.application.name}}",

查看service-registry的环境变量,如图2-32所示。

061-1

图2-32 service-registry的环境变量

上面第一段代码,带入变量后,显示注册中心的两个Pod名和端口号:

#第一个注册中心Pod
"eureka.instance.instance-id":service-registry-0:service-registry:8761
"eureka.instance.hostname": service-registry-0.service-registry:service-registry
#第二个注册中心Pod
"eureka.instance.instance-id":service-registry-1:service-registry:8761
"eureka.instance.hostname": service-registry-1.service-registry:service-registry

接下来,我们看一个微服务gateway-3-gjgfz的启动过程,观察它如何完成服务注册。Pod启动后,读取了关于配置中心的环境变量:

Starting the Java application using /opt/jboss/container/java/run/run-java.sh ...
INFO exec  java -Dspring.profiles.active=openshift
    -Dspring.cloud.config.uri=http://service-config:8888/
    -javaagent:/usr/share/java/prometheus-jmx-exporter/jmx_prometheus_javaagent.jar=
    9779:/opt/jboss/container/prometheus/etc/jmx-exporter-config.yaml -XX:+Us
    eParallelOldGC -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeRatio=
    4 -XX:AdaptiveSizePolicyWeight=90 -XX:MaxMetaspace
    Size=100m -XX:+ExitOnOutOfMemoryError -cp "." -jar /deployments/
    gateway-0.0.1-SNAPSHOT.jar

然后gateway-3-gjgfz微服务很快获取到配置中心中名为openshift为的配置文件:

2021-03-13 01:58:44.020  INFO 1 --- [main]
    c.c.c.ConfigServicePropertySourceLocator : Fetching config from server
    at:http://service-config:8888/
2021-03-13 01:58:44.153  INFO 1 --- [main]
    c.c.c.ConfigServicePropertySourceLocator : Located environment: name=gateway,
    profiles=[openshift], label=null, version=null,

接下来完成服务注册:

2021-03-13 01:58:46.835  INFO 1 --- [           main]
    DiscoveryClientOptionalArgsConfiguration : Eureka HTTP Client uses RestTemplate.
2021-03-13 01:58:46.969  INFO 1 --- [           main]
    o.s.c.n.eureka.InstanceInfoFactory : Setting initial instance status as: STARTING
2021-03-13 01:58:47.042  INFO 1 --- [           main]
    com.netflix.discovery.DiscoveryClient : Initializing Eureka in region us-east-1
2021-03-13 01:58:47.049  INFO 1 --- [           main]
    c.n.d.s.r.aws.ConfigClusterResolver : Resolving eureka endpoints via configuration
2021-03-13 01:58:47.073  INFO 1 --- [           main]
    com.netflix.discovery.DiscoveryClient  : Disable delta property : false
2021-03-13 01:58:47.073  INFO 1 --- [           main]
    com.netflix.discovery.DiscoveryClient : Single vip registry refresh property : null
2021-03-13 01:58:47.073  INFO 1 --- [           main]
    com.netflix.discovery.DiscoveryClient  : Force full registry fetch : false
2021-03-13 01:58:47.073  INFO 1 --- [           main]
    com.netflix.discovery.DiscoveryClient  : Application is null : false
2021-03-13 01:58:47.073  INFO 1 --- [           main]
    com.netflix.discovery.DiscoveryClient  : Registered Applications size is zero : true
2021-03-13 01:58:47.073  INFO 1 --- [           main]
    com.netflix.discovery.DiscoveryClient  : Application version is -1: true
2021-03-13 01:58:47.073  INFO 1 --- [           main]
    com.netflix.discovery.DiscoveryClient  : Getting all instance registry info
    from the eureka server
2021-03-13 01:58:47.146  INFO 1 --- [           main]
    com.netflix.discovery.DiscoveryClient  : The response status is 200
2021-03-13 01:58:47.149  INFO 1 --- [           main]
    com.netflix.discovery.DiscoveryClient  : Starting heartbeat executor: renew
    interval is: 30
2021-03-13 01:58:47.152  INFO 1 --- [           main]
    c.n.discovery.InstanceInfoReplicator  : InstanceInfoReplicator onDemand
    update allowed rate per min is 4
2021-03-13 01:58:47.157  INFO 1 --- [           main]
    com.netflix.discovery.DiscoveryClient  : Discovery Client initialized at
    timestamp 1615600727156 with initial instances count: 7
2021-03-13 01:58:47.158  INFO 1 --- [           main]
    o.s.c.n.e.s.EurekaServiceRegistry     : Registering application GATEWAY
    with eureka with status UP
2021-03-13 01:58:47.159  INFO 1 --- [           main]
    com.netflix.discovery.DiscoveryClient  : Saw local status change event
    StatusChangeEvent [timestamp=1615600727159, current=UP, previous=STARTING]
2021-03-13 01:58:47.162  INFO 1 --- [nfoReplicator-0]
    com.netflix.discovery.DiscoveryClient  : DiscoveryClient_GATEWAY/gateway-3-
    gjgfz:8080: registering service...
2021-03-13 01:58:47.216  INFO 1 --- [nfoReplicator-0]
    com.netflix.discovery.DiscoveryClient  : DiscoveryClient_GATEWAY/gateway-3-
    gjgfz:8080 - registration status: 204
2021-03-13 01:58:47.312  INFO 1 --- [           main]
    o.s.b.web.embedded.netty.NettyWebServer : Netty started on port 8080
2021-03-13 01:58:47.313  INFO 1 --- [           main]
    .s.c.n.e.s.EurekaAutoServiceRegistration : Updating port to 8080
2021-03-13 01:58:47.332  INFO 1 --- [           main]
    com.demo.gateway.Application             : Started Application in 4.556
    seconds (JVM running for 5.166)
2021-03-13 02:03:47.076  INFO 1 --- [trap-executor-0]
    c.n.d.s.r.aws.ConfigClusterResolver : Resolving eureka endpoints via configuration
2021-03-13 02:08:47.077  INFO 1 --- [trap-executor-0]
    c.n.d.s.r.aws.ConfigClusterResolver : Resolving eureka endpoints via configuration

微服务注册成功后,我们到注册中心查看注册成功的应用,如图2-33所示。

063-1

图2-33 在注册中心查看注册成功的应用

总结如下:

1)在上述Spring Cloud代码中,业务微服务访问配置中心,用的是Kubernetes ServiveName:Port,然后读取配置中心的配置,去注册中心注册;

2)微服务在注册中心注册成功后,记录端点的信息是Pod Hostname:Port。

注意,Kubernetes和Spring Cloud完全1:1对应是比较理想的情况,这时候微服务之间的互访、微服务访问配置中心和注册中心,都是在内部完成的。 A+atBmz+/sM8mhiF5vigEzrCDNXkmMMtxGZxs84AZ0XDiusBUK3UcFcG++2tQRhy

点击中间区域
呼出菜单
上一章
目录
下一章
×