一、概述
服务网格是一个独立的基础设施层,用来处理服务之间的通信。现代的云原生应用是由各种复杂技术构建的服务体系,服务网格负责在这些组成部分之间进行可靠的请求传递。目前典型的服务网格通常提供了一组轻量级的网络代理,这些代理会在应用无感知的情况下,同应用并行部署、运行。
前面提到,Istio出自名门,由Google、IBM和Lyft在2017年5月合作推出,它的初始设计目标是在Kubernetes的基础上,以非侵入的方式为运行在集群中的微服务提供流量管理、安全加固、服务监控和策略管理等功能。
Istio有助于降低部署的复杂性,并减轻开发团队的压力。它是一个完全开放源代码的服务网格,透明地分层到现有的分布式应用程序上。它也是一个平台,包括允许它集成到任何日志平台、遥测或策略系统中的api。Istio的多种功能集使我们能够成功、高效地运行分布式微服务体系结构,并提供一种统一的方式来保护、连接和监视微服务。
传统的spring cloud微服务项目

基于Istio架构微服务项目

Istio是基于Sidecar模式、数据平面和控制平台、是主流Service Mesh解决方案。
1.1 Istio特征
连接:对网格内部的服务之间的调用所产生的流量进行智能管理,并以此为基础,为微服务的部署、测试和升级等操作提供有力保障。
安全:为网格内部的服务之间的调用提供认证、加密和鉴权支持,在不侵入代码的情况下,加固现有服务,提高其安全性。
策略:在控制平面定制策略,并在服务中实施。
观察:对服务之间的调用进行跟踪和测量,获取服务的状态信息。
连接
微服务错综复杂,要完成其业务目标,连接问题是首要问题。连接存在于所有服务的整个生命周期中,用于维持服务的运行,算得上重中之重。
相对于传统的单体应用,微服务的端点数量会急剧增加,现代的应用系统在部分或者全部生命周期中,都存在同一服务的不同版本,为不同的客户、场景或者业务提供不同的服务。同时,同一服务的不同版本也可能有不同的访问要求,甚至产生了在生产环境中进行测试的新方法论。错综复杂的服务关系对开发者来说都是很严峻的考验。
针对目前的常见业务形态,这里画一个简单的示意图来描述Service Mesh的连接功能

从不同的外部用户的角度来看,他们访问的都是同一服务端口,但实际上会因为不同的用户识别,分别访问服务A的不同版本;在网格内部,服务A的版本1可能会访问服务B的两个版本,服务A的版本2则只会访问服务B的版本1;服务B的版本1需要访问外部的云服务,版本2则无此需求。
在这个简化的模型中,包含了以下诉求:
◎ 网格内部的调用(服务A→服务B);
◎ 出站连接(服务B→外部云服务);
◎ 入站连接(用户→服务A);
◎ 流量分割(A服务跟B服务只负责与自己相关流量请求);
◎ 按调用方的服务版本进行路由(服务A的版本1分别调用了服务B的版本1和版本2);
◎ 按用户身份进行路由。
这里除了这些问题,还存在一些潜在需求,如下所述。
(1)在网格内部的服务之间如何根据实际需要对服务间调用进行路由,条件可能包括:
◎ 调用的源和目的服务;
◎ 调用内容;
◎ 认证身份。
(2)如何应对网络故障或者服务故障。
(3)如何处理不同服务不同版本之间的关系。
(4)怎样对出站连接进行控制。
(5)怎样接收入站连接来启动后续的整个服务链条。
这些当然不是问题的全部,其中,与流量相关的问题还引发了几个关键的功能需求,如下所述。
(1)服务注册和发现:要求能够对网格中不同的服务和不同的版本进行准确标识,不同的服务可以经由同一注册机构使用公认的方式互相查找。
(2)负载均衡策略:不同类型的服务应该由不同的策略来满足不同的需要。
(3)服务流量特征:在服务注册发现的基础之上,根据调用双方的服务身份,以及服务流量特征来对调用过程进行甄别。
(4)动态流量分配:根据对流量特征的识别,在不同的服务和版本之间对流量进行引导。
连接是服务网格应用过程中从无到有的最重要的一个环节。
安全
安全也是一个常谈常新的话题,在过去私有基础设施结合单体应用的环境下,这一问题并不突出,然而进入容器云时代之后,以下问题出现了。
(1)有大量容器漂浮在容器云中,采用传统的网络策略应对这种浮动的应用是比较吃力的。
(2)在由不同的语言、平台所实现的微服务之间,实施一致的访问控制也经常会因为实现的不一致而困难重重。
(3)如果是共享集群,则服务的认证和加密变得尤为重要,例如:
◎ 服务之间的通信要防止被其他服务监听;
◎ 只有提供有效身份的客户端才可以访问指定的服务;
◎ 服务之间的相互访问应该提供更细粒度的控制功能。
总之,要提供网格内部的安全保障,就应具备服务通信加密、服务身份认证和服务访问控制(授权和鉴权)功能。
上述功能通常需要数字证书的支持,这就隐藏了对CA的需求,即需要完成证书的签发、传播和更新业务。
除了上述核心要求,还存在对认证失败的处理、外部证书(统一 CA)的接入等相关支撑内容。
策略

Istio 通过可动态插拔、可扩展的策略实现访问控制、速率限制、配额管理等功能使得资源在消费者之间公平分配
在Istio中使用Mixer作为策略的执行者,Envoy的每次调用,在逻辑上都会通过Mixer进行事先预检和事后报告,这样Mixer就拥有了对流量的部分控制能力;在Istio中还有为数众多的内部适配器及进程外适配器,可以和外部软件设施一起完成策略的制定和执行。
组件简单介绍,后面会详细讲解
Mixer: Mixer 在整个服务网格中执行访问控制和策略执行,并从 Envoy 代理和其他服务收集遥测数据。
Envoy: 在istio框架中使用Envoy作为代理,使用的是C++开发的软件,用于为服务网格中的所有服务提供所有的入站和出站流量,唯一一个与数据平面打交道的
观察
随着服务数量的增加,监控和跟踪需求自然水涨船高。在很多情况下,可观察的保障都是系统功能的重要组成部分,是系统运维功能的重要保障。 随着廉价服务器(相对于传统小型机)的数量越来越多,服务器发生故障的频率也越来越高,人们开始产生争论:我们应该将服务器视为家畜还是宠物?家畜的特点:是有功能、无个性、可替换;而宠物的特点:是有功能、有个性、难替换。 我们越来越倾向于将服务器视为无个性、可替换的基础设施,如果主机发生故障,那么直接将其替换即可;并且,我们更加关注的是服务的总体质量。因此,微服务系统监控,除了有传统的主机监控,还更加重视高层次的服务健康监控。 服务的健康情况往往不是非黑即白的离散值,而是一系列连续状态,例如我们经常需要关注服务的调用成功率、响应时间、调用量、传输量等表现。 而且,面对数量众多的服务,我们应该能对各种级别和层次的指标进行采样、采集及汇总,获取较为精密、翔实的运行数据,最终通过一定的方法进行归纳总结和展示。 与此同时,服务网格还应提供分布式跟踪功能,对服务的调用链路进行跟踪。
观察性:动态获取服务运行数据和输出,提供强大的调用链、监控和调用日志收集输出的能力。配合可视化工具,可方便运维人员了解服务的运行状况,发现并解决问题。
1.2 Istio与服务治理
Istio是一个服务治理平台,治理的是服务间的访问,只要有访问就可以治理,不在乎这个服务是不是是所谓的微服务,也不要求跑的代码是微服务化的。单体应用不满足微服务用Istio治理也是完全可以的。提起“服务治理”,大家最先想到的一定是“微服务的服务治理”,就让我们从微服务的服务治理说起。
服务治理的三种形态
服务治理的演变至少经过了以下三种形态。
第1种形态:在应用程序中包含治理逻辑
在微服务化的过程中,将服务拆分后会发现一堆麻烦事儿,连基本的业务连通都成了问题。在处理一些治理逻辑,比如怎么找到对端的服务实例,怎么选择一个对端实例发出请求,都需要自己写代码来实现。这种方式简单,对外部依赖少,但会导致存在大量的重复代码。所以,微服务越多,重复的代码越多,维护越难;而且,业务代码和治理逻辑耦合,不管是对治理逻辑的全局升级,还是对业务的升级,都要改同一段代码。
如下图所示

第2种形态:治理逻辑独立的代码
在解决第1种形态的问题时,我们很容易想到把治理的公共逻辑抽象成一个公共库,让所有微服务都使用这个公共库。在将这些治理能力包含在开发框架中后,只要是用这种开发框架开发的代码,就包含这种能力,非常典型的这种服务治理框架就是Spring Cloud。这种形态的治理工具在过去一段时间里得到了非常广泛的应用。
SDK模式虽然在代码上解耦了业务和治理逻辑,但业务代码和 SDK还是要一起编译的,业务代码和治理逻辑还在一个进程内。这就导致几个问题:业务代码必须和 SDK 基于同一种语言,即语言绑定。例如,Spring Cloud等大部分治理框架都基于Java,因此也只适用于 Java 语言开发的服务。经常有客户抱怨自己基于其他语言编写的服务没有对应的治理框架;在治理逻辑升级时,还需要用户的整个服务升级,即使业务逻辑没有改变,这对用户来说是非常不方便的。
如下图所示

第3种形态:治理逻辑独立的进程
SDK模式仍旧侵入了用户的代码,那就再解耦一层,把治理逻辑彻底从用户的业务代码中剥离出来,这就是前面提过的Sidecar模式。显然,在这种形态下,用户的业务代码和治理逻辑都以独立的进程存在,两者的代码和运行都无耦合,这样可以做到与开发语言无关,升级也相互独立。在对已存在的系统进行微服务治理时,只需搭配 Sidecar 即可,对原服务无须做任何修改,并且可以对老系统渐进式升级改造,先对部分服务进行微服务化。
如下图所示

总结
比较以上三种服务治理形态,我们可以看到服务治理组件的位置在持续下沉,对应用的侵入逐渐减少。
微服务作为一种架构风格,更是一种敏捷的软件工程实践,说到底是一套方法论;与之对应的 Istio 等服务网格则是一种完整的实践,Istio 更是一款设计良好的具有较好集成及可扩展能力的可落地的服务治理工具和平台。
所以,微服务是一套理论,Istio是一种实践。
1.3 Istio与Kubernetes
Kubernetes介绍
Kubernetes是一款用于管理容器化工作的负载和服务的可移植、可扩展的开源平台,拥有庞大、快速发展的生态系统,它面向基础设施,将计算、网络、存储等资源进行紧密整合,为容器提供最佳运行环境,并面向应用提供封装好的、易用的工作负载与服务编排接口,以及运维所需的资源规格、弹性、运行参数、调度等配置管理接口,是新一代的云原生基础设施平台。
从平台架构而言,Kubernetes的设计围绕平台化理念,强调插件化设计与易扩展性,这是它与其他同类系统的最大区别之一,保障了对各种不同客户应用场景的普遍适应性。另外,Kubernetes与其他容器编排系统的显著区别是Kubernetes并不把无状态化、微服务化等条件作为可运行的工作负载的约束。
如今,容器技术已经进入产业落地期,而Kubernetes作为容器平台的标准已经得到了广泛应用。

Istio是Kubernetes的好帮手
从场景来看,Kubernetes已经提供了非常强大的应用负载的部署、升级、扩容等运行管理能力。Kubernetes 中的 Service 机制也已经可以做服务注册、服务发现和负载均衡,支持通过服务名访问到服务实例。
从微服务的工具集观点来看,Kubernetes本身是支持微服务的架构,在Pod中部署微服务很合适,也已经解决了微服务的互访互通问题,但对服务间访问的管理如服务的熔断、限流、动态路由、调用链追踪等都不在Kubernetes的能力范围内。那么,如何提供一套从底层的负载部署运行到上层的服务访问治理端到端的解决方案?
目前,最完美的答案就是在Kubernetes上叠加Istio这个好帮手

Kubernetes是Istio的好基座
Istio最大化地利用了Kubernetes这个基础设施,与之叠加在一起形成了一个更强大的用于进行服务运行和治理的基础设施,充分利用了Kubernetes的优点实现Istio的功能,例如:
1.数据面
数据面Sidecar运行在Kubernetes的Pod里,作为一个Proxy和业务容器部署在一起。在服务网格的定义中要求应用程序在运行的时感知不到Sidecar的存在。而基于Kubernetes的一个 Pod 多个容器的优秀设计使得部署运维
对用户透明,用户甚至感知不到部署 Sidecar的过程。用户还是用原有的方式创建负载,通过 Istio 的自动注入服务,可以自动给指定的负载注入Proxy。如果在另一种环境下部署和使用Proxy,则不会有这样的便利。

2.统一服务发现 Istio的服务发现机制非常完美地基于Kubernetes的域名访问机制构建而成,省去了再搭一个类似 Eureka 的注册中心的麻烦,更避免了在 Kubernetes 上运行时服务发现数据不一致的问题。

3.基于Kubernetes CRD描述规则 Istio的所有路由规则和控制策略都是通过 Kubernetes CRD实现的,因此各种规则策略对应的数据也被存储在 Kube-apiserver 中,不需要另外一个单独的 APIServer 和后端的配置管理。所以,可以说Istio的APIServer就是Kubernetes的APIServer,数据也自然地被存在了对应Kubernetes的etcd中。
Istio非常巧妙地应用了Kubernetes这个好基座,基于Kubernetes的已有能力来构建自身功能。Kubernetes里已经有的,绝不再自己搞一套,避免了数据不一致和用户使用体验的问题。
Istio和Kubernetes架构的关系,可以看出,Istio不仅数据面Envoy跑在Kubernetes的Pod里,其控制面也运行在Kubernetes集群中,其控制面组件本身存在的形式也是以Kubernetes Deployment和Service,基于Kubernetes扩展和构建。
回顾一下上面提到的K8S组件
- APIServer
API Server提供了k8s各类资源对象(pod,RC,Service等)的增删改查及watch等HTTP Rest接口,是整个系统的数据总线和数据中心。
kubernetes API Server的功能:
-提供了集群管理的REST API接口(包括认证授权、数据校验以及集群状态变更);
-提供其他模块之间的数据交互和通信的枢纽(其他模块通过API Server查询或修改数据,只有API Server才直接操作etcd);
-是资源配额控制的入口;
-拥有完备的集群安全机制.
- Deployment
一旦运行了 Kubernetes 集群,就可以在其上部署容器化应用程序。 为此,需要创建 Kubernetes Deployment 配置。
Deployment 负责 Kubernetes 如何创建和更新应用程序的实例。
- Service
Service可以看作是一组提供相同服务的Pod对外的访问接口。借助Service,应用可以方便地实现服务发现和负载均衡
- Ingress
ingress是Kubernetes资源的一种,可以让外部请求访问到k8s内部的资源上
总结
Kubernetes在容器编排领域已经成为无可争辩的事实标准;微服务化的服务与容器在轻量、敏捷、快速部署运维等特征上匹配,这类服务在容器中的运行也正日益流行;随着Istio 的成熟和服务网格技术的流行,使用 Istio 进行服务治理的实践也越来越多,正成为服务治理的趋势;而 Istio 与 Kubernetes 的天然融合且基于 Kubernetes 构建,也补齐了Kubernetes的治理能力,提供了端到端的服务运行治理平台。这都使得Istio、微服务、容器及Kubernetes形成一个完美的闭环。
云原生应用采用 Kubernetes 构建应用编排能力,采用 Istio 构建服务治理能力,将逐渐成为企业技术转型的标准配置。
1.4Istio与服务网格
时代选择服务网格
在云原生时代,随着采用各种语言开发的服务剧增,应用间的访问拓扑更加复杂,治理需求也越来越多。原来的那种嵌入在应用中的治理功能无论是从形态、动态性还是可扩展性来说都不能满足需求,迫切需要一种具备云原生动态、弹性特点的应用治理基础设施。

采用Sidecar代理与应用进程的解耦带来的是应用完全无侵入、也屏蔽了开发语言无关等特点解除了开发语言的约束,从而极大降低了应用开发者的开发成本。
这种方式也经常被称为一种应用的基础设施层,类比TCP/IP网络协议栈,应用程序像使用TCP/IP一样使用这个通用代理:TCP/IP 负责将字节码可靠地在网络节点之间传递,Sidecar 则负责将请求可靠地在服务间进行传递。TCP/IP 面向的是底层的数据流,Sidecar 则可以支持多种高级协议(HTTP、gRPC、HTTPS 等),以及对服务运行时进行高级控制,使服务变得可监控、可管理。
然后,从全局来看,在多个服务间有复杂的互相访问时才有服务治理的需求。即我们关注的是这些 Sidecar 组成的网格,对网格内的服务间访问进行管理,应用还是按照本来的方式进行互相访问,每个应用程序的入口流量和出口流量都要经过Sidecar代理,并在Sidecar上执行治理动作。
最后,Sidecar是网格动作的执行体,全局的管理规则和网格内的元数据维护需要通过一个统一的控制面实现。 Sidecar拦截入口流量,执行治理动作。这就引入两个问题: ◎ 增加了两处延迟和可能的故障点; ◎ 多出来的这两跳对于访问性能、整体可靠性及整个系统的复杂度都带来了新的挑战。

所以,对于考虑使用服务网格的用户来说,事情就会变成一个更简单的选择题:是否愿意花费额外的资源在这些基础设施上来换取开发、运维的灵活性、业务的非侵入性和扩展性等便利。
目前,华为、谷歌、亚马逊等云服务厂商将这种服务以云服务形态提供了出来,并和底层的基础设施相结合,提供了完整的服务治理解决方案。这对于广大应用开发者来说,更加方便和友好。
服务网格选择Istio
在多种服务网格项目和产品中,最引人注目的是后来居上的 Istio,它有希望成为继Kubernetes之后的又一款
重量级产品。
Istio 解决了生产大规模集群的性能、资源利用率和可靠性问题,提供了众多生产中实际应用的新特性,已经达到企业级可用的标准。
首先,在控制面上,Istio作为一种全新的设计,在功能、形态、架构和扩展性上提供了远超服务网格的能力范围。它提供了一套标准的控制面规范,向数据面传递服务信息和治理规则。
Istio使用Envoy V2版本的API,即gRPC协议。标准的控制面API解耦了控制面和数据面的绑定。
最后,在大厂的支持上,Istio 由谷歌和 IBM 共同推出,从应用场景的分析规划到本身的定位,从自身架构的设计到与周边生态的结合,都有着比较严密的论证。Istio项目在发起时已经确认了将云原生生态系统中的容器作为核心,将Kubernetes作为管理容器的编排系统,需要一个系统管理在容器平台上运行的服务之间的交互,包括控制访问、安全、运行数据收集等,而 Istio 正是为此而生的;另外,Istio 成为架构的默认部分,就像容器和Kubernetes已经成为云原生架构的默认部分一样。
云原生社区的定位与多个云厂商的规划也不谋而合。华为云已经在 2018 年 8 月率先在其容器服务CCE(Cloud Container Engine)中内置Istio;Google的GKE也在2018年12月宣布内置 Istio;越来越多的云厂商也已经选择将 Istio作为其容器平台的一部分提供给用户,即提供一套开箱即用的容器应用运行治理的全栈服务。正因为看到了 Istio 在技术和产品上的巨大潜力,各大厂商在社区的投入也在不断加大,其中包括Google、IBM、华为、思科、红帽等主流厂商。
istio原理图

总结 时代选择服务网格是因为架构的发展 服务网格选择istio是因为提供一套开箱即用的容器应用运行治理的全栈服务
二、使用示例
2.1 初步感受istio
在docker中是通过container来部署业务的,在k8s里面是通过pod来部署业务的,那么在istio里面如何体现sidecar呢?
猜想:会不会在pod中除了业务需要的container之外还会有一个sidecar的container存在呢?
验证猜想
(1)准备一个资源 first-istio.yaml
apiVersion: apps/v1 ## 定义了一个版本
kind: Deployment ##资源类型是Deployment
metadata:
name: first-istio
spec:
selector:
matchLabels:
app: first-istio
replicas: 1
template:
metadata:
labels:
app: first-istio
spec:
containers:
- name: first-istio ##容器名字 下面容器的镜像
image: registry.cn-hangzhou.aliyuncs.com/sixupiaofei/spring-docker-demo:1.0
ports:
- containerPort: 8080 ##容器的端口
---apiVersion: v1
kind: Service ##资源类型是Service
metadata:
name: first-istio ##资源名字first-istio
spec:
ports:
- port: 80 ##对外暴露80
protocol: TCP ##tcp协议
targetPort: 8080 ##重定向到8080端口
selector:
app: first-istio ##匹配合适的label,也就是找到合适pod
type: ClusterIP ## Service类型ClusterIP创建文件夹istio,然后把first-istio放进去,按照正常的创建流程里面只会有自己私有的containers,不会有sidecar
#执行,会发现 只会有一个containers在运行
kubectl apply -f first-istio.yaml
#查看first-isitio service
kubectl get svc
# 查看pod的具体的日志信息命令
<NolebasePageProperties />
kubectl describe pod first-istio-8655f4dcc6-dpkzh
#删除
kubectl delete -f first-istio.yaml查看pod命令
kubectl get pods

思考:怎么让pod里面自动增加一个Sidecar呢?
有两种方式:手动注入和自动注入
手动注入
(1)删除上述资源,重新创建,使用手动注入sidecar的方式
istioctl kube-inject -f first-istio.yaml | kubectl apply -f -**注意:**istioctl 命令需要先在/etc/profile配置PATH
vim /etc/profile增加isito安装目录配置
export ISTIO_HOME=/home/tools/istio-1.0.6export PATH=ISTIO_HOME/bin`
- 加载profile文件
source profile
(2)查看pod数量
kubectl get pods # 注意该pod中容器的数量 ,会发现容器的数量不同了,变成了2个(3) 查看service
kubectl get svc思考:
我的yaml文件里面只有一个container,执行完之后为什么会是两个呢?
我的猜想另外一个会不会是Sidecar,那么我描述一下这个pod,看看这两个容器到底是什么
# 查看pod执行明细
kubectl describe pod first-istio-75d4dfcbff-qhmxj
发现竟然除了我的容器之外还多了一个代理容器,此时我们大胆猜想这个代理会不会就是sidecar呢
接着往上翻

此时已经看到了我们需要的答案了
查看yaml文件内容
kubectl get pod first-istio-75d4dfcbff-qhmxj -o yaml
总结
这个yaml文件已经不是我们原来的yaml文件了,会发现这个yaml文件还定义了一个proxy的image,这个image是我们提前就已经准备好了的,所以istio是通过改变yaml文件来实现注入一个代理
(4)删除资源
istioctl kube-inject -f first-istio.yaml | kubectl delete -f -**思考:**难道我以后每次都要写那么一大串命令创建sidecar吗,有没有正常的命令来直接创建sidecar呢?
自动注入sidecar
首先自动注入是需要跟命名空间挂钩,所以需要创建一个命名空间,只需要给命名空间开启自动注入,后面创建的资源只要挂载到这个命名空间下,那么这个命名空间下的所有的资源都会自动注入sidecar了
(1)创建命名空间
kubectl create namespace my-istio-ns(2)给命名空间开启自动注入
kubectl label namespace my-istio-ns istio-injection=enabled(3)创建资源,指定命名空间即可
# 查询 istio-demo命名空间下面是否存在资源
kubectl get pods -n my-istio-ns
# 在istio-demo命名空间创建资源
kubectl apply -f first-istio.yaml -n my-istio-ns(4)查看资源
kubectl get pods -n my-istio-ns(5)查看资源明细
kubectl describe pod pod-name -n my-istio-ns
发现除了我的容器之外依旧多了一个代理容器
(6)查看service
kubectl get svc -n my-istio-ns(7)删除资源
kubectl delete -f first-istio.yaml -n my-istio-ns大家应该都已经明白了istio怎么注入sidecar的了吧
sidecar注入总结:
不管是自动注入还是手动注入原理都是在yaml文件里面追加一个代理容器,这个代理容器就是sidecar,这里更推荐自动注入的方式来实现 sidecar 的注入
三、核心知识
3.1 架构
Istio的架构,分为控制平面和数据面平两部分。
数据平面:由一组智能代理([Envoy])组成,被部署为 sidecar。这些代理通过一个通用的策略和遥测中心传递和控制微服务之间的所有网络通信。
控制平面:管理并配置代理来进行流量路由。此外,控制平面配置 Mixer 来执行策略和收集遥测数据。
下图展示了组成每个平面的不同组件:

架构图可以看到,主要分为两个平面,控制面主要包括Istio的一些组件,例如:Pilot、Mixer、Citadel等服务组件;数据面由伴随每个应用程序部署的代理程序Envoy组成,执行针对应用程序的治理逻辑。为了避免静态、刻板地描述组件,在介绍组件的功能前,我们先通过一个动态场景来了解图架构图中对象的工作机制,即观察前端服务对后台服务进行一次访问时,在 Istio 内部都发生了什么,以及 Istio 的各个组件是怎样参与其中的,分别做了哪些事情。
先简单理解
Pilot:提供服务发现功能和路由规则
Mixer:策略控制,比如:服务调用速率限制
Citadel:起到安全作用,比如:服务跟服务通信的加密
Sidecar/Envoy: 代理,处理服务的流量
架构图上带圆圈的数字代表在数据面上执行的若干重要动作。虽然从时序上来讲,控制面的配置在前,数据面执行在后,但为了便于理解,在下面介绍这些动作时以数据面上的数据流为入口,介绍数据面的功能,然后讲解涉及的控制面如何提供对应的支持,进而理解控制面上组件的对应功能。
(1)自动注入:(由架构图得知前端服务跟后端服务都有envoy,我们这里以前端服务envoy为例说明)指在创建应用程序时自动注入 Sidecar代理。那什么情况下会自动注入你?在 Kubernetes场景下创建 Pod时,Kube-apiserver调用管理平面组件的 Sidecar-Injector服务,然后会自动修改应用程序的描述信息并注入Sidecar。在真正创建Pod时,在创建业务容器的同时在Pod中创建Sidecar容器。
# 原始的yaml文件
apiVersion: apps/v1
kind: Deployment
spec:
containers:
- name: nginx
image: nginx
...省略调用Sidecar-Injector服务之后,yaml文件会发生改变
# 原始的yaml文件
apiVersion: apps/v1
kind: Deployment
spec:
containers:
- name: nginx
image: nginx
...省略# 增加一个容器image地址
containers:
- name: sidecar
image: sidecar
...省略总结:会在pod里面自动生产一个代理,业务服务无感知
(2)流量拦截:在 Pod 初始化时设置 iptables 规则,当有流量到来时,基于配置的iptables规则拦截业务容器的入口流量和出口流量到Sidecar上。但是我们的应用程序感知不到Sidecar的存在,还以原本的方式进行互相访问。在架构图中,流出前端服务的流量会被 前端服务侧的 Envoy拦截,而当流量到达后台服务时,入口流量被后台服务V1/V2版本的Envoy拦截。

总结:每个pod中都会有一个代理来来拦截所有的服务流量(不管是入口流量还是出口流量)
(3)服务发现:前端服务怎么知道后端服务的服务信息呢?这个时候就需要服务发现了,所以服务发起方的 Envoy 调用控制面组件 Pilot 的服务发现接口获取目标服务的实例列表。在架构图中,前端服务内的 Envoy 通过 控制平面Pilot 的服务发现接口得到后台服务各个实例的地址,为访问做准备。
总结:Pilot提供了服务发现功能,调用方需要到Pilot组件获取提供者服务信息
(4)负载均衡:数据面的各个Envoy从Pilot中获取后台服务的负载均衡衡配置,并执行负载均衡动作,服务发起方的Envoy(前端服务envoy)根据配置的负载均衡策略选择服务实例,并连接对应的实例地址。
总结:Pilot也提供了负载均衡功能,调用方根据配置的负载均衡策略选择服务实例
(5)流量治理:Envoy 从 Pilot 中获取配置的流量规则,在拦截到 入口 流量和出口 流量时执行治理逻辑。比如说,在架构图中,前端服务的 Envoy 从 Pilot 中获取流量治理规则,并根据该流量治理规则将不同特征的流量分发到后台服务的v1或v2版本。当然,这只是Istio流量治理的一个场景,Istio支持更丰富的流量治理能力。

总结:Pilot也提供了路由转发规则
(6)访问安全:在服务间访问时通过双方的Envoy进行双向认证和通道加密,并基于服务的身份进行授权管理。在架构图中,Pilot下发安全相关配置,在前端模块服务和后端服务的Envoy上自动加载证书和密钥来实现双向认证,其中的证书和密钥由另一个控制平面组件Citadel维护。
总结:Citadel维护了服务代理通信需要的证书和密钥
(7)服务遥测:在服务间通信时,通信双方的Envoy都会连接控制平面组件Mixer上报访问数据,并通过Mixer将数据转发给对应的监控后端。比如说,在架构图中,前端模块服务对后端服务的访问监控指标、日志和调用链都可以通过Mixer收集到对应的监控后端。

总结:Mixer组件可以收集各个服务上的日志,从而可以进行监控
(8)策略执行:在进行服务访问时,通过Mixer连接后端服务来控制服务间的访问,判断对访问是放行还是拒绝。在架构图中,数据面在转发服务的请求前调用Mixer接口检查是否允许访问,Mixer 会做对应检查,给代理(Envoy)返回允许访问还是拒绝, 比如:可以对前端模块服务到后台服务的访问进行速率控制。

总结:Mixer组件可以对服务速率进行控制(也就是限流)
(9)外部访问:在架构图中,外部服务通过Gateway访问入口将流量转发到服务前端服务内的Envoy组件,对前端服务的负载均衡和一些流量治理策略都在这个Gateway上执行。
**总结:**这里总结在以上过程中涉及的动作和动作主体,可以将其中的每个过程都抽象成一句话:服务调用双方的Envoy代理拦截流量,并根据控制平面的相关配置执行相应的服务治理动作,这也是Istio的数据平面和控制平面的配合方式。
3.2 组件
Pilot
Pilot在Istio架构中必须要有
思考: 为什么Envoy能够服务发现?并且Envoy为什么可以流量控制?
就是因为Pilot存在
什么是Pilot
Pilot类似传统C/S架构中的服务端Master,下发指令控制客户端完成业务功能。和传统的微服务架构对比,Pilot 至少涵盖服务注册中心和向数据平面下发规则 等管理组件的功能。
服务注册中心
如图下图所示,Pilot 为 Envoy sidecar 提供服务发现、用于智能路由的流量管理功能(例如,A/B 测试、金丝雀发布等)以及弹性功能(超时、重试、熔断器等)。
Pilot本身不做服务注册,它会提供一个API接口,对接已有的服务注册系统,比如Eureka,Etcd等。
说白了,Pilot可以看成它是Sidecar的一个领导

(1)Platform Adapter是Pilot抽象模型的实现版本,用于对接外部的不同平台 (2)Polit定了一个抽象模型(Abstract model),处理Platform Adapter对接外部不同的平台, 从特定平台细节中解耦 (3)Envoy API负责和Envoy的通讯,主要是发送服务发现信息和流量控制规则给Envoy
流程总结: service服务C会注册到Pilot注册中心平台适配器(Platform Adapter)模块上(假如对接的是Eureka, 那么service服务C会注册到Eureka里面),然后抽象模型(Abstract model)进行平台细节的解耦并且用于处理Platform Adapter对接外部的不同平台,最后通过Envoy API负责和Envoy的通讯,主要是发送服务发现信息和流量控制规则给Envoy
数据平面下发规则
Pilot 更重要的一个功能是向数据平面下发规则,Pilot 负责将各种规则转换换成 Envoy 可识别的格式,通过标准的 协议发送给 Envoy,指导Envoy完成动作。在通信上,Envoy通过gRPC流式订阅Pilot的配置资源。
Pilot将表达的路由规则分发到 Evnoy上,Envoy根据该路由规则进行流量转发,配置规则和流程图如下所示。
规则如下:
# http请求
http:
-match: # 匹配
-header: # 头部
cookie:
# 以下cookie中包含group=dev则流量转发到v2版本中
exact: "group=dev"
route: # 路由
-destination:
name: v2
-route:
-destination:
name: v1
Mixer
Mixer在Istio架构中不是必须的
Mixer分为Policy和Telemetry两个子模块,Policy用于向Envoy提供准入策略控制,黑白名单控制,速率限制等相关策略;Telemetry为Envoy提供了数据上报和日志搜集服务,以用于监控告警和日志查询。
Telemetry介绍
Mixer是一个平台无关的组件。Mixer的Telemetry 在整个服务网格中执行访问控制和策略使用,并从 Envoy 代理和其他服务收集遥测数据,流程如下图所示。

- 遥测报告上报,比如从Envoy中收集数据[请求数据、使用时间、使用的协议等],通过Adapater上
报给Promethues、Heapster等
说白了,就是数据收集,然后通过adapter上传到监控容器里面
policy介绍
policy是另外一个Mixer服务,和istio-telemetry基本上是完全相同的机制和流程。数据面在转发服务的请求前调用istio-policy的Check接口是否允许访问,Mixer 根据配置将请求转发到对应的 Adapter 做对应检查,给代理返回允许访问还是拒绝。可以对接如配额、授权、黑白名单等不同的控制后端,对服务间的访问进行可扩展的控制。

- 策略控制:检查请求释放可以运行访问
Citadel
Citadel在Istio架构中不是必须的
Istio的认证授权机制主要是由Citadel完成,同时需要和其它组件一起配合,参与到其中的组件还有Pilot、Envoy、Mixer,它们四者在整个流程中的作用分别为:
Citadel:用于负责密钥和证书的管理,在创建服务时会将密钥及证书下发至对应的Envoy代理中;
Pilot: 用于接收用户定义的安全策略并将其整理下发至服务旁的Envoy代理中;
Envoy:用于存储Citadel下发的密钥和证书,保障服务间的数据传输安全;
Mixer: 负责核心功能为前置条件检查和遥测报告上报;
流程如下

具体工作流程可描述如下:
Kubernetes某集群节点新部署了服务Service,此时集群中有两个Pod被启动,每个Pod由Envoy代理容器和Service容器构成,在启动过程中Istio的Citadel组件会将密钥及证书依次下发至每个Pod中的Envoy代理容器中,以保证后续服务A,B之间的安全通信。
用户通过Rules API下发安全策略至Pilot组件,Pilot组件通过Pilot-discovery进程整理安全策略中Kubernetes服务注册和配置信息并以Envoy API方式暴露给Envoy。
Pod 中的Envoy代理会通过Envoy API方式定时去Pilot拉取安全策略配置信息,并将信息保存至Envoy代理容器中。
当pod内的服务相互调用时,会调用各自Envoy容器中的证书及密钥实现服务间的通信,同时Envoy容器还会根据用户下发的安全策略进行更细粒度的访问控制。
Mixer在整个工作流中核心功能为前置条件检查和遥测报告上报,在每次请求进出服务时,服务中的Envoy代理会向Mixer发送check请求,检查是否满足一些前提条件,比如ACL检查,白名单检查,日志检查等,如果前置条件检查通过,处理完后再通过Envoy向Mixer上报日志,监控等数据,从而完成审计工作。
使用场景:
在有一些场景中,对于安全要求是非常高的,比如支付,所以Citadel就是用来保证安全的。
回顾kubernetes API Server的功能:
- 提供了集群管理的REST API接口(包括认证授权、数据校验以及集群状态变更);
- 提供其他模块之间的数据交互和通信的枢纽(其他模块通过API Server查询或修改数据,只有API Server才直接操作etcd);
- 资源配额控制的入口;
- 拥有完备的集群安全机制.
总结:
用于负责密钥和证书的管理,在创建服务时会将密钥及证书下发至对应的Envoy代理中
Galley
Galley在istio架构中不是必须的
Galley在控制面上向其他组件提供支持。Galley作为负责配置管理的组件,并将这些配置信息提供给管理面的 Pilot和 Mixer服务使用,这样其他管理面组件只用和 Galley打交道,从而与底层平台解耦。

galley优点
配置统一管理,配置问题统一由galley负责
如果是相关的配置,可以增加复用
配置跟配置是相互隔离而且,而且配置也是权限控制,比如组件只能访问自己的私有配置
MCP协议
Galley负责控制平面的配置分发主要依托于一一种协议,这个协议叫(MCP)
MCP提供了一套配置订阅和分发的API,里面会包含这个几个角色:
source: 配置的提供端,在istio中Galley即是source 说白了就是Galley组件,它提供yaml配置
sink:配置的消费端,istio组件中Pilot和Mixer都属于sink
resource: source和sink关注的资源体,也就是yaml配置

Galley 代表其他的 Istio 控制平面组件,用来验证用户编写的 Istio API 配置。Galley 接管 Istio 获取配置、 处理和分配组件的顶级责任。它将负责将其他的 Istio 组件与从底层平台(例如 Kubernetes)获取用户配置的细节中隔离开来。
说白了:这样其他控制平面(Pilot和 Mixer)面组件只用和 Galley打交道,从而与底层平台解耦。
Sidecar-injector
Sidecar-injector 是负责自动注入的组件,只要开启了自动注入,那么在创建pod的时候就会自动调用Sidecar-injector 服务
配置参数:istio-injection=enabled,我们后面会有案例演示
在istio中sidecar注入有两种方式
需要使用istioctl命令手动注入 (不需要配置参数:istio-injection=enabled)
基于kubernetes自动注入(配置参数:istio-injection=enabled)
手动注入和自动注入会在istio安装之后案例演示
两种区别:
手动注入需要每次在执行配置都需要加上istioctl命令
自动注入只需要做一下开启参数即可
sidecar模式具有以下优势
把业务逻辑无关的功能抽取出来(比如通信),可以降低业务代码的复杂度
sidecar可以独立升级、部署,与业务代码解耦
注入流程
在 Kubernetes环境下,根据自动注入配置,Kube-apiserver在拦截到 Pod创建的请求时,会调用自动注入服务 istio-sidecar-injector 生成 Sidecar 容器的描述并将其插入原 Pod的定义中,这样,在创建的 Pod 内, 除了包括业务容器,还包括 Sidecar容器。这个注入过程对用户透明,用户使用原方式创建工作负载。

总结:sidecar模式具有以下优势
把业务逻辑无关的功能抽取出来(比如通信),可以降低业务代码的复杂度
sidecar可以独立升级、部署,与业务代码解耦
Proxy(Envoy)
Proxy是Istio数据平面的轻量代理。
Envoy是用C++开发的非常有影响力的轻量级高性能开源服务代理。作为服务网格的数据面,Envoy提供了动态服务发现、负载均衡、TLS、HTTP/2 及 gRPC代理、熔断器、健康检查、流量拆分、灰度发布、故障注入等功能。
Envoy 代理是唯一与数据平面流量交互的 Istio 组件。
Envoy组件解析
为了便于理解Istio中Envoy与服务的关系,如图所示:

一个pod里面运行了一个Envoy容器和service A容器,而Envoy容器内部包含了两个进程,分别是Pilot-agent和Envoy两个进程
pilot-agent
pilot-agent跟Envoy打包在同一个docker镜像里面
- pilot-agent作用
- 生成envoy配置
- 启动envoy
- 监控envoy的运行状态,比如envoy出错是pilot-agent负责重启envoy,huozhe envoy配置变更之后reload envoy
Envoy
负责拦截pod流量,负责从控制平面pilot组件获取配置和服务发现,上报数据给mixer组件
Ingressgateway
ingressgateway 就是入口处的 Gateway,从网格外访问网格内的服务就是通过这个Gateway进行的。ingressgateway比较特别,是一个Loadbalancer类型的Service,不同于其他服务组件只有一两个端口,ingressgateway 开放了一组端口,这些就是网格内服务的外部访问端口。
网格入口网关ingressgateway和网格内的 Sidecar是同样的执行体,也和网格内的其他 Sidecar一样从 Pilot处接收流量规则并执行。因为入口处的流量都走这个服务。
流程图如下

由于gateway暴露了一个端口,外部的请求就可以根据这个端口把请求发给gateway了然后由gateway把请求分发给网格内部的pod上
其他组件
在Istio集群中一般还安装grafana、Prometheus、Tracing组件,这些组件提供了Istio的调用链、监控等功能,可以选择安装来完成完整的服务监控管理功能。
总结
主要介绍了一些常见的istio组件,其中有一些组件是istio默认就已经使用了,有一些组件我们后面也会来演示。
3.3 Istio监控功能
prometheus和grafana
Prometheus存储服务的监控数据,数据来自于istio组件mixer上报
Grafana开源数据可视化工具,展示Prometheus收集到的监控数据
istio已经默认帮我们把grafana和prometheus已经默认部署好了
(1)执行命令查看istio自带的组件
kubectl get pods -n istio-ns
我们打开istio-demo.yaml文件找到找到prometheus和grafana


其实istio已经默认帮我们安装好了grafana和prometheus,只是对应的Service类型是clusterIP类型,表示集群内部可以访问,如果我们需要能够通过浏览器访问,我们只需要ingress访问规则即可,ingress之前已经介绍过了,这边就不在重复了
- 配置prometheus-ingress.yaml和grafana-ingress.yaml配置文件
prometheus-ingress.yaml
#ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: prometheus-ingress
namespace: istio-system
spec:
rules:
- host: prometheus.istio.qy.com
http:
paths:
- path: /
backend:
serviceName: prometheus
servicePort: 9090
- 执行命令
kubectl apply -f prometheus-ingress.yaml和grafana-ingress.yamlgrafana-ingress.yaml
#ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: grafana-ingress
namespace: istio-system
spec:
rules:
- host: grafana.istio.qy.com
http:
paths:
- path: /
backend:
serviceName: grafana
servicePort: 3000~
- 执行命令
kubectl get ingress -n istio-system配置prometheus访问域名
在hosts文件里面增加ip域名的映射关系
192.168.187.137 prometheus.istio.qy.com配置grafana访问域名
在hosts文件里面增加ip域名的映射关系
192.168.187.137 grafana.istio.qy.com访问prometheus
浏览器输入地址:prometheus.istio.qy.com

访问grafana
- 设置prometheus地址
找到prometheus在k8s里面服务地址
命令
kubectl get svc -o wide -n istio-system选择DataSources

选择settings ,把url改成prometheus即可
3.4 bookinfo
理解什么是bookinfo
这是istio官方给我们提供的案例,Bookinfo 应用中的几个微服务是由不同的语言编写的。 这些服务对 Istio 并无依赖,但是构成了一个有代表性的服务网格的例子:它由多个服务、多个语言构成,并且 reviews 服务具有多个版本。
下图展示了这个应用的端到端架构。

这个案例部署了一个用于演示Istio 特性的应用,该应用由四个单独的微服务构成。 这个应用模仿在线书店的一个分类,显示一本书的信息。 页面上会显示一本书的描述,书籍的细节(ISBN、页数等),以及关于这本书的一些评论。
Bookinfo 应用分为四个单独的微服务:
productpage. 这个微服务会调用details和reviews两个微服务,用来生成页面。details. 这个微服务中包含了书籍的信息。reviews. 这个微服务中包含了书籍相关的评论。它还会调用ratings微服务。ratings. 这个微服务中包含了由书籍评价组成的评级信息。
reviews 微服务有 3 个版本:
v1 版本不会调用
ratings服务。v2 版本会调用
ratings服务,并使用 1 到 4个黑色星形图标来显示评分信息。v3 版本会调用
ratings服务,并使用 1 到 4个红色星形图标来显示评分信息。
大家一定要从spring cloud思维模式里面跳出来,站着服务网格的立场上思考问题,我们是不需要了解服务的业务代码是什么样的,业务的服务只需要交给istio管理即可
所以大家一定要有一颗拥抱变化的心
sidecar自动注入到微服务
所以第一步我们需要给每一个服务配置一个Sidecar,但是配置sidecar我们前面也说过,可以有两种方式实现,一种是手动注入,一种是自动注入,如果自动注入需要与命名空间相关,需要准备一个命名空间
- 查看命名空间:
kubectl get ns这个时候我们需要创建一个命名空间,需要打上一个lable表示只要是在这个lable的命名空间里面的都自动注入
- 创建命名空间命令:
kubectl create namespace bookinfo-ns- 给命名空间加上label命令
kubectl label namespace bookinfo-ns istio-injection=enabled- 查看命名空间下有哪些label命
kubectl get ns bookinfo-ns --show-labels启动bookinfo
进入istio安装目录:/home/tools/istio-1.0.6/samples/bookinfo/platform/kube
找到bookinfo.yaml文件
查看需要的image个数:
cat bookinfo.yaml | grep image:
里面就是bookinfo案例所需要依赖的镜像地址
- 执行命令
kubectl apply -f bookinfo.yaml -n bookinfo-ns- 查看pod情况
kubectl get pods -n bookinfo-ns
# 会发现有两个container,有两个container的原因是因为我们有自动注入,这边有六个服务,其实只要四个服务,有一个服务有三个版本仅此而已
- 查看pod明细
kubectl describe pods pod名字 -n bookinfo-ns
# 例如:kubectl describe pods reviews-v1-fd6c96c74-cmqcx -n bookinfo-ns会发现有两个container,一个是我们自己的container,另外一个是自动注入的代理container
- 检查一下service
kubectl get svc -n bookinfo-ns
可以看到service的类似clusterip类型
- 验证Bookinfo 应用是否正在运行
请在某个 Pod 中用 curl 命令对应用发送请求,例如 ratings
- 执行命令
kubectl exec -it $(kubectl get pod -l app=ratings -n bookinfo-ns -o jsonpath='{.items[0].metadata.name}') -c ratings -n bookinfo-ns -- curl productpage:9080/productpage | grep -o "<title>.*</title>"
看到如图所示表示bookinfo启动成功
命令分析一下
kubectl get pod -l app=ratings -n bookinfo-ns -o jsonpath='{.items[0].metadata.name}';表示的是输出ratings 这个运行时pod的名字
kubectl exec -it $(kubectl get pod -l app=ratings -n bookinfo-ns -o jsonpath='{.items[0].metadata.name}') -c ratings -n bookinfo-ns -- curl productpage:9080/productpage | grep -o "<title>.*</title>":进入到ratings内部,然后发送一个http测试,根据响应结果找到title标签思考:能否通过页面的方法访问bookinfo项目呢
所以下面我们用ingress方式访问bookinfo
通过ingress方式访问
- 找到productpage-service服务的端口,打开bookInfo.yaml文件

需要给productpage暴露的9080端口进行ingress域名绑定
- 新建productpageIngress.yaml
#ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: productpage-ingress
spec:
rules:
- host: productpage.istio.qy.com
http:
paths:
- path: /
backend:
serviceName: productpage
servicePort: 9080- 查询productpage这个pod分布在那台服务上,执行命令
kubectl get pods -o wide -n bookinfo-ns服务发现在w1机器上
- 配置hosts文件
192.168.187.137 productpage.istio.qy.com- 执行命令
kubectl apply -f productpageIngress.yaml -n bookinfo-ns- 访问地址:productpage.istio.qy.com

点击Normal user,查看书籍相关的评论
不停的刷新会有三个Reviews版本一个是不带星的一个是带黑色星的一个是带红星的,跟架构图一样的
通过istio的ingressgateway访问
确定 Ingress 的 IP 和端口
现在 Bookinfo 服务启动并运行中,需要使应用程序可以从外部访问 Kubernetes 集群,例如使用浏览器。可以用Istio Gateway来实现这个目标。
- 为应用程序定义 Ingress 网关
地址:/home/tools/istio-1.0.6/samples/bookinfo/networking有一个bookinfo-gateway.yaml
kubectl apply -f bookinfo-gateway.yaml -n bookinfo-ns- 查看gateway
kubectl get gateway -n bookinfo-ns有了gateway之后我们需要配置一些环境变量
配置gateway ip环境
export INGRESS_HOST=$(kubectl get po -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')
# 把ingressgateway的ip设置成环境变量
# 分析命令意思
kubectl get po -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}'
# 表示获取istio组件ingressgateway组件的ip
也就是说192.168.187.137就是ingressgateway组件的ip
配置gateway端口
export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')把ingressgateway的端口设置成环境变量
分析命令意思
kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}'
# 表示获取istio组件ingressgateway组件的端口设置gateway地址
把前面的host跟端口组成gateway地址
export GATEWAY_URL=$INGRESS_HOST:$INGRESS_PORT查看INGRESS_PORT环境端口
env | grep INGRESS_PORT
测试
http://192.168.187.137:31380/productpage
不停的刷新会有三个Reviews版本一个是不带星的一个是带黑色星的一个是带红星的,跟架构图一样的
流量管理
放开bookinfo自定义路由权限
这个文件也是起到了一个路由的功能,必须先执行这个文件之后gateway路由规则才可以自定义
- 执行destination-rule-all.yaml
kubectl apply -f destination-rule-all.yaml -n bookinfo-ns- 查看
kubectl get DestinationRule -n bookinfo-ns- 打开destination-rule-all.yaml 分析一下
里面定义了各个微服务的路由资源
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule # 声明了一个资源,这个资源也是需要依赖于crd
metadata:
name: productpage
spec:
host: productpage
subsets:
- name: v1
labels:
version: v1 # 版本
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule # 声明了一个资源,这个资源也是需要依赖于crd
metadata:
name: reviews
spec:
host: reviews
subsets:
- name: v1
labels:
version: v1 # 版本
- name: v2
labels:
version: v2 # 版本
- name: v3
labels:
version: v3 # 版本
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: ratings
spec:
host: ratings
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
- name: v2-mysql
labels:
version: v2-mysql
- name: v2-mysql-vm
labels:
version: v2-mysql-vm
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: details
spec:
host: details
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
基于版本方式控制
只需要在/home/tools/istio-1.0.6/samples/bookinfo/networking下执行virtual-service-reviews-v3.yaml即可
- 打开virtual-service-reviews-v3.yaml文件
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v3此时会把所有的路由的流量全部都切换到v3版本也就是全部都是红星的版本
- 执行命令
kubectl apply -f virtual-service-reviews-v3.yaml -n bookinfo-ns- 再次刷新页面
http://192.168.187.137:31380/productpage

- 删除版本控制命令
kubectl delete -f virtual-service-reviews-v3.yaml -n bookinfo-ns再次刷新页面有其它版本了,这是基于版本的方式来控制流量
基于权重的流量版本控制
只需要在/home/tools/istio-1.0.6/samples/bookinfo/networking下执行virtual-service-reviews-50-v3.yaml即可
- 打开virtual-service-reviews-50-v3.yaml文件
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
weight: 50 # 50%的流量到v1
- destination:
host: reviews
subset: v3
weight: 50 # 50%的流量到v3
此时会把所有的路由的流量会在v1和v3之间进行切换,也就是无星和红星页面
- 执行命令
kubectl apply -f virtual-service-reviews-50-v3.yaml -n bookinfo-ns- 再次刷新页面
http://192.168.187.137:31380/productpage
无星页面和红星页面之间切换
- 删除命令
kubectl delete -f virtual-service-reviews-50-v3.yaml -n bookinfo-ns基于用户来控制流量版本
只需要在/home/tools/istio-1.0.6/samples/bookinfo/networking下执行virtual-service-reviews-jason-v2-v3.yaml即可
- 打开virtual-service-reviews-jason-v2-v3.yaml文件
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- match:
- headers:
end-user:
exact: jason
route:
- destination:
host: reviews
subset: v2
- route:
- destination:
host: reviews
subset: v3
在登录的时候会在header头部增加一个jason,如果是jason登录那么会访问v2版本,其它的人访问的是v3
- 执行命令
kubectl apply -f virtual-service-reviews-jason-v2-v3.yaml -n bookinfo-ns- 再次刷新页面
http://192.168.187.137:31380/productpage
全是红星页面,因为我不是jason用户所以流量全都在v3版本
- 删除命令
kubectl delete -f virtual-service-reviews-jason-v2-v3.yaml -n bookinfo-ns故障注入
为了测试微服务应用程序 Bookinfo 的弹性,在访问的的时候会在header头部增加一个jason,如果是jason访问那么会访问v2版本,其它的人访问的是v3。 访问v3版本的人会注入一个50%几率的延迟2S请求访问。
故障注入:可以故意引发Bookinfo 应用程序中的 bug。尽管引入了 2 秒的延迟,我们仍然期望端到端的流程是没有任何错误的。
- 创建故障注入规则-执行:test.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- match:
- headers:
end-user:
exact: jason
route:
- destination:
host: reviews
subset: v2
- fault:
delay:
percent: 50
fixedDelay: 2s
route:
- destination:
host: reviews
subset: v3
- 执行:test.yaml
kubectl apply -f test.yaml -n bookinfo-ns测试
1.通过浏览器打开 Bookinfo 应用。
2.使用headers头部不包含jason关机键, 访问到 /productpage 页面。
3.你期望 Bookinfo 主页在有50%几率大约 2 秒钟加载完成并且没有错误,有50%的几率正常加载
4.查看页面的响应时间:
打开浏览器的 开发工具 菜单
打开 网络 标签
重新加载
productpage页面。你会看到页面加载实际上用了大约 6s。
流量的迁移
一个常见的用例是将流量从一个版本的微服务逐渐迁移到另一个版本。在 Istio 中,您可以通过配置一系列规则来实现此目标, 这些规则将一定百分比的流量路由到一个或另一个服务。在本任务中,您将会把 50% 的流量发送到 reviews:v1,另外 50% 的流量发送到 reviews:v3。然后,再把 100% 的流量发送到 reviews:v3 来完成迁移。
(1)让所有的流量都到v1
kubectl apply -f virtual-service-all-v1.yaml(2)将v1的50%流量转移到v3
kubectl apply -f virtual-service-reviews-50-v3.yaml(3)确保v3版本没问题之后,可以将流量都转移到v3
kubectl apply -f virtual-service-reviews-v3.yaml(4)访问测试,看是否都访问的v3版本
体验Istio的Observe(观察)
观察mixer组件上报的服务数组
采集指标:自动为Istio生成和收集的应用信息,可以配置的YAML文件
进入bookinfo/telemetry目录下面
- 如果需要metrics收集日志,需要先执行
kubectl apply -f metrics-crd.yaml- 检查一下
kubectl get instance -n istio-system多次属性页面让metrics收集数据:http://192.168.187.137:31380/productpage
现在需要访问普罗米修斯看看有没有拿到metrics收集到的数据,我们可以通过ingress来访问
- 检查普罗米修斯ingress存不存在
kubectl get ingress -n istio-system- 不存在则启动ingress
kubectl apply -f prometheus-ingress.yaml- 访问普罗米修斯域名
prometheus.istio.qy.com检查一下有没有数据筛选选择: istio_requests_total
收集原理:

- 启动grafana来可视化查看,检查grafana的ingress存不存在
kubectl get ingress -n istio-system- 启动
kubectl apply -f grafana-ingress.yaml- 访问grafana域名
grafana.istio.qy.com
- 配置grafana对应的普罗米修斯ip
- 查找普罗米修斯ip
kubectl get svc -o wide -n istio-system

- 访问istio mixer


这边就可以看到内存和CPU使用情况了
四、安装
Istio支持在不同的平台下安装其控制平面,例如Kubernetes、Mesos和虚拟机等。 课程上以 Kubernetes 为基础讲解如何在集群中安装 Istio (Istio 1.0.6 要求Kubernetes的版本在1.11及以上)。 可以在本地或公有云上搭建Istio环境,也可以直接使用公有云平台上已经集成了Istio的托管服务。
4.1 在本地搭建Istio环境
Kubernetes集群环境
目前有许多软件提供了在本地搭建Kubernetes集群的能力,例如Minikube/kubeadm都可以搭建kubernetes集群,我这边所选用的是kubeadm来安装Kubernetes集群。
Kubeadm 是一个工具,它提供了 kubeadm init 以及 kubeadm join 这两个命令作为快速创建 kubernetes 集群的最佳实践。
准备机器
两台centos7的虚拟机,地址分别为
192.168.187.137
192.168.187.138
大家根据自己的情况来准备centos7的虚拟机。
虚拟机要保证彼此之间能够ping通,也就是处于同一个网络中。
Kubernets官网推荐虚拟机的配置最低要求:2核2G(这边建议最低2核3G配置)
Docker环境
在每一台机器上都安装好Docker,我这边使用的版本为18.09.0
# docker查看版本命令
docker --version修改hosts文件
(1)设置master角色,在192.168.187.138打开hosts文件
# 打开hosts文件
vi /etc/hosts
# 设置192.168.187.138为master的hostname,用m来表示
192.168.187.138 m
# 设置192.168.187.137为worker的hostname,用w1来表示
192.168.187.137 w1(2)设置worker角色,在192.168.187.137打开hosts文件
# 打开hosts文件
vi /etc/hosts
# 设置192.168.187.138为master的hostname,用m来表示
192.168.187.138 m
# 设置192.168.187.137为worker的hostname,用w1来表示
192.168.187.137 w1(3)使用ping测试一下
ping m
ping w1kubeadm安装版本
安装的版本是1.14.0
kubernetes集群网络插件-calico
calico网络插件:https://docs.projectcalico.org/v3.9/getting-started/kubernetes/
calico,同样在master节点上操作
Calico为容器和虚拟机工作负载提供一个安全的网络连接。
验证 Kubernetes安装
1)在master节点上检查集群信息
命令:kubectl get nodes
2)监控 w1节点的状态 :kubectl get nodes -w
监控成 ready状态
3)查询pod 命令:kubectl get pods -n kube-system
注意:Kubernetes集群安装方式有很多,大家可以安装自己熟悉的方式搭建Kubernetes, 这里只是介绍本次课程上使用的kubernets集群环境
4.2 安装Istio
在Istio的版本发布页面https://github.com/istio/istio/releases/tag/1.0.6下载安装包并解压(我用的是一个比较稳定的版本1.0.6版本,放到master上面,以Linux平台的istio-1.0.6-linux.tar.gz为例)
1.解压tar -xzf istio-1.0.6-linux.tar.gz
2.进入istio目录cd istio-1.0.6/
Istio的安装目录及其说明
| 文件/文件夹 | 说明 |
|---|---|
| bin | 包含客户端工具,用于和Istio APIS交互 |
| install | 包含了Consul和Kubernetes平台的Istio安装脚本和文件,在Kubernetes平台上分为YAML资源文件和Helm安装文件 |
| istio.VERSION | 配置文件包含版本信息的环境变量 |
| samples | 包含了官方文档中用到的各种应用实例如bookinfo/helloworld等等,这些示例可以帮助读者理解Istio的功能以及如何与Istio的各个组件进行交互 |
| tools | 包含用于性能测试和在本地机器上进行测试的脚本文件和工具 |
有以下几种方式安装Istio:
使用install/kubernetes文件夹中的istio-demo.yaml进行安装;
使用Helm template渲染出Istio的YAML安装文件进行安装;
使用Helm和Tiller方式进行安装。
课程中使用的是使用install/kubernetes文件夹中的istio-demo.yaml进行安装
快速部署Istio
Kubernetes CRD介绍
比如Deployment/Service/etc等资源是kubernetes本身就支持的类型,除了这些类型之外kubernetes还支持资源的扩展,说白了就是可以自定义资源类型,如果没有CRD的支持的话,istio一些资源类型是创建不成功的
#crds.yaml路径:
istio-1.0.6/install/kubernetes/helm/istio/templates/crds.yaml
# 执行
kubectl apply -f crds.yaml
# 统计个数
kubectl get crd -n istio-system | wc -lKubernetes平台对于分布式服务部署的很多重要的模块都有系统性的支持,借助如下一些平台资源可以满足大多数 分布式系统部署和管理的需求,但是在不同应用业务环境下,对于平台可能有一些特殊的需求,这些需求可以抽象为Kubernetes的扩展资源,而 Kubernetes的CRD(CustomResourceDefifinition)为这样的需求提供了轻量级的机制
执行安装命令
(1)根据istio-1.0.6/install/kubernetes/istio-demo.yaml创建资源
kubectl apply -f istio-demo.yaml
# 会发现有这么多的资源被创建了,很多很多 ,里面的命名空间用的是istio-system2)查看核心组件资源
kubectl get pods -n istio-system
kubectl get svc -n istio-system
可以看到有3个是completed,其它的组件都必须是running, completed表示的是用的是k8s里面JOB资源,表示这个任务已经执行结束了
可以看到比如citadel有了,pilot有了,sidecar也有了,其它的比如ingress网关都有了,监控也有了
回顾K8S组件以及使用
回顾课程涉及到的kubernetes组件
Deployment
一旦运行了 Kubernetes 集群,就可以在其上部署容器化应用程序。 为此,需要创建 Kubernetes Deployment 配置。
Deployment 负责 Kubernetes 如何创建和更新应用程序的实例。
创建 Deployment 后,Kubernetes master 将应用程序实例调度到集群中的各个节点上。
创建nginx_deployment.yaml文件
apiVersion: apps/v1 ## 定义了一个版本
kind: Deployment ##k8s资源类型是Deployment
metadata: ## metadata这个KEY对应的值为一个Maps
name: nginx-deployment ##资源名字 nginx-deployment
labels: ##将新建的Pod附加Label
app: nginx ##一个键值对为key=app,valuen=ginx的Label。
spec: #以下其实就是replicaSet的配置
replicas: 3 ##副本数为3个,也就是有3个pod
selector: ##匹配具有同一个label属性的pod标签
matchLabels: ##寻找合适的label,一个键值对为key=app,value=nginx的Labe
app: nginx
template: #模板
metadata:
labels: ##将新建的Pod附加Label
app: nginx
spec:
containers: ##定义容器
- name: nginx ##容器名称
image: nginx:1.7.9 ##镜像地址
ports:
- containerPort: 80 ##容器端口(1)执行资源文件命令
kubectl apply -f nginx_deployment.yaml(2)查看pod
kubectl get pods
# 查看pod详情
kubectl get pods -o wide(3)查看deployment命令
kubectl get deployment(4)查看deployment详情命令
kubectl get deployment -o wideLabels and Selectors
顾名思义,就是给一些资源打上标签的
labels
当资源很多的时候可以用可以用labels标签来对资源分类
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
labels:
app: nginx
# 表示名称为nginx-pod的pod,有一个label,key为app,value为nginx。
#我们可以将具有同一个label的pod,交给selector管理selectors
如果我想使用这个标签里面的k8s资源,那么需要用到k8s里面selector组件,用selector来匹配到特定的的label
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels: # 定义了一个labels,key=app,value=nginx
app: nginx
spec:
replicas: 3
selector: # 用selector匹配具有同一个label属性的pod标签
matchLabels:
app: nginx查看pod的label标签命令:
kubectl get pods --show-labelsNamespace
命名空间就是为了隔离不同的资源。比如:Pod、Service、Deployment等。可以在输入命令的时候指定命名空间-n,如果不指定,则使用默认的命名空间:default。
查看一下当前的所用命名空间:kubectl get namespaces/ns
查看一下kube-system命名空间:kubectl get pods -n kube-system
(1)创建自己的namespace
my-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: myns(2)执行命令:kubectl apply -f my-namespace.yaml
(3)查看命令
kubectl get ns
# 删除命名空间
kubectl delete namespaces 空间的名字注意:
删除一个namespace会自动删除所有属于该namespace的资源。
default和kube-system命名空间不可删除。
Service
集群内部访问方式(ClusterIP)
Pod虽然实现了集群内部互相通信,但是Pod是不稳定的,比如通过Deployment管理Pod,随时可能对Pod进行扩缩容,这时候Pod的IP地址是变化的。能够有一个固定的IP,使得集群内能够访问。也就是之前在架构描述的时候所提到的,能够把相同或者具有关联的Pod,打上Label,组成Service。而Service有固定的IP,不管Pod怎么创建和销毁,都可以通过Service的IP进行访问
k8s用service来解决这个问题,因为service会对应一个不会的ip,然后内部通过负载均衡到相同label上的不同pod机器上
(1)创建whoami-deployment.yaml文件
apiVersion: apps/v1 ## 定义了一个版本
kind: Deployment ##资源类型是Deployment
metadata: ## metadata这个KEY对应的值为一个Maps
name: whoami-deployment ##资源名字
labels: ##将新建的Pod附加Label
app: whoami ##key=app:value=whoami
spec: ##资源它描述了对象的
replicas: 3 ##副本数为1个,只会有一个pod
selector: ##匹配具有同一个label属性的pod标签
matchLabels: ##匹配合适的label
app: whoami
template: ##template其实就是对Pod对象的定义 (模板)
metadata:
labels:
app: whoami
spec:
containers:
- name: whoami ##容器名字 下面容器的镜像
image: jwilder/whoami
ports:
- containerPort: 8000 ##容器的端口
jwilder/whoami这是一个可以在docker仓库里面拉取到的镜像,是官方提供的一个演示的镜像
(1)执行命令
kubectl apply -f whoami-deployment.yaml(2)查看详情
kubectl get pods -o wide(3)在集群内正常访问
curl 192.168.221.80:8000/192.168.14.6:8000/192.168.14.7:8000(5)测试:删除其中一个pod,查看重新生成的ip有没有变化
kubectl delete pod whoami-deployment-678b64444d-jdv49新加的pod地址发生了变化
(6) Service 登场
查询svc命名空间下的资源
kubectl get svc(7)创建自己的service空间
创建:kubectl expose deployment deployment名字
例如:kubectl expose deployment whoami-deployment(8)重新查询service空间,会发现有一个whoami-deployment的service,ip为10.107.4.74
[root@m k8s]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d12h
whoami-deployment ClusterIP 10.107.4.74 <none> 8000/TCP 3s
(9)访问service:curl 10.107.4.74:8000
多试几次会发现service会负载到其中的一个pod上
(10)查看service
kubectl describe svc service名字
例如:kubectl describe svc whoami-deployment
[root@m k8s]# kubectl describe svc whoami-deployment
Name: whoami-deployment
Namespace: default
Labels: app=whoami
Annotations: <none>
Selector: app=whoami
Type: ClusterIP
IP: 10.107.4.74
Port: <unset> 8000/TCP
TargetPort: 8000/TCP
Endpoints: 192.168.190.86:8000,192.168.190.87:8000,192.168.190.89:8000
Session Affinity: None
Events: <none>
# 说白了 service下面挂在了Endpoints节点(11)将原来的节点扩容到5个
kubectl scale deployment whoami-deployment --replicas=5(12)删除service命令
kubectl delete service service名字
kubectl delete service whoami-deployment总结:其实Service存在的意义就是为了Pod的不稳定性,而上述探讨的就是关于Service的一种类型Cluster IP
外部服务访问集群中的Pod(NodePort)
也是Service的一种类型,可以通过NodePort的方式
说白了,因为外部能够访问到集群的物理机器IP,所以就是在集群中每台物理机器上暴露一个相同的端口锁,比如32008

操作
(1)先删除之前的service
kubectl delete svc whoami-deployment(2)再次查看命令
kubectl get svc发现whoami-deployment已被删除
(3)查看pod命令
kubectl get pods(4)创建type为NodePort的service
kubectl expose deployment whoami-deployment --type=NodePort查看:kubectl get svc

并且生成了一个port端口,会有一个8000端口映射成宿主机的31504端口
注意上述的端口31504,实际上就是暴露在集群中物理机器上的端口
lsof -i tcp:31504
netstat -ntlp|grep 31504浏览器通过物理机器的IP访问
http://192.168.187.137:31504/
curl 192.168.187.137:31504/NodePort虽然能够实现外部访问Pod的需求,但是需要占用了各个物理主机上的端口
删除资源
kubectl delete -f whoami-deployment.yaml
kubectl delete svc whoami-deploymentIngress
前面我们也学习可以通过service nodeport方式实现外部访问Pod的需求,但是会占用了各个物理主机上的端口,所以 这种方式不好
删除资源
# 删除pod
kubectl delete -f whoami-deployment.yaml
# 删除service
kubectl delete svc whoami-deployment那接下来还是基于外部访问内部集群的需求,使用Ingress实现访问whoami需求。
(1)创建whoami-service.yaml文件
创建pod和service
apiVersion: apps/v1 ## 定义了一个版本
kind: Deployment ##资源类型是Deployment
metadata: ## metadata这个KEY对应的值为一个Maps
name: whoami-deployment ##资源名字
labels: ##将新建的Pod附加Label
app: whoami ##key=app:value=whoami
spec: ##资源它描述了对象的
replicas: 3 ##副本数为1个,只会有一个pod
selector: ##匹配具有同一个label属性的pod标签
matchLabels: ##匹配合适的label
app: whoami
template: ##template其实就是对Pod对象的定义 (模板)
metadata:
labels:
app: whoami
spec:
containers:
- name: whoami ##容器名字 下面容器的镜像
image: jwilder/whoami
ports:
- containerPort: 8000 ##容器的端口
apiVersion: v1
kind: Service
metadata:
name: whoami-service
spec:
ports:
- port: 80
protocol: TCP
targetPort: 8000
selector:
app: whoami
(2)执行资源命令
kubectl apply -f whoami-service.yaml(3)创建whoami-ingress.yaml文件
apiVersion: extensions/v1beta1
kind: Ingress # 资源类型
metadata:
name: whoami-ingress # 资源名称
spec:
rules: # 定义规则
- host: whoami.qy.com # 定义访问域名
http:
paths:
- path: / # 定义路径规则,/ 表示能够命中任何路径规则
backend:
serviceName: whoami-service # 把请求转发给service资源,这个service就是我们前面运行的service
servicePort: 80 # service的端口(4)执行命令:
kubectl apply -f whoami-ingress.yaml(5)、查看ingress资源:
kubectl get ingress(6)查看ingress资源详细:
kubectl describe ingress whoami-ingress(7)、修改win的hosts文件,添加dns解析
192.168.187.137 whoami.qy.com(8)、打开浏览器,访问whoami.qy.com
流程总结

浏览器发送请求给ingress,ingress根据规则配置把请求转发给对应的service,由于service配置了pod,所以请求最终发给了pod内对应的服务
总结
ingress转发请求更加灵活,而且不需要占用物理机的端口,所以建议使用这种方式转发外部请求到集群内部