服务网格(Server Mesh)

342 阅读20分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情

服务网格(Server Mesh)

  1. 服务网格的需求:对于Docker,Kubernetes解决了部署问题,但是它们没有解决运行时的问题,这就是服务网格的由来

  2. 什么是解决了部署问题?使用Docker,Kubernetes等功能可以显著减轻部署增量操作的负担,但是部署完毕不是生产的最后一步,应用程序仍需运行,因此问题就变成了:我们能否像Docker,Kubernetes标准化部署操作一样,对应用程序的运行时操作进行标准化?

  3. 服务网格的治理模式:

    • 内嵌于应用程序
    • SDK
    • Sidecar(使用这种)
  4. 示意图:

image.png

在上图中,Servce A可以看作一个容器,Sidecar proxy可以看作一个容器,所以上图可以这样理解: 让服务集中解决业务逻辑的问题,高级网络相关的功能与业务逻辑剥离,并封装为独立的运行单元并作为服务的反向透明代理,从而不在与业务紧密关联

换句话说:微服务的业务程序独立运行,而网络功能则以独立的代理层工作与服务端和客户端之间,专门为代理的服务提供熔断,限流,追踪,指标采集和服务发现等功能,而这些由各服务的专门代理层联合组成的服务通信网络称之为:服务网格(Server Mesh)

可以看到微服务的业务程序在proxy之上,

服务网格工作流程:用户的请求先到达Sidecar,然后由Sidecar反向代理给Service A,Service A响应,并发给Sidecar,最后由Sidecar给客户端

数据平面与控制平面

数据平面:触及系统中的每个数据包或请求,负责服务发现,健康检查,路由,负载均衡,身份验证/授权和可观测性,它负责具体的可操作的过程

控制平面:为网格中的所有正在运行的数据平面提供策略和配置,从而将所有数据平面来联合构建为分布式系统,它不接触系统中的任何数据包或请求

一旦启用Service Mesh,服务间的通信将遵循以下通信逻辑:

  • 微服务彼此间不会直接进行通信,而是由各服务前端的Service Mesh的代理程序进行
  • 服务间的通信的局部故障可由Service Mesh自动处理
  • Service Mesh中的各服务的代理程序由控制平面集中管理;各代理程序之间的通信网络也称为数据平面
  • 部署与容器编排平台时,各代理程序会以微服务容器的Sidecar(边车)模式运行

服务网格的实现方案

数据平面的主流解决方案:Linkerd,Nginx,Envoy,Haproxy,Traefik

控制平面:Istio,Nelson,SmartStack

主流的:

Linkerd,Envoy,Istio

服务网格中的南北流和东西流量

image.png

东西流量:服务网格中个服务之间通信的流量

南北流量:服务网格外部的客户端进入到服务网格所请求的流量,如上图:这类流量需要API GATEWAY(前端代理)

为什么要用到服务网格

若没有用到服务网格,则会出现如下问题:

  • 服务间通信安全无从保证:假如现在有两个Pod,这来每个Pod需要相互通信,那么在kubernetes上,这两个Pod有可能在不同的节点之上,这就涉及到跨节点通信。如果节点与节点之间的流量不是加密的,那后果可想而知...,但是在kubernetes上的网络插件,可能会进行节点和节点之间的加密。但是在想,如果一个节点部署多个Pod,而且多个Pod需要通信,这时同处于一个节点的Pod是不会加密的,这又设计到泄密的情况...
  • 跟踪通信延迟非常困难:想知道通信延迟和通信故障非常困难
  • 负载均衡功能有限:在kubernetes的负载均衡有Service,Ingress,而Service只能实现一些基本的负载调度,无法实现高级负载调度,比如:流量切割,流量迁移,A/B测试,流量镜像...,就算使用Ingress实现滚动发布,金丝雀发布,但这也只是基于Pod数量实现的,它依然无法基于流量比例来实现。

Istio

  1. 为什么使用Istio?

    通过负载均衡,service-to-service身份验证,监视等方法,Istio可以部署服务网格,,你可以在整个环境中部署一个特殊的Sidecar代理来为服务添加Istio的支持,该代理可以拦截微服务之间的所有网络通信,然后使用控制平面来管理网格,其中包括:

    • HTTP,gRPC,WebSocket和TCP流量的自动负载均衡
    • 使用丰富的路由规则,重试,故障转移,和故障注入对流量进行细粒度控制
    • 支持访问控制,速率限制,和配额的可插拔策略和配置API
    • 集群内所有的流量的自动度量,日志和跟踪,包括集群的入口,出口
    • 在具有强大的基于身份的身份验证和授权的集中,实现安全,的服务到服务的通信
  2. Istio的核心功能

    • 流量管理:Istio的简单规则配置的流量路由允许你控制服务之间的流量和API调用流。Istio简化了服务属性(如:断路器,超时,重试的配置),并且简化了设置重要任务(如:A/B测试,金丝雀测试和按百分比划分的分阶段测试)
    • 安全:Istio提供了底层的安全通信通道
    • 观察:Istio的健壮跟踪,监视,和日志能使你深入了解网络部署,通过Istio的监视功能,可以真正服务性能如何影响上游和下游的事情,而它的自定义仪表板提供了对所有服务的性能的可见性,并可以看到该性能如何影响其它的流程
    • 平台的支持:可以在虚拟机上安装,可以在kubernetes的Deployment中安装...

Istio架构

  1. Istio服务网格分为:数据平面和控制平面

  2. 数据平面: 由一组智能代理(Envoy)组成,被部署为 sidecar。这些代理负责协调和控制微服务之间的所有网络通信。他们还收集和报告所有网格流量的遥测数据。

    控制平面: 管理并配置代理来进行流量路由。

  3. 架构图:

image.png

Istio 中的流量分为数据平面流量和控制平面流量。数据平面流量是指工作负载的业务逻辑发送和接收的消 息。控制平面流量是指在 Istio 组件之间发送的配置和控制消息用来编排网格的行为。Istio 中的流量管理特 指数据平面流量。

# Envoy

Istio 使用 [Envoy](https://envoyproxy.github.io/envoy/) 代理的扩展版本。Envoy 是用 C++ 开发的高性能代理,用于协调服务网格中所有服务的入站和出站流量。Envoy 代理是唯一与数据平面流量交互的 Istio 组件。

Envoy 代理被部署为服务的 sidecar,在逻辑上为服务增加了 Envoy 的许多内置特性,例如:

-   动态服务发现
-   负载均衡
-   TLS 终端
-   HTTP/2 与 gRPC 代理
-   熔断器
-   健康检查
-   基于百分比流量分割的分阶段发布
-   故障注入
-   丰富的指标

这种 sidecar 部署允许 Istio 提取大量关于流量行为的信号作为[属性](https://www.bookstack.cn/read/istio-1.8-zh/673b7c3c6cca824f.md#attributes)。Istio 可以使用这些属性来实施策略决策,并将其发送到监视系统以提供有关整个网格行为的信息。

sidecar 代理模型还允许您向现有的部署添加 Istio 功能,而不需要重新设计架构或重写代码。您可以在[设计目标](https://www.bookstack.cn/read/istio-1.8-zh/761af52fa49c99a3.md#design-goals)中读到更多关于为什么我们选择这种方法的信息。

由 Envoy 代理启用的一些 Istio 的功能和任务包括:

-   流量控制功能:通过丰富的 HTTP、gRPC、WebSocket 和 TCP 流量路由规则来执行细粒度的流量控制。
-   网络弹性特性:重试设置、故障转移、熔断器和故障注入。
-   安全性和身份验证特性:执行安全性策略以及通过配置 API 定义的访问控制和速率限制。
-   基于 WebAssembly 的可插拔扩展模型,允许通过自定义策略实施和生成网格流量的遥测。

过程

image.png

如图有两个服务A,B,当A和B通信时,就有Envoy来代理进行通信,但是当A在通过自身的Envoy访问B时,流量究竟是被如何管理,如何配置的?A和B通信时流量需不需要加密,咋加密?A访问B时,B作为客户端,B需不需要认证A...,这些配置都应该在Envoy的代理程序配置就好的。从这可以看到在部署应用程序时的Sidecar时各通用的,然后Envoy其内部的配置有可能需要根据需求而额外定义,好在Envoy支持LDS,CDS,RDS,EDS等等的xDS API动态配置,一个Envoy是这样配置,那10个呢?100个呢?,1k,1w个呢?很显然在手动配置是不现实的

因此控制平面的各个组件就来完成Sidecar在服务网格运行时所需要的各种各样的管理功能,

哪些功能?比如上面所说的:

  • 流量治理,流量控制
  • 可观测性
  • 安全,通信授权

整体架构图

image.png

Pilot

Pilot 为 Envoy sidecar 提供服务发现、用于智能路由的流量管理功能(例如,A/B 测试、金丝雀发布等)以及弹性功能(超时、重试、熔断器等)。

Pilot提供给Envoy各种各样功能的配置文件,比如:LDS,RDS,EDS,CDS,都是由Pilot提供的

Pilot相当于xDS服务器,用来接收管理服务器的配置信息,从而应用到Envoy上的xDS API中

Pilot的子组件:

  • Pla'tfrom Adapter:平台适配器,Istio可能部署在K8S之上,裸机上,公有云上...,所以Platfrom Adapter针对不同的底层平台进行适配,并完成从平台特有的服务模型到Istio服务模型的转换
  • Abstract Model:抽象聚合层,聚合来自Platfrom Adapter不同平台的服务和配置规则并对上提供统一的接口,从而解耦Envoy API 的底层平台
  • Envoy API:Pilot通过xDS服务器提供服务发现接口xDS API,xDS服务器 接收并维护Envoy代理的连接,并基于Envoy订阅的资源名称进行配置分发
  • Rules API:高级流量管理规则的API 接口,用户通过该API 配置流量规则并由其转换为低级配置,而后通过discovery API分发到Envoy实例

Pilot组件的程序文件名:Istio-Pilot

总结如图:

image.png

Mixer

Mixer主要由两个守护进程组成:

  • istio-telemetry:负责收集遥测数据;网格中的服务间发生调用时,相关服务的Envoy将上报遥测数据之istio-telemetry,并由istio-telemetry根据配置将指标数据存入后端
  • istio-policy:负责执行访问策略和管理配额;网络中的服务间发生调用时,相关服务的Envoy将调用istio-policy的Check接口检查是否允许访问,并由istio-policy根据配置将请求转发至对应的Adapter作对检查,而后响应以“允许”或“拒绝”
  • 相关附件:istio-tracing和Prometheus(istio-telemetry存入后端就是这类组件)

总结如图:

image.png

Citadel

Citadel负责各个服务的私钥和数字证书管理,用于提供自动生成,分发,轮换,及撤销私钥和数据证书的功能

相关的程序文件为:istio-citadel

在Kubernetes平台之上,Citadel的工作方式是基于Secret资源将证书及私钥注入到Sidecar容器中。

Galley

Galley是服务于Pilot,Mixer的组件

Galley它检验进入网络的配置信息的格式和内容的正确性,并将这些配置信息提供给Pilot,Mixer,然后由Pilot将配置信息部署到Envoy中

Galley从底层平台接收配置信息并完成分发,从而将其它组件同底层平台解耦

image.png

如上图,用户下发命令给控制平面,控制平面将适用于Envoy命令下发给Envoy,那这里如果用户下发的命令是不合理的,不合适的怎么办?那这时Galley就起作用了,Galley负责检测,筛选可以下发的信息,然后发给Pilot,Mixer,

Ingress和Egress

image.png

Istio Sidecar Injector---注入Proxy

常见的将Sidecar注入Pod中的方法有两种:

  • 手工注入:使用istioctl客户端工具进行注入
  • 自动注入:使用Istioctl sidecar injector自动完成注入过程

Istio可视化

image.png

安装Istio

  1. Ingress和Egress全部都在Istio中部署(也就是说在控制面板中部署),在Istio中的也叫做:istio-ingressgateway和istio-egressgateway

  2. istio.io的官网: istio.io/

    以1.8版本为例的官网地址: istio.io/v1.8/zh/doc…

  3. 下载Istio

    在github上: github.com/istio/istio…

image.png

  1. 将下载的文件传到Linux上,并解压

    [root@master ~]# tar xzvf istio-1.8.0-linux-amd64.tar.gz 
    [root@master ~]# cd istio-1.8.0/bin/
    [root@master bin]# mv istioctl /usr/local/bin/#查看版本:
    [root@master ~]# istioctl version
    client version: 1.8.0
    control plane version: 1.8.0
    data plane version: 1.8.0 (2 proxies)
    ​
    
  2. 部署Istio

    [root@master ~]# istioctl install --set profile=demo         #以demo的方式去安装,(安装的内容比较全,便于初学者)
    #注意这时大部分情况下,会失败,是因为需要的镜像在国外,本地访问不到,那这时,等它失败然后在istio-system命名空间下查看deployment的状态,将三个deployment的yaml文件导出:
    [root@master ~]# kubectl get deploy istiod -n istio-system -o yaml > istiod.yaml
    #剩下的两个也是同样的操作,然后在配置文件中修改镜像的tag,然后在pull拉取,最后在apply,yaml文件
    
  3. 部署完之后,查看istio-system命名空间的所有应用

    [root@master ~]# kubectl get all -n istio-system
    NAME                                        READY   STATUS    RESTARTS   AGE
    pod/istio-egressgateway-75dbd877cb-rd6x9    1/1     Running   0          37m
    pod/istio-ingressgateway-756879c745-2npm2   1/1     Running   0          37m
    pod/istiod-6bf46fc8cc-g2wkc                 1/1     Running   0          38m
    ​
    NAME                           TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                                                                      AGE
    service/istio-egressgateway    ClusterIP      10.101.205.165   <none>        80/TCP,443/TCP,15443/TCP                                                     37m
    service/istio-ingressgateway   LoadBalancer   10.102.61.130    <pending>     15021:31665/TCP,80:32257/TCP,443:31890/TCP,31400:32477/TCP,15443:30285/TCP   37m
    service/istiod                 ClusterIP      10.98.32.34      <none>        15010/TCP,15012/TCP,443/TCP,15014/TCP                                        38m
    ​
    NAME                                   READY   UP-TO-DATE   AVAILABLE   AGE
    deployment.apps/istio-egressgateway    1/1     1            1           37m
    deployment.apps/istio-ingressgateway   1/1     1            1           37m
    deployment.apps/istiod                 1/1     1            1           38m
    ​
    NAME                                              DESIRED   CURRENT   READY   AGE
    replicaset.apps/istio-egressgateway-75dbd877cb    1         1         1       37m
    replicaset.apps/istio-ingressgateway-756879c745   1         1         1       37m
    replicaset.apps/istiod-6bf46fc8cc                 1         1         1       38m
    
  4. 检验最后的安装成果

    #先将部署istio的文件导出
    [root@master ~]# istioctl manifest generate --set profile=demo > f.yaml
    #在检测
    [root@master ~]# istioctl verify-install -f f.yaml 
    

部署实例---BookInfo

实例介绍

BookInfo示例部署了一个用于演示多种 Istio 特性的应用,该应用由四个单独的微服务构成。 这个应用模仿在线书店的一个分类,显示一本书的信息。 页面上会显示一本书的描述,书籍的细节(ISBN、页数等),以及关于这本书的一些评论。

Bookinfo 应用分为四个单独的微服务:

  • productpage. 这个微服务会调用 detailsreviews 两个微服务,用来生成页面。
  • details. 这个微服务中包含了书籍的信息。
  • reviews. 这个微服务中包含了书籍相关的评论。它还会调用 ratings 微服务。
  • ratings. 这个微服务中包含了由书籍评价组成的评级信息。

reviews 微服务有 3 个版本:

  • v1 版本不会调用 ratings 服务。
  • v2 版本会调用 ratings 服务,并使用 1 到 5 个黑色星形图标来显示评分信息。
  • v3 版本会调用 ratings 服务,并使用 1 到 5 个红色星形图标来显示评分信息。

image.png

Bookinfo 应用中的几个微服务是由不同的语言编写的。 这些服务对 Istio 并无依赖,但是构成了一个有代表性的服务网格的例子:它由多个服务、多个语言构成,并且 reviews 服务具有多个版本。

部署实例

要在Istio中部署这一应用,无需对应用自身做出任何改变,只需要简单的在Istio环境中对服务进行配置运行,具体来说:就是把Sidecar注入到每一个微服务中去即可

image.png

所有的微服务都和 Envoy sidecar 集成在一起,被集成服务所有的出入流量都被 sidecar 所劫持,这样就为外部控制准备了所需的 Hook,然后就可以利用 Istio 控制平面为应用提供服务路由、遥测数据收集以及策略实施等功能。

注意:在本版本(1.8.0)中,运行下面的命令是不行的,这样发现创建出来没有Pod

kubectl label namespace default istio-injection=enabled

应使用另一种方法:

kubectl apply -f <(istioctl kube-inject -f /root/istio-1.8.0/samples/bookinfo/platform/kube/bookinfo.yaml)

2、等待Pod全部Running之后,查看其中一个Pod

[root@master ~]# kubectl describe pods details-v1-cfc496c78-65n8w 
#会发现由两个容器details,istio-proxy,
#其实还有一个容器:istio-init,初始化容器,它的目的就是:在主容器运行之前运行结束,它负责将直接指向应用程序的流量,给代理到istio-proxy容器,然后在由istio-proxy做流量处理

3、测试BookInfo是否在运行

[root@master ~]# kubectl exec -it $(kubectl get pod -l app=ratings -o jsonpath='{.items[0].metadata.name}') -c ratings -- curl productpage:9080/productpage | grep -o "<title>.*</title>"
<title>Simple Bookstore App</title>                   #出来此结果即可

BookInfo应用对外开放

现在Bookinfo应用正常运行,在网格内部也能请求,那如何让网格外部的流量也能访问到bookinfo应用,也就是说如何让上图中的“Product page”,这个入口服务对外开放?

这就需要Istio中的Ingress-gateway,也就是Pod:istio-ingressgateway

针对bookinfo应用,istio-ingressgateway有两个任务:

  • 使用后istio-ingressgateway把“Product page”,这个入口服务开放出去
  • 把流量开放进网格以后,还应该把这些流量如何调度给“Product page”

那么想要将流量由外部引入进网格,同样也需要两步骤:

  • 先把访问“Product page”入口流量的服务,给引入到istio-ingressgateway中去
  • 在借助istio-ingressgateway,将流量对外部开放

如何去做?

可以看到istio-ingressgateway,是istio-system命名空间中的Pod(是个Envoy),它不属于bookinfo玩个内部的组件,说白了istio-ingressgateway,它属于istio控制平面的一组成部分。

那所以,外部的流量先到控制平面的istio-ingress-gateway上,然后由istio-ingress-gateway统一发往,调度到服务网格的入口服务处(Product page)

如图所示:

image.png

1、创建”Gateway,VirtualService“,由istio引入的CRD资源

apiVersion: networking.istio.io/v1alpha3     #注意此资源组
kind: Gateway
metadata:
  name: bookinfo-gateway
spec:
  selector:     #它定义了下面的字段规则,会生效在bookinfo服务网格中各哪一个Envoy上,有下面的”istio: ingressgateway“可以知道它生效在ingressgateway中的Envoy上
    istio: ingressgateway # use istio default controller     #这个意思就是说,选择哪个Envoy?选择istio服务网格ingressgateway中的Envoy去生效下面的listener
  servers:         #由pilot将这里的CDR规则,转化为Envoy中的配置,这里servers定义的是listener
  - port:
      number: 80           #监听80
      name: http 
      protocol: HTTP                    #servers中定义的字段,相当于在Envoy中的listener配置,意思是:任何主机(这里说的就是外部访问的主机)只要访问80端口,都被这个listener所捕获,也就相当于被ingressgateway所捕获
    hosts:
    - "*"        #所有主机
---
#在Envoy要想实现流量转发(尤其是实现HTTP流量的转发),光定义listener是不够的,我们还需要定义过滤器链,这个过滤器链有个重要的过滤器是个L4层过滤器的,但是它会引入L7,7层过滤器,也就是router过滤器,来实现路由表的过滤,上面定义的listener,所以下面送一个listener内部的router过滤规则
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: bookinfo
spec:
  hosts:                #表示下面的流量转发规则,会应用到那个主机上,所谓的主机就是指bookinfo网格中所有的Envoy上,那*代表全部的Envoy,但是...往下看
  - "*"
  gateways:        #通常同时定义host和gateway两个字段有特殊的含义,这个表示:下面所定义的路由规则只在”bookinfo-gateway“这个gateway上生效
  - bookinfo-gateway
  http:              #下面就定义了,匹配条件和路由规则
  - match:
    - uri:
        exact: /productpage        #exact示精确匹配,prefix代表前缀匹配
    - uri:
        prefix: /static
    - uri:
        exact: /login
    - uri:
        exact: /logout
    - uri:
        prefix: /api/v1/products
    route:           #这个字段代表:上面的匹配成功之后,给路由到哪去?
    - destination:   #目的地
        host: productpage        #这个host代表:svc的名称,它代表bookinfo中productpage的svc名称,这里定义的svc会被pilot发现,发现之后被pilot引入到服务网格内部去,然后在服务网格中借助kubernetes做解析,解析成这个svc后面所对应Pod的IP地址,这样一来就将那个流量发送到“Product page”入口服务中去了
        port:
          number: 9080        #代表路由到哪个svc中的那个端口

2、应用这个gateway

[root@master ~]# kubectl apply -f istio-1.8.0/samples/bookinfo/networking/bookinfo-gateway.yaml#查看gateway
[root@master ~]# kubectl get gateway
NAME               AGE
bookinfo-gateway   6h42m
#查看VirtualService,可以看到整个vs绑定到名为”bookinfo-gateway“的gateway上了(就相当于router应用到listener上了)
[root@master ~]# kubectl get vs
NAME       GATEWAYS             HOSTS   AGE
bookinfo   [bookinfo-gateway]   [*]     7h27m
​
#查看istio-ingressgateway
[root@master ~]# kubectl get svc -n istio-system
NAME                   TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                                                                      AGE
istio-egressgateway    ClusterIP   10.101.205.165   <none>        80/TCP,443/TCP,15443/TCP                                                     23h
istio-ingressgateway   NodePort    10.102.61.130    <none>        15021:31665/TCP,80:32257/TCP,443:31890/TCP,31400:32477/TCP,15443:30285/TCP   23h
istiod                 ClusterIP   10.98.32.34      <none>        15010/TCP,15012/TCP,443/TCP,15014/TCP                                        23h
​
#可以发现在istio-ingressgateway有个端口映射:80:32257,所以在浏览器访问32257端口

3、确认从集群外部访问应用

[root@master ~]# curl -s http://192.168.9.131:32257/productpage | grep -o "<title>.*</title>"
<title>Simple Bookstore App</title>         #出现此结果

浏览器访问:

image.png

Bookinfo应用---网格内部路由规则

还看这张图:

image.png

我们现已可以将外部流量,引入至Product page入口服务了,并且也能访问,那在bookinfo网格内部流量该如歌调度呢?比如说:我想从Product page入口服务,调度至Reviews-v2版本上,或者调度至Details上,如何实现?

看个文件

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule             #如果说gateway,VirtualService,定义各listener和router规则,那DestinationRule就相当于定义了Cluster调度,以及Cluster调度算法。DestinationRule主要对应Cluster上,如何配置Cluster内部细节的,router可以调用Cluster去定义,但是Cluster细节还需要DestinationRule去定义。比如通过Product page去访问Reviews,会在Product page中的Envoy上的Cluster生成名为Reviews的集群,当流量经过Product page发往Reviews-Pod的过程呢? 就靠下面的定义来决定最终流量发往Reviews那个版本
metadata:
  name: productpage
spec:
  host: productpage
  subsets:
  - name: v1
    labels:
      version: v1
---
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

创建DestinationRule

[root@master ~]# kubectl apply -f istio-1.8.0/samples/bookinfo/networking/destination-rule-all.yaml

查看DestinationRule

[root@master ~]# kubectl get destinationrules
NAME          HOST          AGE
details       details       3s
productpage   productpage   3s
ratings       ratings       3s
reviews       reviews       3s