增量 Istio 第一部分:流量管理

如何在不部署 Sidecar 代理的情况下使用 Istio 进行流量管理。

2018 年 11 月 21 日 | 作者:Sandeep Parikh

流量管理是 Istio 提供的关键优势之一。Istio 流量管理的核心是将流量流和基础设施扩展分离的能力。这使您能够以没有像 Istio 这样的服务网格无法实现的方式控制您的流量。

例如,假设您想执行 金丝雀部署。使用 Istio,您可以指定服务的 v1 接收 90% 的传入流量,而该服务的 v2 仅接收 10%。使用标准的 Kubernetes 部署,实现此目的的唯一方法是手动控制每个版本的可用 Pod 数量,例如运行 v1 的 9 个 Pod 和运行 v2 的 1 个 Pod。这种类型的手动控制很难实现,并且随着时间的推移,可能会遇到扩展问题。有关更多信息,请查看 使用 Istio 的金丝雀部署

在将更新部署到现有服务时,也会出现相同的问题。虽然您可以使用 Kubernetes 更新部署,但这需要用 v2 Pod 替换 v1 Pod。使用 Istio,您可以部署 v2 的服务,并使用内置的流量管理机制在网络级别将流量转移到您的更新服务,然后删除 v1 Pod。

除了金丝雀部署和一般流量转移外,Istio 还使您能够实现动态请求路由(基于 HTTP 标头)、故障恢复、重试、断路器和故障注入。有关更多信息,请查看 流量管理文档

这篇文章介绍了一种技术,突出了您可以增量实施 Istio 的一种特别有用的方法——在这种情况下,只有流量管理功能——而无需单独更新每个 Pod。

设置:为什么要实施 Istio 流量管理功能?

当然,第一个问题是:为什么要这样做?

如果您是拥有大型集群且许多团队进行部署的众多组织之一,那么答案非常清楚。假设团队 A 开始使用 Istio 并想要在服务 A 上启动一些金丝雀部署,但团队 B 还没有开始使用 Istio,因此他们没有部署 Sidecar。

使用 Istio,团队 A 仍然可以通过让服务 B 通过 Istio 的入口网关调用服务 A 来实现他们的金丝雀部署。

背景:Istio 网格中的流量路由

但是,如何在不更新每个应用程序的 Pod 以包含 Istio Sidecar 的情况下使用 Istio 的流量管理功能?在回答这个问题之前,让我们快速概述一下流量如何进入 Istio 网格以及如何路由流量。

属于 Istio 网格的 Pod 包含一个 Sidecar 代理,它负责调解发送到 Pod 的所有入站和出站流量。在 Istio 网格中,Pilot 负责将高级路由规则转换为配置并将它们传播到 Sidecar 代理。这意味着当服务相互通信时,它们的路由决策是根据客户端确定的。

假设您有两个属于 Istio 网格的服务,服务 A 和服务 B。当 A 要与 B 通信时,Pod A 的 Sidecar 代理负责将流量定向到服务 B。例如,如果您想在服务 B 的 v1 和 v2 之间拆分 50/50 的流量,则流量将按如下方式流动

50/50 Traffic Split
50/50 流量拆分

如果服务 A 和服务 B 不属于 Istio 网格,则没有 Sidecar 代理知道如何将流量路由到服务 B 的不同版本。在这种情况下,您需要使用另一种方法从服务 A 获取流量到服务 B,遵循您设置的 50/50 规则。

幸运的是,标准的 Istio 部署已经包含一个专门处理 Istio 网格外部的入口流量的 网关。此网关用于允许来自集群外部的入口流量通过外部负载均衡器,或允许来自 Kubernetes 集群内部但服务网格外部的入口流量。它可以配置为将传入的入口流量代理到相应的 Pod,即使它们没有 Sidecar 代理。虽然这种方法允许您利用 Istio 的流量管理功能,但它确实意味着通过入口网关的流量将产生额外的一跳。

50/50 Traffic Split using Ingress Gateway
使用入口网关的 50/50 流量拆分

实际操作:使用 Istio 进行流量路由

了解这种方法的一种简单方法是首先使用 平台设置 指示设置您的 Kubernetes 环境,然后使用 Helm 安装 最小 Istio 配置文件,包括仅流量管理组件(入口网关、出口网关、Pilot)。以下示例使用 Google Kubernetes Engine

首先,设置和配置 GKE

$ gcloud container clusters create istio-inc --zone us-central1-f
$ gcloud container clusters get-credentials istio-inc
$ kubectl create clusterrolebinding cluster-admin-binding \
   --clusterrole=cluster-admin \
   --user=$(gcloud config get-value core/account)

接下来,安装 Helm生成一个最小的 Istio 安装——只有流量管理组件

$ helm template install/kubernetes/helm/istio \
  --name istio \
  --namespace istio-system \
  --set security.enabled=false \
  --set galley.enabled=false \
  --set sidecarInjectorWebhook.enabled=false \
  --set mixer.enabled=false \
  --set prometheus.enabled=false \
  --set pilot.sidecar=false > istio-minimal.yaml

然后创建 istio-system 命名空间并部署 Istio

$ kubectl create namespace istio-system
$ kubectl apply -f istio-minimal.yaml

接下来,部署 Bookinfo 示例,但不包含 Istio Sidecar 容器

压缩
$ kubectl apply -f @samples/bookinfo/platform/kube/bookinfo.yaml@

现在,配置一个新的网关,允许从 Istio 网格外部访问评论服务,一个新的 VirtualService,它将流量均匀地拆分到评论服务的 v1 和 v2 之间,以及一组新的 DestinationRule 资源,这些资源将目标子集匹配到服务版本

$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: reviews-gateway
spec:
  selector:
    istio: ingressgateway # use istio default controller
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - "*"
  gateways:
  - reviews-gateway
  http:
  - match:
    - uri:
        prefix: /reviews
    route:
    - destination:
        host: reviews
        subset: v1
      weight: 50
    - destination:
        host: reviews
        subset: v2
      weight: 50
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: reviews
spec:
  host: reviews
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
  - name: v3
    labels:
      version: v3
EOF

最后,部署一个可以使用 curl 测试的 Pod(并且没有 Istio Sidecar 容器)

压缩
$ kubectl apply -f @samples/sleep/sleep.yaml@

测试您的部署

现在,您可以使用 curl 命令通过睡眠 Pod 测试不同的行为。

第一个示例是使用标准 Kubernetes 服务 DNS 行为向评论服务发出请求(注意:下面的示例使用 jq 来过滤 curl 的输出)

$ export SLEEP_POD=$(kubectl get pod -l app=sleep \
  -o jsonpath={.items..metadata.name})
$ for i in `seq 3`; do \
  kubectl exec -it $SLEEP_POD curl http://reviews:9080/reviews/0 | \
  jq '.reviews|.[]|.rating?'; \
  done
{
  "stars": 5,
  "color": "black"
}
{
  "stars": 4,
  "color": "black"
}
null
null
{
  "stars": 5,
  "color": "red"
}
{
  "stars": 4,
  "color": "red"
}

请注意,我们如何从评论服务的三个版本获得响应(null 来自没有评分的评论 v1),并且没有在 v1 和 v2 之间获得均匀的拆分。这是预期的行为,因为 curl 命令正在使用 Kubernetes 服务负载均衡器跨评论服务的三个版本进行负载均衡。为了访问评论的 50/50 拆分,我们需要通过入口网关访问服务

$ for i in `seq 4`; do \
  kubectl exec -it $SLEEP_POD curl http://istio-ingressgateway.istio-system/reviews/0 | \
  jq '.reviews|.[]|.rating?'; \
  done
{
  "stars": 5,
  "color": "black"
}
{
  "stars": 4,
  "color": "black"
}
null
null
{
  "stars": 5,
  "color": "black"
}
{
  "stars": 4,
  "color": "black"
}
null
null

任务完成!这篇文章展示了如何部署 Istio 的最小安装,它只包含流量管理组件(Pilot、入口网关),然后使用这些组件将流量定向到评论服务的特定版本。并且没有必要部署 Istio Sidecar 代理来获得这些功能,因此对现有工作负载或应用程序几乎没有中断。

这篇文章使用内置的入口网关(以及一些 VirtualServiceDestinationRule 资源)展示了如何轻松地利用 Istio 的流量管理来处理集群外部的入口流量和集群内部的服务到服务流量。这种技术是采用 Istio 的增量方法的一个很好的示例,并且在 Pod 由不同的团队拥有或部署到不同命名空间的实际情况下特别有用。

分享此博文