本文发布已超过一年。较早的文章可能包含过时的内容。请检查页面中的信息自发布以来是否已不再准确。

使用 Istio 服务网格进行请求路由和策略管理

编者按:今天的文章是关于 Istio 的三部分系列文章的第二篇。

上一篇文章中,我们查看了一个由四个独立的微服务组成的简单应用 (Bookinfo)。文章展示了如何在不改变任何应用代码的情况下,使用 Kubernetes 和启用 Istio 的集群来部署应用。文章还概述了如何查看 Istio 提供的运行中服务的 L7 指标。

本文将继续深入探讨 Istio,并使用 Bookinfo 进行示例。具体来说,我们将介绍 Istio 的另外两个功能:请求路由和策略管理。

运行 Bookinfo 应用

和之前一样,我们运行 Bookinfo 应用的 v1 版本。在集群中安装 Istio 后,我们使用以下命令启动bookinfo-v1.yaml 中定义的应用程序:

kubectl apply -f \<(istioctl kube-inject -f bookinfo-v1.yaml)

我们为应用创建了一个 Ingress 资源

cat \<\<EOF | kubectl create -f -

apiVersion: extensions/v1beta1

kind: Ingress

metadata:

name: bookinfo

annotations:

    kubernetes.io/ingress.class: "istio"

spec:

rules:

- http:

        paths:

        - path: /productpage

            backend:

                serviceName: productpage

                servicePort: 9080

        - path: /login

            backend:

                serviceName: productpage

                servicePort: 9080

        - path: /logout

            backend:

                serviceName: productpage

                servicePort: 9080

EOF

然后我们检索了 Istio Ingress 控制器的 NodePort 地址

export BOOKINFO\_URL=$(kubectl get po -n istio-system -l istio=ingress -o jsonpath={.items[0].status.hostIP}):$(kubectl get svc -n istio-system istio-ingress -o jsonpath={.spec.ports[0].nodePort})

最后,我们将浏览器指向 http://$BOOKINFO_URL/productpage,以查看运行中的 v1 应用

HTTP 请求路由

现有的容器编排平台,如 Kubernetes、Mesos 和其他微服务框架,允许操作员控制何时将流量发送到特定的 Pod/VM 集合 (例如,通过添加/移除特定标签)。与现有技术不同,Istio 将流量流与基础设施扩缩容解耦。这使得 Istio 能够提供各种流量管理功能,这些功能位于应用代码之外,包括用于 A/B 测试、金丝雀发布、渐进式发布、使用超时、重试、熔断器进行故障恢复,以及用于测试跨服务故障恢复策略兼容性的故障注入的动态 HTTP 请求路由

为了演示,我们将部署 reviews 服务的 v2 版本,并使用 Istio 使其仅对特定的测试用户可见。我们可以使用这个 YAML 文件创建一个 Kubernetes Deployment,名为 reviews-v2:

apiVersion: extensions/v1beta1

kind: Deployment

metadata:

name: reviews-v2

spec:

replicas: 1

template:

    metadata:

        labels:

            app: reviews

            version: v2

    spec:

        containers:

        - name: reviews

            image: istio/examples-bookinfo-reviews-v2:0.2.3

            imagePullPolicy: IfNotPresent

            ports:

            - containerPort: 9080

从 Kubernetes 的角度来看,v2 Deployment 添加了额外的 Pod,这些 Pod 会被 reviews service 的选择器纳入轮询负载均衡算法中。这也是 Istio 的默认行为。

在我们启动 reviews:v2 之前,我们将启动 Bookinfo 应用的最后一个服务,ratings。v2 版本使用 ratings 服务为每个评论提供对应的评分星级。

kubectl apply -f \<(istioctl kube-inject -f bookinfo-ratings.yaml)

如果我们现在启动 reviews:v2,我们会看到浏览器响应在 v1 (没有对应评分的评论) 和 v2 (带有黑色评分星级的评论) 之间交替出现。然而,这并不会发生,因为我们将使用 Istio 的流量管理功能来控制流量。

使用 Istio,新版本不需要根据运行中的 Pod 数量来决定是否可见。版本可见性由指定精确标准的规则来控制。为了演示,我们首先使用 Istio 指定我们将 100% 的 reviews 服务流量发送到 v1 Pod。

立即为服务网格中的每个服务设置一个默认规则是 Istio 的最佳实践。这样做可以避免新版本(可能不稳定)意外可见。然而,为了演示的目的,我们只对 reviews 服务执行此操作:

cat \<\<EOF | istioctl create -f -

apiVersion: config.istio.io/v1alpha2

kind: RouteRule

metadata:

  name: reviews-default

spec:

  destination:

      name: reviews

  route:

  - labels:

          version: v1

      weight: 100

EOF

这个命令指示服务网格将 reviews 服务的 100% 流量发送到带有标签“version: v1”的 Pod。有了这条规则,我们可以安全地部署 v2 版本而不会暴露它。

kubectl apply -f \<(istioctl kube-inject -f bookinfo-reviews-v2.yaml)

刷新 Bookinfo 网页,确认没有发生任何变化。

此时,我们有很多选项来决定如何暴露 reviews:v2。例如,如果想进行简单的金丝雀测试,可以使用类似这样的规则将 10% 的流量发送到 v2:

apiVersion: config.istio.io/v1alpha2

kind: RouteRule

metadata:

  name: reviews-default

spec:

  destination:

      name: reviews

  route:

  - labels:

          version: v2

      weight: 10

  - labels:

          version: v1

      weight: 90

对于服务版本的早期测试,更好的方法是更具体地限制对其的访问。为了演示,我们将设置一条规则,使 reviews:v2 仅对特定的测试用户可见。我们通过设置第二条优先级更高的规则来做到这一点,这条规则只在请求匹配特定条件时应用:

cat \<\<EOF | istioctl create -f -

apiVersion: config.istio.io/v1alpha2

kind: RouteRule

metadata:

name: reviews-test-v2

spec:

destination:

    name: reviews

precedence: 2

match:

    request:

        headers:

            cookie:

                regex: "^(.\*?;)?(user=jason)(;.\*)?$"

route:

- labels:

        version: v2

    weight: 100

EOF

这里我们指定条件是请求头中需要包含值为“tester”的用户 cookie。如果这条规则不匹配,我们将回退到 v1 的默认路由规则。

如果我们使用用户名“tester”(无需密码)登录 Bookinfo UI,我们现在将看到应用的 v2 版本(每个评论包含 1-5 个黑色评分星)。其他所有用户都不受此更改的影响。

v2 版本经过全面测试后,我们可以使用 Istio 继续进行金丝雀测试(使用之前展示的规则),或者我们可以简单地将所有流量从 v1 迁移到 v2,也可以通过一系列权重小于 100 的规则(例如:10、20、30、... 100)以渐进方式迁移。这种流量控制与实现每个版本的 Pod 数量无关。例如,如果我们启用了自动扩缩容并且流量很大,我们很可能会看到 v2 Pod 相应地扩容,同时 v1 Pod 独立地缩容。有关使用自动扩缩容进行版本路由的更多信息,请参阅“使用 Istio 进行金丝雀部署”

在我们的示例中,我们将使用一个命令将所有流量发送到 v2:

cat \<\<EOF | istioctl replace -f -

apiVersion: config.istio.io/v1alpha2

kind: RouteRule

metadata:

  name: reviews-default

spec:

  destination:

      name: reviews

  route:

  - labels:

          version: v2

      weight: 100

EOF

我们还应该移除为测试用户创建的特殊规则,这样它就不会覆盖我们将来决定进行的任何发布。

istioctl delete routerule reviews-test-v2

在 Bookinfo UI 中,我们将看到现在 reviews 服务的 v2 版本已经对所有用户开放。

策略执行

Istio 提供了策略执行功能,例如配额、前置条件检查和访问控制。我们可以通过一个示例来演示 Istio 开放且可扩展的策略框架:限速。

让我们假设 Bookinfo 的 ratings 服务是一个外部付费服务——例如,Rotten Tomatoes®——具有每秒 1 个请求 (req/sec) 的免费配额。为了确保应用程序不超过此限制,我们将指定一个 Istio 策略,在达到限制后切断请求。为此,我们将使用 Istio 的内置策略之一。

要设置 1 req/sec 的配额,我们首先配置一个带有速率限制的 memquota 处理程序:

cat \<\<EOF | istioctl create -f -

apiVersion: "config.istio.io/v1alpha2"

kind: memquota

metadata:

name: handler

namespace: default

spec:

quotas:

- name: requestcount.quota.default

    maxAmount: 5000

    validDuration: 1s

    overrides:

    - dimensions:

            destination: ratings

        maxAmount: 1

        validDuration: 1s

EOF

然后我们创建一个 quota 实例,将传入的属性映射到配额维度,并创建一个使用该实例和 memquota 处理程序的 rule

cat \<\<EOF | istioctl create -f -

apiVersion: "config.istio.io/v1alpha2"

kind: quota

metadata:

name: requestcount

namespace: default

spec:

dimensions:

    source: source.labels["app"] | source.service | "unknown"

    sourceVersion: source.labels["version"] | "unknown"

    destination: destination.labels["app"] | destination.service | "unknown"

    destinationVersion: destination.labels["version"] | "unknown"

---

apiVersion: "config.istio.io/v1alpha2"

kind: rule

metadata:

name: quota

namespace: default

spec:

actions:

- handler: handler.memquota

    instances:

    - requestcount.quota

EOF

为了看到限速效果,我们将对应用产生一些负载:

wrk -t1 -c1 -d20s http://$BOOKINFO\_URL/productpage

在网页浏览器中,我们会注意到当负载生成器运行时(即,每秒生成超过 1 个请求),浏览器流量被切断了。页面上不再是每个评论旁边的黑色星级,而是显示一条消息,指示评分当前不可用。

停止负载生成器意味着不再超出限制:当我们刷新页面时,黑色星级会重新出现。

总结

我们向您展示了如何在无需重启任何服务的情况下,将 HTTP 请求路由和策略注入等高级功能引入配置了 Istio 的服务网格中。这使您可以专注于开发和部署,而无需担心服务网格的持续管理;服务范围的策略可以随时添加。

在本系列的下一篇也是最后一篇文章中,我们将重点介绍 Istio 的安全和认证能力。我们将讨论如何在服务网格中保护所有服务间的通信,即使面对能够访问网络的内部人员,也无需对应用代码或部署进行任何更改。