为外部服务配置故障转移

了解如何为网格外部的端点配置本地负载均衡和故障转移。

Jun 4, 2021 | By Ram Vennam - Solo.io

Istio 强大的 API 可用于解决各种使用服务网格时遇到的问题。 许多用户对其强大的入口和东西向流量能力都比较了解,但是除此之外它也为出口(向外)流量提供了诸多功能。 当您的应用程序需要与外部服务(例如云服务商提供的数据库端点)通信时, 该功能就可以派上用场。根据工作负载运行的位置,通常可以有多个端点供用户选择。 例如,Amazon 的 DynamoDB 就可以提供跨区域的多个端点。 由于延迟原因,常常希望选择使用离您工作负载最近的端点, 但是为了防止未按照预期正常工作的情况发生,您可能需要配置自动故障转移使流量可以指向另一个端点。

与在服务网格内运行的服务类似,您可以对 Istio 进行离群检测配置来实现将流量指向健康端点的故障转移操作, 并且该配置仍然对您的应用程序完全透明。在此示例中,我们将使用 Amazon DynamoDB 端点选择与 Google Kubernetes Engine(GKE) 集群中运行的工作负载处于相同或接近的主要区域。 另外我们还会为其配置一个故障转移区域。

路由端点
主路由http://dynamodb.us-east-1.amazonaws.com
故障路由http://dynamodb.us-west-1.amazonaws.com

故障转移

使用 ServiceEntry 定义外部端点

本地负载均衡基于 regionzone 工作,这通常是从 Kubernetes 节点上设置的标签推断出来的。 首先,确定您工作负载所处的位置:

$ kubectl describe node | grep failure-domain.beta.kubernetes.io/region
                    failure-domain.beta.kubernetes.io/region=us-east1
                    failure-domain.beta.kubernetes.io/region=us-east1

在此示例中,GKE 集群节点在 us-east1 区域中运行。

接下来,创建一个 ServiceEntry 来聚合您要使用的端点。在此示例中, 我们选择了 mydb.com 作为 host。这也是您应用程序需要配置的连接地址。 然后将主要端点的 locality 设置为与您工作负载相同的区域:

apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
  name: external-svc-dns
spec:
  hosts:
  - mydb.com
  location: MESH_EXTERNAL
  ports:
  - number: 80
    name: http
    protocol: HTTP
  resolution: DNS
  endpoints:
  - address: dynamodb.us-east-1.amazonaws.com
    locality: us-east1
    ports:
      http: 80
  - address: dynamodb.us-west-1.amazonaws.com
    locality: us-west
    ports:
      http: 80

让我们部署一个 sleep 容器作为发送请求的测试源。

Zip
$ kubectl apply -f @samples/sleep/sleep.yaml@

从这个 sleep 容器中尝试发起对 http://mydb.com 的 5 次访问:

$ for i in {1..5}; do kubectl exec deploy/sleep -c sleep -- curl -sS http://mydb.com; echo; sleep 2; done
healthy: dynamodb.us-east-1.amazonaws.com
healthy: dynamodb.us-west-1.amazonaws.com
healthy: dynamodb.us-west-1.amazonaws.com
healthy: dynamodb.us-east-1.amazonaws.com
healthy: dynamodb.us-east-1.amazonaws.com

您将看到 Istio 会分别向两个端点都发送请求。 但是我们只希望请求被发送到与我们节点标记为相同区域的端点中。

为此,我们需要配置一个 DestinationRule

使用 DestinationRule 设置故障转移条件

Istio 的 DestinationRule 允许您配置负载均衡、连接池和离群检测设置。 我们可以指定用于将端点标识为不健康并将其从负载均衡池中删除的条件。

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: mydynamodb
spec:
  host: mydb.com
  trafficPolicy:
    outlierDetection:
      consecutive5xxErrors: 1
      interval: 15s
      baseEjectionTime: 1m

上面的 DestinationRule 将端点配置为每 15 秒扫描一次,如果任何端点出现 5xx 错误代码形式的失败请求,即使只出现一次,在接下来的一分钟内它也会被标记为不健康状态。 在此熔断机制未被触发时,流量将被路由到与 Pod 相同的区域。

如果我们再次运行 curl 命令,应该看到流量总是流向 us-east1 端点。

$ for i in {1..5}; do kubectl exec deploy/sleep -c sleep -- curl -sS http://mydb.com; echo; sleep 2; done

healthy: dynamodb.us-east-1.amazonaws.com
healthy: dynamodb.us-east-1.amazonaws.com
healthy: dynamodb.us-east-1.amazonaws.com
healthy: dynamodb.us-east-1.amazonaws.com
healthy: dynamodb.us-east-1.amazonaws.com

模拟失败

接下来,让我们看看如果 us-east 端点出现故障会发生什么情况。 为了模拟这种情况,让我们对 ServiceEntry 进行修改并为 us-east 端点设置一个无效端口:

apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
  name: external-svc-dns
spec:
  hosts:
  - mydb.com
  location: MESH_EXTERNAL
  ports:
  - number: 80
    name: http
    protocol: HTTP
  resolution: DNS
  endpoints:
  - address: dynamodb.us-east-1.amazonaws.com
    locality: us-east1
    ports:
      http: 81 # INVALID - This is purposefully wrong to trigger failover
  - address: dynamodb.us-west-1.amazonaws.com
    locality: us-west
    ports:
      http: 80

再次运行 curl 命令,结果展示流量在无法连接到 us-east 端点后自动故障转移到 us-west 区域:

$ for i in {1..5}; do kubectl exec deploy/sleep -c sleep -- curl -sS http://mydb.com; echo; sleep 2; done
upstream connect error or disconnect/reset before headers. reset reason: connection failure
healthy: dynamodb.us-west-1.amazonaws.com
healthy: dynamodb.us-west-1.amazonaws.com
healthy: dynamodb.us-west-1.amazonaws.com
healthy: dynamodb.us-west-1.amazonaws.com

您可以通过运行以下命令检查 us-east 端点的离群检测状态:

$ istioctl pc endpoints <sleep-pod> | grep mydb
ENDPOINT                         STATUS      OUTLIER CHECK     CLUSTER
52.119.226.80:81                 HEALTHY     FAILED            outbound|80||mydb.com
52.94.12.144:80                  HEALTHY     OK                outbound|80||mydb.com

HTTPS 的故障转移

为外部 HTTPS 服务配置故障转移同样简单。您的应用程序仍然可以继续使用纯 HTTP 协议,并且您可以让 Istio 代理将 TLS 源指向 HTTPS。

apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
  name: external-svc-dns
spec:
  hosts:
  - mydb.com
  ports:
  - number: 80
    name: http-port
    protocol: HTTP
    targetPort: 443
  resolution: DNS
  endpoints:
  - address: dynamodb.us-east-1.amazonaws.com
    locality: us-east1
  - address: dynamodb.us-west-1.amazonaws.com
    locality: us-west

上面的 ServiceEntry 在 80 端口上定义了 mydb.com 服务, 并将流量重定向到 443 端口上的真实 DynamoDB 端点。

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: mydynamodb
spec:
  host: mydb.com
  trafficPolicy:
    tls:
      mode: SIMPLE
    loadBalancer:
      simple: ROUND_ROBIN
      localityLbSetting:
        enabled: true
        failover:
          - from: us-east1
            to: us-west
    outlierDetection:
      consecutive5xxErrors: 1
      interval: 15s
      baseEjectionTime: 1m

DestinationRule 现在执行 TLS 源并配置离群检测。 该规则还配置了一个故障转移字段, 您可以在其中准确指定哪些区域是故障转移目标。当您定义了多个区域时,这非常有用。

总结

Istio 的 VirtualServiceDestinationRule API 提供流量路由、 故障恢复和故障注入功能,因此您可以创建具有弹性的应用程序。 ServiceEntry API 也将其中许多功能扩展到服务网格之外的服务中。