服务网格ASM的流量管理功能可以实现应用的流量迁移。本文将通过示例介绍如何通过ASM实现TCP应用流量的迁移。

前提条件

背景信息

本文以Istio官方Task TCP-Traffic-Shifting为例来讲述如何实现在一个TCP服务的两个版本之间进行流量灰度切换。该Task中的服务是一个简单地Echo服务,在v1版本中,该服务在收到的数据在前面加上“one”并返回;在v2版本中,该服务在收到的数据前面加上“two”并返回。

步骤一:部署示例应用

我们需要部署两个Deployment,如下图所示,打开容器服务控制台,选择应用 - 无状态,然后选择您希望部署的目标集群和namespace,然后点击右上方的使用模版创建。

  1. 部署TCP- Echo应用的2个版本。
    1. 登录容器服务控制台
    2. 在控制台左侧导航栏中,单击集群
    3. 集群列表页面中,单击目标集群名称或者目标集群右侧操作列下的应用管理
    4. 无状态页面命名空间下拉列表中选择命名空间。
    5. 单击右上方的使用模版创建
    6. 使用模板创建页面,将下面的Yaml模版粘贴到模版文本框中,单击创建
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: tcp-echo-v1
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: tcp-echo
            version: v1
        template:
          metadata:
            labels:
              app: tcp-echo
              version: v1
          spec:
            containers:
            - name: tcp-echo
              image: docker.io/istio/tcp-echo-server:1.1
              imagePullPolicy: IfNotPresent
              args: [ "9000", "one" ]
              ports:
              - containerPort: 9000
      ---
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: tcp-echo-v2
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: tcp-echo
            version: v2
        template:
          metadata:
            labels:
              app: tcp-echo
              version: v2
          spec:
            containers:
            - name: tcp-echo
              image: docker.io/istio/tcp-echo-server:1.1
              imagePullPolicy: IfNotPresent
              args: [ "9000", "two" ]
              ports:
              - containerPort: 9000
      无状态页面可以看到新创建的两个版本的TCP-Echo应用。
  2. 创建一个服务,并将其对外暴露。
    1. 登录容器服务控制台
    2. 在控制台左侧导航栏中,单击集群
    3. 集群列表页面中,单击目标集群名称或者目标集群右侧操作列下的应用管理
    4. 在集群管理页左侧导航栏中,单击服务
    5. 服务页面命名空间下拉列表中选择命名空间。
    6. 服务页面,单击右上角的创建
    7. 创建服务对话框中,设置服务的相关信息,然后单击创建
      需要设置的参数如下所示:
      • 名称:设为tcp-echo。
      • 类型:选择服务类型,即服务访问的方式。支持虚拟集群IP节点端口负载均衡
        说明 您的服务类型为虚拟集群IP时,才能设置实例间发现服务(Headless Service)。您可以使用无头Service与其他服务发现机制进行接口,而不必与Kubernetes的实现捆绑在一起。
      • 关联:选择tcp-echo-v1。
        说明 将从关联的Deployment中提取app这个标签作为Service的Selector,这决定了Kubernetes service将流量转发到哪个Deployment。由于tcp-echo-v1和tcp-echo-v2两个Deployment拥有相同的标签,即app:tcp-echo,因此可以关联任一Deployment。
      • 外部流量策略:可选值为Local或Cluster。
        说明 您的服务类型为节点端口负载均衡时,才能设置外部流量策略
      • 端口映射名称设为tcp;服务端口容器端口设为9000;协议设为TCP。
      • 注解:为该服务添加一个注解(annotation),配置负载均衡的参数,例如设置service.beta.kubernetes.io/alicloud-loadbalancer-bandwidth:20表示将该服务的带宽峰值设置为20Mbit/s,从而控制服务的流量。更多参数请参见t16677.html#concept_sxm_l3s_vdb
      • 标签:您可为该服务添加一个标签,标识该服务。
      服务(Service)页面可以看到新创建的服务,type-echo。

步骤二:设置服务网格ASM的路由规则

通过设置服务网格的Istio网关、虚拟服务和目标规则,将流量全部指向tcp-echo服务的v1版本。

  1. 登录ASM控制台
  2. 在左侧导航栏,选择服务网格 > 网格管理
  3. 网格管理页面,找到待配置的实例,单击实例的名称或在操作列中单击管理
  4. 创建服务网关。
    1. 控制平面区域,选择Gateway页签,然后单击新建
    2. 新建页面,从命名空间下拉列表中选择default,并在文本框中输入以下Yaml文件内容,单击确定
      apiVersion: networking.istio.io/v1alpha3
      kind: Gateway
      metadata:
        name: tcp-echo-gateway
      spec:
        selector:
          istio: ingressgateway
        servers:
        - port:
            number: 31400
            name: tcp
            protocol: TCP
          hosts:
          - "*"
      Gateway页签下可以看到新创建的名为tcp-echo-gateway的Istio网关。
  5. 创建虚拟服务。
    1. 控制平面区域,选择VirtualService页签,然后单击新建
    2. 新建页面,从命名空间下拉列表中选择default,并在文本框中输入以下Yaml文件内容,单击确定
      apiVersion: networking.istio.io/v1alpha3
      kind: VirtualService
      metadata:
        name: tcp-echo
      spec:
        hosts:
        - "*"
        gateways:
        - tcp-echo-gateway
        tcp:
        - match:
          - port: 31400
          route:
          - destination:
              host: tcp-echo
              port:
                number: 9000
              subset: v1
      VirtualService页签下可以看到新创建的名为tcp-echo的虚拟服务。
  6. 创建目标规则。
    1. 控制平面区域,选择DestinationRule页签,然后单击新建
    2. 新建页面,从命名空间下拉列表中选择default,并在文本框中输入以下Yaml文件内容,单击确定
      apiVersion: networking.istio.io/v1alpha3
      kind: DestinationRule
      metadata:
        name: tcp-echo-destination
      spec:
        host: tcp-echo
        subsets:
        - name: v1
          labels:
            version: v1
        - name: v2
          labels:
            version: v2
      DestinationRule页签下可以看到新创建的名为tcp-echo-destination的目标规则。

步骤三:部署入口网关

在入口网关中,添加端口31400,并指向Istio网关的31400端口。

  1. 登录ASM控制台
  2. 在左侧导航栏,选择服务网格 > 网格管理
  3. 网格管理页面,找到待配置的实例,单击实例的名称或在操作列中单击管理
  4. 数据平面区域,单击部署入口网关
  5. 部署入口网关页面,为集群添加入口网关。
    1. 部署集群列表中选择要部署入口网关的集群。
    2. 指定负载均衡的类型为公网访问
    3. 选择负载均衡。
      • 使用已有负载均衡:从已有负载均衡列表中选择。
      • 新建负载均衡:单击新建负载均衡,从下拉列表中选择所需的负载均衡规格。
      说明 建议您为每个Kubernetes服务分配一个SLB。如果多个Kubernetes服务复用同一个SLB,存在以下风险和限制:
      • 使用已有的SLB会强制覆盖已有监听,可能会导致您的应用不可访问。
      • Kubernetes通过Service创建的SLB不能复用,只能复用您手动在控制台(或调用OpenAPI)创建的SLB。
      • 复用同一个SLB的多个Service不能有相同的前端监听端口,否则会造成端口冲突。
      • 复用SLB时,监听的名字以及虚拟服务器组的名字被Kubernetes作为唯一标识符。请勿修改监听和虚拟服务器组的名字。
      • 不支持跨集群复用SLB。
    4. 单击添加端口,将名称设为tcp,服务端口容器端口设为31400。
      说明 服务端口指的是整个网格对外暴露的端口,是外部访问使用的端口;而容器端口指的是从服务端口进来的流量所指向的Istio网关端口,所以容器端口需要与Istio网关一致。
    5. 单击确定

步骤四:检查部署结果

通过Kubectl确认tcp-echo服务的流量指向是否符合预期。

  1. 通过Kubectl客户端连接至Kubernetes集群。详情请参见通过kubectl连接Kubernetes集群
  2. 执行以下命令,获得服务的地址与端口。
    export INGRESS_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="tcp")].port}')
  3. 使用telnet指令向tcp-echo服务发起连接请求。
    telnett $INGRESS_HOST $INGRESS_PORT
    Trying xxx.xxx.xxx.xxx...
    Connected to xxx.xxx.xxx.xxx.
    Escape character is '^]'
  4. 输入任意字符串,按Enter发送,返回的字符串前面带了“one”。这说明tcp-echo服务已经成功部署,且流量全部指向了tcp-echo-v1版本。

步骤五:按比例将流量路由到tcp-echo-v2

此处将20% 的流量指向tcp-echo-v2版本,其余80% 仍然打到tcp-echo-v1。

  1. 修改ASM实例的虚拟服务配置。
    1. 控制平面区域,选择VirtualService页签
    2. VirtualService页面,单击tcp-echo所在操作列中的yaml
      VirtualService页签下可以看到新创建的名为tcp-echo的虚拟服务。
    3. 编辑实例页面的文本框中输入以下Yaml文件内容,单击确定
      apiVersion: networking.istio.io/v1alpha3
      kind: VirtualService
      metadata:
        name: tcp-echo
      spec:
        hosts:
        - "*"
        gateways:
        - tcp-echo-gateway
        tcp:
        - match:
          - port: 31400
          route:
          - destination:
              host: tcp-echo
              port:
                number: 9000
              subset: v1
            weight: 80
          - destination:
              host: tcp-echo
              port:
                number: 9000
              subset: v2
            weight: 20
  2. 执行以下命令,向tcp-echo服务发起10次请求。
    $ for i in {1..10}; do \
    docker run -e INGRESS_HOST=$INGRESS_HOST -e INGRESS_PORT=$INGRESS_PORT -it --rm busybox sh -c "(date; sleep 1) | nc $INGRESS_HOST $INGRESS_PORT"; \
    done
    one Mon Nov 12 23:38:45 UTC 2018
    two Mon Nov 12 23:38:47 UTC 2018
    one Mon Nov 12 23:38:50 UTC 2018
    one Mon Nov 12 23:38:52 UTC 2018
    one Mon Nov 12 23:38:55 UTC 2018
    two Mon Nov 12 23:38:57 UTC 2018
    one Mon Nov 12 23:39:00 UTC 2018
    one Mon Nov 12 23:39:02 UTC 2018
    one Mon Nov 12 23:39:05 UTC 2018
    one Mon Nov 12 23:39:07 UTC 2018
    根据以上请求的分发情况,可以看到20% 的流量指向了tcp-echo-v2。
    说明 您的测试结果可能不一定总是10次中有2次打到tcp-echo-v2,但从较长时间范围的总体比例来看,一定是接近20% 的。