增量式应用 Istio 第一部分,流量管理

流量管理是 Istio 提供的重要优势之一。Istio 流量管理的核心是在将通信流量和基础设施的伸缩进行解耦。如果没有 Istio 这样的服务网格,这种流量控制方式是不可能实现的。

例如,您希望执行一次金丝雀发布。当使用 Istio 时,您可以指定 service 的 v1 版本接收 90% 的传入流量,而该 service v2 版本仅接收 10%。如果使用标准的 Kubernetes deployment,实现此目的的唯一方法是手动控制每个版本的可用 Pod 数量,例如使 9 个 Pod 运行 v1 版本,使 1 个 Pod 运行 v2 版本。这种类型的手动控制难以实现,并且随着时间的推移可能无法扩展。有关更多信息,请查看使用 Istio 进行金丝雀发布

部署现有 service 的更新时存在同样的问题。虽然您可以使用 Kubernetes 更新 deployment,但它需要将 v1 Pod 替换为 v2 Pod。使用 Istio,您可以部署 service 的 v2 版本,并使用内置流量管理机制在网络层面将流量转移到更新后的 service,然后删除 v1 版本的 Pod。

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

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

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

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

如果你是众多拥有大量团队和大型集群的组织中的一员,那么答案是很清楚的。假设 A 团队正在开始使用 Istio,并希望在 service A 上开始一些金丝雀发布,但是 B 团队还没有开始使用 Istio,所以他们没有部署 sidecar。

使用 Istio,A 团队仍然可以让 service B 通过 Istio 的 ingress gateway 调用 service A 来实现他们的金丝雀发布。

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

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

Pod 包含一个 sidecar 代理,该代理作为 Istio 网格的一部分,负责协调 Pod 的所有入站和出站流量。在 Istio 网格中,Pilot 负责将高级路由规则转换为配置并将它们传播到 sidecar 代理。这意味着当服务彼此通信时,它们的路由决策是由客户端确定的。

假设您有 service A 和 service B 两个服务,他们是 Istio 网格的一部分。当 A 想要与 B 通信时,Pod A 的 sidecar 代理负责将流量引导到 service B。例如,如果你希望到 service B v1 版本和 v2 版本之间的流量按 5050 分割,流量将按如下方式流动:

50/50 流量分割
50/50 流量分割

如果 service A 和 B 不是 Istio 网格的一部分,则没有 sidecar 代理知道如何将流量路由到 service B 的不同版本。在这种情况下,您需要使用另一种方法来使 service A 到 service B 的流量遵循您设置的 5050 规则。

幸运的是,标准的 Istio 部署已经包含了一个 Gateway,它专门处理 Istio 网格之外的入口流量。此 Gateway 用于允许通过外部负载均衡器进入的集群外部入口流量;或来自 Kubernetes 集群,但在服务网格之外的入口流量。网关可以进行配置,对没有 Sidecar 支持的入口流量进行代理,引导流量进入相应的 Pod。这种方法允许您利用 Istio 的流量管理功能,其代价是通过入口网关的流量将产生额外的跃点。

使用 Ingress Gateway 的 50/50 流量分割
使用 Ingress Gateway 的 50/50 流量分割

实践:Istio 流量路由

一种实践的简单方法是首先按照平台设置说明设置 Kubernetes 环境,然后使用 Helm 安装仅包含流量管理组件(ingress gateway、egress gateway、Pilot)的 Istio。下面的示例使用 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 namespace 并部署 Istio:

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

然后,在没有 Istio sidecar 容器的前提下部署 Bookinfo 示例:

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

现在,配置一个新的 Gateway 允许从 Istio 网格外部访问 reviews service;一个新的 VirtualService 用于平均分配到 reviews service v1 和 v2 版本的流量;以及一系列新的、将目标子集与服务版本相匹配的 DestinationRule 资源:

$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: reviews-gateway
spec:
  selector:
    istio: ingressgateway # 使用 istio 默认控制器
  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 容器):

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

测试您的部署

现在就可以通过 Sleep pod 使用 curl 命令来测试不同的行为了。

第一个示例是使用标准 Kubernetes service DNS 行为向 reviews service 发出请求(注意:下面的示例中使用了 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"
}

请注意我们是如何从 reviews service 的所有三个版本获得响应(null 来自 reviews v1 版本,它没有评级数据)并且流量没有在 v1 和 v2 版本间平均拆分。这是预期的行为,因为 curl 命令在 reviews service 所有三个版本之间进行 Kubernetes service 负载均衡。为了以 5050 的流量拆分形式访问 reviews,我们需要通过 ingress Gateway 访问 service:

$ 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

任务完成!这篇文章展示了如何部署仅包含流量管理组件(Pilot、ingress Gateway)的 Istio 的最小安装,然后使用这些组件将流量定向到特定版本的 reviews service。由于不是必须通过部署 Istio sidecar 代理来获得这些功能,因此几乎没有给现有工作负载或应用程序造成中断。

这篇文章展示了如何利用 Istio 及内置的 ingress Gateway(以及一些 VirtualServiceDestinationRule 资源),轻松实现集群外部入口流量和集群内部服务到服务的流量管理。这种技术是增量式应用 Istio 的一个很好的例子,在 Pod 由不同团队拥有,或部署到不同命名空间的现实案例中尤其有用。

这些信息有用吗?
Do you have any suggestions for improvement?

Thanks for your feedback!