持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情
服务网格(Server Mesh)
-
服务网格的需求:对于Docker,Kubernetes解决了部署问题,但是它们没有解决运行时的问题,这就是服务网格的由来
-
什么是解决了部署问题?使用Docker,Kubernetes等功能可以显著减轻部署增量操作的负担,但是部署完毕不是生产的最后一步,应用程序仍需运行,因此问题就变成了:我们能否像Docker,Kubernetes标准化部署操作一样,对应用程序的运行时操作进行标准化?
-
服务网格的治理模式:
- 内嵌于应用程序
- SDK
- Sidecar(使用这种)
-
示意图:
在上图中,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
服务网格中的南北流和东西流量
东西流量:服务网格中个服务之间通信的流量
南北流量:服务网格外部的客户端进入到服务网格所请求的流量,如上图:这类流量需要API GATEWAY(前端代理)
为什么要用到服务网格
若没有用到服务网格,则会出现如下问题:
- 服务间通信安全无从保证:假如现在有两个Pod,这来每个Pod需要相互通信,那么在kubernetes上,这两个Pod有可能在不同的节点之上,这就涉及到跨节点通信。如果节点与节点之间的流量不是加密的,那后果可想而知...,但是在kubernetes上的网络插件,可能会进行节点和节点之间的加密。但是在想,如果一个节点部署多个Pod,而且多个Pod需要通信,这时同处于一个节点的Pod是不会加密的,这又设计到泄密的情况...
- 跟踪通信延迟非常困难:想知道通信延迟和通信故障非常困难
- 负载均衡功能有限:在kubernetes的负载均衡有Service,Ingress,而Service只能实现一些基本的负载调度,无法实现高级负载调度,比如:流量切割,流量迁移,A/B测试,流量镜像...,就算使用Ingress实现滚动发布,金丝雀发布,但这也只是基于Pod数量实现的,它依然无法基于流量比例来实现。
Istio
-
为什么使用Istio?
通过负载均衡,service-to-service身份验证,监视等方法,Istio可以部署服务网格,,你可以在整个环境中部署一个特殊的Sidecar代理来为服务添加Istio的支持,该代理可以拦截微服务之间的所有网络通信,然后使用控制平面来管理网格,其中包括:
- HTTP,gRPC,WebSocket和TCP流量的自动负载均衡
- 使用丰富的路由规则,重试,故障转移,和故障注入对流量进行细粒度控制
- 支持访问控制,速率限制,和配额的可插拔策略和配置API
- 集群内所有的流量的自动度量,日志和跟踪,包括集群的入口,出口
- 在具有强大的基于身份的身份验证和授权的集中,实现安全,的服务到服务的通信
-
Istio的核心功能
- 流量管理:Istio的简单规则配置的流量路由允许你控制服务之间的流量和API调用流。Istio简化了服务属性(如:断路器,超时,重试的配置),并且简化了设置重要任务(如:A/B测试,金丝雀测试和按百分比划分的分阶段测试)
- 安全:Istio提供了底层的安全通信通道
- 观察:Istio的健壮跟踪,监视,和日志能使你深入了解网络部署,通过Istio的监视功能,可以真正服务性能如何影响上游和下游的事情,而它的自定义仪表板提供了对所有服务的性能的可见性,并可以看到该性能如何影响其它的流程
- 平台的支持:可以在虚拟机上安装,可以在kubernetes的Deployment中安装...
Istio架构
-
Istio服务网格分为:数据平面和控制平面
-
数据平面: 由一组智能代理(Envoy)组成,被部署为 sidecar。这些代理负责协调和控制微服务之间的所有网络通信。他们还收集和报告所有网格流量的遥测数据。
控制平面: 管理并配置代理来进行流量路由。
-
架构图:
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 的可插拔扩展模型,允许通过自定义策略实施和生成网格流量的遥测。
过程
如图有两个服务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在服务网格运行时所需要的各种各样的管理功能,
哪些功能?比如上面所说的:
- 流量治理,流量控制
- 可观测性
- 安全,通信授权
整体架构图
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
总结如图:
Mixer
Mixer主要由两个守护进程组成:
- istio-telemetry:负责收集遥测数据;网格中的服务间发生调用时,相关服务的Envoy将上报遥测数据之istio-telemetry,并由istio-telemetry根据配置将指标数据存入后端
- istio-policy:负责执行访问策略和管理配额;网络中的服务间发生调用时,相关服务的Envoy将调用istio-policy的Check接口检查是否允许访问,并由istio-policy根据配置将请求转发至对应的Adapter作对检查,而后响应以“允许”或“拒绝”
- 相关附件:istio-tracing和Prometheus(istio-telemetry存入后端就是这类组件)
总结如图:
Citadel
Citadel负责各个服务的私钥和数字证书管理,用于提供自动生成,分发,轮换,及撤销私钥和数据证书的功能
相关的程序文件为:istio-citadel
在Kubernetes平台之上,Citadel的工作方式是基于Secret资源将证书及私钥注入到Sidecar容器中。
Galley
Galley是服务于Pilot,Mixer的组件
Galley它检验进入网络的配置信息的格式和内容的正确性,并将这些配置信息提供给Pilot,Mixer,然后由Pilot将配置信息部署到Envoy中
Galley从底层平台接收配置信息并完成分发,从而将其它组件同底层平台解耦
如上图,用户下发命令给控制平面,控制平面将适用于Envoy命令下发给Envoy,那这里如果用户下发的命令是不合理的,不合适的怎么办?那这时Galley就起作用了,Galley负责检测,筛选可以下发的信息,然后发给Pilot,Mixer,
Ingress和Egress
Istio Sidecar Injector---注入Proxy
常见的将Sidecar注入Pod中的方法有两种:
- 手工注入:使用istioctl客户端工具进行注入
- 自动注入:使用Istioctl sidecar injector自动完成注入过程
Istio可视化
安装Istio
-
Ingress和Egress全部都在Istio中部署(也就是说在控制面板中部署),在Istio中的也叫做:istio-ingressgateway和istio-egressgateway
-
istio.io的官网: istio.io/
以1.8版本为例的官网地址: istio.io/v1.8/zh/doc…
-
下载Istio
在github上: github.com/istio/istio…
-
将下载的文件传到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) -
部署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文件 -
部署完之后,查看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 -
检验最后的安装成果
#先将部署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. 这个微服务会调用details和reviews两个微服务,用来生成页面。details. 这个微服务中包含了书籍的信息。reviews. 这个微服务中包含了书籍相关的评论。它还会调用ratings微服务。ratings. 这个微服务中包含了由书籍评价组成的评级信息。
reviews 微服务有 3 个版本:
- v1 版本不会调用
ratings服务。 - v2 版本会调用
ratings服务,并使用 1 到 5 个黑色星形图标来显示评分信息。 - v3 版本会调用
ratings服务,并使用 1 到 5 个红色星形图标来显示评分信息。
Bookinfo 应用中的几个微服务是由不同的语言编写的。 这些服务对 Istio 并无依赖,但是构成了一个有代表性的服务网格的例子:它由多个服务、多个语言构成,并且 reviews 服务具有多个版本。
部署实例
要在Istio中部署这一应用,无需对应用自身做出任何改变,只需要简单的在Istio环境中对服务进行配置运行,具体来说:就是把Sidecar注入到每一个微服务中去即可
所有的微服务都和 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)
如图所示:
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> #出现此结果
浏览器访问:
Bookinfo应用---网格内部路由规则
还看这张图:
我们现已可以将外部流量,引入至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