摘要: 在本教程中,我们将使用Istio演示服务网格最强大的特性之一:“以请求为基准的路由”。这个特性允许把具有指定HTTP头标记的任意请求路由到特定的目标,这只有在(OSI)第7层代理中才可能实现。没有哪个第4层的负载均衡器或代理可以实现此功能。

本文要点

  • 本教程将演示如何在Kubernetes集群中安装和使用Istio服务网格,并讨论如何充分利用Istio的路由功能。
  • 探讨第4层和第7层网络代理之间的区别,并了解如何充分利用L7代理的好处。
  • 用户可以借助Istio的可扩展性和服务网格的一般功能实现路由场景,节省大量的时间和资源。

在本教程中,我们将使用Istio演示服务网格最强大的特性之一:“以请求为基准的路由”。这个特性允许把具有指定HTTP头标记的任意请求路由到特定的目标,这只有在(OSI)第7层代理中才可能实现。没有哪个第4层的负载均衡器或代理可以实现此功能。

如果你想继续阅读本文,我们假设你有一个正在运行的Kubernetes集群。对于本教程来说,一个包含1个主节点和2个工作节点的小集群就足够了。

Istio是什么?

Istio是一个服务网格。它由控制平面和数据平面组成。对于数据平面,它使用Envoy代理。Envoy本身是一个L7代理和通信总线,专为基于微服务的现代化体系结构而设计。关于Envoy代理的更多信息,有大量的文档可供查阅。关于Istio是什么,官方文档 也提供了一个很好的解释。

istio gateway tcp_ide


图1:使用Istio Pilot把路由配置注入作为服务“挎斗(sidecar )”运行的Envoy代理

以请求为基准的路由

Istio提供高级的流量管理功能。以请求为基准的路由特性允许我们定义针对传入请求的复杂规则,并决定如何处理请求。以下是可能的用例:

  1. 金丝雀测试——将一小部分用户流量重定向到新的服务版本;
  2. 向不同的用户提供不同的版本——不同价格计划或来自不同地区的用户可能由不同的环境提供服务(例如,这可能是实现GDPR要求的一部分);
  3. A/B测试;
  4. 逐步滚动。

本教程将介绍如何逐步滚动。

步骤0:安装一个Kubernetes集群

为了创建一个集群,你可以使用任何Kubernetes解决方案。在本教程中,我们将使用免费的Kublr演示程序部署一个集群。

步骤1:安装Istio控制平面

一种选择是遵照官方的Istio快速入门教程 ,在Kubernetes集群中安装控制平面。安装步骤取决于你本地的机器类型(Mac、Linux、Windows),因此我们不会在这里重复安装本地istioctl应用程序和kubectl的标准步骤,这两个CLI工具将用于管理Kubernetes和Istio。

下面是为已经熟悉Kubernetes的读者做的一个不那么详细的说明(如果这些信息不够,那么我们建议你遵照官方指南按步骤完成):

  1. 安装Kubernetes集群(使用上面列出的其中一种方法,或者使用已有的测试/开发集群) ;
  2. 在本地安装kubectl https://kubernetes.io/docs/tasks/tools/install-kubectl/ (我们将使用它管理Kubernetes集群);
  3. 从GitHub发布页面安装istioctl (用于把Envoy代理注入到pod以及设置路由规则和策略),安装很简单:
    a. 对于Mac或Linux,运行curl -L https://git.io/getLatestIstio | sh -;
    b. 对于Windows,只要从zip压缩包中提取文件,并把二进制文件复制到PATH(可以简单地复制到c:\\windows\\system32\\)或者从/bin/目录下运行所有的istioctl.exe命令;
    c. 切换到提取出的文件所在的文件夹,使用kubectl apply -f install/kubernetes/istio-demo.yaml进行安装。

你需要一个Kubernetes客户端配置文件以及访问集群仪表板。创建集群的方法不同,获取它们的方式也有所不同。由于我们的示例集群是用Kublr部署的,你可以在Kublr仪表板中找到以下链接,并将配置文件下载到~/.kube/config (在Windows上是%USERPROFILE%/.kube/config),然后导航到Kubernetes仪表板:

istio gateway tcp_devops_02

使用配置文件中的凭据(找到“username: admin”,并使用该用户及其列出的密码登录到仪表板)。你应该会看到如下所示的仪表板,单击侧边栏中的“NAMESPACES”将显示以下3个默认名称空间:

istio gateway tcp_devops_03

Istio组件将安装到它们自己的名称空间中。导航到你下载的Istio发布存档,提取并运行:kubectl apply -f install/kubernetes/ Istio -demo.yaml。

你会看到创建了许多组件,每个组件在Istio官方文档中都有介绍,或者,你可以打开yaml文件查看注释—每个资源都记录在该文件中。然后我们可以浏览命名空间,并检查是否一切都创建成功:

istio gateway tcp_操作系统_04

点击istio-system命名空间,确保在组件创建过程中没有错误或问题。它看起来应该是下面这个样子:

istio gateway tcp_操作系统_05

大约有50个事件;你可以滚动查看“成功”状态,并注意是否有错误。如果出现错误,你可以在Istio GitHub问题页面上发布Bug报告,向开发人员指出问题。

我们需要找到“istio-ingress”服务的入口点,以便知道将流量发送到哪里。导航到侧栏中的“istia-system”命名空间。如果在创建后没有看到这个命名空间,只需刷新浏览器页面,然后选择该命名空间,单击“Services”,找到外部端点,如下图所示:

istio gateway tcp_操作系统_06

在我们的例子中,它是一个AWS弹性负载均衡器,但是,你可能会看到一个IP地址,这取决于集群设置。我们将使用这个端点地址访问我们的演示用Web服务。

步骤2:使用Envoy Proxy Sidecar部署演示用Web服务

现在我们终于到了本教程最有趣的部分。让我们研究下该服务网格的路由功能。首先,我们将部署两个演示用Web服务:“blue”和“green”,就像我们在前一个教程中所做的那样。

把以下内容复制到名为my-websites.yaml的文件中:

apiVersion: apps/v1beta1kind: Deploymentmetadata:  name: web-v1  namespace: defaultspec:  replicas: 1  template:    metadata:      labels:        app: website        version: website-version-1    spec:      containers:      - name: website-version-1        image: kublr/kublr-tutorial-images:v1        resources:          requests:            cpu: 0.1            memory: 200---apiVersion: apps/v1beta1kind: Deploymentmetadata:  name: web-v2  namespace: defaultspec:  replicas: 1  template:    metadata:      labels:        app: website        version: website-version-2    spec:      containers:      - name: website-version-2        image: kublr/kublr-tutorial-images:v2        resources:          requests:            cpu: 0.1            memory: 200---apiVersion: apps/v1beta1kind: Deploymentmetadata:  name: web-v3  namespace: defaultspec:  replicas: 1  template:    metadata:      labels:        app: website        version: website-version-3    spec:      containers:      - name: website-version-3        image: kublr/kublr-tutorial-images:v3        resources:          requests:            cpu: 0.1            memory: 200---apiVersion: v1kind: Servicemetadata:  name: websitespec:  ports:  - port: 80    targetPort: 80    protocol: TCP    name: http  selector:    app: website

请注意,当你想要把Envoy sidecar和pod一起使用时,应提供“app”标签(供请求跟踪特性使用),而且服务定义中的“spec.ports.name”必须正确命名(http、http2、grpc、redis、mongo),否则,Envoy将把该服务流量作为普通的TCP服务对待,你将无法在那些服务中使用7层特性!

此外,pod只能作为集群中单个“服务”的目标。正如你上面看到的那样,定义文件有三个简单的部署,每个部署使用Web服务的不同版本(v1/v2/v3),而这三个简单的服务,每个都指向相应的部署。

现在,我们将使用“istioctl ku -inject”命令 将所需的Envoy代理配置添加到这个文件中的pod定义中。它将生成一个新的yaml文件,其中包含已经准备好使用kubectl部署的Envoy sidecar的其他组件,运行:istioctl kube-inject -f my-websites。yaml - o my-websites-with-proxy.yaml。

输出文件将包含额外的配置,你可以查看“my-websites-with-proxy.yaml”文件。这个命令使用预定义的ConfigMap “isti -sidecar-injector”(在我们之前安装Istio时安装了它),并将所需的sidecar配置和参数添加到部署定义中。当我们部署新文件“my-websites-with-proxy.yaml”时,每个pod将有两个容器,一个是演示应用程序和一个是Envoy代理。在这个新文件上运行创建命令:kubectl apply -f my-websites-with-proxy.yaml。

按照预期,你将看到以下输出:

deployment \u0026quot;web-v1\u0026quot; createddeployment \u0026quot;web-v2\u0026quot; createddeployment \u0026quot;web-v3\u0026quot; createdservice \u0026quot;website\u0026quot; created

让我们检查一下这些pod,看看Envoy sidecar是否已经存在:kubectl get pods。

可以看到,每个pod有两个容器,一个是网站容器,另一个是代理sidecar:

istio gateway tcp_devops_07

此外,你还可以检查Envoy代理的日志,运行:kubectl logs -c istio-proxy。

你会看到许多输出,最后几行类似下面这样:

add/update cluster outbound|80|version 1|website.default.svc.cluster.local starting warmingadd/update cluster outbound|80|version-2|website.default.svc.cluster.local starting warmingadd/update cluster outbound|80|version-3|website.default.svc.cluster.local starting warmingwarming cluster outbound|80|version-3|website.default.svc.cluster.local completewarming cluster outbound|80|version-2|website.default.svc.cluster.local completewarming cluster outbound|80|version-1|website.default.svc.cluster.local complete

这表明,代理sidecar健康状况良好,正在那个pod中运行。

现在,我们需要部署最小的Istio配置资源,将流量路由到我们的服务和pod,将以下清单保存到一个名为“website-routing.yaml”的文件中:

---apiVersion: networking.istio.io/v1alpha3kind: Gatewaymetadata:  name: website-gatewayspec:  selector:    # 我们希望把哪个pod暴露为Istio路由器    # 该标签指向从istio-demo.yaml文件安装时默认的那个    istio: ingressgateway  servers:  - port:      number: 80      name: http      protocol: HTTP    # 我们在这里指定Kubernetes服务名    # 我们希望使用这个Gateway提供服务    hosts:    - \u0026quot;*\u0026quot;---apiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata:  name: website-virtual-servicespec:  hosts:  - \u0026quot;*\u0026quot;  gateways:  - website-gateway  http:  - route:    - destination:        host: website        subset: version-1---apiVersion: networking.istio.io/v1alpha3kind: DestinationRulemetadata:  name: websitespec:  host: website  subsets:  - name: version-1    labels:      version: website-version-1  - name: version-2    labels:      version: website-version-2  - name: version-3    labels:      version: website-version-3

这些是Gateway、VirtualService和DestinationRule。这些自定义的Istio资源用于管理和配置Istio -ingressgateway pod的Ingress行为。我们将在下一个教程中更深入地描述它们,并介绍Istio配置的技术细节。现在,部署这些资源来访问我们的示例站点:kubectl create -f website-routing.yaml。

下一步是访问我们的演示网站。我们部署了三个“版本”,每个版本显示不同的页面文本和颜色,但是,目前我们只能通过Istio Ingress到达版本1。让我们访问端点,确保Web服务已经部署。

查找外部端点,运行:kubectl get services istio-ingressgateway -n istio-system。

或者通过浏览istio-ingressgateway服务找到它,如下所示(我们在本教程的开头已经看到过):

istio gateway tcp_运维_08

点击它可以访问外部端点。你可能会看到几个链接,因为一个链接指向HTTPS,另一个链接指向负载均衡器的HTTP端口。

使我们的“website” Kubernetes服务仅指向单个部署的确切配置是我们为网站创建的Istio VirtualService。它告诉Envoy代理仅把对“website”服务的请求路由到标签为“version: website-version-1”的pod(你可能已经注意到,“website”服务的清单只从pod中选择一个标签“app: website”,但没有指定从哪个“version”标签选择,所以,如果没有Envoy逻辑,则Kubernetes服务本身会轮循所有标签为“app: website”的pod,包括版本1、2和3)。

要更改我们看到的网站版本,你可以更改VirtualService清单的以下部分并重新部署:

http: - route:- destination:    host: website    subset: version-1

“subset”是我们从DestinationRule中选出的、要路由到的正确目标,我们将在下一个教程中深入学习这些资源。

步骤3: 逐步滚动

通常,当应用程序的新版本需要使用小部分流量进行测试(金丝雀部署)时,普通的Kubernetes方法是使用一个新的、但具有相同pod标签的Docker镜像创建第二个部署,这会导致向这个pod标签发送流量的“服务”也会在第二个部署时新插入的pod之间进行平衡。然而,你无法轻易将10%的流量路由到新部署(要精确达到10%,你得根据所需的百分比保持两个部署之间的pod副本比率, 如9个“v1 pods”和1个“v2 pod”,或者18个“v1 pods”和2个“v2 pods”),而且不能使用HTTP头将请求路由到特定的版本。

Istio通过其灵活的VirtualService配置解决了这一限制。例如,如果你想使用90/10规则来路由流量,很简单,如下所示:

---apiVersion: networking.istio.io/v1alpha3kind: Gatewaymetadata:  name: website-gatewayspec:  selector:    # 我们希望把哪个pod暴露为Istio路由器    # 该标签指向从istio-demo.yaml文件安装时默认的那个    istio: ingressgateway  servers:  - port:      number: 80      name: http      protocol: HTTP    # 我们在这里指定Kubernetes服务名    # 我们希望使用这个Gateway提供服务    hosts:    - \u0026quot;*\u0026quot;---apiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata:  name: website-virtual-servicespec:  hosts:  - \u0026quot;*\u0026quot;  gateways:  - website-gateway  http:  - route:    - destination:        host: website        subset: version-1      weight: 90    - destination:        host: website        subset: version-2      weight: 10---apiVersion: networking.istio.io/v1alpha3kind: DestinationRulemetadata:  name: websitespec:  host: website  subsets:  - name: version-1    labels:      version: website-version-1  - name: version-2    labels:      version: website-version-2  - name: version-3    labels:      version: website-version-3

GitHub https://github.com/kublr/istio-blog-sample 上提供了本文的源代码。

小结

我们希望本教程可以让对Istio、它是如何工作的以及如何利用它进行比较复杂的网络路由有一个良好的高层视角。Istio简化了那些需要花费更多时间和资源的场景的实现。这是一项任何人在研究服务网格时都应该考虑的强大技术。