Nacos 是阿里开源的一个针对微服务架构的综合解决方案,它提供了服务发现、配置管理、服务治理等功能。它致力于帮助开发者快速实现动态服务发现、服务配置、服务元数据及流量管理,以更敏捷、更容易地构建、交付和管理微服务平台。

Spring Cloud 支持 Nacos 作为其服务发现和配置管理的解决方案之一。通过将 Nacos 与 Spring Cloud 集成,开发者可以轻松地实现服务的注册与发现、动态配置管理等功能。在Spring Cloud 应用中,开发者可以通过添加 Nacos 的依赖和配置,将应用注册到 Nacos 服务器,并通过 Nacos 提供的 API或 控制台进行服务的管理和配置。

本文主要介绍在 Nacos 注册的服务信息迁移到服务网格,在此之前先了解一下 Spring Cloud 和 Service Mesh。

关于 Spring Cloud

Spring Cloud 基于 Spring Boot 开发,提供一套完整的微服务解决方案,具体包括服务注册与发现,配置中心,全链路监控,API网关,熔断器,远程调用框架,工具客户端等选项中立的开源组件,并且可以根据需求对部分组件进行扩展和替换。它是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过 SpringBoot 风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。

SpringBoot 专注于快速方便的开发单个个体微服务。

SpringCloud 是关注全局的微服务协调整理治理框架,它将 SpringBoot 开发的一个个单体微服务整合并管理起来,SpringBoot可以离开 SpringCloud 独立开发项目,但是 SpringCloud 离不开 SpringBoot,属于依赖关系。

Spring Cloud 优缺点

优点
  • 低耦合度:Spring Cloud 中的微服务架构使得每个服务都可以独立开发和部署,减少了模块之间的耦合度。从而提高了系统的可维护性和可扩展性,使得开发团队可以更加灵活地调整和优化系统结构。
  • 降低开发成本:通过微服务架构,开发者可以并行开发不同的服务,减少了开发过程中的阻塞和等待时间。这种并行开发模式大大提高了开发效率,降低了开发成本。
  • 简化配置:Spring Cloud通过注解和配置中心等方式简化了配置管理。这使得开发者可以更加专注于业务逻辑的实现,而不需要过多关注配置文件的编写和维护。
  • 支持跨平台开发:Spring Cloud的微服务架构可以使用不同的语言开发,这使得开发者可以更加灵活地选择适合的技术栈,提高了开发的灵活性和效率。
  • 丰富的组件支持:Spring Cloud提供了丰富的组件支持,如服务发现、负载均衡、熔断器、分布式配置等。这些组件可以帮助开发者快速构建稳定、可靠的微服务应用。
缺点
  • 维护成本高:由于 Spring Cloud 采用了微服务架构,每个服务都是独立的模块,因此需要针对每个服务进行部署、监控和维护。这会增加维护成本和管理工作量。
  • 分布式系统复杂性:Spring Cloud 主要针对分布式系统的开发和部署,因此需要处理分布式系统的复杂性,如服务发现、负载均衡、熔断等。这需要开发人员具备较高的技术水平和经验。
  • 版本兼容性问题:随着 Spring Cloud 的不断发展,各个组件的版本可能会发生变化,导致版本兼容性问题。在升级 Spring Cloud 的组件时,需要注意版本之间的兼容性,以免出现不必要的问题。

关于服务网格

服务网格(service mesh)是处理服务间通信的基础设施层。通过网络代理阵列的形式,为现代云原生应用提供透明、可靠的网络通信。服务网格本质是解决微服务间如何更好通信的问题,通过负载均衡、灰度路由、熔断限流等治理规则,合理编排流量,实现最大化的集群服务能力,是服务治理演进的产物;

以 Istio 为例简要说明其功能。 Istio 有助于降低这些部署的复杂性。它是一个完全开源的服务网格,可以透明地分层到现有的分布式应用程序上。它也是一个平台,包括允许它集成到任何日志记录平台、遥测或策略系统的 API。Istio的多样化功能集使你能够成功高效地运行分布式微服务架构,并提供保护、连接和监控微服务的统一方法。


服务网格的优缺点

优点
  • 服务发现与负载均衡:服务网格允许开发人员轻松地编目和跟踪其网络上所有已注册服务的网络位置和健康状况。通过内置的负载均衡机制,服务网格能够控制流量整形等功能,动态调整流量分布,确保服务的高可用性。
  • 故障注入与测试:服务网格可以将随机故障注入到各种服务中,以测试系统的弹性和健壮性,提高系统的容错能力。
  • 透明的可见性:服务网格可以对所有流量进行跟踪和监控,提供详细的流量度量、统计分析和日志记录,帮助开发人员快速定位和解决问题。
  • 统一的流量控制和管理:服务网格提供了一个单一的、准确的、可控的面向所有服务的出口,方便对流量进行统一管理和控制,如智能路由、超时重试、熔断等。
  • 轻量无侵入接入:业务应用系统通过服务网格技术架构可以轻量级接入,无需对应用本身进行大量修改,减少改造成本。
  • 性能优化:服务网格提供了流量管理、安全加速、负载均衡等性能优化策略,有助于提升微服务应用的性能和响应
缺点
  • 增加了复杂性:服务网格将 Sidecar 代理和其他组件引入分布式环境中,增加了整体链路和操作运维的复杂性。
  • 延迟:由于服务网格在请求路径中增加了一个或多个代理层,会为系统调用增加额外的延迟。

Spring Cloud 和 服务网格的功能对比如下:

功能列表

Spring Cloud

Isito

服务注册与发现

支持,基于 Eureka,consul 等组件,提供 server,和 Client 管理

支持,基于 XDS 接口获取服务信息,并依赖“虚拟服务路由表”实现服务发现

链路监控

支持,基于 Zikpin 或者 Pinpoint 或者 Skywalking 实现

支持,基于 sideCar 代理模型,记录网络请求信息实现

API网关

支持,基于 zuul 或者 spring-cloud-gateway 实现

支持,基于 Ingress gateway 以及 egress 实现

熔断器

支持,基于 Hystrix 实现

支持,基于声明配置文件,最终转化成路由规则实现

服务路由

支持,基于网关层实现路由转发

支持,基于 iptables 规则实现

安全策略

支持,基于 spring-security 组件实现,包括认证,鉴权等,支持通信加密

支持,基于 RBAC 的权限模型,依赖 Kubernetes 实现,同时支持通信加密

配置中心

支持,springcloud-config 组件实现

不支持

性能监控

支持,基于 Spring cloud 提供的监控组件收集数据,对接第三方的监控数据存储

支持,基于 SideCar 代理,记录服务调用性能数据,并通过 metrics adapter,导入第三方数据监控工具

日志收集

支持,提供 client,对接第三方日志系统,例如ELK

支持,基于 SideCar代理,记录日志信息,并通过 log adapter,导入第三方日志系统

K8s 下部署 Nacos

注:默认所执行的命令空间为 default,如更改命名空间请查看修改相关 YAML 文件

  • 克隆项目
git clone https://github.com/nacos-group/nacos-k8s.git

部署 NFS

  • 创建角色
kubectl create -f deploy/nfs/rbac.yaml
  • 创建 ServiceAccount 和部署 NFS-Client Provisioner
kubectl create -f deploy/nfs/deployment.yaml

  • 创建 NFS StorageClass
kubectl create -f deploy/nfs/class.yaml

  • 验证NFS部署成功
kubectl get pod -l app=nfs-client-provisioner

Nacos 服务注册信息迁移服务网格_spring

部署数据库

cd nacos-k8s

kubectl create -f deploy/mysql/mysql-nfs.yaml

  • 验证数据库是否正常工作
kubectl get pod | grep mysql

Nacos 服务注册信息迁移服务网格_Servicemesh_02

注:默认的 YAML文件 部署的 mysql 是初始化完成了的,若使用另外版本可数据库初始化可参考 https://github.com/alibaba/nacos/blob/develop/distribution/conf/mysql-schema.sql


部署 Nacos

  • 修改 deploy/nacos/nacos-pvc-nfs.yaml
data:
  mysql.host: "数据库地址"
  mysql.db.name: "数据库名称"
  mysql.port: "端口"
  mysql.user: "用户名"
  mysql.password: "密码"

  • 创建 Nacos
kubectl create -f nacos-k8s/deploy/nacos/nacos-pvc-nfs.yaml

  • 验证 Nacos节点启动成功
kubectl get pod -l app=nacos

Nacos 服务注册信息迁移服务网格_Servicemesh_03


访问后台管理 ui 界面

nacos 部署方式中支持部署 ingress,浏览器访问 http://nacos-web.nacos-demo.com/nacos/index.html ,默认用户名密码nacos\nacos进行管理后台的访问

注:访问 ingress 需要对 nacos-web.nacos-demo.com 进行 dns 解析,解析 ip 为 slave 节点ip,您的集群需要提前安装 ingress controller。若没有部署 ingress 也可以通过使用 haproxy 代理在集群外浏览器访问。Nacos 服务注册信息迁移服务网格_spring_04

部署应用

我们部署一个 nacos 官方提供的一个实例服务并将服务注册到 nacos,获取示例代码请查阅 https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-cloud-example/nacos-spring-cloud-discovery-example

修改配置

spring-cloud-provider-example 的 application.yaml 如下所示,注意修改 nacos 的 server-addr 和mysql url 地址以及密码

# application.yaml
server:
  port: 8070
spring:
  application:
    name: spring-cloud-provider
  cloud:
    nacos:
      discovery:
        server-addr: nacos-0.nacos-headless.default.svc.cluster.local:8848,nacos-1.nacos-headless.default.svc.cluster.local:8848,nacos-2.nacos-headless.default.svc.cluster.local:8848

  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://mysql.default.svc.cluster.local:3306/nacos_devtest?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
    username: nacos
    password: nacos
  main:
    allow-bean-definition-overriding: true


spring-cloud-consumer-example 的 application.yaml和 如下:

# application.yaml
server:
  port: 8080
spring:
  application:
    name: spring-cloud-consumer
  cloud:
    nacos:
      discovery:

修改完成后,将 consumer 和 provider 其打包成 jar包

# mvn clean pakage
# 会产生一个 target目录包含了编好的 jar包
# ls target 
classes  generated-sources  maven-archiver  maven-status  nacos-spring-cloud-consumer-example-0.2.0-SNAPSHOT.jar  nacos-spring-cloud-consumer-example-0.2.0-SNAPSHOT.jar.original
打包镜像

将打包好的两个jar 包编译成容器镜,以 consumer 为例 Dockerfile 如下:(provider 将consumer jar包改为 provider jar 包)

FROM harbor.chinauos.com/uos-container-amd64/openjdk-17:17.0.8
COPY target/nacos-spring-cloud-consumer-example-0.2.0-SNAPSHOT.jar /nacos-spring-cloud-consumer-example-0.2.0-SNAPSHOT.jar
ENTRYPOINT ["java","--add-opens","java.base/java.lang=ALL-UNNAMED","-jar","/nacos-spring-cloud-consumer-example-0.2.0-SNAPSHOT.jar"]

将构建好的镜像推送到镜像仓库


部署

spring-provider.yaml 如下

# 以Deployment部署Pod
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-cloud-provider
  namespace: dev
spec:
  selector:
    matchLabels:
      app: spring-cloud-provider
  replicas: 1
  template:
    metadata:
      labels:
        app: spring-cloud-provider
    spec:
      containers:
      - name: spring-cloud-provider
        image: sunyuxuan/provuder:latest
        ports:
        - containerPort: 8070
        env:
        - name: MYSQL_PASSWORD
          valueFrom:
            secretKeyRef:
              name: secret
              key: password
---
# 创建Pod的Service
apiVersion: v1
kind: Service
metadata:
  name: spring-cloud-provider
  namespace: dev
spec:
  ports:
  - port: 8070
    protocol: TCP
    targetPort: 8070
  selector:
    app: spring-cloud-provider

spring-cloud-consumer.yaml 如下:

# 以Deployment部署Pod
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-cloud-consumer
  namespace: dev
spec:
  selector:
    matchLabels:
      app: spring-cloud-consumer
  replicas: 1
  template:
    metadata:
      labels:
        app: spring-cloud-consumer
    spec:
      containers:
      - name: spring-cloud-consumer
        image: sunyuxuan/consumer:latest
        ports:
        - containerPort: 8080
---
# 创建Pod的Service
apiVersion: v1
kind: Service
metadata:
  name: spring-cloud-consumer
  namespace: dev
spec:
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    app: spring-cloud-consumer

连接数据库的 secrect.yaml 如下:

apiVersion: v1
kind: Secret
metadata:
  name: secret
  namespace: default
data:
  password: bmFjb3M=

应用上述 YAML文件后 观察 pod 状态

# kubectl get pod -n default
NAME                                      READY   STATUS    RESTARTS        AGE
mysql-t7d5j                               1/1     Running   0               4h34m
nacos-0                                   1/1     Running   0               4h34m
nacos-1                                   1/1     Running   0               3h33m
nacos-2                                   1/1     Running   0               4h34m
nfs-client-provisioner-6d64b99f4c-9rb8c   1/1     Running   1 (101s ago)   4h35m
spring-cloud-consumer-678dd594b9-kmnkj    1/1     Running   0               92m
spring-cloud-provider-84dc87db75-dqlgk    1/1     Running   0               92m

登录到nacos 观察 两个服务是否已经注册成功Nacos 服务注册信息迁移服务网格_Cloud_05

迁移到服务网格

1、部署服务网格,可参考《在容器云平台部署服务网格》,并创建 Servicemesh Control Plane完成。

Nacos 服务注册信息迁移服务网格_spring_06

2、应用改造,去除依赖。删除组件,包括网关,熔断器,注册中心,负载均衡,链路跟踪组件,同时删除对应 client 的 SDK;

provider 和 consumer 的 pom.xml 去除掉 nacos 的依赖

Nacos 服务注册信息迁移服务网格_spring_07

consumer代码去除掉 nacos 配置部分

package com.alibaba.nacos.example.spring.cloud;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

/**
 * @author xiaojing
 */
@SpringBootApplication
public class NacosConsumerApplication {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(NacosConsumerApplication.class, args);
    }

    @RestController
    public class TestController {

        private final RestTemplate restTemplate;

        public TestController(RestTemplate restTemplate) {this.restTemplate = restTemplate;}

        @RequestMapping(value = "/echo/{str}", method = RequestMethod.GET)
        public String echo(@PathVariable String str) {
            return restTemplate.getForObject("http://service-provider/echo/" + str, String.class);
        }
    }
}

provider 去除掉 nacos 部分

package com.alibaba.nacos.example.spring.cloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author xiaojing
 */
@SpringBootApplication
public class NacosProviderApplication {

        public static void main(String[] args) {
                SpringApplication.run(NacosProviderApplication.class, args);
        }

        @RestController
        class EchoController {
                @RequestMapping(value = "/echo/{string}", method = RequestMethod.GET)
                public String echo(@PathVariable String string) {
                        return "Hello Nacos Discovery " + string;
                }
        }
}

3、应用容器化

将两个服务构建出 jar 包并构建出容器镜像推送到镜像仓库

# mvn clean package

4、修改 Yaml 文件开启 sidecar 自动注入,并部署应用。
Nacos 服务注册信息迁移服务网格_spring_08

# oc apply -f spring-consumer.yaml
# oc apply -f spring-provider.yaml
## oc get pod -n default
NAME                                     READY   STATUS    RESTARTS   AGE
spring-cloud-consumer-77f679f955-qb8mx   2/2     Running   0          7m15s
spring-cloud-provider-7d4d8df88c-szhqq   2/2     Running   0          6m22s

5、访问 Kiali 观察服务。Kiali 是 Service Mesh的管理控制台,能够查看各个服务的运行状态以及策略执行情况。

# 在部署 Servicemesh Control Plane 的命名空间获取 Kiali 路由
# oc get route -n istio-system | grep kiali
kiali                                                kiali-istio-system.apps.utccp.example.com                                                       kiali                  20001         reencrypt/Redirect   None

Nacos 服务注册信息迁移服务网格_spring_09



参考

K8S实战 部署spring-cloud+nacos+MySQL服务

Nacos