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

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

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

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

本文通过使用 Bookinfo 更深入地研究 Istio。具体来说,我们将研究 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 部署 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 部署添加了其他 Pod,reviews 服务选择器将其包含在循环负载平衡算法中。这也是 Istio 的默认行为。

在启动 reviews:v2 之前,我们将启动四个 Bookinfo 服务中的最后一个服务,即 ratings,v2 版本使用该服务来提供与每个评论对应的评级星级

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 相应地扩大,而 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 评级服务是一项外部付费服务(例如,烂番茄®),其免费配额为每秒 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

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

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

总结

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

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