服务网格 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. 使用模板创建页面,将下面的 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
      无状态(Deployment)页面可以看到新创建的两个版本的 TCP-Echo 应用。TCP-Eco list
  2. 创建一个服务,并将其对外暴露。
    1. 登录容器服务控制台
    2. 在左侧导航栏中选择路由与负载均衡 > 服务
    3. 服务(Service)页面,单击右上角的创建
    4. 创建服务对话框中,设置服务的相关信息,单击创建
      需要设置的参数如下所示:
      • 名称:设为 tcp-echo
      • 关联:选择 tcp-echo-v1。
        说明 将从关联的 Deployment 中提取 app 这个标签作为 Service 的 Selector,这决定了 Kubernetes service 将流量转发到哪个 Deployment。由于tcp-echo-v1 和 tcp-echo-v2 两个 Deployment 拥有相同的标签,即 app:tcp-echo,因此可以关联任一 Deployment。
      • 端口映射名称设为 tcp;服务端口容器端口设为 9000;协议设为 TCP。
      服务(Service)页面可以看到新创建的服务,type-echo。

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

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

  1. 登录 ASM 控制台
  2. 在左侧导航栏,选择服务网格 > 网格实例
  3. 网格实例页面,找到待配置的实例,单击实例的名称或在操作列中单击管理
  4. 创建 Istio 网关。
    1. 控制平面区域,选择Istio网关页签,然后单击新建
    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:
          - "*"
      Istio网关页签下可以看到新创建的名为 tcp-echo-gateway 的 Istio 网关。
  5. 创建虚拟服务。
    1. 控制平面区域,选择虚拟服务页签,然后单击新建
    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
      虚拟服务页签下可以看到新创建的名为 tcp-echo 的虚拟服务。
  6. 创建目标规则。
    1. 控制平面区域,选择目标规则页签,然后单击新建
    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
      目标规则页签下可以看到新创建的名为 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. 控制平面区域,选择虚拟服务页签
    2. 虚拟服务页面,单击 tcp-echo 所在操作列中的 yaml
      虚拟服务页签下可以看到新创建的名为 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% 的。