使用同可用区优先路由

当您遇到跨可用区网络延迟导致的服务响应时间增加或成本上升等问题时,可以使用同可用区优先路由(简称同AZ路由)功能,优先保证服务请求在同一个可用区内完成处理,减少网络传输延迟,降低因跨区域流量产生的额外费用,提高整体服务的运行效率和稳定性。ASM支持在不需要修改应用代码的情况下实现同AZ路由。本文以入口网关访问httpbin应用为例,介绍如何使用同AZ路由。

前提条件

  • 已添加集群到ASM实例。具体操作,请参见添加集群到ASM实例

  • ACK集群中的节点存在至少两个可用区,本示例中分别使用cn-hongkong-b和cn-hongkong-c,您可以在容器服务管理控制台查看集群节点所对应的ECS所在地域及可用区。更多信息,请参见地域和可用区

    说明

    本示例中sleep应用部署在可用区cn-hongkong-b,helloworld-v1应用部署到可用区cn-hongkong-b中,helloworld-v2应用部署到可用区cn-hongkong-c中,您可以根据实际集群所使用的可用区进行替换。

背景信息

同AZ路由是指在客户端对一个目标服务的访问过程中,可以根据这个客户端所在的地域、可用区拓扑信息,优先路由到与该客户端在同一个节点或者可用区的目标服务上的路由行为。同AZ路由本身属于负载均衡策略的范畴,它能够使流量尽可能在同一个可用区内流转,以保证服务间的调用延迟最低。

步骤一:部署示例应用

  1. 使用以下内容,创建sleep.yaml

    说明

    如下示例中,sleep应用部署到可用区cn-hongkong-b,您可以根据实际集群所使用的可用区进行替换。

    展开查看sleep.yaml

    # Copyright Istio Authors
    #
    #   Licensed under the Apache License, Version 2.0 (the "License");
    #   you may not use this file except in compliance with the License.
    #   You may obtain a copy of the License at
    #
    #       http://www.apache.org/licenses/LICENSE-2.0
    #
    #   Unless required by applicable law or agreed to in writing, software
    #   distributed under the License is distributed on an "AS IS" BASIS,
    #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    #   See the License for the specific language governing permissions and
    #   limitations under the License.
    
    ##################################################################################################
    # Sleep service
    ##################################################################################################
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: sleep
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: sleep
      labels:
        app: sleep
        service: sleep
    spec:
      ports:
      - port: 80
        name: http
      selector:
        app: sleep
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: sleep
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: sleep
      template:
        metadata:
          labels:
            app: sleep
        spec:
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                - matchExpressions:
                  - key: failure-domain.beta.kubernetes.io/zone
                    operator: In
                    values:
                      - 'cn-hongkong-b'
          terminationGracePeriodSeconds: 0
          serviceAccountName: sleep
          containers:
          - name: sleep
            image: curlimages/curl
            command: ["/bin/sleep", "3650d"]
            imagePullPolicy: IfNotPresent
            volumeMounts:
            - mountPath: /etc/sleep/tls
              name: secret-volume
          volumes:
          - name: secret-volume
            secret:
              secretName: sleep-secret
              optional: true
  2. 执行以下命令,在集群中部署sleep应用。

    kubectl apply -f sleep.yaml
  3. 使用以下内容,创建helloworld.yaml

    说明

    如下示例中,helloworld-v1应用部署到可用区cn-hongkong-b中,helloworld-v2应用部署到可用区cn-hongkong-c中,您可以根据实际集群所使用的可用区进行替换。

    展开查看helloworld.yaml

    apiVersion: v1
    kind: Service
    metadata:
      name: helloworld
      labels:
        app: helloworld
        service: helloworld
    spec:
      ports:
      - port: 5000
        name: http
      selector:
        app: helloworld
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: helloworld-v1
      labels:
        app: helloworld
        version: v1
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: helloworld
          version: v1
      template:
        metadata:
          labels:
            app: helloworld
            version: v1
        spec:
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                - matchExpressions:
                  - key: failure-domain.beta.kubernetes.io/zone
                    operator: In
                    values:
                      - 'cn-hongkong-b'
          containers:
          - name: helloworld
            image: docker.io/istio/examples-helloworld-v1
            resources:
              requests:
                cpu: "100m"
            imagePullPolicy: IfNotPresent #Always
            ports:
            - containerPort: 5000
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: helloworld-v2
      labels:
        app: helloworld
        version: v2
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: helloworld
          version: v2
      template:
        metadata:
          labels:
            app: helloworld
            version: v2
        spec:
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                - matchExpressions:
                  - key: failure-domain.beta.kubernetes.io/zone
                    operator: In
                    values:
                      - 'cn-hongkong-c'
          containers:
          - name: helloworld
            image: docker.io/istio/examples-helloworld-v2
            resources:
              requests:
                cpu: "100m"
            imagePullPolicy: IfNotPresent #Always
            ports:
            - containerPort: 5000
                            
  4. 执行以下命令,在集群中部署helloworld应用。

    kubectl apply -f helloworld.yaml
  5. 执行以下命令,查询目标服务的注册信息。

    kubectl exec "$(kubectl get pod -l app=sleep -o jsonpath='{.items[0].metadata.name}')"  -c sleep -- curl localhost:15000/clusters | grep helloworld

    预期输出:

    outbound|5000||helloworld.default.svc.cluster.local::172.28.32.49:5000::region::cn-hongkong
    outbound|5000||helloworld.default.svc.cluster.local::172.28.32.49:5000::zone::cn-hongkong-b
    outbound|5000||helloworld.default.svc.cluster.local::172.28.32.49:5000::sub_zone::
    outbound|5000||helloworld.default.svc.cluster.local::172.28.32.49:5000::canary::false
    outbound|5000||helloworld.default.svc.cluster.local::172.28.32.49:5000::priority::0
    .......
    outbound|5000||helloworld.default.svc.cluster.local::172.28.33.155:5000::region::cn-hongkong
    outbound|5000||helloworld.default.svc.cluster.local::172.28.33.155:5000::zone::cn-hongkong-c
    outbound|5000||helloworld.default.svc.cluster.local::172.28.33.155:5000::sub_zone::
    outbound|5000||helloworld.default.svc.cluster.local::172.28.33.155:5000::canary::false
    outbound|5000||helloworld.default.svc.cluster.local::172.28.33.155:5000::priority::0

    由预期输出得到,两个helloworld应用的优先级相同,均为priority::0。说明当sleep客户端调用helloworld服务时,两个helloworld应用的路由策略相同。

步骤二:设置同AZ优先路由

通过应用目标规则来解决优先级问题,为服务helloworld.default.svc.cluster.local启用同AZ路由的能力。

说明

您可以调整连续5xx错误(consecutive5xxErrors)、间隔时间(interval)以及最小的移除时间长度(baseEjectionTime)参数,实现同可用区路由的优先调用。本示例将在第一个请求失败时触发故障转移。

  1. 使用以下内容,创建helloworld-failover.yaml

    apiVersion: networking.istio.io/v1beta1
    kind: DestinationRule
    metadata:
      name: helloworld-failover
      namespace: default
    spec:
      host: helloworld.default.svc.cluster.local
      trafficPolicy:
        connectionPool:
          http:
            maxRequestsPerConnection: 1
        loadBalancer:
          localityLbSetting:
            enabled: true
          simple: ROUND_ROBIN
        outlierDetection:
          baseEjectionTime: 1m
          consecutive5xxErrors: 1
          interval: 1s
                            
  2. 执行以下命令,查看工作负载的优先级。

    kubectl exec "$(kubectl get pod -l app=sleep -o jsonpath='{.items[0].metadata.name}')"  -c sleep -- curl localhost:15000/clusters | grep helloworld
                            

    预期输出:

    outbound|5000||helloworld.default.svc.cluster.local::172.28.32.49:5000::region::cn-hongkong
    outbound|5000||helloworld.default.svc.cluster.local::172.28.32.49:5000::zone::cn-hongkong-b
    outbound|5000||helloworld.default.svc.cluster.local::172.28.32.49:5000::sub_zone::
    outbound|5000||helloworld.default.svc.cluster.local::172.28.32.49:5000::canary::false
    outbound|5000||helloworld.default.svc.cluster.local::172.28.32.49:5000::priority::0
    .......
    outbound|5000||helloworld.default.svc.cluster.local::172.28.33.155:5000::region::cn-hongkong
    outbound|5000||helloworld.default.svc.cluster.local::172.28.33.155:5000::zone::cn-hongkong-c
    outbound|5000||helloworld.default.svc.cluster.local::172.28.33.155:5000::sub_zone::
    outbound|5000||helloworld.default.svc.cluster.local::172.28.33.155:5000::canary::false
    outbound|5000||helloworld.default.svc.cluster.local::172.28.33.155:5000::priority::1

    由预期输出得到,两个helloworld应用的优先级不同,分别为priority::0priority::1。说明当sleep客户端调用helloworld服务时,同AZ路由的策略生效。

步骤三:验证同AZ优先路由

从位于可用区cn-hongkong-b中的客户端sleep应用发送请求,调用helloworld服务。启用同AZ优先路由之后,所有流量都应指向同在可用区cn-hongkong-b中的helloworld-v1应用。其中,sleep应用部署在可用区cn-hongkong-b,helloworld-v1应用部署到可用区cn-hongkong-b中,helloworld-v2应用部署到可用区cn-hongkong-c中。

  1. 重复执行以下命令,访问helloworld服务。

    kubectl exec  -c sleep "$(kubectl get pod -l app=sleep -o jsonpath='{.items[0].metadata.name}')"  -- curl -sSL helloworld:5000/hello

    预期输出:

    Hello version: v1, instance: helloworld-v1-6f88967849-sq2h2

    由预期输出得到,返回结果始终为helloworld-v1服务。

  2. 缩容helloworld-v1服务。

    1. 执行以下命令,缩容helloworld-v1服务到0个Pod,模拟该服务不可用状态。

      kubectl scale deploy helloworld-v1 --replicas=0
    2. 等待几秒后,重复执行以下命令,访问helloworld服务。

      kubectl exec  -c sleep "$(kubectl get pod -l app=sleep -o jsonpath='{.items[0].metadata.name}')"  -- curl -sSL helloworld:5000/hello

      预期输出:

      Hello version: v2, instance: helloworld-v2-75db5f978d-s7v4k

      由预期输出得到,当本可用区的helloworld-v1服务不可用时,会自动切换到另一可用区cn-hongkong-c中的helloworld-v2服务。

  3. 扩容helloworld-v1服务。

    1. 执行以下命令,扩容helloworld-v1服务到1个Pod,恢复可用区cn-hongkong-b中的helloworld-v1应用服务。

      kubectl scale deploy helloworld-v1 --replicas=1
    2. 等待几秒后,重复执行以下命令,访问helloworld服务。

      kubectl exec  -c sleep "$(kubectl get pod -l app=sleep -o jsonpath='{.items[0].metadata.name}')"  -- curl -sSL helloworld:5000/hello

      预期输出:

      Hello version: v1, instance: helloworld-v1-6f88967849-sq2h2

      由预期输出得到,返回结果始终为helloworld-v1服务。