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

微服务敏捷开发不简单


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


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

相信实践过微服务敏捷开发的同学都曾经遇到过以下情况:

    1. 开发接口时,应用无法独立地联调测试,需要依赖于下游的返回,所以一般都需要一个完整的开发环境,这个环境需要包含所有的其他应用。
    2. A 同学辛辛苦苦,终于开发好了一个接口,但是部署到开发环境后,发现返回值一直是错的,就是不符合预期,百思不得其解。最终根据日志、arthas 层层跟踪下去,发现原来是另一个同事更新了下游应用的代码,导致原有逻辑发生了变更。
    3. A 同学准备开始联调测试了,这时候他要找到开发 B 和 C 吼一嗓子确认:“我要开始测试了哈兄弟们,你们都别动环境,不要重启和 debug 哈”。B 同学 和 C 同学一脸懵逼:“我自己这还有个逻辑没理清楚呢,刚改完代码准备测一发,你这一测试联调我就不能动环境了,我这功能得等到什么时候才能开发好”
    4. 排查问题好麻烦啊,要不直接 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 中流转。为什么一定要这样,我们来简单分析一下:

    1. 如果流量走到 A 或者 C 的基线环境,因为他们都没有包含 feature1 相关的代码,所以肯定是无法正常测试和联调 feature1 对应的功能。
    2. 如果流量走到 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治理中心组件
    1. 登录容器服务控制台
    2. 在左侧导航栏单击市场 > 应用目录
    3. 应用目录页面搜索并单击ack-mse-pilot
    4. ack-mse-pilot页面右侧集群列表中选择集群,然后单击创建

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

安装MSE微服务治理组件大约需要2分钟,请耐心等待。

创建成功后,会自动跳转到目标集群的发布页面,检查安装结果。如果出现以下页面,展示相关资源,则说明安装成功。

  1. 为ACK命名空间中的应用开启MSE微服务治理
    1. 登录MSE治理中心控制台
    2. 在左侧导航栏选择微服务治理中心 > K8s集群列表
    3. K8s集群列表页面搜索框列表中选择集群名称集群ID,然后输入相应的关键字,单击 ???? 图标。
    4. 单击目标集群操作列的管理
    5. 集群详情页面命名空间列表区域,单击目标命名空间操作列下的开启微服务治理。(如果您的基线环境部署在 default 这个 namespace 中,则目标命名空间为 default)
    6. 开启微服务治理对话框中单击确认

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 保持一致。

  1. App Name

应用在接入 MSE 时所使用的应用名,请根据实际业务情况进行配置,注意这个值需要和本次所启动的应用保持一致。

  1. Tag

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

  1. Agent 地址

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

  1. 开启 RPC 灰度 ✅

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

  1. 开启标签染色 ✅

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

  1. 开启消息灰度 ✅

请根据业务实际情况选择是否开启,目前仅支持 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 的启动端口。

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



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


1. 验证接入成功

完成上述配置之后,我们启动应用。首先,启动应用的时候,您会看到这两个提示,证明端云互联功能已经生效。

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

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

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

2. 发起流量调用

假设我们发往网关的请求是 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 全链路灰度配置 。

上一篇:win7限制,怎么更改win7的C:\WINDOWS\system32\drivers\etc\hosts文件


下一篇:开发人员如何成为架构师