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

2022年05月10日 阅读数:5
这篇文章主要向大家介绍如何低成本玩转微服务敏捷开发,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

微服务敏捷开发不简单

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

微服务给你们带来了敏捷开发的特性,基于敏捷开发带来的便利,让咱们能够在同一个时间内多个迭代/feature 并行开发。但微服务架构自己也给开发环境带来了必定的复杂性:每一个 feature 的修改点均可能会被分散在多个应用中,须要多个应用互相配合才能完成总体的逻辑。这些应用既须要互相配合好,又不能让他们互相影响,因此敏捷开发有时候也不是那么容易。html

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

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

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

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

为了方便表述,咱们用 A、B、C、D 来代指 A、B、C、D 的线上稳定版本,也叫作基线版本;A一、C1 来代指 feature1 环境中的 A 和 C ;B二、C二、D2 来来代指 feature2 环境中 B、C、D。spring

那么开发测试 feature1 的同窗会要求他的请求,准确地在 A1 ---> B ---> C1 ---> D 中流转。为何必定要这样,咱们来简单分析一下: a. 若是流量走到 A 或者 C 的基线环境,由于他们都没有包含 feature1 相关的代码,因此确定是没法正常测试和联调 feature1 对应的功能。 b. 若是流量走到 B二、D2 环境,大多数状况下是能够正常工做的,由于正常状况下 B2 和 D2 中的修改是不会影响 feature1 的。可是由于 feature1 和 feature2 多是由不一样的同窗开发的,或者有不一样的开发排期和节奏,他们有本身的开发、重启、debug 节奏,因此大几率仍是会出现上文中提到的场景。api

综上所述,让流量在 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]%

3、 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://oscimg.oschina.net/oscnet/up-1dddda7ccb5c769e66e98b047ba798db05e.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网关实现全链路灰度等文档。