k8s之服务网格istio
一、概念
服务网格将程序开发的网络功能和程序本身解耦,网络功能下沉到基础架构,由服务网格实现服务之间的负载均衡等功能,并且除网络功能外,还提供了其他更高级的功能,比如全链路加密、监控、链路追踪等
1、服务网格的功能
服务网格作为透明代理,可以运行于任何基础设施环境,而且和应用非常靠近,服务网格功能大致如下:
- 负载均衡:运行环境中的微服务实例通常处于动态变化状态,而且经常可能出现个别实例不能正常提供服务、处理能力减弱、卡顿等现象。但由于所有请求对服务网格来说是可见的,因此可以通过提供高级负载均衡算法来实现更加智能、高效的流量分发,以降低延时,提高可靠性
- 服务发现:以微服务模式运行的应用变更非常频繁,应用实例的频繁增加和减少带来的问题是,如何精确地发现新增实例以及避免将请求发送给已不存在的实例而变得更加复杂。服务网格可以提供简单、统一、平台无关的多种服务发现机制,如基于DNS、键-值对存储的服务发现机制
- 熔断:在动态环境中,服务实例中断或不健康导致的服务中断可能会经常发生,这就要求应用或者其他工具具有快速监测并从负载均衡池中移除不提供服务实例的能力,这种能力也称熔断,而服务网格可以很容易地实现基于请求和连接级别的熔断机制
- 动态路由:如果要讲流量从一个版本切换到另一个版本,从一个数据中心切换到另一个数据中心,并且保证服务无中断,服务网格提供的动态路由机制和特定的部署策略(如Blue/Green部署)结合起来,实现上述目标将会变得更加容易
- 安全通信:在微服务环境中,不同的服务实例间的通信变得更加复杂,如何保证这些通信是在安全、授权的情况下进行的就非常重要,通过将安全机制(如TLS加解密和授权)实现在服务网格上,不仅可以避免在不同应用上的重复实现,而且很容易在整个基础设施层更新安全机制,甚至无须对应用做任何操作
- 多语言支持:服务网格作为独立运行的透明代理,很容易支持多语言
- 多协议支持:同多语言支持一样,实现多协议支持也非常容易
- 指标和分布式追踪:服务网格对整个基础设施层的可见性使得它不仅可以暴露单个服务的运行指标,而且可以暴露整个集群的运行指标
- 重试和最后期限:服务网格的重试功能可以避免将其嵌入业务代码中,同时最后期限使得应用允许一个请求的最长生命周期,而不是无休止地重试
2、服务网格产品
- Linkerd:是Buoyant公司在2016年率先开源的高性能网络代理程序,它的出现标志着服务网格时代的开始
- Envoy:同Linkerd一样,Envoy也是一款高性能的网络代理程序,为云原生应用而设计
- Istio:Istio受Google、IBM、Lyft及RedHat等公司的大力支持和推广,于2017年5月发布,底层为Envoy
- Conduit:2017年12月发布,是Buoyant公司的第二款服务网格品,根据Linkerd在生产线上的实际使用经验而设计,并以最小复杂性作为设计基础
3、istio的架构
Istio 服务网格从逻辑上分为数据平面和控制平面
- 数据平面 由一组智能代理(Envoy)组成,被部署为 Sidecar。这些代理负责协调和控制微服务之间的所有网络通信。它们还收集和报告所有网格流量的遥测数据
- 控制平面 管理并配置代理来进行流量路由

Envoy:Istio的数据平面使用的是Envoy,Envoy是以C++开发的高性能代理,用于调解服务网格中所有服务的所有入站和出站流量。Envoy代理是唯一一个与数据平面流量交互的Istio组件,Envoy通过Sidecar的方式和业务应用部署在同一个Pod内,在逻辑上为服务增加了许多内置特性,例如
- 动态服务发现
- 负载均衡
- TLS终止
- HTTP/2 & gRPC代理
- 熔断器
- 健康检查、基于百分比流量拆分的灰度发布
- 故障注入
- 丰富的度量指标
由Envoy代理启用的一些Istio功能和任务包括:
- 流量控制功能:通过丰富的HTTP、gRPC、WebSocket和TCP流量路由规则来执行细粒度的流量控制
- 网络弹性功能:重试、故障转移、熔断器和故障注入功能
- 安全性和身份认证功能:执行安全策略,通过配置强制实行API的访问控制和速率限制
- 遥测:基于WebAssembly的可插拔扩展模型,允许通过自定义策略执行和生成网格流量的遥测
Istiod为Istio的控制平面,提供服务发现、配置、证书管理、加密通信和认证功能
Istiod将控制流量行为的高级路由规则转换为Envoy特定的配置,并在运行时将其传播给Sidecar。可以使用Istio流量管理API让Istiod重新构造行时将其传播给Sidecar。可以使用Istio流量管理API让Istiod重新构造
从架构图可以看出Istiod由Pilot、Citadel和Galley组成,功能如下:
- Pilot:为Envoy Sidecar提供服务发现的功能,为智能路由(例如A/B测试、金丝雀部署等)和弹性(超时、重试、熔断器等)提供流量管理功能。Pilot将控制流量行为的高级路由规则转换为特定于Envoy的配置,并在运行时将它们传播到Sidecar。Pilot将平台特定的服务发现机制抽象化,并将其合成为符合Envoy数据平面API的任何Sidecar都可以使用的标准格式,这种松耦合使得Istio能够在多种环境下运行(如Kubernetes、Consul、Nomad),同时保持用于流量管理的相同操作界面
- Citadel:通过内置身份和凭证管理可以提供强大的服务与服务之间的最终用户身份验证,可用于升级服务网格中未加密的流量,并为运维人员提供基于服务标识而不是网络控制的强制执行策略的能力。从0.5版本开始,Istio支持基于角色的访问控制,以控制服务访问
- Galley:负责配置管理的组件,用于验证配置信息的格式和正确性。Galley使用网格配置协议(Mesh Configuration Protocol)和其他组件进行配置的交互
4、istio核心资源
和Kubernetes资源一致,Istio的配置也是通过声明式自定义资源配置来加载的。常用的核心资源有VirtualService、DestinationRule、GatewayServiceEntry、Sidecar等
(1)、VirtualService
Istio 最为重要的一 个概念就是VirtualService(虚拟服务),VirtualService基于Istio和对应平台提供的基本连通性和服务发现能力将请求路由到对应的目标。每一个VirtualService包含一组路由规则,Istio将每个请求根据路由匹配到指定的目标地址,VirtualService可以实现更加细粒度的流量分发,比如灰度发布等,典型用例是可调配流量发送到指定服务器的不同版本,比如60%发送到v1版本,40%发送到v2版本,如下例子:

- apiVersion:对应的API版本
- kind:创建的资源类型,本例子为VirtualService
- metadata:元数据,可以定义annotations、labels、name等
- spec.hosts:客户端向服务端发送请求时使用的一个或多个地址,可以是IP地址、DNS名称、也可以是完全限定域名也可以是通配符*或者*abc.com
- spec.gateways: 指定gateway名称,gateway是需要单独创建的,后面有例子
- spec.http:路由规则配置,将流量发送到hosts字段指定的目标
- spec.http.match:路由规则条件,根据条件制定更精细的路由,exact表示完全匹配
- spec.http.route:具体的路由规则,destination字段指定了符合此条件的流量的实际目标地址,上图中当匹配的路由规则为/pro时,流量会被请求到v1版本,否则请求到v2版本
注:VirtualService路由规则按照从上往下的顺序进行匹配,第一个规则有最高的优先级,如果不满足第一个路由规则,则流量会选择下一个规则
除了上述路由匹配外,VirtualService也支持域名+路径方式匹配,如图:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: vs-bookinfo
spec:
hosts:
- "www.test.com"
gateways:
- bookinfo-gateway
http:
- match:
- uri:
exact: /pro
route:
- destination:
host: abc
subset: v1
- match:
- uri:
prefix: /test
route:
- destination:
host: def
subset: v2
更多匹配方式可参考: Istio / Virtual Service
https://istio.io/latest/docs/reference/config/networking/virtual-service/#HTTPMatchRequest
VirtualService也支持HTTP重定向、HTTP Rewrtite、HTTP重试、HTTP流量镜像、故障注入、解决跨域等功能,具体可以参考:Istio / Virtual Service
https://istio.io/latest/docs/reference/config/networking/virtual-service/
注意:VirtualService是有命名空间区分的,不同的namespace需要指定namespace字段的
(2)、DestinationRule
Istio另一个比较重要的资源是DestinationRule(目标规则),可以将VirtualService理解为k8s的service层面,将DestinationRule理解为Service后端真实的目标地址,DestinationRule可以对后端真实的pod进行划分成不同的subnet,区分新旧版本,然后VirtualService可以针对不同的版本进行流量管控,比如上图中的v1和v2就是通过DestinationRule来进行划分的,DestinationRule的配置实例如下:

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: dest-rule
spec:
host:
- "www.test.com"
subsets:
- name: v1
labels:
version: v1 #DestinationRule根据pod的标签划分版本
- name: v2
labels:
version: v2
DestinationRule是根据pod的标签不同来划分版本,比如存在3个Deployment创建的3个pod,标签分别为version:v1、version:v2、version:v3,此时DestinationRule就可以根据此标签实现灰度发布、A/B测试
除了划分不同的版本外,DestinationRule也支持服务熔断、更多的负载均衡策略、TLS认证等,具体可参考:Istio / Destination Rule
https://istio.io/latest/zh/docs/reference/config/networking/destination-rule/
注意:DestinationRule是有命名空间区分的,不同的namespace需要指定namespace字段的
(3)、Gateway
Istio同样支持网关功能,可以使用Gateway在网格最外层接收HTTP/TCP流量,并将流量转发到网格内的某个服务,gateway一般和VirtualService配置使用,并配置一个可以被外部服务访问的域名,从而外部服务可以通过该域名访问网格内的服务
在安装istio后,会在命名空间istio-system下自动安装ingressgateway的pod来充当ingress Gateway,其中Ingress Gateway为入口网关,可以将网格内的服务“暴露”出去,如图:

上图中的pod即为入口网关,标签默认为istio=ingressgateway
下面是一个gateway配置的例子,如图:

- selector:必选字段,选择由哪个ingressgateway的Pod发布服务,默认为istio-system命名空间下具有istio=ingressgateway标签的pod
- servers:必选字段,表示发布的服务列表,用于描述发布服务的属
- port:指定gateway使用的端口性,比如代理监听的端口、协议和端口的名称等
- hosts:必选字段,Gateway发布的服务地址,也就是允许用户访问的域名,可以配置为“*”,表示任何域名都可以被代理,本例子中为” * “
注意:Gateway是有命名空间区分的,不同的namespace需要指定namespace字段的
二、istio安装
github地址如下:
https://github.com/istio/istio
1、下载最新版本的istio,并将可执行文件添加到环境变量中,如下:
curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.17.1 TARGET_ARCH=x86_64 sh -
cp istio-1.17.1/bin/istioctl /usr/bin/
安装目录包含:
samples/目录下的示例应用程序bin/目录下的istioctl客户端二进制文件
执行如下命令安装(本例子是在k8s-master节点上上执行的):
istioctl install --set profile=demo -y

demo表示指定demo这个配置文件来进行安装,选择它是因为它包含了一组专为测试准备的功能集合,另外还有用于生产或性能测试的配置组合,更多配置可参考:Istio / 安装配置文件
安装完成后可以看到创建了命令空间istio-system以及pod和svc信息,如图:

注:上图中service中,ingressgateway表示入站网关,egressgateway表示出站网关
三、测试
1、部署官方内置的测试用例 Bookinfo,本例子中在默认namespace下进行,Bookinfo应用分为4个单独的微服务:
- productpage:这个微服务会调用details和reviews两个微服务,用来生成页面
- details:这个微服务包含图书的信息
- reviews:这个微服务包含图书相关的评论,它还会调用ratings微服务
- ratings:ratings微服务中包含由图书评价组成的评级信息
除此之外,reviews微服务还分为3个版本:
- v1版本:不会调用ratings服务,即不会在页面显示书的评分
- v2版本:会调用ratings服务,并使用1~5个黑色星形图标来显示评分信息
- v3版本:会调用ratings服务,并使用1~5个红色星形图标来显示评分信息
(1)、给命名空间添加标签,指示 Istio 在部署应用的时候,自动注入 Envoy 边车代理,如下:
kubectl label namespace default istio-injection=enabled #如果是别的命名空间就修改default位置
(2)、执行如下命令进行部署:
kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml

注意:每个pod中都有两个容器,一个是业务容器,一个是边车容器

BookInfo 应用已经部署,可以在服务器上通过productpage的svc地址来访问页面,如图:

如果要先实现通过外部访问,需要配置gateway和VirtualService来实现域名访问
2、创建Gateway,定义资源文件gateway.yml,配置域名为www.test.com,如图:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: bookinfo-gateway
spec:
selector:
istio: ingressgateway #使用istio安装完默认的入口网关
servers:
- port:
number: 80 #访问的网关端口
name: http
protocol: HTTP
hosts:
- "www.test.com" #此入口网关允许通过的域名,也可以配置*
3、定义virtual-service.yaml,内容如下:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: vs-bookinfo
spec:
hosts:
- "www.test.com" #也可以配置*,允许全部域名通过
gateways:
- bookinfo-gateway
http:
- match:
- uri:
exact: /productpage
- uri:
prefix: /static
- uri:
exact: /login
- uri:
exact: /logout
- uri:
prefix: /api/v1/products
route:
- destination:
host: productpage
port:
number: 9080 #应用对应的端口
上面的virtual-service中的uri都指向了productpage,实际使用时可根据不同的uri指向不同的服务,只需要配置多个match和rule即可
注意:执行完gateway和virtual-service后,即可将应用关联到istio gateway
4、查看创建的gateway和virtual-service,如图:

5、访问,将域名www.test.com解析至集群任意一个安装了kube-proxy的节点IP上,通过ingressgateway的service的NodePort端口即可访问,如图:

访问流程为:通过域名www.test.com请求流量到达入口网关,然后根据virtualservice中配置的路由规则,指向到destination中对应的服务名和端口(此服务名和端口就是项目部署后对应的service服务和端口),因为入口网关没有配置EXTERNAL-IP地址,因此默认只能通过网关80端口对应的NodePort端口32412来请求

从上图看出EXTERNAL-IP并没设置,如果EXTERNAL-IP值已设置,说明环境正在使用外部负载均衡,可以用其为ingress gateway 提供服务。 如果EXTERNAL-IP值为<none>(或持续显示<pending>), 说明环境没有提供外部负载均衡,无法使用ingress gateway,只能通过服务的NodePort访问网关
配置EXTERNAL-IP,直接通过域名访问:
1、首先设置EXTERNAL-IP,此IP地址要设置为LoadBalancer,比如SLB地址或者通过keepalived虚拟出来的IP,本例子中使用的是keepalived虚拟的IP地址,编辑svc,添加EXTERNAL-IP
kubectl edit svc istio-ingressgateway -n istio-system

设置后直接通过ingress gateway的IP:80端口加路由即可访问,如图:

但实际项目中,我们都是通过域名来访问的,此时可以将域名解析到此igress gateway的IP地址上即可,如果不限制任何域名的请求,那么在gateway.yml和virtualservice中就将host位置设置为”*” ,如图:


2、测试,添加本地hosts,配置两个域名解析到SLB(ingress gateway地址),访问,如图:



安装可视化工具kiali:
kubectl create -f samples/addons/kiali.yaml

修改svc的类型为NodePort,通过NodePort端口访问
kubectl patch svc -n istio-system kiali -p '{"spec": {"type": "NodePort"}}'

通过任意节点的IP:30035即可访问


