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

使用 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% 的评论流量仅发送到 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 评级服务是一个外部付费服务——例如,烂番茄®——提供每秒 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 个请求),浏览器流量会被切断。页面现在显示一条消息,指示评级当前不可用,而不是每个评论旁边的黑色星级。

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

总结

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

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