如何低成本玩转微服务敏捷开发

微服务敏捷开发不简单

安得环境千万套,大庇开发小哥俱欢笑

微服务给大家带来了敏捷开发的特性,基于敏捷开发带来的便利,让我们可以在同一个时间内多个迭代/feature 并行开发。但微服务架构本身也给开发环境带来了一定的复杂性:每个 feature 的修改点都可能会被分散在多个应用中,需要多个应用互相配合才能完成整体的逻辑。这些应用既需要互相配合好,又不能让他们互相影响,所以敏捷开发有时候也不是那么容易。

相信实践过微服务敏捷开发的同学都曾经遇到过以下情况: a. 开发接口时,应用无法独立地联调测试,需要依赖于下游的返回,所以一般都需要一个完整的开发环境,这个环境需要包含所有的其他应用。 b. A 同学辛辛苦苦,终于开发好了一个接口,但是部署到开发环境后,发现返回值一直是错的,就是不符合预期,百思不得其解。最终根据日志、arthas 层层跟踪下去,发现原来是另一个同事更新了下游应用的代码,导致原有逻辑发生了变更。 c. A 同学准备开始联调测试了,这时候他要找到开发 B 和 C 吼一嗓子确认:“我要开始测试了哈兄弟们,你们都别动环境,不要重启和 debug 哈”。B 同学 和 C 同学一脸懵逼:“我自己这还有个逻辑没理清楚呢,刚改完代码准备测一发,你这一测试联调我就不能动环境了,我这功能得等到什么时候才能开发好”。 d. 排查问题好麻烦啊,要不直接 debug 一下吧,这 IDEA 远程 debug 刚连上去呢,立马就传来了同事的声音:“谁 XX 又在瞎动环境啊,怎么刚刚还能跑的接口现在就出错了”。 以上这些问题显然会影响项目的进度,非常容易造成项目延期。对于此刻的开发小哥哥而言,拥有一套属于自己的独立环境,带来的幸福感也许比有一套属于自己的小房子还大。

流量闭环是微服务敏捷开发的基础

上文中提到的问题,其实都是因为没有在开发环境中,精准地控制流量在 feature 环境内流转。

为什么精准地控制流量如此重要?举个最简单的微服务架构图来说明,这里假设应用的调用链路为 A ---> B ---> C ---> D ,现在同时开发两个 feature, feature1 和 feature2 。feature1 需要修改 A 和 C 的代码, feature2 需要修改 B、C 和 D 的代码。

为了方便表述,我们用 A、B、C、D 来代指 A、B、C、D 的线上稳定版本,也叫做基线版本;A1、C1 来代指 feature1 环境中的 A 和 C ;B2、C2、D2 来来代指 feature2 环境中 B、C、D。

那么开发测试 feature1 的同学会要求他的请求,准确地在 A1 ---> B ---> C1 ---> D 中流转。为什么一定要这样,我们来简单分析一下: a. 如果流量走到 A 或者 C 的基线环境,因为他们都没有包含 feature1 相关的代码,所以肯定是无法正常测试和联调 feature1 对应的功能。 b. 如果流量走到 B2、D2 环境,大多数情况下是可以正常工作的,因为正常情况下 B2 和 D2 中的修改是不会影响 feature1 的。但是因为 feature1 和 feature2 可能是由不同的同学开发的,或者有不同的开发排期和节奏,他们有自己的开发、重启、debug 节奏,所以大概率还是会出现上文中提到的场景。

综上所述,让流量在 feature 环境内流转非常重要,是微服务敏捷开发的基础。 在这里插入图片描述

如何准确地让请求在 feature 环境内流转呢?最简单的办法是每个迭代/Feature 的都享有一套独立的完整环境,这套独立的环境包含了整个微服务应用集所有的应用,包含注册中心和接入层,这样就能确保流量在 feature 环境里闭环,不用担心应用之间互相影响。 在这里插入图片描述

这个解决方案虽然简单,但是问题也很显而易见,成本比较大。我们假设微服务应用有10 个,每个应用只部署一台,以 java 为例,部署一个 java 应用按 2C4G 的 共享标准型 ECS 进行计算,维护一套环境一年的成本是 10 × 140 × 12 = 16800 元,如果同时有 4 套环境,即只支持两个迭代并行开发,每个迭代只有 2 个 feature,这样一年的成本就是 67200 元,而且我们可以发现,这里面计算公式使用的是乘法,当应用增加和环境增加时,成本的增加是成倍的。

注意,这里只是单纯地计算了应用使用的 ECS 的成本,其他周边的配套设施我们还没有计算,因为我们的开发、联调、测试是需要确保端到端的全流程都是足够顺利的,那这里就还会涉及到 域名/SLB/网关/注册中心这些资源,这些资源一般比较固定,不会需要进行大的修改,但是在多套环境的方案下这些资源也需要维护多套,成本还会进一步上升。

那么,有没有一个比较优雅地方式,既能享受到微服务架构带来的敏捷开发的便利,又不会给日常开发环境的搭建带来很大的成本呢?基于 MSE(微服务引擎 MSE,以下简称 MSE)标签路由功能使用开发环境隔离方案是您的不二之选。

如何低成本玩转敏捷开发

什么是 MSE 开发环境隔离,简单地说就是将 feature 环境的隔离方式从简单的物理隔离转为逻辑隔离。借助于 MSE 提供的逻辑隔离,您只需要维护一套完整的基线环境,在增加 feature 环境时,只需要单独部署这个 feature 所涉及到改动的应用即可,而不需要在每个 feature 环境都部署整套的微服务应用及其配套设施。

我们称这唯一的一套完整的环境为基线环境。基线环境包含了所有微服务应用,也包含了服务注册中心、域名、SLB、网关 等其他设施,而 feature 环境中只包含了这个 feature 中需要修改的应用。这样维护 n 套 feature 环境的成本,就变成了加法,而不是原来的乘法,由 n × m 变成了 n + m。差不多相当于零成本增加 feature 环境,这样我们就可以放心地扩容出多套 feature 环境,每个开发小哥哥都可以轻松拥有属于自己的独立环境,尽情地享受微服务敏捷开发。 在这里插入图片描述

从上图中我们可以看到,feature1 对应的流量,在发现 feature1 中存在 A1 应用时,一定会去往 A1 节点,A1在调用B的时候发现 feature1 环境中不存在 B1 ,则会将请求发到 基线版本的 B 中;B在调用C时,发现 feature1 环境存在 C1 应用,又会返回到 feature1 环境中,依次类推,确保了流量会在 feature1 环境中闭环。

注意,在这个过程中,您不需要修改任何代码和配置,直接接入 MSE 微服务治理即可使用,不会给您增加任何开发成本。

如何使用 MSE 开发环境隔离

场景分析

在描述如何使用开发环境隔离之前,我们先分析一下目前常用的开发环境具体的场景,这里选了三种典型的场景。

场景一 所有的开发环境都在本地,或者说公司内自建的 IDC ,这类场景下开发环境的所有应用都部署在本地的机房。

场景二 公司通过专线打通了办公网与阿里云上的 VPC ,两边的网络实现了互联互通。 开发、测试环境主要部署在阿里云,但是正在开发的工程,有一部分是跑在本地办公网的,甚至是直接跑在开发同事的个人电脑上。

场景三 公司内并没有专线来打通了办公网与阿里云上的 VPC。 但是希望能让本地启动的应用,连接上阿里云上的开发测试集群,并且实现精准的流量隔离。

首先说一下结论,这上面的三个场景,目前都是可以完整的支持的。其中场景一和场景二都不涉及到网络打通这部分的内容,其实只需要根据基本的 MSE 接入方式和 MSE 打标方式即可直接使用起来。

场景三比场景一和场景二多了一个网络打通的功能,所以会多一个端云互联的步骤,这里面会要求注册中心需要使用 MSE 提供的 Nacos。

在这个最佳实践中,我们会以场景三来实操。如果您的使用场景不是场景三,那么您可以忽略,同时也不需要关注端云互联相关的操作。

一 、开通 MSE 微服务治理专业版 登录MSE治理中心控制台,如果您尚未开通 MSE 微服务治理,请根据提示开通专业版。如果您已经开通了MSE 微服务治理基础版,请根据概览页中右侧的提示,升级到 专业版。

二 、 完成基线环境接入 1. 接入 MSE 治理

首先,您需要将基线环境的所有应用接入到 MSE 中,接入方式与您开发环境中应用部署环境有关。这里我们使用的是阿里云容器服务 ACK。更多接入场景请参考 MSE 帮助文档 MSE 微服务治理快速入门

  1. 在ACK中安装MSE治理中心组件 a. 登录容器服务控制台。 b. 在左侧导航栏单击市场 > 应用目录。 c. 在应用目录页面搜索并单击ack-mse-pilot。 d. 在ack-mse-pilot页面右侧集群列表中选择集群,然后单击创建在这里插入图片描述

安装MSE微服务治理组件大约需要2分钟,请耐心等待。 创建成功后,会自动跳转到目标集群的发布页面,检查安装结果。如果出现以下页面,展示相关资源,则说明安装成功。 3. 为ACK命名空间中的应用开启MSE微服务治理 a. 登录MSE治理中心控制台。 b. 在左侧导航栏选择微服务治理中心 > K8s集群列表。 c. 在K8s集群列表页面搜索框列表中选择集群名称集群ID,然后输入相应的关键字,单击 🔍 图标。 d. 单击目标集群操作列的管理。 e. 在集群详情页面命名空间列表区域,单击目标命名空间操作列下的开启微服务治理。(如果您的基线环境部署在 default 这个 namespace 中,则目标命名空间为 default) f. 在开启微服务治理对话框中单击确认

2. 部署基线应用

首先,我们将分别部署 spring-cloud-zuul、spring-cloud-a、spring-cloud-b、spring-cloud-c 这四个业务应用,模拟出一个真实的调用链路。

Demo 应用的结构图下图,应用之间的调用,既包含了 Spring Cloud 的调用,也包含了 Dubbo 的调用,覆盖了当前市面上最常用的两种微服务框架。这些应用都是最简单的 Spring Cloud 、 Dubbo 的标准用法,您也可以直接在 https://github.com/aliyun/alibabacloud-microservice-demo/tree/master/mse-simple-demo 项目上查看源码。 在这里插入图片描述

您可以使用 kubectl 或者直接使用 ACK 控制台来部署应用。部署所使用的 yaml 文件如下。 注意,上文中提到,使用端云互联的前提是注册中心使用的是 MSE 中的 Nacos,所以请您在部署之前修改 yaml 文件中的 spring.cloud.nacos.discovery.server-addr 和 dubbo.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:
      annotations:
        msePilotCreateAppName: spring-cloud-zuul
      labels:
        app: 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
              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:
      annotations:
        msePilotCreateAppName: spring-cloud-a
      labels:
        app: 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:
      annotations:
        msePilotCreateAppName: spring-cloud-b
      labels:
        app: 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:
      annotations:
        msePilotCreateAppName: spring-cloud-c
      labels:
        app: 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

3. 验证基线环境接入成功

完成上述步骤后,您的基线环境就已经部署好了。您可以在 MSE 控制台中找到对应的 Region 查看应用列表,以及应用详情页的节点情况。 在本地配置好 K8s 集群对应的 kubeconfig 文件,执行命令,结果如下

➜  ~ kubectl get svc,deploy
NAME                 TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)        AGE
service/kubernetes   ClusterIP      192.168.0.1              443/TCP        7d
service/zuul-slb     LoadBalancer   192.168.87.95   47.94.143.53   80:31983/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.143.53:80/A/a 发起调用,并查看返回结果

➜  ~ curl http://47.94.143.53:80/A/a
A[10.242.0.90] -> B[10.242.0.91] -> C[10.242.0.152]%

三、 IDEA 启动的应用接入 feature 环境

在这一步中,我们将演示如何在网络没有打通的情况下,将你本机启动的应用接入到 feature 环境。首先您需要将您 K8s 集群的 kubeconfig 文件保存到本机,并进行如下操作。

  1. 下载源码 本工程所有源码都在 https://github.com/aliyun/alibabacloud-microservice-demo 中,将代码 git clone 到本地,并且找到 mse-simple-demo 文件夹中的 A、B、C、gateway 四个应用,就是本次最佳实践所使用的公测。

  2. 安装 CloudToolkit 插件 安装最新版本的 Cloud Toolkit,安装详情请参考官网 https://www.aliyun.com/product/cloudtoolkit。

  3. 填写阿里云 AK、SK 由于使用端云互联功能的时候,需要访问您 MSE 的资源,所以这里需要您填写您的 AK、SK,并确保此 AK、SK拥有访问 MSE 资源的权限。 点击 IDEA 的 Tools 中找到 Preference ,找到 Alibaba Cloud Toolkit 中 Accounts ,配置 Access Key ID 和 Access Key Secret 信息,并点击保存。 在这里插入图片描述 在这里插入图片描述

  4. 配置 MSE 参数 点击 IDEA 的 Tools 中找到 Preference ,找到 Alibaba Cloud Toolkit 中 Microservice 下的 MSE ,点击 开启微服务治理,并安装下图的方式进行配置即可。 在这里插入图片描述

对图中的几个参数做一下说明

  1. LicenseKey 您阿里云账号对应的 MSE 产品的 LicenseKey ,请在https://mse.console.aliyun.com/#/msc/app/accessType 中的选择 ECS 集群,在 安装 MSE Agent 章节找到 LicenseKey 的值。 注意:请您做好 LicenseKey 的保密工作。 注意:各个 Region 的 LicenseKey 值可能不一致,请选择对应的 Region,并和基线环境接入的 Region 保持一致。
  2. App Name 应用在接入 MSE 时所使用的应用名,请根据实际业务情况进行配置,注意这个值需要和本次所启动的应用保持一致。
  3. Tag 此应用所属的环境 Tag,基线不用填,其他请根据实际业务情况进行填写。如果此应用属于 feature1 环境,请填写 feature1。
  4. Agent 地址 选择自己应用所在的地域,需要和 LienseKey 所在的地域、以及基线环境接入的地域 都保持一致。
  5. 开启 RPC 灰度 ✅ 支持对 Spring Cloud 和 Dubbo 近5年内的所有版本的流量进行精准控制。默认情况下请开启,除非您明确知道关闭此选项的使用场景,否则请勿关闭此选项。
  6. 开启标签染色 ✅ 推荐开启,开启后经过此应用的流量就只会在对应的 Tag 环境中流转。
  7. 开启消息灰度 ✅ 请根据业务实际情况选择是否开启,目前仅支持 RocketMQ 4.5 及以上版本。更多消息灰度相关的信息,请参考全链路灰度,消息部分。

5.配置端云互联参数

我们已经知道,在网络未打通的时候,联通本机环境和云上 K8s 集群需要使用 端云互联功能,所以这里我们需要配置一下端云互联。 首先需要配置一下代理模式为 K8s,点击 IDEA 的 Tools 中找到 Preference ,找到 Alibaba Cloud Toolkit 中 Microservice 下的 Proxy ,点击 AddProfile 增加一个名称为 k8s 的代理。然后点击右侧的 Add 按钮,选择代理类型 为 Kubernetes,并选择正确的 配置文件地址 和 命名空间。 在这里插入图片描述

 然后,点击 IDEA 的 Tools 中找到 Preference ,找到 Alibaba Cloud Toolkit 中 Microservice 下的 MicroService ,找到 端云互联 功能打 ✅。选择产品为 微服务引擎 MSE,并选择与上面部署时一致的 Region 、 实例 和 命名空间 ,代理选择刚刚配置好的 k8s,如果您的应用是 Spring Cloud 应用,还必须在  本地 Spring Cloud 服务端口中 配置 Tomcat 的启动端口。
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/ad3def72eb6b4960afd2dd400e89a5c7.png)

四 启动应用,轻松开始联调和测试

  1. 验证接入成功 完成上述配置之后,我们启动应用。首先,启动应用的时候,您会看到这两个提示,证明端云互联功能已经生效。 在这里插入图片描述 在这里插入图片描述

同时为了验证 MSE 微服务治理是否接入成功,我们可以登录 MSE 控制台的应用列表界面进行查看。在控制台中我们也可以看到,我的本机应用已经成功接入到 MSE ,并且成功打上了 feature1 的标签。 在这里插入图片描述

  1. 发起流量调用 假设我们发往网关的请求是 http 请求,希望这个请求再 feature1 中完成闭环,只需要在请求的 header 中添加 x-mse-tag=feature1 即可,流量会自动在 feature1 环境内完成闭环。注意这里的 key 为 x-mse-tag 为固定值,feature1 则需要和环境的标签(即上文中配置的 alicloud.service.tag)保持一致。 如果您的请求来源为 Dubbo,则需要在 RpcContext 中增加 Attachment ,内容也是 x-mse-tag=feature1。
➜  ~ curl http://47.94.143.53:80/A/a
A[10.242.0.90] -> B[10.242.0.91] -> C[10.242.0.152]%
➜  ~ curl http://47.94.143.53:80/A/a
A[10.242.0.90] -> B[10.242.0.91] -> C[10.242.0.152]%
➜  ~ curl -H"x-mse-tag:feature1" http://47.94.143.53:80/A/a
Afeature1[xx.xxx.12.118] -> B[10.242.0.91] -> C[10.242.0.152]%
➜  ~ curl -H"x-mse-tag:feature1" http://47.94.143.53:80/A/a
Afeature1[xx.xxx.12.118] -> B[10.242.0.91] -> C[10.242.0.152]%
➜  ~ curl -H"x-mse-tag:feature1" http://47.94.143.53:80/A/a
Afeature1[xx.xxx.12.118] -> B[10.242.0.91] -> C[10.242.0.152]%

如果您不方便在请求中增加 header 的话,还可以在 MSE 控制台配置规则,比如设置成 name=xiaohong 或 xiaohua 的流量进入到 feature1 环境。如下图所示。 在这里插入图片描述

开启流量规则之后,我们继续验证,可以看到,满足条件的请求会被转发到 feature1 环境。

➜  ~ curl http://47.94.143.53:80/A/a
A[10.242.0.90] -> B[10.242.0.91] -> C[10.242.0.152]%
➜  ~ curl http://47.94.143.53:80/A/a\?name\=xiaohong
Afeature1[30.225.12.118] -> B[10.242.0.91] -> C[10.242.0.152]%
➜  ~ curl http://47.94.143.53:80/A/a\?name\=xiaohua
Afeature1[30.225.12.118] -> B[10.242.0.91] -> C[10.242.0.152]%

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值