微服务敏捷开发最佳实践

本文介绍本地网络与阿里云不互通场景下实现微服务敏捷开发的最佳实践。

背景信息

遇到的问题

有微服务敏捷开发实践经验的开发测试,经常会遇到如下几种问题。

  • 开发人员开发接口,因为依赖其他接口返回,所以无法独立进行联调测试,需要一个包含依赖的其他所有应用的环境。

  • 开发人员将开发的接口部署到环境后,联调测试不符合预期,通过日志等方式排查,发现是依赖的其他应用代码有变更,导致原有的逻辑发生了变更。

  • 多位开发人员并行开发多个应用,在联调测试时,只能允许一个应用联调测试,其他应用需要在该应用联调测试完成后才能逐个进行。

  • 应用联调测试报错,Debug时其他开发测试正在联调测试的接口也报错。

解决方案

让流量在Feature环境内流转非常重要,是微服务敏捷开发的基础。

微服务带来了敏捷开发的便利,但是微服务架构本身也给开发环境带来了一定的复杂性,多个应用间形成完整的流量闭环逻辑,才能避免应用间相互影响。微服务引擎MSE将应用直接接入到微服务治理,绑定环境并配置标签路由后,即可实现应用的精准流量控制,既能享受到微服务架构带来的敏捷开发的便利,又不会给日常开发环境的搭建带来很大的成本。

  • 方案一:每个迭代或Feature都享有一套独立的完整环境。

    这套独立的环境包含了整个微服务应用集所有的应用,包含注册中心和接入层。

    从该方案的计算方法我们可以发现,当应用增加和环境增加时,成本成倍的增加。

    • 优点:实现简单。

    • 缺点:成本较高。

  • 方案二:基于MSE标签路由功能使用开发环境隔离。

    表面上看起来有很多套环境,每个环境都有一套完整的微服务应用。但是这些环境内的有些应用节点不只属于某一个环境,是被多个环境共享,大大降低了成本。只需要维护一套完整的基线环境,基线环境包含了所有微服务应用,也包含了服务注册中心、域名、SLB、网关等其他设施。在增加Feature环境时,只需要单独部署这个Feature所涉及到改动的应用即可,而不需要在每个Feature环境都部署整套的微服务应用及其配套设施。

    维护N套Feature环境的成本计算方法为:N+M。与方案一乘法计算方法相比,相当于零成本增加Feature环境。这样我们就可以放心地扩容出多套Feature环境,每位研发测试都可以轻松拥有属于自己的独立环境。

    具体实现方案如下图所示。流量流转架构图

本文以开发环境为本地网络与阿里云不互通(本地网络没有专线来连通本地网络与阿里云上的VPC,但是希望能让在本地网络启动的应用连接上阿里云上的开发测试集群,并实现精准的流量隔离)为例进行说明。如果是以下两种典型的常见开发环境,只需要根据基本的MSE接入方式和MSE标签路由方式即可直接使用。

  • 开发环境的所有应用都部署在本地的机房:所有的开发环境都在本地,或者本地网络内自建的数据中心,每个开发小组希望拥有一套独立的开发环境,避免应用间相互影响。

  • 本地网络与阿里云通过专线互联互通:本地网络通过专线与阿里云上的VPC实现了互联互通。开发、测试环境主要部署在阿里云,但是正在开发的工程,有一部分是运行在本地网络的,甚至直接运行在开发人员的个人电脑上,希望有一套环境可以独立地进行联调测试。

前提条件

步骤一:将应用接入MSE微服务治理

将ACK微服务应用接入MSE治理中心,具体操作,请参见ACK微服务应用接入MSE治理中心微服务治理

步骤二:部署应用并验证

分别部署spring-cloud-zuul、spring-cloud-a、spring-cloud-b、spring-cloud-c这四个业务应用,以及注册中心Nacos Server,模拟出一个真实的调用链路。

您也可以直接在Demo中获取对应的源码。

Demo应用的结构图下图,应用之间的调用,既包含了Spring Cloud的调用,也包含了Dubbo的调用,覆盖了当前市面上最常用的两种微服务框架,这些应用均采用了Spring Cloud 、Dubbo的标准用法。部署应用

  1. 容器服务控制台左侧导航栏中,单击集群

  2. 集群列表页面,单击目标集群名称或者目标集群右侧操作列下的详情

  3. 在集群管理页左侧导航栏,选择工作负载 > 无状态

  4. 无状态页面选择命名空间,然后单击使用YAML创建资源

    使用容器服务控制台来部署应用为例,使用的YAML文件如下:

    说明

    使用端云互联的前提是注册中心使用的是MSE中的Nacos,所以请您在部署之前修改YAML文件中的 spring.cloud.nacos.discovery.server-addrdubbo.registry.address为您自己购买的MSE Nacos地址。若您使用的是MSE Nacos域名为公网域名,还需要确保为公网域名开启了白名单。

    展开查看代码

    # 部署业务应用
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: spring-cloud-zuul
    spec:
      selector:
        matchLabels:
          app: spring-cloud-zuul
      template:
        metadata:
          labels:
            app: spring-cloud-zuul
            msePilotCreateAppName: spring-cloud-zuul
        spec:
          containers:
            - env:
                - name: JAVA_HOME
                  value: /usr/lib/jvm/java-1.8-openjdk/jre
                - name: spring.cloud.nacos.discovery.server-addr    //MSE Nacos地址配置
                  value: 'mse-xxxxxxx-nacos-ans.mse.aliyuncs.com:8848'
              image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-zuul:1.0.0
              imagePullPolicy: Always
              name: spring-cloud-zuul
              ports:
                - containerPort: 20000
    
    ---
    apiVersion: v1
    kind: Service
    metadata:
      annotations:
        service.beta.kubernetes.io/alibaba-cloud-loadbalancer-spec: slb.s1.small
        service.beta.kubernetes.io/alicloud-loadbalancer-address-type: internet
      name: zuul-slb
    spec:
      ports:
        - port: 80
          protocol: TCP
          targetPort: 20000
      selector:
        app: spring-cloud-zuul
      type: LoadBalancer
    status:
      loadBalancer: {}
    
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: spring-cloud-a
    spec:
      selector:
        matchLabels:
          app: spring-cloud-a
      template:
        metadata:  
          labels:
            app: spring-cloud-a
            msePilotCreateAppName: spring-cloud-a
        spec:
          containers:
            - env:
                - name: JAVA_HOME
                  value: /usr/lib/jvm/java-1.8-openjdk/jre
                - name: spring.cloud.nacos.discovery.server-addr
                  value: 'mse-xxxxxxx-nacos-ans.mse.aliyuncs.com:8848'
                - name: dubbo.registry.address
                  value: 'nacos://mse-xxxxxxx-nacos-ans.mse.aliyuncs.com:8848'
              image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-a:1.0.0
              imagePullPolicy: Always
              name: spring-cloud-a
              ports:
                - containerPort: 20001
              livenessProbe:
                tcpSocket:
                  port: 20001
                initialDelaySeconds: 10
                periodSeconds: 30
    
    
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: spring-cloud-b
    spec:
      selector:
        matchLabels:
          app: spring-cloud-b
      template:
        metadata:
          labels:
            app: spring-cloud-b
            msePilotCreateAppName: spring-cloud-b
        spec:
          containers:
            - env:
                - name: JAVA_HOME
                  value: /usr/lib/jvm/java-1.8-openjdk/jre
                - name: spring.cloud.nacos.discovery.server-addr
                  value: 'mse-xxxxxxx-nacos-ans.mse.aliyuncs.com:8848'
                - name: dubbo.registry.address
                  value: 'nacos://mse-xxxxxxx-nacos-ans.mse.aliyuncs.com:8848'
              image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-b:1.0.0
              imagePullPolicy: Always
              name: spring-cloud-b
              ports:
                - containerPort: 20002
              livenessProbe:
                tcpSocket:
                  port: 20002
                initialDelaySeconds: 10
                periodSeconds: 30
    
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: spring-cloud-c
    spec:
      selector:
        matchLabels:
          app: spring-cloud-c
      template:
        metadata:
          labels:
            app: spring-cloud-c
            msePilotCreateAppName: spring-cloud-c
        spec:
          containers:
            - env:
                - name: JAVA_HOME
                  value: /usr/lib/jvm/java-1.8-openjdk/jre
                - name: spring.cloud.nacos.discovery.server-addr
                  value: 'mse-xxxxxxx-nacos-ans.mse.aliyuncs.com:8848'
                - name: dubbo.registry.address
                  value: 'nacos://mse-xxxxxxx-nacos-ans.mse.aliyuncs.com:8848'
              image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-c:1.0.0
              imagePullPolicy: Always
              name: spring-cloud-c
              ports:
                - containerPort: 20003
              livenessProbe:
                tcpSocket:
                  port: 20003
                initialDelaySeconds: 10
                periodSeconds: 30
  5. MSE治理中心控制台中选择对应的Region查看应用列表,进入应用详情页的节点情况。

    • 在本地配置好K8s集群对应的kubeconfig文件后,执行以下命令:

      kubectl get svc,deploy

      预期输出:

      NAME                 TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)        AGE
      service/kubernetes   ClusterIP      192.168.xx.xx     <none>         4xx/TCP        7d
      service/zuul-slb     LoadBalancer   192.168.xx.xx     47.94.xx.xx     80:319xx/TCP   9m30s
      
      NAME                                READY   UP-TO-DATE   AVAILABLE   AGE
      deployment.apps/spring-cloud-a      1/1     1            1           9m30s
      deployment.apps/spring-cloud-b      1/1     1            1           9m30s
      deployment.apps/spring-cloud-c      1/1     1            1           9m30s
      deployment.apps/spring-cloud-zuul   1/1     1            1           9m30s
    • 执行curl http://47.94.xx.xx/A/a 发起调用,执行以下命令:

      curl http://47.94.xx.xx /A/a

      预期返回结果:

      A[10.242.xx.xx] -> B[10.242.xx.xx] -> C[10.242.xx.xx]%

步骤三:将IDEA启动的应用接入环境

在这一步中,将演示如何在网络没有互通的情况下,将本机启动的应用接入到开发环境。首先您需要将您K8s集群的kubeconfig文件保存到本机。

  1. 下载源码。本示例工程所有源码都在 Demo中,将代码通过Git命令克隆到本地,并且找到mse-simple-demo文件夹中的A、B、C、gateway四个应用,即本次最佳实践所使用的示例。

  2. 安装CloudToolkit插件。最新版本的Cloud Toolkit详情,请参见 CloudToolkit

  3. 在IDEA的顶部菜单栏依次选择Tools > Alibaba Cloud > Preferences,在IDEA中填写AccessKey ID、AccessKey Secret。

    说明

    由于使用端云互联功能的时候,需要访问您的MSE资源,所以这里需要您填写您的AccessKey ID、AccessKey Secret,并确保此AccessKey ID、AccessKey Secret拥有访问MSE资源的权限。

    IDEA

  4. 在弹出的Preferences页面左侧选择Alibaba Cloud Toolkit > Accounts,配置AccessKey ID、AccessKey Secret信息,单击保存

    AK

  5. Preferences页面左侧选择Alibaba Cloud Toolkit > Microservice > MSE,单击开启微服务治理,并配置参数。

    参数

    描述

    LicenseKey

    您阿里云账号对应的MSE产品的 LicenseKey ,您可在MSE控制台的概览页面查看LicenseKey的值。

    image

    说明

    请您做好LicenseKey的保密工作。各个Region的LicenseKey值可能不一致,请选择对应的Region,并和开发环境接入的Region保持一致。

    App Name

    应用在接入MSE时所使用的应用名,请根据实际业务情况进行配置。

    说明

    应用名需要和本次所启动的应用保持一致。

    Tag

    此应用所属的环境Tag,基线环境不用填,其他请根据实际业务情况进行填写。如果此应用属于feature1环境,请填写feature1。

    Agent地址

    选择自己应用所在的地域,需要和LicenseKey所在的地域、以及基线环境接入的地域都保持一致。

    开启RPC灰度

    支持对Spring Cloud和Dubbo的流量进行精准控制。默认情况下请开启,除非您明确知道关闭此选项的使用场景,否则请勿关闭此选项。

    开启标签染色

    推荐开启,开启后经过此应用的流量就只会在对应的Tag环境中流转。

    开启消息灰度

    请根据业务实际情况选择是否开启,目前仅支持RocketMQ 4.5及以上版本。更多消息灰度相关的信息,请参见步骤三:为应用开启RocketMQ消息灰度

    开启MSE

  6. 配置端云互联参数。

    1. 首先需要配代理模式为 K8s。在IDEA的Preference页面左侧选择Alibaba Cloud Toolkit > Microservice > Proxy,单击AddProfile增加一个名称为K8s的代理。

    2. 单击右侧的Add按钮,选择代理类型Kubernetes,并选择配置文件地址命名空间

    3. 在IDEA的Preference页面左侧选择Alibaba Cloud Toolkit > Microservice,查找并勾选端云互联并进行参数配置。其中产品为微服务引擎 MSE,并选择与部署时一致的Region实例命名空间代理选择已经配置的K8s代理,如果您的应用是Spring Cloud应用,还需要在本地Spring Cloud服务端口中配置Tomcat的启动端口。

      端云

步骤四:启动应用,联调验证

首先,验证环境是否接入成功。

  1. 在IDEA中启动应用。启动应用的时候,您如果看到端云互联成功的提示,证明端云互联功能已经生效。

    info

  2. 登录MSE治理中心控制台,在应用列表界面进行查看。

    本示例中本机应用已经成功接入到MSE,并且成功打上了feature1的标签。

    接入成功

步骤五:发起流量调用进行测试

如果您发往网关的Request是HTTP请求,并且这个请求需要在某个环境(例如上文中配置的环境标签feature1)中完成闭环,那么您需要在请求的Header中添加x-mse-tag=[feature1],配置完成后流量会自动在指定的环境内完成闭环。注意这里的Key为x-mse-tag是固定值,其参数值名称需要和环境标签(上文中配置的alicloud.service.tag)保持一致。

如果您的请求来源为Dubbo,则需要在RpcContext中增加Attachment,内容也是x-mse-tag=[feature1]

环境标签配置完成后,使用curl命令发起流量调用请求,返回结果如下:

  • 执行以下Curl命令:

    curl http://47.94.143.xx:80/A/a

    调用结果如下:

    A[10.242.xx.90] -> B[10.242.xx.91] -> C[10.242.xx.152]%
  • 执行以下Curl命令:

    curl -H"x-mse-tag:feature1" http://47.94.143.xx:80/A/a

    调用结果如下:

    Afeature1[xx.xx.12.118] -> B[10.242.xx.91] -> C[10.242.xx.152]%

您还可以在MSE治理中心控制台配置转发规则,比如设置name=xiaoming的流量进入到feature1环境。

  1. 登录MSE治理中心控制台

  2. 在左侧导航栏,选择治理中心 > 应用治理,然后单击目标应用名称为spring-cloud-a的资源卡片。

  3. 在左侧导航栏,单击流量治理,然后单击标签路由页签,在打了feature1标签的应用流量规则下方单击添加

  4. 创建标签路由面板中配置流量规则,然后单击确定

    本文示例中设置的流量规则条件为name=xiaoming为spring-cloud-a配置流量规则

    关于流量规则的配置,请参见配置标签路由

    流量规则生效后,可在应用概览页面查看流量分布结果。

  5. 开启流量规则之后,使用curl命令发起流量调用请求,查看返回结果。

    • 执行以下Curl命令:

      curl http://47.94.xx.xx:80/A/a

      调用结果如下:

      A[10.242.0.xx] -> B[10.242.0.xx] -> C[10.242.0.xx]%
    • 执行以下Curl命令:

      curl http://47.94.xx.xx:80/A/a\?name\=xiaoming

      调用结果如下:

      Afeature1[30.225.12.xx] -> B[10.242.0.xx] -> C[10.242.0.xx]%
    • 执行以下Curl命令:

      curl http://47.94.xx.xx:80/A/a\?name\=xiaoming

      调用结果如下:

      Afeature1[30.225.12.xx] -> B[10.242.0.xx] -> C[10.242.0.xx]%

如果您的网关应用不属于Java体系,则需要在网关层配置规则,目前已经支持MSE云原生网关、Nginx和K8s Ingress等,详细的接入方式,请参见 基于MSE云原生网关实现全链路灰度基于Ingress-nginx网关实现全链路灰度等文档。