Istio学习笔记
术语服务网格(Service Mesh)用于描述微服务之间的网络,以及通过此网络进行的服务之间的交互。随着服务数量和复杂度的增加,服务网格将变的难以理解和管理。
对服务网格的需求包括:服务发现、负载均衡、故障恢复、指标和监控,以及A/B测试、金丝雀发布、限速、访问控制、端对端身份验证等。
使用云平台给DevOps团队带来了额外的约束,为了Portability开发人员通常需要使用微服务架构,运维人员则需要管理非常多数量的服务。Istio能够连接、保护、控制、观察这些微服务。
Istio是运行于分布式应用程序之上的透明(无代码入侵)服务网格,它同时也是一个平台,提供集成到其它日志、监控、策略系统的接口。
Istio的实现原理是,为每个微服务部署一个Sidecar,代理微服务之间的所有网络通信。在此基础上你可以通过Istio的控制平面实现:
- 针对HTTP、gRPC、WebSocket、TCP流量的负载均衡
- 细粒度的流量控制行为,包括路由、重试、故障转移、故障注入(fault injection)
- 可拔插的策略层+配置API,实现访问控制、限速、配额
- 自动收集指标、日志,跟踪集群内所有流量,包括Ingress/Egress
- 基于强身份认证和授权来保护服务之间的通信
使用Istio你可以很容易的通过配置,对流量和API调用进行控制。服务级别的可配置属性包括断路器(circuit breakers)、超时、重试。
Istio支持基于流量百分比切分的A/B测试、金丝雀滚动发布、分阶段滚动发布。
可以提供安全信道,管理身份验证和授权,加密通信流量。
联用K8S的网络策略可以获得更多益处,例如保护Pod-to-Pod之间的通信。
Istio强大的跟踪、监控、日志能力,让服务网格内部结构更容易观察 —— 一个服务的性能对上下游的影响可以直观的展现在仪表盘上。
Istio的Mixer组件——通用的策略和监控(Telemetry)中心(Hub)——负责策略控制、指标收集。Mixer提供后端基础设施(例如K8S)的隔离层,Istio的其它部分不需要关注后端细节。
Istio当前支持:
- Kubernetes上的Service
- 通过Consul注册的服务
- 在独立虚拟机上运行的服务
从整体上看,Istio的服务网格由数据平面、控制平面两部分组成:
- 数据平面由一系列作为Sidecar部署的智能代理(Envoy)构成。这些代理联合Mixer,中继、控制所有微服务之间的网络通信。需要注意,还有一些Envoy是独立部署(而非Sidecar)的,用来实现K8S Ingress控制器、Istio的Ingress/Egress网关
- 控制平面负责管理、配置智能代理,实现流量路由;配置Citadel实现TLS证书管理;配置Mixers来应用策略、收集指标
架构图:
Istio使用一个扩展过的Envoy版本。Envoy是基于C++开发的高性能代理,Istio使用它的以下特性:
- 动态服务发现
- 负载均衡
- TLS termination —— 可将后端的HTTP服务包装为HTTPS
- HTTP/2和gRPC代理
- 断路器
- 健康检查
- 分阶段(基于流量百分比)发布
- 故障注入
- 丰富的监控指标
一般情况下Envoy在和目标服务的相同Pod中,以Sidecar形式部署。少量的Istio组件的主进程就是Envoy,包括Ingress控制器、Ingress/Egress网关。
一个平台无关的组件:
- 为服务网格应用访问控制策略
- 从Envoy和其它服务中收集指标
- Envoy收集的请求级别的属性,被发送到Mixer进行分析
Mixer提供了一个灵活的插件模型,让Istio能够灵活的和多种宿主机环境、基础设施后端进行对接。
该组件是Istio的控制器,它会监控各种规则、策略(通常存储在K8S中),一旦配置文件发生变化,就会提取、处理,并同步给Envoy:
- 为Envoy提供服务发现
- 为智能路由(AB测试、金丝雀部署……)提供流量管理能力
- 提供弹性(Resiliency)——超时、重试、断路器等
- 分发身份验证策略给Envoy
Pilot将高级别的路由规则转换为Envoy理解的配置信息,并在运行时将这些配置传播到Sidecars。
Pilot将平台相关的服务发现机制抽象为标准的(Envoy data plane API,xDS)格式,这让Istio可以在K8S、Consul、Nomad等多种环境下运行。
负责证书、密钥的管理。提供服务-服务之间、或者针对终端用户的身份验证功能,可以加密服务网格中的流量。
部署服务网格带来了额外的性能损耗,下面是Istio组件的推荐资源配置:
- 对于启用了访问日志(默认启用)的Sidecar,每1000请求/s可以配备1个vCPU,如果没有启用访问日志则配备0.5个vCPU
- 节点上的Fluentd是资源消耗大户,它会捕获并上报日志,如果不关心可以排除Sidecar日志不上报
- 在大部分情况下,Mixer Check有超过80%的缓存命中率,在此前提下,可以为Mixer的Pod每1000请求/s配备0.5个vCPU
- 由于在通信两端都引入了Sidecar代理、并且需要上报Mixer,大概会引入10ms的延迟
任何控制平面组件都支持多实例部署,在K8S中可以考虑配置HPA。
考虑将Mixer的check、report功能分开部署。当前官方Chart已经分开,分别对应istio-policy、istio-telemetry两个Deployment。
术语 | 说明 |
Service |
一个应用程序“行为单元”,它对应服务注册表(例如K8S的集群服务DNS)中的唯一的名称 服务具有0-N个网络端点(Endpoint),这些端点可以是Pod、容器、虚拟机 |
Service versions |
也叫subsets,这是持续部署(CD)中的概念 在持续部署时,网格中可能运行着某个微服务的多种变体,这些变体的二进制代码不同,但是API版本不一定不同。这些变体代表了对某个服务的迭代性的改变,部署在不同的环境中 |
Source | 访问当前服务的下游(downstream)客户端 |
Host | 客户端连接到服务器时所使用的DNS名称 |
Destination | 表示一个可寻址的服务,请求/连接在经过路由处理后,将发送到Destination |
- 进一步完善istiod(单体化)
- 可观察性增强:更多配置项、对追踪采样率的控制、更新的Grafana仪表盘
- 更好的虚拟机支持(未来将更进一步强化):WorkloadEntry资源让非K8S工作负载成为Istio的一等公民,物理机或者虚拟机和Pod现在处于同一级别。你可以定义同时由Pod和物理机Back的服务。这让逐步迁移到K8S提供便利
1.5版本的最大变化是,控制平面的大部分组件(pilot、citadel、sidecar-injector)合并到名为istiod的单体应用中,现在istiod负责:
- 配置处理和推送
- 证书分发
- Sidecar注入
以下组件被移除:
- SDS的node-agent(合并到Pilot Agent)
Mixer被废弃,但是仍然可以使用。
新版本HTTP遥测基于Envoy过滤器Stats filter实现,可以节省50%的CPU用量。
- 提升了 ServiceEntry 的性能。
- 修复了 readiness 探针不一致问题
- 通过定向局部更新的方式改善了配置更新的性能
- 添加了为 host 设置所在负载均衡器设置的选项
- 修复了 Pod 崩溃会触发过度配置推送的问题
- 添加了使用 Istio CNI 时对 iptables 的探测
- 添加了 consecutive_5xx 和 gateway_errors 作为离群值探测选项。
- 提升了 EnvoyFilter 匹配性能
- 添加了对 HTTP_PROXY 协议的支持
- 改进了 iptables 设置,默认使用 iptables-restore
- 默认开启自动协议探测
- 添加 Beta 认证 API。新 API 分为 PeerAuthentication 和 RequestAuthenticaiton,面向工作负载
- 添加认证策略,支持 deny 操作和语义排除
- Beta 版本默认开启自动 mTLS
- 稳定版添加 SDS
- Node agent 和 Pilot agent 合并,移除了 Pod 安全策略的需要,提升了安全性
- 合并 Citadel 证书发放功能到 Pilot
- 支持 Kubernetes first-party-jwt 作为集群中 CSR 认证的备用 token
- 通过 Istio Agent 向 Prometheus 提供密钥和证书
- 支持 Citadel 提供证书给控制平面
- 为 v2 版本的遥测添加 TCP 协议支持
- 在指标和日志中支持添加 gRPC 响应状态码
- 改进 v2 遥测流程的稳定性
- 为 v2 遥测的可配置性提供 alpha 级别的支持
- 支持在 Envoy 节点的元数据中添加 AWS 平台的元数据
- 更新了 Mixer 的 Stackdriver 适配器,以支持可配置的刷新间隔来跟踪数据
- 支持对 Jaeger 插件的 headless 收集服务
- 修复了 kubernetesenv 适配器以提供对名字中有.的 Pod 的支持
- 改进了 Fluentd 适配器,在导出的时间戳中提供毫秒级输出
仍然以用户体验改进为主,简化配置难度。
可以简化安装、Mesh的操控,并很大程度上改善性能。
istio-proxy内部生成HTTP指标的特性,从试验升级为Alpha。
istio-proxy内部生成TCP指标(试验)
不需要配置DestinationRule即可自动启用。
该版本主要专注于用户体验的改善。
为了使用Istio的路由特性,服务端口名必须遵循特定的命名约定,来声明其使用的协议。
从1.3开始,出站流量的协议可以自动识别为TCP或HTTP,这意味着服务端口命名协定不再必须
现在大部分的安全策略(例如RBAC)已经直接在Envoy里面实现了。
istio-proxy现在可以直接向Prometheus发送数据,而不需要istio-telemetry来丰富、完善信息。
对于Envoy中的高级特性,Istio可能尚未提供对应API。你现在可以基于EnvoyFilter API来定制:
- LDS返回的HTTP/TCP监听器,及其过滤器链
- RDS返回的Envoy HTTP路由配置
- CDS返回的上游集群
- 添加注解 traffic.sidecar.istio.io/includeInboundPorts,不再强制要求服务在deployment的YAML中声明contrainerPort
- 添加IPv6的试验性支持
- 在多集群环境下,优化了基于位置的路由
- 优化了ALLOW_ANY模式(即允许任何未知的出站流量,配置位于configmap istio中)下的出站流量策略。针对未知HTTP/HTTPS主机+已知端口的流量,会原样的转发
- 支持设置针对上游服务的idle超时
- 改进了NONE模式(不用iptables,不进行流量捕获)的Sidecar支持
- 可以配置Sidecar的Envoy的DNS刷新率,防止DNS服务器过载
- 扩展自签名Citadel根证书有效期为10年
- 可以为Deployment添加PodSpec注解 sidecar.istio.io/rewriteAppHTTPProbers: "true",来重写健康探针
- 对Envoy统计信息生成的完全控制,基于注解控制
- Prometheus生成的不包含在指标中
控制平面性能:Pilot的CPU、内存消耗受网格的配置变化情况、工作负载变化情况、连接到Pilot的代理数量的影响。增加Pilot的实例数来可以减少配置分发处理的时间,提高性能。网格中包含1000服务、2000工作负载、1000QPS的情况下,单个Pilot实例消耗1个vCPU和1.5GB内存。
数据平面性能:
- CPU,代理在1000QPS下大概消耗0.6个vCPU
- 内存,代理的内存消耗取决于它需要处理的配置数量,大量的Listener、Cluster、Route会增加内存使用。此外,代理在1000QPS下需要消耗50MB内存
- 延迟,请求需要同时经过客户端/服务器的sidecar,在1000QPS下P99大概是10ms级别。启用策略检查(Check)会额外增加延迟。L4过滤器的逻辑比较简单,因此TCP流量不会引入显著延迟
- CRD从Istio的Chart中独立出来,放入istio-init这个Chart。这避免了升级Istio而丢失CR数据
- 添加几个安装配置Profile,这些配置提供了常用的安装模式,简化了Istio的安装过程
- 增强了多集群集成
- 新的资源类型Sidecar。用于对工作负载的边车代理进行细粒度的控制,特别是可以限制边车能够向哪些服务发送流量。此资源可以减少需要计算和传输给边车代理的配置信息,提升启动速度,减少资源消耗,提升控制平面可扩容性。在大型集群中,建议为每个命名空间提供一个Sidecar资源
- 支持限制服务(ServiceEntry、VirtualService)的可见范围。exportTo可用于限制哪些命名空间能够访问本服务。除了在Istio CR上指定exportTo以外,你还可以在K8S的Service上使用networking.istio.io/exportTo注解
- Gateway引用VirtualService时,可能存在歧义,因为不同命名空间可能定义具有相同hostname的VirtualService。在1.1版本中,你可以用namespace/hostname-match的形式来来设置hosts字段,以避免歧义。类似的在Sidecar中你也可以为egress配置为这种形式
- 支持设置ServiceEntry的Locality,以及关联的SAN(用于mTLS)。现在使用HTTPS端口的ServiceEntry不再需要配套的VirtualService来工作
- 位置感知路由(Locality-Aware Routing),优先路由到位于当前地理位置(Locality)的服务端点
- 简化了多集群模式的安装、支持额外的部署模式。现在可以用Ingress网关连接多个集群,而不需要Pod级别的VPN。为了HA可以在各集群中部署控制平面,这种部署方式下位置感知路由自动开启。可以将命名空间扩展到多个集群,以创建全局命名空间
- Istio的Ingress组件被废弃
- 性能和可扩容性得到提升
- 默认关闭访问日志
- 健康检查探针,当启用mTLS的情况下,支持Kubernetes的Readiness/Liveness探针。当启用mTLS后,进入Envoy的HTTP探针请求会被拒绝,1.1能够自动进行HTTP探针的重写,将探针请求转发给pilot-agent,并由后者直接转发给工作负载,绕开Envoy的TLS认证
- 集群RBAC配置,将RbacConfig资源替换为ClusterRbacConfig资源
- 基于SDS的身份配置(Identity Provisioning ),不需要重启Envoy即可实现动态证书轮换,实现on-node的密钥生成增强了安全性
- 在HTTP/gRPC的基础上,支持TCP服务的授权
- 支持基于终端用户组的授权
- Ingress网关控制器支持外部证书管理,新的控制器用于支持动态的加载、轮换外部证书
- 定制PKI集成,支持Vault PKI,基于Vault CA签发证书
- 策略检查默认关闭,主要是出于性能方面的考虑
- 性能的增强:
- 极大的减少了Envoy默认生成的统计信息的收集
- 为Mixer工作负载添加了负载限制的功能
- 改善了Mixer和Envoy之间的交互协议
- 适配器现在能够影响入站请求的头和路由
- 进程外适配器,生产级可用,进程内适配器被废弃
- 添加了默认的用于跟踪TCP连接的指标
在1.0版本Istio只提供了一种基于扁平网络的多集群方案:Istio控制平面部署在其中单个Kubernetes集群中。这种方案要求各集群的 Pod 地址范围不能重叠,且所有的 Kubernetes 控制平面API Server 互通。看上去就是物理上把多个Kubernetes并到一个Istio控制面下,在Istio看来是同一个网格。这种方案的网络要求苛刻,实际应用并不多。
1.1版本对多集群上做了非常多的增强,除了保留1.0扁平网络作为一种单控制面的方案外,还提出了另外两种方案供用户根据实际环境和需求灵活选择,这两种方案都不要求是扁平网络:
- 多控制平面方案:在每个集群中安装完整的Istio控制平面,可以看成是一种松散的联邦,集群间服务在Gateway处联通即可。通过一个全局的DNS将跨集群的请求路由到另外一个集群里去。这种集群的访问是基于Istio的ServiceEntry和Gateway来实现的,配置较多且复杂,需用户自己维护
- 一种集群感知(Split Horizon EDS)的单控制平面方案:Istio控制平面只在一个Kubernetes集群中安装,Istio控制平面仍然需要连接所有Kubernetes集群的K8S API Server。每个集群都有集群标识和网络标识。在服务间访问时,如果目标是本集群的负载,则类似单集群的方式直接转发;如果是其他集群的实例,则将流量转发到集群的入口网关上,再经过网关转发给实际负载
在1.1版本中添加了一个重要字段exportTo。用于控制VirtualService、DestinationRule和 ServiceEntry 跨Namespace的可见性。这样就可以控制一个Namespace下定义的资源是否可以被其他Namespace下的Envoy访问。
如果不设置exportTo则默认全局可见。目前exportTo仅仅支持两个取值:
".",表示仅仅当前命名空间可以使用当前资源
"*",表示任何命名空间都可以使用当前资源
如果服务对Pod不可见,则Istio不会为该Pod的Envoy生成Listener、Cluster等信息,因而可以减少内存消耗。
Sidecar是具有命名空间的资源。每个Sidecar应用到命名空间的一个和多个工作负载,工作负载的选择通过workloadSelector进行,如果不指定workloadSelector(每个命名空间只能有一个这样的Sidecar)则Sidecar应用到命名空间的所有(没有被其它带有workloadSelector的Sidecar匹配的)工作负载。
如果命名空间包含多个没有workloadSelector的Sidecar,或者多个Sidecar的workloadSelector匹配同一工作负载,则网格的行为是未定义的。
配置示例一,允许prod-us1命名空间的Pod发起针对prod-us1, prod-apis, istio-system命名空间的公共服务的egress流量:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
apiVersion: networking.istio.io/v1alpha3 kind: Sidecar metadata: name: default namespace: prod-us1 spec: # 需要通信的服务白名单,可以很大程度上减少Envoy内存消耗 # 经过测试,创建此资源后,istioctl proxy-config listener PODNAME 输出立刻变得非常少 # 但是并没有禁止对外访问,可能和ALLOW_ANY有关? egress: - hosts: # 支持namespace/hostname-match形式的host规则 - "prod-us1/*" - "prod-apis/*" - "istio-system/*" # 当前命名空间 - "./ratings.default.svc.cluster.local" |
配置示例二:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
apiVersion: networking.istio.io/v1alpha3 kind: Sidecar metadata: name: default namespace: prod-us1 spec: # 入站流量规则 ingress: # 对于从9080端口入站的HTTP流量,将其转发给Sidecar关联的工作负载监听的UDS - port: number: 9080 protocol: HTTP name: somename defaultEndpoint: unix:///var/run/someuds.sock # 出站流量规则 egress: # 允许针对istio-system命名空间的出站流量 - hosts: - "istio-system/*" # 允许针对prod-us1命名空间的9080端口的HTTP流量 - port: number: 9080 protocol: HTTP name: egresshttp hosts: - "prod-us1/*" |
配置示例三,如果工作负载没有启用基于Iptables的流量劫持,则Sidecar资源是唯一的配置工作负载边车端口的途径:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# 假设匹配的目标工作负载没有设置Iptables规则(没有istio-init容器)并且代理元数据ISTIO_META_INTERCEPTION_MODE设置为NONE apiVersion: networking.istio.io/v1alpha3 kind: Sidecar metadata: name: no-ip-tables namespace: prod-us1 spec: # 启用此规则的工作负载选择器 workloadSelector: labels: app: productpage # 入站流量规则 ingress: - port: # 让边车绑定 0.0.0.0:9080 number: 9080 protocol: HTTP name: somename # 并且将9080的流量转发给工作负载的8080端口 defaultEndpoint: 127.0.0.1:8080 # 如果为整个代理设置了元数据则不需要 captureMode: NONE # 出站流量规则 egress: # 让边车绑定 127.0.0.1:3306,并且将流量转发给外部的MySQL服务mysql.gmem.cc:3306 - port: number: 3306 protocol: MYSQL name: egressmysql captureMode: NONE bind: 127.0.0.1 hosts: # 外部服务,命名空间部分写* - "*/mysql.gmem.cc" # 外部服务用ServiceEntry来定义 apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: external-svc-mysql namespace: ns1 spec: hosts: - mysql.gmem.cc ports: - number: 3306 name: mysql protocol: MYSQL location: MESH_EXTERNAL resolution: DNS |
字段captureMode指定流向监听器的流量如何被捕获(劫持),仅仅当监听器绑定到IP地址的情况下此字段才有意义:
取值 | 说明 |
DEFAULT | 由环境定义的默认捕获模式 |
IPTABLES | 基于Iptables重定向进行捕获 |
NONE |
不进行流量捕获,当用在:
|
Istio支持使用三元组:Region、Zone、Sub-zone来描述网格的地理位置,地理位置通常精确到某个数据中心。Istio能够使用此地理位置信息来对负载均衡池进行优先级控制。
在1.1版本中,基于地理位置的负载均衡仍然是试验特性,且默认关闭。设置环境变量PILOT_ENABLE_LOCALITY_LOAD_BALANCING到所有Pilot实例以开启此特性。
服务发现平台负责自动产生地理位置信息,在K8S中,Pod的地理位置基于它所在Node的知名标签来获取。在云环境下,Node的这些标签会自动设置,否则你需要手工设置Node标签。这些标签包括:
failure-domain.beta.kubernetes.io/region
failure-domain.beta.kubernetes.io/zone
注意,K8S没有sub-zone的概念。
基于地理位置的负载均衡,其默认模式是地理位置优先负载均衡(locality-prioritized load balancing )。Istio会提示Envoy,尽可能将流量转发给和当前Enovy更近的目标工作负载实例。
另一种模式是地理位置权重负载均衡(Locality-weighted load balancing),直接指定不同地理位置负责提供服务的权重。
Istio的命令行配置工具,用于创建、查询、修改、删除Istio配置资源。
选项 | 说明 |
--context | 使用的kubeconfig context,默认"" |
-i | Istio安装在的命名空间,默认istio-system |
-c | 使用的kubeconfig |
--namespace | 在哪个命名空间执行命令 |
--log_output_level |
日志级别,格式为scope:level,scope:level...默认default:info scope取值:ads, default, model, rbac level取值:debug, info, warn, error, none |
一组子命令,用于操控Istio的身份验证策略:
1 2 3 4 5 6 7 |
# 列出所有服务的身份验证策略 istioctl authn tls-check # 仅仅显示单个服务的 istioctl authn tls-check grafana.istio-system.svc.k8s.gmem.cc # 服务主机名和端口 状态 身份验证策略(服务器配置) 客户端配置 # HOST:PORT STATUS SERVER CLIENT AUTHN POLICY DESTINATION RULE # grafana.istio-system...:3000 OK HTTP HTTP grafana-ports-mtls-disabled/istio-system - |
创建策略、规则:
1 |
istioctl create -f example-routing.yaml |
获取策略、规则:
1 2 3 4 5 6 7 |
# 获取所有虚拟服务 istioctl get virtualservices # 获取所有目的地规则 istioctl get destinationrules # 输出为YAML istioctl get virtualservice bookinfo -o yaml |
替换掉策略、规则:
1 |
istioctl replace -f example-routing.yaml |
删除策略、规则,命令格式:
1 |
istioctl delete <type> <name> [<name2> ... <nameN>] [flags] |
示例:
1 2 3 4 |
istioctl delete virtualservice bookinfo # 也可以通过yaml文件删除 istioctl delete -f example-routing.yaml |
注册一个服务实例(例如一个VM)到服务网格中:
1 |
istioctl register <svcname> <ip> [name1:]port1 [name2:]port2 ... [flags] |
注销虚拟服务实例,命令格式:
1 |
istioctl deregister <svcname> <ip> [flags] |
一组试验性命令,未来可能修改或删除。
子命令 | 说明 | ||||
convert-ingress |
尽最大努力的把Ingress资源转换为VirtualService,示例:
输出的VirtualService总是以svc.cluster.local为集群DNS后缀,可能需要修改 |
||||
metrics |
打印一个或多个虚拟服务的统计指标,示例:
|
||||
rbac |
检查是否有权限访问服务,格式:
示例:
|
||||
post-install |
安装后相关操作: post-install webhook disable 禁用Webhook |
||||
remove-from-mesh |
将工作负载从Mesh中移除: remove-from-mesh deployment 移除此Deployment对应的Pod的Sidecar并重启 |
||||
upgrade | 检查新版本并升级 | ||||
wait |
等待和Istio有关的资源Ready |
手工的将Envoy Sidecar注入到K8S的工作负载的定义文件中。定义文件中包含多种不支持注入的资源是安全的:
1 2 3 4 |
# 输出到标准输出 kubectl apply -f <(istioctl kube-inject -f resource.yaml) # 输出到文件 istioctl kube-inject -f deployment.yaml -o deployment-injected.yaml |
一组命令,用于Dump出Envoy代理的各方面的配置:
子命令 | 说明 | ||
bootstrap |
获取Envoy实例的Boostrap信息:
|
||
cluster | 获取Envoy实例的集群配置信息 | ||
listener | 获取Envoy实例的listener配置信息 | ||
route | 获取Envoy实例的路由配置信息 |
本节不做更细致的描述,具体参考运维-Dump出Envoy配置一节。
查询从Pilot最后一次发送到各Envoy的xDS同步的状态:
1 2 3 |
istioctl proxy-status # PROXY CDS LDS EDS RDS PILOT VERSION # details-v1-67c8f895b4-dnddn.default SYNCED SYNCED SYNCED (100%) SYNCED istio-pilot-77646875cd-jqmdh 1.0.2 |
分析配置文件并打印校验信息: istioctl analyze <file>... [flags]
示例:
1 2 3 4 5 6 7 8 |
# 分析当前服务网格 istioctl analyze # 分析应用指定配置文件后的当前服务网格 istioctl analyze a.yaml b.yaml my-app-config/ # 不连接到服务网格,进行分析 istioctl analyze --use-kube=false a.yaml b.yaml my-app-config/ |
访问Istio的Web UI。
1 2 3 4 5 6 7 8 9 10 11 12 |
# 为Istio控制仪表盘中打开指定Pod的ControlZ WebUI istioctl dashboard controlz <pod-name[.namespace]> [flags] # 为指定的Sidecar打开Envoy Admin仪表盘 istioctl dashboard envoy <pod-name[.namespace]> [flags] # 打开集成的软件 istioctl dashboard grafana [flags] istioctl dashboard jaeger [flags] istioctl dashboard kiali [flags] istioctl dashboard prometheus [flags] istioctl dashboard zipkin [flags] |
生成、应用(到K8S)Istio资源的清单文件,或者显示Diff:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# 生成清单文件直接写入K8S istioctl manifest apply --dry-run # 只是打印会做什么 --filename,-f # 指向IstioOperator资源,用于在Base配置上进行Overlay,可以指定多次 --force # 即使校验失败也继续 --kubeconfig,-c # Kubeconfig文件 --set,-s # 覆盖IstioOperator中的值,下载Istio并到install/kubernetes/operator/profiles # 中看有哪些值,Spec字段下的值可以直接Set,例如Spec.hub可以 -s hub=docker.gmem.cc --wait,-w # 等待必要的资源就绪 # 应用默认配置 istioctl manifest apply # 设置变量 istioctl manifest apply --set values.grafana.enabled=true # 应用Profile,可用的Profile列表位于install/kubernetes/operator/profiles istioctl manifest apply --set profile=demo --skip-confirmation # 特殊字符转义 istioctl manifest apply --set "values.sidecarInjectorWebhook.injectedAnnotations.container\.apparmor\.security\.beta\.kubernetes\.io/istio-proxy=runtime/default" # 显示两个目录或文件的区别 istioctl manifest diff # 只是生成到标准输出,不Apply到K8S istioctl manifest generate [flags] # 从Helm Values或IstioControlPlane转换为IstioOperator格式的配置 istioctl manifest migrate |
列出、Dump、Diff可用的Profile,这些Profile可以在manifest命令中引用:
1 2 3 4 5 6 7 8 9 10 |
# 列出Profile istioctl profile list [flags] # 打印出来 istioctl profile dump [<profile>] [flags] # 显示两个Profile的差异 istioctl profile diff <file1.yaml> <file2.yaml> [flags] |
这是Istio的负载测试工具。
1 2 |
# 子命令 # 选项 # 目标URL fortio command [flags] target |
子命令 | 说明 |
load | 执行负载测试 |
server | 启动GRPC ping/http服务 |
grpcping | 启动GRPC客户端 |
report | 启动用于查看报告的UI服务器 |
redirect | 启动仅执行重定向的服务器 |
curl | 简单的调试一个URL,显示响应的详细信息 |
选项 | 说明 |
-H value | 添加额外的请求头 |
-abort-on int | 如果遇到目标HTTP状态码,立即终止运行。例如503或-1,表示套接字错误 |
-allow-initial-errors | 允许预热期间的错误,且不终止运行 |
-c int | 连接/Goroutine/线程的并发数,默认4 |
-compression | 启用HTTP压缩 |
-http1.0 | 使用HTTP 1.0而非1.1 |
-k | 不校验服务器端证书 |
-keepalive | 传输层连接重用,默认true |
-qps float | 目标QPS,如果为0则不做任何休眠,尽可能提高QPS,默认8 |
-n int | 运行指定的次数 |
-t duration | 运行指定的时间,默认5s,指定为0则一直运行 |
-p string | 统计的百分比值(即耗时N%百分比的请求的最大耗时值),默认 50,75,90,99,99.9 |
-timeout duration | HTTP连接/读取超时,默认15s |
-user string | HTTP基本认证的凭证信息,格式user:pswd |
-loglevel lv | 日志级别,可以取值Warning、Error等 |
Mixer的命令行客户端。
用于执行前置条件检查、配额(quota allocations)。Mixer期望以一组Attributes为输入,它基于自己的配置来判断调用哪个适配器、如何传递参数,进而进行策略检查、配额。
选项 | 短选项 | 说明 | ||
--attributes | -a | 自动感知的属性列表,形式name1=value1,name2=value2... | ||
--bool_attributes | -b | 布尔值属性的列表 | ||
--bytes_attributes | 字节值属性的列表 | |||
--concurrency | -c | 并行的调用Mixer的线程数 | ||
--double_attributes | -d | 浮点数属性的列表 | ||
--duration_attributes | 时间长度属性的列表 | |||
--int64_attributes | -i | 整数属性的列表 | ||
--string_attributes | -s | 字符串属性的列表 | ||
--stringmap_attributes | string:string映射类型的属性列表,形式name1=k1:v1;k2:v2,name2=k3:v3... | |||
--timestamp_attributes | -t |
时间戳属性的列表 时间戳必须以ISO8601格式指定,例如:
|
||
--mixer | -m | 目标Mixer实例 | ||
--quotas | -q | 需要分配的配额的列表,形式name1=amount1,name2=amount2... | ||
--repeat | -r | 连续发送指定次数的请求,默认1 | ||
--trace_jaeger_url | 追踪相关 | |||
--trace_log_spans | ||||
--trace_zipkin_url |
用于产生遥感(telemetry)数据。Mixer期望以一组Attributes为输入,它基于自己的配置来判断调用哪个适配器、如何传递参数,进而输出遥感数据。
选项和check子命令一样。
1 2 3 |
cd /home/alex/Applications curl -L https://git.io/getLatestIstio | sh - mv istio-1.0.3 istio |
然后把istio/bin目录加入PATH环境变量。
Istio可以自动为Pod注入Sidecar,它基于Webhook实现此功能。
你可以执行下面的命令,确认MutatingAdmissionWebhook、ValidatingAdmissionWebhook这两种Admission controllers是否默认启用:
1 2 3 |
kubectl -n kube-system exec kube-apiserver-xenon -- kube-apiserver -h | grep enable-admission-plugins # 如果在你的K8S版本中,in addition to default enabled ones...后面的括号中有MutatingAdmissionWebhook # 和ValidatingAdmissionWebhook则说明默认已经启用 |
如果没有启用,则需要修改API Server的命令行参数。
此外,你还需要检查admissionregistration API是否启用:
1 2 |
kubectl api-versions | grep admissionregistration admissionregistration.k8s.io/v1beta1 |
和手工注入不同,自动注入是发生在Pod级别的。在Deployment上看不到任何变化,你需要直接检查Pod才能看到注入的Envoy。
为了加入到服务网格,K8S中的相关Pod、Service必须满足如下条件:
- 命名端口:服务(指K8S的服务,不是Pod)的端口必须被命名,为了使用Istio的路由特性,端口名必须是
<protocol>[-<suffix>]的形式。protocol可以是http, http2, grpc, mongo或者redis,suffix是可选的,如果指定suffix则必须在前面加上短横线。如果protocol不支持,或者端口没有命名,则针对该端口的流量作为普通TCP流量看待(除非通过Protocol: UDP明确指定为UDP)。示例:
1234567891011apiVersion: v1kind: Servicemetadata:name: detailsspec:ports:- port: 9080# 命名服务端口name: httpselector:app: details - 服务关联:如果一个Pod属于多个Kubernetes Service,则这些Service不得将同一端口用作不同协议
- app和version标签:建议为Pod添加app和version两个标签,分别表示应用程序的名称和版本。在分布式追踪(distributed tracing)中app标签用于提供上下文信息。在Metrics收集时app、version标签也提供上下文信息
Istio的Chart定义在install/kubernetes/helm/instio目录下 。默认情况下,Istio会以Sub chart的方式安装很多组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
dependencies: # 这是一个Webhook,自动为所有Pod注入Sidecar。默认启用 - name: sidecarInjectorWebhook version: 1.0.3 condition: sidecarInjectorWebhook.enabled # 安全模块,即citadel。默认禁用 - name: security version: 1.0.3 condition: security.enabled # Ingress控制器,用于处理K8S的Ingress资源。默认禁用 - name: ingress version: 1.0.3 condition: ingress.enabled # 入口、出口网关。默认启用 - name: gateways version: 1.0.3 condition: gateways.enabled # 默认启用,核心组件 - name: mixer version: 1.0.3 condition: mixer.enabled # 默认启用,核心组件 - name: pilot version: 1.0.3 condition: pilot.enabled # 默认禁用 - name: grafana version: 1.0.3 condition: grafana.enabled # 默认启用 - name: prometheus version: 1.0.3 condition: prometheus.enabled # 用于绘制调用链图。默认禁用,被kiali代替 - name: servicegraph version: 1.0.3 condition: servicegraph.enabled # APM。默认禁用 - name: tracing version: 1.0.3 condition: tracing.enabled # 用于服务器端的配置合法性验证。默认启用 - name: galley version: 1.0.3 condition: galley.enabled # 服务网格可视化。默认禁用 - name: kiali version: 1.0.3 condition: kiali.enabled # 负责基于ACME执行数字证书签名。默认禁用 - name: certmanager version: 1.0.3 condition: certmanager.enabled |
如需禁用,在values.yaml中修改对应配置项即可。
1.0.3版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
docker pull docker.io/istio/proxyv2:1.0.3 docker pull docker.io/istio/proxy_init:1.0.3 docker pull docker.io/istio/sidecar_injector:1.0.3 docker pull docker.io/istio/galley:1.0.3 docker pull docker.io/istio/mixer:1.0.3 docker pull docker.io/istio/pilot:1.0.3 docker pull docker.io/istio/citadel:1.0.3 docker pull docker.io/istio/servicegraph:1.0.3 docker pull docker.io/jaegertracing/all-in-one:1.5 docker pull docker.io/kiali/kiali:v0.9 docker pull docker.io/prom/prometheus:v2.3.1 docker pull quay.io/jetstack/cert-manager-controller:v0.3.1 docker pull quay.io/coreos/hyperkube:v1.7.6_coreos.0 docker pull grafana/grafana:5.2.3 |
1.1.4版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
docker pull docker.io/istio/kubectl:1.1.4 docker pull docker.io/istio/node-agent-k8s:1.1.4 docker pull docker.io/istio/citadel:1.1.4 docker pull docker.io/istio/proxy_init:1.1.4 docker pull docker.io/istio/proxyv2:1.1.4 docker pull docker.io/istio/galley:1.1.4 docker pull docker.io/istio/pilot:1.1.4 docker pull docker.io/istio/mixer:1.1.4 docker pull docker.io/istio/sidecar_injector:1.1.4 docker pull docker.io/jaegertracing/all-in-one:1.9 docker pull docker.io/kiali/kiali:v0.16 docker pull docker.io/prom/prometheus:v2.3.1 docker pull busybox:1.30.1 docker pull grafana/grafana:6.0.2 docker pull quay.io/jetstack/cert-manager-controller:v0.6.2 |
到Github下载Istio的压缩包,解压后在其根目录执行。
我的环境下,镜像拉取需要Secret:
1 2 3 4 5 6 7 8 9 |
export PSWD=... # 创建命名空间 kubectl create ns istio-system # 创建访问Docker镜像仓库的保密字典 kubectl -n istio-system create secret docker-registry gmemregsecret \ --docker-server=docker.gmem.cc --docker-username=alex \ --docker-password=$PSWD --docker-email=k8s@gmem.cc |
对于Istio 1.1,需要首先安装独立的Chart istio-init,进行CRD的注册:
1 |
helm install install/kubernetes/helm/istio-init --name istio-init --namespace istio-system -f install/kubernetes/helm/istio-init/overrides/gmem.yaml |
安装Istio组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# 创建入口网关控制器所需的数字证书,这里是*.k8s.gmem.cc的通配符证书 # 注意,ingressgateway没有自动reload数字证书的能力,必须重启Pod pushd /home/alex/Documents/puTTY/mesh.gmem.cc kubectl create -n istio-system secret generic istio-ingressgateway-certs --from-file=. popd # 创建kiali的Ingress所需的数字证书保密字典 pushd /etc/letsencrypt/live/kiali.k8s.gmem.cc kubectl create -n istio-system secret generic gmemk8scert-kiali --from-file=tls.crt=fullchain.pem,tls.key=privkey.pem popd # 创建Jaeger的Ingress所需的数字证书保密字典 pushd /etc/letsencrypt/live/jaeger.k8s.gmem.cc kubectl create -n istio-system secret generic gmemk8scert-jaeger --from-file=tls.crt=fullchain.pem,tls.key=privkey.pem popd # 为默认证书设置imagePullSecrets kubectl -n istio-system patch serviceaccount default -p '{"imagePullSecrets": [{"name": "gmemregsecret"}]}' # 不让Citadel使用自签名根证书 pushd ~/Documents/puTTY/ touch empty.pem kubectl -n istio-system create secret generic cacerts --from-file=ca-cert.pem=ca.crt \ --from-file=ca-key.pem=ca.key \ --from-file=root-cert.pem=ca.crt \ --from-file=cert-chain.pem=empty.pem popd # 挂载Ceph卷所需的密钥 kubectl get secrets pvc-ceph-key -o yaml --export | kubectl -n istio-system apply -f - # 安装Istio # 1.0 helm install install/kubernetes/helm/istio --name istio --namespace istio-system --set kiali.dashboard.passphrase=$PSWD -f install/kubernetes/helm/istio/overrides/gmem.yaml # 1.1 kubectl -n istio-system create secret generic kiali --from-literal=username=alex --from-literal=passphrase=$PSWD # 为kiali的服务账号授予必要的权限 kubectl create clusterrolebinding istio-system-kiali-role-binding --clusterrole=cluster-admin --serviceaccount=istio-system:kiali-service-account |
1 2 3 4 5 6 7 8 9 10 11 |
helm install install/kubernetes/helm/istio --name istio-minimal --namespace istio-system \ --set security.enabled=false \ --set ingress.enabled=false \ --set gateways.istio-ingressgateway.enabled=false \ --set gateways.istio-egressgateway.enabled=false \ --set galley.enabled=false \ --set sidecarInjectorWebhook.enabled=false \ --set mixer.enabled=false \ --set prometheus.enabled=false \ --set global.proxy.envoyStatsd.enabled=false \ --set pilot.sidecar=false |
最小化安装时,仅仅安装pilot组件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
helm delete istio --purge kubectl -n istio-system delete all --all kubectl delete ns istio-system kubectl delete crd adapters.config.istio.io kubectl delete crd apikeys.config.istio.io kubectl delete crd attributemanifests.config.istio.io kubectl delete crd authorizations.config.istio.io kubectl delete crd bypasses.config.istio.io kubectl delete crd certificates.certmanager.k8s.io kubectl delete crd checknothings.config.istio.io kubectl delete crd circonuses.config.istio.io kubectl delete crd cloudwatches.config.istio.io kubectl delete crd clusterissuers.certmanager.k8s.io kubectl delete crd deniers.config.istio.io kubectl delete crd destinationrules.networking.istio.io kubectl delete crd dogstatsds.config.istio.io kubectl delete crd edges.config.istio.io kubectl delete crd envoyfilters.networking.istio.io kubectl delete crd fluentds.config.istio.io kubectl delete crd gateways.networking.istio.io kubectl delete crd handlers.config.istio.io kubectl delete crd httpapispecbindings.config.istio.io kubectl delete crd httpapispecs.config.istio.io kubectl delete crd instances.config.istio.io kubectl delete crd issuers.certmanager.k8s.io kubectl delete crd kubernetesenvs.config.istio.io kubectl delete crd kuberneteses.config.istio.io kubectl delete crd listcheckers.config.istio.io kubectl delete crd listentries.config.istio.io kubectl delete crd logentries.config.istio.io kubectl delete crd memquotas.config.istio.io kubectl delete crd meshpolicies.authentication.istio.io kubectl delete crd metrics.config.istio.io kubectl delete crd noops.config.istio.io kubectl delete crd opas.config.istio.io kubectl delete crd policies.authentication.istio.io kubectl delete crd prometheuses.config.istio.io kubectl delete crd quotas.config.istio.io kubectl delete crd quotaspecbindings.config.istio.io kubectl delete crd quotaspecs.config.istio.io kubectl delete crd rbacconfigs.rbac.istio.io kubectl delete crd rbacs.config.istio.io kubectl delete crd redisquotas.config.istio.io kubectl delete crd reportnothings.config.istio.io kubectl delete crd rules.config.istio.io kubectl delete crd servicecontrolreports.config.istio.io kubectl delete crd servicecontrols.config.istio.io kubectl delete crd serviceentries.networking.istio.io kubectl delete crd servicerolebindings.rbac.istio.io kubectl delete crd serviceroles.rbac.istio.io kubectl delete crd signalfxs.config.istio.io kubectl delete crd solarwindses.config.istio.io kubectl delete crd stackdrivers.config.istio.io kubectl delete crd statsds.config.istio.io kubectl delete crd stdios.config.istio.io kubectl delete crd templates.config.istio.io kubectl delete crd tracespans.config.istio.io kubectl delete crd virtualservices.networking.istio.io |
如果你的Kubernetes集群不使用缺省的集群DNS后缀(即cluster.local),则很多组件的参数需要修改,可以参考chart-istio项目,搜索{{ .Values.global.domain }}找到需要修改的位置。
注意:1.1版本的Chart这块已经处理好,修改values.yaml中的global.proxy.clusterDomain即可。
Istio暴露了几个Configmap,修改这些Configmap以影响Istio的行为。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
apiVersion: v1 kind: ConfigMap metadata: name: istio namespace: istio-system labels: app: istio chart: istio-1.0.5 release: istio heritage: Tiller data: mesh: |- # 设置为true则禁用策略检查 # 不影响遥测指标的报送 disablePolicyChecks: false # 是否启用请求跟踪 enableTracing: true # 设置为空字符串可以禁用访问日志 accessLogFile: "/dev/stdout" # Deprecated: mixer is using EDS mixerCheckServer: istio-policy.istio-system.svc.k8s.gmem.cc:9091 mixerReportServer: istio-telemetry.istio-system.svc.k8s.gmem.cc:9091 # 如果设置为true,则Mixer的策略服务不可用导致策略检查失败时,允许请求而非拒绝 policyCheckFailOpen: false defaultConfig: # # Envoy和应用之间、Envoy之间的TCP链接超时 connectTimeout: 10s # ### ADVANCED SETTINGS ############# # istio-proxy容器中,Envoy的配置存放在何处 configPath: "/etc/istio/proxy" # Envoy二进制文件的路径 binaryPath: "/usr/local/bin/envoy" # The pseudo service name used for Envoy. serviceCluster: istio-proxy # 如果发生偶然性的Envoy reload,旧的Envoy进程保留多长时间 drainDuration: 45s parentShutdownDuration: 1m0s # # 将入口流量重定向到Envoy的模式,对出口流量无影响,后者总是使用iptables REDIRECT # 取值: # REDIRECT,使用iptables REDIRECT来NAT,并重定向到Envoy。源地址信息会丢失 # TPROXY,使用iptables TPROXY来重定向到Envoy,同时保留原始的source/dest IP地址和端口 # TPROXY可以在不对封包进行任何变动(例如NAT)的情况下重定向封包给一个本地套接字 # 可以用于高级过滤和操控。此模式会配置sidecar,启用CAP_NET_ADMIN能力,这是TPROXY所需的 # interceptionMode: REDIRECT # # Envoy在本地环回网卡上监听的管理端口,你可以进入istio-proxy容器并执行 # curl http://localhost:15000/获取Envoy的诊断信息 # 访问https://lyft.github.io/envoy/docs/operations/admin.html了解更多信息 proxyAdminPort: 15000 # # Envoy工作线程数量,如果设置为0,自动根据CPU核心数量确定 concurrency: 0 # # Zipkin/Jaeger的访问地址,Envoy向其报送APM数据 zipkinAddress: zipkin.istio-system:9411 # # Sidecar和Istio控制平面的mTLS认证策略 controlPlaneAuthPolicy: NONE # # Pilot服务的地址 discoveryAddress: istio-pilot.istio-system:15007 |
可以跨越多个Kubernetes集群来部署Istio服务网格。
- 两个或更多的运行1.9+版本的Kubernetes集群
- 有权在其中一个集群上部署Istio控制平面
- 使用RFC1918网络、VPN或更高级的网络技术将集群连接在一起,并且满足:
- 所有集群的Pod网络CIDR、Service网络CIDR不重叠
- 所有集群的Pod网络可以相互路由
- 所有集群的API Server可相互联通
- Helm 2.7.2或更高版本
Istio 将用户 pod 流量转发到 proxy 的默认方式是使用 privileged 权限的 istio-init 容器,Istio CNI插件可以消除这一priviledged要求。
Istio CNI Plugin是Kubernetes CNI的一个实现,Kubernetes CNI插件是一个链,在创建和销毁pod的时候会调用链上所有插件来安装和卸载容器的网络。
此插件会运行一个Daemonset: istio-cni-node,在每个节点上安装CNI插件。此插件负责写入iptables规则。
启用此插件,istio-init容器不再需要。
首先安装CNI插件:
1 |
helm install install/kubernetes/helm/istio-cni --name istio-cni --namespace kube-system |
然后,安装Istio,注意设置参数:
1 |
helm install install/kubernetes/helm/istio --name istio --namespace istio-system --set istio_cni.enabled=true |
此版本的Helm Chart不正常,安装出来的不是isitd单体模式。
推荐使用 istioctl manifest命令来安装,生成的清单文件包括namespace istio-system,因此删除操作:
1 |
istioctl manifest generate <your original installation options> | kubectl delete -f - |
会导致预先创建手工创建在istio-system中的资源丢失。建议先 istioctl manifest generate,然后修改它。
使用istioctl预置的Profile不能满足需要的话,可通过-f参数自己提供一个IstioOperator资源,在其中覆盖参数值:
1 |
istioctl manifest generate -f gmem.yaml |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
apiVersion: install.istio.io/v1alpha1 kind: IstioOperator metadata: namespace: istio-system spec: hub: docker.gmem.cc/istio imagePullSecrets: - gmemregsecret addonComponents: kiali: enabled: true grafana: enabled: true tracing: enabled: true values: global: logging: level: "default:info" proxy: clusterDomain: "k8s.gmem.cc" logLevel: info privileged: true accessLogFile: /dev/stdout trustDomain: "k8s.gmem.cc" sds: enabled: true security: selfSigned: false prometheus: hub: docker.gmem.cc/prom grafana: image: repository: docker.gmem.cc/grafana tracing: jaeger: hub: docker.gmem.cc/jaegertracing kiali: hub: docker.gmem.cc/kiali |
1.5.0版本中-f的行为异常,不能生成资源清单。暂时可用-s代替:
1 2 3 4 5 6 7 8 9 10 11 12 |
istioctl manifest apply \ -s profile=demo -s hub=docker.gmem.cc/istio -s values.global.logging.level="default:info" \ -s values.global.proxy.clusterDomain=k8s.gmem.cc \ -s values.global.proxy.logLevel=info -s values.global.proxy.privileged=true \ -s values.global.proxy.accessLogFile=/dev/stdout \ -s values.global.trustDomain=k8s.gmem.cc -s values.global.sds.enabled=true \ -s values.security.selfSigned=false -s values.prometheus.hub=docker.gmem.cc/prom \ -s values.grafana.image.repository=docker.gmem.cc/grafana \ -s values.tracing.jaeger.hub=docker.gmem.cc/jaegertracing \ -s values.kiali.hub=docker.gmem.cc/kiali -s values.kiali.contextPath=/ \ -s values.tracing.contextPath=/ \ -s values.global.imagePullSecrets[0]=gmemregsecret -s values.global.imagePullPolicy=Always |
1 |
istioctl manifest generate --set profile=demo | kubectl delete -f - |
警告:命名空间也会被删除。
1 2 3 4 5 |
export HUB=docker.gmem.cc export TAG=1.5 # 使用本地构建的,位于$GOPATH/src/github.com/istio/proxy/bazel-bin/src/envoy/envoy的Istio Proxy export USE_LOCAL_PROXY=1 export GOOS=linux |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
export BAZEL_BUILD_ARGS="-c dbg" cd $GOPATH/src/istio.io/ git clone https://github.com/istio/proxy.git # 安装Bazelisk sudo wget -O /usr/local/bin/bazel https://github.com/bazelbuild/bazelisk/releases/download/v0.0.8/bazelisk-linux-amd64 sudo chmod +x /usr/local/bin/bazel # 安装相关依赖 sudo apt-get install libtool cmake automake autoconf make ninja-build curl unzip build-essential software-properties-common sudo pip install virtualenv # 安装GCC 7+ 或 Clang/LLVM 7+,因为使用了C++14。我们使用LLVM 7.1.0 cd/home/alex/CPP/llvm wget https://github.com/llvm/llvm-project/releases/download/llvmorg-7.1.0/clang+llvm-7.1.0-x86_64-linux-gnu-ubuntu-14.04.tar.xz xz -d clang+llvm-7.1.0-x86_64-linux-gnu-ubuntu-14.04.tar.xz tar xvf clang+llvm-7.1.0-x86_64-linux-gnu-ubuntu-14.04.tar mv clang+llvm-7.1.0-x86_64-linux-gnu-ubuntu-14.04 7.1.0 # Bazel文件格式化工具 go get -u github.com/bazelbuild/buildtools/buildifier # 用于移动应用打包 go get -u github.com/bazelbuild/buildtools/buildozer # 执行构建 cd proxy bazel-proxy/external/envoy/bazel/setup_clang.sh /home/alex/CPP/llvm/7.1.0 http_proxy=http://127.0.0.1 https_proxy=http://127.0.0.1:8087 make |
1 2 3 4 5 6 7 8 9 10 11 |
cd $GOPATH/src/istio.io/ git clone https://github.com/istio/istio.git cd istio/ # 构建出Docker镜像 make docker # 推送镜像 make push # 生成K8S资源清单 make generate_yaml |
Istio提供了一个示例应用程序Bookinfo,我可以部署该应用 ,并为其配置服务网格,以学习Istio。该应用程序能够显示书籍的基本信息,包括ISBN、页数,还能显示书籍的评论信息。
Bookinfo由4个独立的微服务组成:
- productpage,调用details、reviews渲染页面
- details,提供书籍基本信息
- reviews,提供书评信息,调用ratings。该服务有v1、v2、v3三个版本
- ratings,提供书籍的评级信息
下图显示了没有部署Istio之前,Bookinfo的端对端架构:
下图显示受到Istio服务网格管理的Bookinfo的架构:
如果启用了自动化的Sidecar注入,你需要在安装到的命名空间上打标签:
1 |
kubectl label namespace default istio-injection=enabled |
打上此标签后,default命名空间中创建的新Pod,自动会有一个名为stio-proxy的容器,它运行istio/proxyv2镜像。
如果没有启用自动化的Sidecar注入,你需要执行:
1 |
kubectl apply -f <(istioctl kube-inject -f samples/bookinfo/platform/kube/bookinfo.yaml) |
然后,执行下面的命令安装所有组件:
1 |
kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml |
等所有容器都运行起来了,检查一下当前命名空间,应该多出6个Pod、4个Service。
当所有Pod都进入Running状态后,你需要创建一个(入口)网关,这样浏览器才能访问到集群中的Bookinfo服务:
1 |
kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml |
上述命令会创建一个Gateway,一个VirtualService:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# kubectl get gateway bookinfo-gateway -o yaml --export apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: bookinfo-gateway namespace: default spec: selector: istio: ingressgateway servers: - hosts: - '*' port: name: http number: 80 protocol: HTTP # kubectl get virtualservice bookinfo -o yaml --export apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: bookinfo spec: gateways: - bookinfo-gateway # 和前面的网关绑定 hosts: - '*' http: - match: # 以下3个URL模式发送给productpage服务 - uri: exact: /productpage - uri: exact: /login - uri: exact: /logout - uri: prefix: /api/v1/products route: - destination: host: productpage port: number: 9080 |
我的本地环境,通过配置路由,允许集群外部访问ClusterIP:
1 2 3 |
kubectl get svc istio-ingressgateway -n istio-system # NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE # istio-ingressgateway NodePort 10.109.97.20 <none> 80:20080/TCP,443:20443/TCP,31400:31400/TCP,15011:20493/TCP,8060:1164/TCP,853:5745/TCP,15030:1299/TCP,15031:11899/TCP 14d |
因此直接使用上述地址10.109.97.20即可。如果你使用NodePort方式,则需要使用宿主机IP + 映射后的端口。
如果使用外部负载均衡器(LoadBalancer类的Service),则参考如下命令:
1 2 3 4 5 6 |
export INGRESS_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}') export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].port}') export SECURE_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="https")].port}') # 某些环境下,LB通过主机名而非IP暴露 export INGRESS_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].hostname}') |
1 2 3 4 5 |
export INGRESS_HOST=`kubectl -n istio-system get service istio-ingressgateway --no-headers | awk '{print $3}'` export INGRESS_PORT=80 export GATEWAY_URL=$INGRESS_HOST:$INGRESS_PORT curl -o /dev/null -s -w "%{http_code}\n" http://${GATEWAY_URL}/productpage |
如果一切正常,应该打印200到控制台。 如果通过浏览器查看,界面右侧的Reviews面板会随机出现无星、黑星、红星,这种随机是K8S的Service负载均衡机制导致的。
下面的DestinationRule声明了productpage、reviews、ratings、details等微服务的不同版本。版本通过Pod的version标签来区分。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# kubectl apply -f samples/bookinfo/networking/destination-rule-all.yaml apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule 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 --- 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 --- |
一个简单的HTTP测试服务,用于试验Istio的各种特性:
1 |
kubectl apply -f samples/httpbin/httpbin.yaml |
Fortio是Istio的负载测试工具,提供CLI和Web UI。
Fortio能够按照指定的QPS来执行HTTP请求,录制执行耗时、百分比分布(percentiles,例如p99表示99%的请求耗时小于的数值)直方图。
1 |
kubectl apply -f samples/httpbin/sample-client/fortio-deploy.yaml |
这是一个基于Ubuntu的容器,它启动后只是简单的休眠。你可以通过kubectl连接到此容器并执行curl命令:
1 |
kubectl apply -f samples/sleep/sleep.yaml |
下面的例子创建了几个“虚拟服务“,这些虚拟服务将请求流量全部路由给各微服务的v1版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# kubectl apply -f samples/bookinfo/networking/virtual-service-all-v1.yaml apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: productpage spec: hosts: - productpage http: - route: - destination: host: productpage subset: v1 --- apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: reviews spec: hosts: - reviews http: - route: - destination: host: reviews subset: v1 --- apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: ratings spec: hosts: - ratings http: - route: - destination: host: ratings subset: v1 --- apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: details spec: hosts: - details http: - route: - destination: host: details subset: v1 --- |
虚拟服务的会被转换为Envoy配置信息,在若干秒内传递到所有Pod的Sidecar,达到最终一致性。
虚拟服务可以遮蔽掉K8S的同名Service,启用了Envoy Sidecar的Pod(包括入口网关)访问productpage、reviews、ratings、details这几个微服务(各自对应了一个K8S Service、一个虚拟服务、以及一个DNS名称)时,会自动经由上面定义的几个虚拟服务处理,其效果就是全部访问v1版本的微服务。虚拟服务的路由逻辑在请求方的Sidecar中实现。
现在再打开浏览器访问/productpage,你会发现,不管刷新多少次,Reviews面板总是显示为无星。
从本章开始处的架构图可以知道,productpage会访问reviews服务,我们看一下前者Sidecar日志,可以发现类似下面的内容:
1 2 3 |
"GET /reviews/0 HTTP/1.1" 200 - 0 295 85 84 "-" "python-requests/2.18.4" "4e51c6d9-b7d7-483f-912c-986b4ca1ccff" # 目标服务 实际访问的IP:PORT组合 出站流量 访问reviews的v1版本 # 服务端 客户端 "reviews:9080" "172.27.252.190:9080" outbound|9080|v1|reviews.default.svc.k8s.gmem.cc - 10.105.173.219:9080 172.27.121.172:56202 |
在看看后者v1版本的Sidecar日志,可以发现相呼应的内容:
1 2 3 |
"GET /reviews/0 HTTP/1.1" 200 - 0 295 14 8 "-" "python-requests/2.18.4" "148f4c19-8106-4833-8a62-7b464b33e807" # 入站流量 "reviews:9080" "127.0.0.1:9080" inbound|9080||reviews.default.svc.k8s.gmem.cc - 172.27.252.190:9080 172.27.121.172:49704 |
你可以修改虚拟服务reviews,将v1替换为v2、v3,看看有何效果:
1 |
kubectl edit virtualservice reviews |
虚拟服务支持根据请求信息路由,下面的例子依据请求头end-user的值决定使用什么版本的reviews服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-test-v2.yaml apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: reviews spec: hosts: - reviews http: # 如果登陆用户为alex,则使用v2版本 - match: - headers: end-user: exact: alex route: - destination: host: reviews subset: v2 # 否则使用v1版本 - route: - destination: host: reviews subset: v1 |
现在,刷新/productpage可以发现是无星,以alex登陆(密码随意)后则显示为黑星。
故障注入(Fault Injection)属于破坏性测试,用于考验应用的韧性(resiliency)。
下面的例子,专为alex用户在reviews:v2和ratings之间引入7s的延迟:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# kubectl apply -f samples/bookinfo/networking/virtual-service-ratings-test-delay.yaml apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: ratings spec: hosts: - ratings http: # 对于alex用户,全部请求引入7s延迟,使用ratings的v1版本 - match: - headers: end-user: exact: alex fault: delay: percent: 100 fixedDelay: 7s route: - destination: host: ratings subset: v1 # 对于其它用户,也使用ratings的v1版本,但是不引入延迟 - route: - destination: host: ratings subset: v1 |
以alex访问/productpage,页面会在挂起6秒后提示reviews不可用。这是由于productpage到reviews的超时被硬编码为6秒,而reviews到ratings的超时被硬编码为10秒,我们的故障注入导致productpage访问reviews出现超时错误。
除了timeout之外,你还可以注入abort形式的故障,即立刻访问一个HTTP错误码。
Istio支持逐步的将流量从一个版本迁移到另一个,通常是从老版本迁移到新版本。
定义虚拟服务时,你可以设置不同destination的权重,并逐步调整,直到完全切换到另一个版本。下面的例子会让你由50%的概率看到无星或红星:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# kubectl apply -f samples/bookinfo/networking/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 - destination: host: reviews subset: v3 weight: 50 |
你可以为虚拟服务中的路由规则设置超时,默认的HTTP调用超时为15秒。
下面的例子把reviews服务的timeout设置为1ms,这意味着访问该服务的客户端都会基本都会收到超时错误:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# kubectl edit virtualservice reviews apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: reviews namespace: default spec: hosts: - reviews http: - route: - destination: host: reviews subset: v1 # 立即超时 timeout: 1ms |
此时再访问/productpage,会提示product reviews are currently unavailable。
istio-ingressgateway负责处理Gateway资源,就像K8S中Ingress Controller负责处理Ingress资源那样。
istio-ingressgateway默认已经同时支持HTTP/HTTPS,并在80/443端口上监听。
下面的例子创建一个HTTPS网关:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: httpbin-gateway spec: selector: istio: ingressgateway servers: - port: number: 443 name: https protocol: HTTPS tls: # 单向认证 mode: SIMPLE # 下面指定istio-ingressgateway的Pod的一个文件系统路径,提示HTTPS所需的证书、私钥的所在位置 # 默认情况下,网关控制器会将保密字典istio-ingressgateway-certs挂载到/etc/istio/ingressgateway-certs目录下 # 但是此保密字典默认并不创建,挂载也是可选的(optional: true),本文在《安装到K8S》一章已经创建了保密字典 # 如果你要多套数字证书需要使用,则必须在部署Charts时指定: # --set gateways.istio-ingressgateway.secretVolumes[N].name=ingressgateway-*-certs \ # --set gateways.istio-ingressgateway.secretVolumes[N].secretName=istio-ingressgateway-*-certs \ # --set gateways.istio-ingressgateway.secretVolumes[N].mountPath=/etc/istio/ingressgateway-*-certs \ # ... # 来挂载所有数字证书,添加新证书后,deployment istio-ingressgateway必须被编辑以使用新证书,对应Pod会重启 # 使用通配符证书以免除此麻烦 serverCertificate: /etc/istio/ingressgateway-certs/tls.crt privateKey: /etc/istio/ingressgateway-certs/tls.key # 如果mode为MUTUAL则需要提供CA证书位置 caCertificates: /etc/istio/ingressgateway-certs/ca.crt hosts: - "httpbin.k8s.gmem.cc" EOF |
你总是需要一个VirtualService来配合Gateway:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: httpbin spec: hosts: - "httpbin.k8s.gmem.cc" gateways: - httpbin-gateway http: - match: - uri: prefix: /status - uri: prefix: /delay route: - destination: port: number: 8000 host: httpbin EOF |
注意SSL Termination在网关,后端服务不需要启用HTTPS。
完成上述步骤后,访问https://httpbin.k8s.gmem.cc/status/418可以看到如下输出:
1 2 3 4 5 6 7 8 9 10 11 12 |
# 这里是入口网关的IP curl -HHost:httpbin.k8s.gmem.cc --resolve httpbin.k8s.gmem.cc:443:10.102.172.91 https://httpbin.k8s.gmem.cc/status/418 # -=[ teapot ]=- # _...._ # .' _ _ `. # | ."` ^ `". _, # \_;`"---"`|// # | ;/ # \_ _/ # `"""` |
和上一节的单向认证类似,但双向(mutual TLS )认证需要额外为入口网关控制器提供一个CA证书,用于校验客户端的数字证书。
使用官方Istio的Chart安装时,它默认从保密字典istio-ingressgateway-ca-certs加载CA证书,且挂载到/etc/istio/ingressgateway-ca-certs。
参考下面的代码修改Gateway的定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: httpbin-gateway spec: selector: istio: ingressgateway servers: - port: number: 443 name: https protocol: HTTPS tls: # 双向认证 mode: MUTUAL # 客户端证书和私钥 serverCertificate: /etc/istio/ingressgateway-certs/tls.crt privateKey: /etc/istio/ingressgateway-certs/tls.key # 服务器用来校验客户端证书的CA证书 caCertificates: /etc/istio/ingressgateway-ca-certs/ca-chain.cert.pem hosts: - "httpbin.k8s.gmem.cc" EOF |
然后,通过CA为客户端签发证书,并在访问服务器时指定自己的密钥和此证书:
1 |
curl ... --cacert /home/alex/Documents/puTTY/ca.crt --cert alex.cert --key alex.key ... |
要从服务网格内访问外部URL,需要配置绕过代理的IP范围,或者配置出口网关,否则无法访问仅仅能得到404:
1 2 3 4 |
SLEEP_POD=`kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name}` kubectl exec -it $SLEEP_POD -c sleep -- curl -o /dev/null -s -w "%{http_code}\n" http://gmem.cc # 404 |
下面是通过出口网关访问gmem.cc的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: gmem-ext spec: hosts: - gmem.cc ports: - number: 443 name: https protocol: HTTPS # 如果解析名称: # NONE,假设入站连接已经被解析。也就是说必须路由给请求发起者已经解析出的那个IP # STATIC,使用Endpoint中定义的静态IP地址 # DNS,通过DNS服务器解析。可能转发给不同的IP(与请求者的解析结果不同) resolution: DNS # 服务位置: # MESH_EXTERNAL,提示目标服务位于网格外部 # MESH_INTERNAL,提示目标服务位于网格内部。用于那些手工加入的服务,例如手工加入到K8S网格的VM # 对于网格外部的服务,必须明确指定,否则Kiali会显示Unkown节点 location: MESH_EXTERNAL EOF |
现在再尝试上面的kubectl exec命令,会发现能正常获得响应。
当访问外部HTTPS服务(TLS协议)时,可能需要同时创建ServiceEntry + VirtualService。此外如同访问网格内部服务一样,你可以通过VirtualService对外部服务进行流量管理。
下面的例子,允许网格内部访问mynewproddb.idc2提供的MySQL服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: mynewproddb spec: hosts: - mynewproddb.idc2 ports: - name: tcp number: 3306 protocol: TCP resolution: NONE location: MESH_EXTERNAL |
断路器(和异常检测)是创建韧性微服务应用所常用的一种模式,可以让应用程序免受上游(Upstream,即调用链中更加远离根的节点)服务失败、延迟峰值(latency spikes)或其它网络异常的侵害。
下面的示例配置了一个断路器,它在客户端访问httpbin服务时生效:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: httpbin spec: host: httpbin trafficPolicy: # 在客户端Sidecar中维护连接池,包含断路器配置(负载限制) connectionPool: # 对应Envoy的断路器配置 tcp: # 最大TCP连接数 maxConnections: 1 http: # 最大等待转发的、从应用容器发来的HTTP请求数 http1MaxPendingRequests: 1 # 每个TCP连接最多可以处理的HTTP请求数 maxRequestsPerConnection: 1 # 异常检测,用于剔除不正常的上游服务的实例 outlierDetection: # 将实例从负载均衡池中剔除,需要连续的错误(HTTP 5XX或者TCP断开/超时)次数 consecutiveErrors: 1 # 分析是否需要剔除的频率,多久分析一次 interval: 1s # 实例被剔除后,至少多久不得返回负载均衡池 baseEjectionTime: 3m # 负载均衡池中最多有多大比例被剔除 maxEjectionPercent: 100 EOF |
登陆到fortio来访问受断路器控制的httpbin服务:
1 2 3 |
FORTIO_POD=`kubectl get pod -l app=fortio -o jsonpath={.items..metadata.name}` # 负载测试 # 并发2,不限QPS,执行20次 kubectl exec -it $FORTIO_POD -c fortio -- fortio load -c 2 -qps 0 -n 20 -loglevel Error http://httpbin:8000/get |
上面的fortio命令并发度为2,而断路器仅仅允许1个并发请求,因此会有几率出现错误:
1 2 3 4 5 6 7 8 9 10 11 12 |
Fortio 1.0.1 running at 0 queries per second, 4->4 procs, for 20 calls: http://httpbin:8000/get Aggregated Function Time : count 20 avg 0.022291169 +/- 0.03153 min 0.000243608 max 0.086822606 sum 0.44582338 # target 50% 0.0055 # target 75% 0.0225 # target 90% 0.0834113 # target 99% 0.0864815 # target 99.9% 0.0867885 Sockets used: 5 (for perfect keepalive, would be 2) # 错误率15% Code 200 : 17 (85.0 %) Code 503 : 3 (15.0 %) All done 20 calls (plus 0 warmup) 22.291 ms avg, 86.0 qps |
如果将并发度设置为20,连续发送1000次,则错误率飙高到90%:
1 2 |
Code 200 : 94 (9.4 %) Code 503 : 906 (90.6 %) |
查询客户端Proxy的统计信息,可以看到更多细节:
1 2 3 4 5 6 7 |
kubectl exec -it $FORTIO_POD -c istio-proxy -- sh -c 'curl localhost:15000/stats' | grep httpbin | grep pending # cluster.outbound|8000||httpbin.default.svc.k8s.gmem.cc.upstream_rq_pending_active: 0 # cluster.outbound|8000||httpbin.default.svc.k8s.gmem.cc.upstream_rq_pending_failure_eject: 0 # pstream_rq_pending_overflow提示被断路掉的请求次数 # cluster.outbound|8000||httpbin.default.svc.k8s.gmem.cc.upstream_rq_pending_overflow: 10326 # cluster.outbound|8000||httpbin.default.svc.k8s.gmem.cc.upstream_rq_pending_total: 1016 |
下面的虚拟服务,将所有请求发送给v1,同时镜像一份给v2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: httpbin spec: hosts: - httpbin http: - route: - destination: host: httpbin subset: v1 weight: 100 # 流量镜像 mirror: host: httpbin subset: v2 EOF |
发送给镜像服务的请求,其Host/Authority请求头被自动加上-shadow后缀。
利用Mixer和Sidercar,可以获得服务网格的各种统计指标(Metrics)、日志,或者跨越不同的服务进行分布式的调用链追踪。
通过配置,Istio可以自动收集网格中某个服务的统计指标,或者为每次请求产生日志。
下面的例子,为每个请求生成一个Prometheus指标值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# 定义一个指标(Metrics)类型的instances的规格,将Istio请求的属性映射为包含维度、度量的指标 # 针对每次请求,都会根据这里的配置,生成一个metrics类型的实例 apiVersion: "config.istio.io/v1alpha2" kind: metric metadata: # 名称 name: doublerequestcount namespace: istio-system spec: # 度量信息,为固定值2 value: "2" # 维度信息 dimensions: # 维度名 维度值 # context,source,destination等都是预定义的Istio请求属性 reporter: conditional((context.reporter.kind | "inbound") == "outbound", "client", "server") # 竖线用于提供默认值 source: source.workload.name | "unknown" destination: destination.workload.name | "unknown" # 除了请求属性表达式外,还可以使用字面值 message: '"twice the fun!"' monitored_resource_type: '"UNSPECIFIED"' --- # 配置一个prometheus适配器,也叫处理器(Handler) # 适配器能够处理instances并和输出到外部系统 apiVersion: "config.istio.io/v1alpha2" kind: prometheus metadata: name: doublehandler namespace: istio-system spec: metrics: # 生成的Prometheus指标的名称,注意,prometheus适配器总是自动添加istio_前缀 - name: double_request_count # instance规格的全限定名称,提供当前适配器的输入 instance_name: doublerequestcount.metric.istio-system # 指标的类型 kind: COUNTER # 产生哪些标签 label_names: # 这些维度作为标签 - reporter - source - destination - message --- # 将doublerequestcount.metric的所有instance派发给适配器prometheus apiVersion: "config.istio.io/v1alpha2" kind: rule metadata: name: doubleprom # 定义在默认配置命名空间,且没有match配置,因此会派发所有instance namespace: istio-system spec: actions: - handler: doublehandler.prometheus instances: - doublerequestcount.metric |
下面的例子,为每个请求产生一条日志:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# 定义另外一种instance —— 日志条目 —— 的规格 apiVersion: "config.istio.io/v1alpha2" kind: logentry metadata: name: newlog namespace: istio-system spec: # 日志严重度级别 severity: '"warning"' # 日志时间戳 timestamp: request.time # 日志变量 variables: source: source.labels["app"] | source.workload.name | "unknown" user: source.user | "unknown" destination: destination.labels["app"] | destination.workload.name | "unknown" responseCode: response.code | 0 responseSize: response.size | 0 latency: response.duration | "0ms" monitored_resource_type: '"UNSPECIFIED"' --- # 这种处理器处理instance并输出到标准输出 apiVersion: "config.istio.io/v1alpha2" kind: stdio metadata: name: newhandler namespace: istio-system spec: severity_levels: # 将instance.severity == warning映射为Params.Level.WARNING这个日志级别 warning: 1 # 生成JSON格式的日志 outputAsJson: true --- # 将logentry的所有实例发送给stdio处理 apiVersion: "config.istio.io/v1alpha2" kind: rule metadata: name: newlogstdio namespace: istio-system spec: # 匹配所有请求 match: "true" actions: - handler: newhandler.stdio instances: - newlog.logentry --- |
将上述两端配置文件保存为yaml文件,并通过kubectl apply命令安装到K8S,然后进行下一步试验。
打开Istio内置Prometheus服务的Web界面, 输入istio_double_request_count进行查询,可以看到每个微服务的请求次数都被client、server方分别报送,并预先聚合了。
实际上Istio官方提供的Chart中,以及提供了HTTP/TCP请求的次数、持续时间、请求长度、应答长度,以及访问日志的instance/handler/rule配置,可以直接安装使用。
打开Istio内置Grafana服务的Web界面,可以看到Istio目录下预定义好了7个仪表盘页面。该服务使用grafana/grafana:5.0.4版本的镜像。
参考配置请求限速一节。
你可以使用Mixer中可见的任何请求属性来控制对服务的访问。下面的例子禁用了对ratings:v3的访问:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# 这种适配器用于提示Envoy拒绝访问 apiVersion: "config.istio.io/v1alpha2" kind: denier metadata: name: denyreviewsv3handler spec: status: # 状态码和消息 code: 7 message: Not allowed --- # instance apiVersion: "config.istio.io/v1alpha2" kind: checknothing metadata: name: denyreviewsv3request spec: --- # rule apiVersion: "config.istio.io/v1alpha2" kind: rule metadata: name: denyreviewsv3 spec: # 只要是reviews访问ratings的v3版本,立即拒绝 match: destination.labels["app"] == "ratings" && source.labels["app"]=="reviews" && source.labels["version"] == "v3" actions: - handler: denyreviewsv3handler.denier instances: [ denyreviewsv3request.checknothing ] |
Istio也支持基于请求属性的黑白名单,下面的例子和上面的denier是等价的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# 列表检查器,值匹配列表项则进入黑或白名单 apiVersion: config.istio.io/v1alpha2 kind: listchecker metadata: name: whitelist spec: overrides: ["v1", "v2"] # 静态值列表 # 这是白名单而非黑名单 blacklist: false --- # 列表条目instance,从请求抽取单个值 apiVersion: config.istio.io/v1alpha2 kind: listentry metadata: name: appversion spec: # 取版本号 value: destination.labels["version"] --- # 关联两者,效果就是检查版本号,如果不是v1/v2则拒绝访问 apiVersion: config.istio.io/v1alpha2 kind: rule metadata: name: checkversion spec: match: destination.labels["app"] == "ratings" actions: - handler: whitelist.listchecker instances: - appversion.listentry |
以--set tracing.enabled=true选项安装Istio Chart,即可使用。由于默认设置的采样率较低,你需要反复访问多次/productpage才能捕获到Trace。
打开Istio内置Jaeger服务的Web界面,点击顶部按钮切换到Search页签,在Find Traces面板中Service选取productpage,点击Find Traces,看到如下的Trace列表:
点击其中一个Trace,可以查看其调用链信息:
可以看到,这个由单次/productpage请求产生的调用链由6个Span构成,每个Span对应一个彩色条带。每个Span消耗的时间都标注在界面上了。
打开Istio内置Jaeger服务的Web界面,点击顶部按钮切换到Dependencies页签,可以看到Jaeger依据Traces计算出的服务依赖关系图(DAG,有向无环图):
尽管Istio的Sidecar能够自动发送Span给Jaeger,要将这些Span合并为完整的Trace还需要额外的信息,应用程序必须提供必要的HTTP请求头。
每个微服务都需要收集调用它的HTTP请求的x-request-id、x-b3-traceid、x-b3-spanid、x-b3-parentspanid、x-b3-sampled、x-b3-flags、x-ot-span-context头,并在它调用任何上游(Upstream,上图的ratings是reviews的Upstream)微服务时,带上这些头。
Bookinfo中,productpage服务基于Python编写,收集请求头的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
def getForwardHeaders(request): headers = {} if 'user' in session: headers['end-user'] = session['user'] incoming_headers = [ 'x-request-id', 'x-b3-traceid', 'x-b3-spanid', 'x-b3-parentspanid', 'x-b3-sampled', 'x-b3-flags', 'x-ot-span-context' ] for ihdr in incoming_headers: val = request.headers.get(ihdr) if val is not None: headers[ihdr] = val return headers |
reviews服务则基于Java编写, 收集请求头的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
@GET @Path("/reviews/{productId}") public Response bookReviewsById(@PathParam("productId") int productId, @HeaderParam("end-user") String user, @HeaderParam("x-request-id") String xreq, @HeaderParam("x-b3-traceid") String xtraceid, @HeaderParam("x-b3-spanid") String xspanid, @HeaderParam("x-b3-parentspanid") String xparentspanid, @HeaderParam("x-b3-sampled") String xsampled, @HeaderParam("x-b3-flags") String xflags, @HeaderParam("x-ot-span-context") String xotspan) { int starsReviewer1 = -1; int starsReviewer2 = -1; if (ratings_enabled) { JsonObject ratingsResponse = getRatings(Integer.toString(productId), u ser, xreq, xtraceid, xspanid, xparentspanid, xsampled, xflags, xotspan); } } |
Istio默认会追踪任何请求, 对于开发、测试环境这是可以的,线上环境通常需要降低采样率,否则压力太大。调整采样率有两种方式:
- 使用官方Chart安装时,调整pilot.traceSampling,当前使用的Chart版本已经将其设置为1.0,即百分之一的采样率
- 修改Deployment istio-pilot的环境变量PILOT_TRACE_SAMPLING
采样率的精度是0.01
1 2 3 4 |
samples/bookinfo/platform/kube/cleanup.sh kubectl delete -f samples/httpbin/httpbin.yaml kubectl delete -f samples/httpbin/sample-client/fortio-deploy.yaml kubectl delete -f samples/sleep/sleep.yaml |
Istio的流量管理模型,从根本上将流量(traffic flow)和基础设施扩容(infrastructure scaling)进行了解耦。通过Pilot,你可以通过更细致的规则来说明流量如何流动,而不是简单的指定哪个VM/Pod来接收流量。例如,你可以指定某个服务的5%的流量发往金丝雀版本(不管金丝雀的实例数量),或者指定根据请求内容来选择特定版本的服务:
将流量(traffic flow)和基础设施扩容解耦后,Istio可以在应用程序代码外部提供大量的流量管理特性,包括AB测试的请求路由、逐步发布、金丝雀发布,基于超时、重试、断路器的故障恢复,以及为了测试故障恢复策略而进行的故障注入(fault injection)。
流量管理的核心组件是Pilot,它管理、配置所有部署在服务网格中的Envoy代理实例,对Envoy的完整生命周期负责。Pilot除了可以配置路由规则、配置故障恢复特性之外,还维护网格中所有服务的规范(canonical)模型。Envoy就是基于此模型,通过自己的发现服务,找到网格中其它Envoy。
每个Envoy实例都基于从Pilot得到的信息、以及针对负载均衡池中的实例进行周期性的健康检查,来维护负载均衡信息。根据此负载均衡信息,Envoy能够在遵守路由规则的同时,智能的在目的实例之间分发请求。
规范模型是独立于底层平台的,和底层平台的对接由Pilot中的适配器负责,适配器调用底层平台的API(例如K8S适配器实现了必要的控制器来Watch API Server,获取Pod、Ingress等资源的变动),并产生适当的规范模型。
Pilot启用了服务发现,路由表,以及对负载均衡池的动态更新。
使用Pilot的规则配置(Rule Configuration),可以制定高级别的流量管理规则。这些规则会被转换为低级别的配置信息,并分发给Envoy实例。
如上文所述,网格中服务的规范模型由Pilot维护。Istio还引入了服务版本的概念,用于对服务实例进行细粒度区分。服务版本既可以指服务API的版本(v1,v2),也可以指服务运行环境(stg,pdt)或任何其它属性。
在上图中,服务的客户端对服务的不同版本毫无感知。客户端仍然使用服务的IP或主机名来访问,只是Envoy在客户端-服务器之间拦截并转发了请求和响应。
Envoy根据你通过Pilot指定的路由规则,决定实际使用的服务版本。这让应用程序代码和被依赖服务之间实现解耦,可分别独立演化。在路由规则中,你可以指定让Envoy依据源/目标的消息头、Tag来判定服务版本。
Istio假设服务网格的所有出入流量都经由Envoy代理转发。通过这种前置代理可以实现面向用户的服务,包括AB测试、金丝雀部署。在访问外部服务时,Envoy可以实现必要的故障恢复特性,包括超时、重试,并获取外部服务的性能指标。
Istio能够在网格中服务实例之间进行负载均衡。
Istio假设基础设置提供一个服务注册表(service registry,例如K8S的Service),此表跟踪某个服务的VM/Pod集。此服务注册表应当负责新实例的加入、不健康实例的移除。
Istio从服务注册表中读取服务的信息,并生成平台无关的服务发现接口。Envoy执行服务发现(基于xDS和Pilot交互),并动态的更新其本地负载均衡池。
网格中的服务,利用DNS名称来相互访问。所有针对某个服务的流量,都会在出站前经由Envoy进行重新路由。负载均衡池是决定路由到哪个节点的关键因素。Envoy支持多种负载均衡算法,但是Istio目前仅仅允许:轮询、随机、带权重的最少请求。
Envoy还会周期性的检查负载均衡池中实例的健康情况,这种检查从外部(准确的说是服务消费者)发出,而不是像K8S Pod健康检查那样,在Pod本地执行。
判断实例是否健康时,Envoy遵循一种断路器模式(circuit breaker pattern),此模式下调用实例API的故障率决定了实例是否健康。当健康检查连续失败指定的次数后,实例被移除负载均衡池;当被移除的实例连续成功指定次数后,它又回到负载均衡池。
如果实例以503代码响应健康检查请求,则它会立即被调用者移出负载均衡池。
Envoy提供了一系列开箱即用的错误恢复机制:
- 超时
- 有限次数的重试,支持可变的重试延迟(jitter)
- 限制针对上游服务的并发连接数、并发请求数
- 主动健康检查——针对负载均衡池中所有成员进行健康检查,并移除不健康实例,移回健康实例
- 可精细控制的断路器(被动健康检查) ,同样针对负载均衡池中的每个实例
所有这些特性,都可以在运行时,利用Istio的流量管理规则动态配置。
联用主动、被动健康检查,可以更大程度上降低访问到不健康实例的几率。联用底层基础设施的健康检查机制,不健康实例可以很快的被剔除。
利用流量管理规则,你可以为每个服务/版本来配置默认的故障恢复特性。
用于辅助测试端对端的故障恢复能力。
Istio支持多种具体的协议,并向其注入错误信息。而不是粗暴的杀死Pod,或者在TCP层延迟/污染网络包。你可以在应用层注入更加有意义的错误,例如HTTP的状态码。
Istio支持对特定请求进行错误注入,或者指定多少百分比的请求被错误注入。
可以注入的错误有两类:
- 延迟:模拟网络延迟和上游服务过载
- 中止:模拟上游服务错误,通常使用HTTP错误码、TCP连接错误的方式
流量镜像(Traffic mirroring)也叫shadowing,允许开发团队在最小风险的前提下改变产品的功能。其做法是将线上环境的流量复制到一个“镜像服务“中,镜像服务和主请求处理链是隔离的。
VirtualService支持为route配置mirror,实现流量镜像。
网关是运行在服务网格边缘的负载均衡器,它处理入站或出站的TCP/HTTP连接。
Istio提供入口网关,出口网关,对应:
- 的CRD为Gateway、Gateway+ServiceEntry
- 的Controller为istio-ingressgateway、istio-egressgateway
当提起术语“网关”时,基于上下文可能指Gateway这种资源,也可能指出口/入口网关的Service/Pod。
在典型的Kubernetes环境下,Ingress控制器读取Ingress资源,提供外部访问K8S集群的入口。
而使用Istio时,Ingress资源的地位可以由Gateway + VirtualServices代替。在集群内部组件相互交互时,不需要配置Gateway资源。需要注意,Istio也可以作为Ingress控制器,处理K8S原生的Ingress资源。
使用Istio Gateway时的典型的客户端访问时序如下:
- 客户端发起针对负载均衡器的请求
- 负载均衡器将请求转发给集群的istio-ingressgateway服务,该服务可以部署为NodePort,它的后端是一个Deployment
- IngressGateway根据Gateway资源、VirtualService资源的配置信息,将请求路由给K8S的Service
- Gateway提供端口、协议、数字证书信息
- VirtualService提供到K8S Service的路由信息
- K8S Service将请求路由给Pod
该时序如下图所示,注意IngressGateway的Sidecar负责转发客户端请求:
每当你创建/修改Gateway、VirtualService资源,Pilot会监测到并将其转换为Envoy配置,然后发送给相关的Sidecar,包括运行在IngressGateway Pod中的Envoy。
默认情况下,启用了Istio的微服务无法访问集群外部的URL, 这是因为Envoy修改了Pod所在网络命名空间的Iptables,透明的将所有出口流量都重定向到Sidecar,而不经任何配置Sidecar只会处理网格内部的Destination。
要将外部服务暴露给启用了Sidecar的网格内部Pod/VM(它们是外部服务的客户端),必须:
- 定义ServiceEntry,这种自定义资源允许针对指定的外部IP/DNS名称的流量通过Sidecar。ServiceEntry支持通配符匹配主机,可以降低配置工作量
- 或者,配置一个IP范围,让针对此IP范围的出站请求经过/绕过Envoy代理。你可以设置Chart变量,
12# 仅仅10.5.0.0/16走Envoy代理,其它直连。你可以将此Pod网络、K8S服务网络的CIDR配置到该字段中。这样,所有针对网格外部的访问都开放了--global.proxy.includeIPRanges="10.5.0.0/16"则当从网格内部访问10.5网段时直接绕过代理。
注意:此特性已经弃用,可以使用配置字典istio-sidecar-injector中的的includeOutboundIpRanges,或者Pod注解traffic.sidecar.istio.io/includeOutboundIPRanges代替
强调一下,访问集群内部域名、IP地址不受限制,即使不配置VirtualService也可以访问,而且Kiali直接可以识别出这种流量。
ServiceEntry还可以配合Gateway+VirtualService,将出口流量导向特殊的服务 —— istio-egressgateway,这种配置方式的适用场景是:
- 离开网格的所有流量必须流经一组专用节点,这一组节点有特殊的监控和审查
- 集群中的一般性节点不能联通网格外部
Istio使用一个简单的配置模型来描述API调用/TCP流量应该如何在服务网格中流动。使用此配置模型,你可以:
- 配置服务级别的属性,例如断路器、超时、重试
- 配置通用的持续交付(CD)任务,例如金丝雀发布(Canary rollout,即灰度发布)、A/B测试、阶段性部署(Staged rollout)——基于百分比的流量分配
Istio的配置模型映射为K8S的资源,包括VirtualService、DestinationRule、ServiceEntry、Gateway四种。在K8S中你可以用kubectl来配置这些资源,它们都是CR。
定义服务的请求如何在服务网格中路由。虚拟服务能够将请求路由给服务的不同版本,甚至是完全不同的服务。
VirtualService通过Istio的服务注册表定义了如何路由到一个或多个特定的Destinations(目标服务)。VirtualService可以根据请求的属性(如HTTP头、路径等)来匹配请求,并根据这些规则将流量路由到不同的目标。这意味着,对于给定的域名,基于请求的不同属性(比如URL路径),VirtualService可以将流量路由到多个不同的Destination。
虚拟服务自身不会在Istio服务注册表中添加新的服务条目。它的spec.hosts,用来指定进行路由时,如何匹配主机名,这通常对应HTTP请求的Host头。
Istio服务注册表中服务条目的来源包括:
- K8s服务
- ServiceEntry定义的服务
- Consul、Eureka等服务发现机制暴露的服务
- 在某些情况下,服务信息可以通过Istio的配置文件静态定义。这种方式较少用于生产环境,但可能在测试或特定场景下有其用途
下面的示例,将针对review服务的请求,3/4发给v1版本,1/4发给v2版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: # 虚拟服务的名字 name: reviews spec: hosts: # 访问到此虚拟服务时,使用主机名,可以是DNS名称、带通配符的DNS名称、IP地址 # DNS名称可以是FQDN,对于K8S这样的平台,可以使用短DNS名称(无点号),例如使用reviews代替reviews.default.svc.cluster.local # Istio会根据此虚拟服务(而非K8S的Service)所在的命名空间,生成FQDN # 注意: # 1、每个host只能被单个虚拟服务声明。单个虚拟服务可以用于描述多个HTTP/TCP端口的流量属性 # 2、对于HTTP/TCP流量,hosts字段均生效 # 3、对于网格内部的服务(存在于服务注册表),必须总是使用服务DNS名,不得使用IP地址 - reviews http: # 路由规则的数组 - route: - destination: # host必须是Istio服务注册表中,没有歧义的名称。此注册表中的服务来源包括: # 1、底层平台(K8S)服务注册表的全部条目(即K8S Service) # 2、通过ServiceEntry声明的服务条目 # # 对于K8S用户需要注意,使用DNS短名时,Istio将根据DestinationRule的命名空间来生成FQDN # 而不是当前VirtualService的命名空间 host: reviews # 发送给reviews服务实例的v1子集 # 这里仅仅指出子集的名称,子集的规格通过DestinationRule配置 subset: v1 # 路由到此版本的权重,取值0-100 weight: 75 - destination: host: reviews # 如果目标服务仅仅暴露单个端口,则不需要声明下面这个字段 port: 80 subset: v2 weight: 25 |
默认情况下HTTP请求的超时为15秒,你可以覆盖此默认值。重试也类似:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
... spec: ... http: - route: # 超时 timeout: 10s # 重试 retries: # 最多重试次数 attempts: 3 # 每次重试的超时 perTryTimeout: 2s |
也可以在请求级别覆盖超时、重试配置,只需要提供Envoy规定的特殊请求头x-envoy-upstream-rq-timeout-ms、x-envoy-max-retries即可。
你可以为http进行故障注入,要么是delay,要么是abort。
下面的例子,为ratings服务的v1版本10%的请求引入5秒的延迟:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: ratings spec: hosts: - ratings http: - fault: delay: # 多少比例的请求被注入故障 percent: 10 # 引入固定5秒的延迟 fixedDelay: 5s route: - destination: host: ratings subset: v1 |
下面的例子,则为10%的请求引发400错误:
1 2 3 4 5 6 7 8 9 10 |
... spec: ... http: - fault: abort: # 多少比例的请求被注入故障 percent: 10 # 返回的HTTP状态码 httpStatus: 400 |
delay和abort是可以联合使用的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
... metadata: name: ratings spec: http: # 从reviews服务v2版本发起的,针对ratings服务v1版本的请求 - match: - sourceLabels: app: reviews version: v2 fault: # 引入5秒延迟 delay: fixedDelay: 5s # 为10%请求引发400错误 abort: percent: 10 httpStatus: 400 |
规则可以仅仅针对满足特定条件的请求。
可以限定请求者的标签,在K8S环境下即Pod的Label:
1 2 3 4 5 6 7 8 9 |
... spec: http: # 仅针对reviews服务v2版本发起的请求 - match: - sourceLabels: app: reviews version: v2 route: ... |
也可以限定HTTP请求头:
1 2 3 4 5 6 7 8 |
... spec: http: - match: - headers: # end-user头必须为alex end-user: exact: alex |
还可以限定请求URL路径、HTTP方法、HTTP Scheme、HTTP Authority值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
... spec: http: - match: - uri: # 请求URL,除去scheme://host部分,必须以/api/v1开头 # 除了prefix,还可以配置 # exact,表示精确匹配 # regex,表示正则式匹配 prefix: /api/v1 - scheme: exact: https - authority: regex: ".*.gmem.cc" - method: regex: "GET|POST" |
多个条件可以进行逻辑与/或。在单个match元素中声明多个条件,则为AND:
1 2 3 4 5 6 7 8 9 10 |
spec: http: - match: # 同时限定Pod标签、请求头 - sourceLabels: app: reviews version: v2 headers: end-user: exact: alex |
在多个match元素中分别声明,则为OR:
1 2 3 4 5 6 7 8 9 10 11 |
spec: http: - match: # Pod标签匹配 - sourceLabels: app: reviews version: v2 # 或者请求头匹配 - headers: end-user: exact: alex |
如果对于相同的目的地(host + subset),配置了两个或更多的规则(http子元素),那么第一个(配置文件最前面)匹配的规则优先级最高。
Istio支持为虚拟服务配置跨源资源共享策略:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
spec: http: - route: corsPolicy: # 允许发起CORS请求的源,设置为*则允许任何源 allowOrigin: - gmem.cc # CORS请求可以使用的HTTP方法 allowMethods: - POST - GET # Access-Control-Allow-Credentials头 allowCredentials: false # 允许的请求头 allowHeaders: - X-Foo-Bar # preflight request可以被缓存的时间 maxAge: "1d" |
虚拟服务可以直接返回301重定向给调用者:
1 2 3 4 5 6 7 |
spec: http: redirect: # 重定向时覆盖URL查询路径 uri: /v1/bookRatings # 重定向时覆盖URL的Authority(Host)部分 authority: newratings.default.svc.cluster.local |
虚拟服务可以在转发请求给Dest之前,对HTTP请求的特定部分进行修改:
1 2 3 4 5 6 7 8 9 |
spec: http: - match: - uri: prefix: /ratings # 将URL前缀从/ratings修改为/v1/bookRatings rewrite: uri: /v1/bookRatings authority: ... |
参考上文的例子。
这里是一个TCP路由的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: tcp-route namespace: default spec: hosts: - mesh.gmem.cc tcp: - match: - port: 3306 source_labels: app: mysql-client route: - destination: host: mysql subset: v2 port: number: 3306 weight: 100 - match: - port: 2181 route: - destination: host: zk subset: v1 port: number: 2181 weight: 100 |
在VirtualService定义了路由规则后,DestinationRule进一步应用一系列的策略到请求上。DestinationRule定义了对于特定目标服务的流量策略,如负载均衡策略、连接池大小、TLS设置等。当VirtualService根据规则将流量路由到特定目标后,DestinationRule定义了如何处理到达这些目标的流量。
DestinationRule中的spec.host同样是指定这个规则应用到哪些请求,不会在Istio注册表中定义服务条目。
下面的示例,声明了服务子集:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: reviews spec: # 访问什么服务时应用此规则 host: reviews # 配置服务的子集 # 在K8S中,服务的具有标签version=v1的Pod,并加入到v1子集 subsets: - name: v1 labels: version: v1 - name: v2 labels: version: v2 |
可以针对所有子集配置:
1 2 3 4 5 6 7 8 9 10 11 |
... spec: host: reviews trafficPolicy: # 负载均衡策略 loadBalancer: # 算法:ROUND_ROBIN(默认) # LEAST_CONN, O(1)复杂度算法,随机选取两个健康实例,并取其中活动请求少的 # RANDOM # PASSTHROUGH,直接转发给调用者请求的IP地址,不进行任何负载均衡 simple: RANDOM |
也可以针对特定子集:
1 2 3 4 5 6 7 8 |
spec: subsets: - name: v2 labels: version: v2 trafficPolicy: loadBalancer: simple: ROUND_ROBIN |
基于一致性哈希的负载均衡可以实现基于HTTP请求头、Cookie或其它属性的软的会话绑定(Session affinity):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: bookinfo-ratings spec: host: ratings.prod.svc.cluster.local trafficPolicy: loadBalancer: consistentHash: # 三选一 # 基于请求头进行哈希计算 httpHeaderName: end-user # 基于源IP地址进行哈希计算 useSourceIp: true # 基于Cookie进行哈希计算,如果Cookie不存在,Envoy会自动产生 httpCookie: # Cookie的名称、路径、生命周期 name: user path: .. ttl: 0s |
可以限制某个服务/子集的并发度:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
spec: subsets: - name: v1 labels: version: v1 trafficPolicy: # 限制并发 # 每个Sidecar(也就是每个Pod)为每个上游服务的实例都配备一个连接池,断路器依赖此连接池才能工作 # connectionPool可以有tcp、http两个字段 connectionPool: tcp: # 最大连接数 maxConnections: 100 # 连接超时 connectTimeout: http: # 最大等待处理的请求数量 http1MaxPendingRequests: 1024 # HTTP2最大请求数 http2MaxRequests: 1024 # 每个TCP连接可以被多少请求共享使用(重用),设置为1则禁用keep-alive maxRequestsPerConnection: 32 # 最大重试次数 maxRetries: 3 |
可以为某个服务/子集配置断路器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
spec: subsets: - name: v1 labels: version: v1 trafficPolicy: # 异常检测,断路 outlierDetection: # 将实例从负载均衡池中剔除,需要连续的错误(HTTP 5XX或者TCP断开/超时)次数 consecutiveErrors: 1 # 分析是否需要剔除的频率,多久分析一次 interval: 1s # 实例被剔除后,至少多久不得返回负载均衡池 baseEjectionTime: 3m # 负载均衡池中最多有多大比例被剔除 maxEjectionPercent: 100 |
用于访问不受网格管理的服务,将其作为服务条目添加到Istio内部管理的服务注册表(service registry)中。
ServiceEntry允许在Istio的内部服务注册表中手动添加条目,这些条目可以是网格内部的服务(MESH_INTERNAL)或网格外的服务(MESH_EXTERNAL)。这意味着你可以使Istio意识到那些不直接由Istio管理的服务。
对于MESH_INTERNAL类型的ServiceEntry,它用于添加网格内的服务到Istio的内部服务发现系统,这些服务可能由于某些原因(比如,它们运行在不同的Kubernetes集群或不同的网络命名空间中)而未被自动识别为网格服务。
下面的例子,允许Envoy代理针对gmem.cc的HTTP请求:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: gmem-ext-svc spec: hosts: # 可以使用通配符(仅仅针对网格外部服务),表示一个白名单,允许从服务网格内部访问它们 # 注意,1.0.5貌似不支持通配符? - *.gmem.cc location: MESH_EXTERNAL resolution: DNS ports: - number: 80 name: http protocol: HTTP - number: 443 name: https protocol: HTTPS |
ServiceEntry可以和VirtualService一起工作,作为VirtualService的host。这是因为从概念上来说,来自底层基础设施注册表的服务、ServiceEntry定义的服务,都是Istio的概念性的Service。
下面的例子以主机名来静态的指定外部服务端点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: external-svc-dns spec: hosts: - foo.bar.com location: MESH_EXTERNAL ports: - number: 80 name: https protocol: HTTP resolution: DNS # 端点列表 endpoints: - address: us.foo.bar.com ports: https: 8080 - address: uk.foo.bar.com ports: https: 9080 - address: in.foo.bar.com ports: https: 7080 |
下面的例子以IP来指定外部服务端点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: external-svc-mongocluster spec: hosts: - mymongodb.somedomain # 下面的字段仅仅支持resolution=NONE|STATIC addresses: - mymongodb.ip/32 ports: - number: 27018 name: mongodb protocol: MONGO location: MESH_INTERNAL # 使用endpoints中定义的静态IP地址 resolution: STATIC # 解析到: endpoints: - address: 2.2.2.2 - address: 3.3.3.3 |
关于hosts、addresses,以及服务的“唯一性标识”,Istio官方API文档有如下说明:
- hosts字段:
- 对于非HTTP服务(包括不透明HTTPS)来说,此字段中指定的DNS名称被忽略
- 对于非HTTP服务,如果此字段指定的是DNS名称,则ports,或/和addresses中的IP地址用于唯一性的标识目的地
- addresses字段: 表示关联到此服务的虚拟IP地址,可以是CIDR前缀:
- 对于HTTP服务来说,此字段被忽略,目的地根据HTTP头Host/Authority确定
- 对于非HTTP服务,hosts头被忽略。如果指定了addresses,则入站请求的IP地址和addresses指定的CIDR进行匹配,如果匹配成功,则认为请求的是当前服务。如果不指定addresses,则入站流量仅仅根据ports来识别,这种情况下,服务的访问端口不能被网格内部任何其它服务共享
这段描述让人费解,也不符合实际试验结果。下面的两个ServiceEntry,都是非HTTP服务,都使用443端口,都没有指定addresses字段,但是没有任何冲突出现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: cdn.gmem.cc spec: hosts: - cdn.gmem.cc ports: - name: tcp number: 443 protocol: TCP resolution: DNS location: MESH_EXTERNAL --- apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: blog.gmem.cc spec: hosts: - blog.gmem.cc ports: - name: tcp number: 443 protocol: TCP resolution: DNS location: MESH_EXTERNAL |
生成的Envoy配置如下(可以看到集群、端点信息都是正常有效的):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
// istioctl proxy-config cluster ubuntu --fqdn=cdn.gmem.cc -o json [ { "name": "outbound|443||cdn.gmem.cc", "type": "STRICT_DNS", "hosts": [ { "socketAddress": { "address": "cdn.gmem.cc", "portValue": 443 } } ] } ] // istioctl proxy-config cluster ubuntu --fqdn=blog.gmem.cc -o json [ { "name": "outbound|443||blog.gmem.cc", "type": "STRICT_DNS", "hosts": [ { "socketAddress": { "address": "blog.gmem.cc", "portValue": 443 } } ] } ] // istioctl proxy-config endpoint ubuntu --cluster='outbound|443||cdn.gmem.cc' -o json [ { "name": "outbound|443||cdn.gmem.cc", "addedViaApi": true, "hostStatuses": [ { "address": { "socketAddress": { "address": "27.221.30.56", "portValue": 443 } } } ] } ] // istioctl proxy-config endpoint ubuntu --cluster='outbound|443||blog.gmem.cc' -o json [ { "name": "outbound|443||blog.gmem.cc", "addedViaApi": true, "hostStatuses": [ { "address": { "socketAddress": { "address": "39.107.94.255", "portValue": 443 } } ] } ] |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: unix-domain-socket-example spec: hosts: - "example.unix.local" location: MESH_EXTERNAL ports: - number: 80 name: http protocol: HTTP resolution: STATIC endpoints: - address: unix:///var/run/example/socket |
配置在网格的边缘,用于外部访问服务网格,为TCP/HTTP流量提供负载均衡。 Gateway仅仅用在独立运行Istio Proxy的那些Ingress/Egress网关上,定义在网格边缘上暴露的名字:
- 如果Gateway定义在Ingress网关上,那么对网格外部暴露名字。当然,外部需要将这些名字的DNS记录指向Istio网关对外的IP地址
- 如果Gateway定义在Egress网关上:
- 如果通过配置限定所有出口流量必须走Egress网关,那么对集群内部暴露名字
- 使用VirtualService,可以将某个名字强行引导自网关,自网关出去
和Kubernetes的Ingress不同,Istio网关仅仅在L4-L6上配置,而不使用L7。 你可以把Gateway绑定到VirtualService,进而使用Istio标准的方式来管理HTTP请求和TCP流量。
Gateway可以定义多个“服务器”(servers),每个服务器可以匹配多个hosts,注意,这些hosts同样只是用来说明哪些名字匹配这个Gateway定义。每个服务器会在网关Istio Proxy中产生一个监听端口,这个端口有port字段确定。
当流量进入网关后,具体转发给谁,取决于此流量HTTP头Host,和Istio什么服务条目(来自K8s的服务或者ServiceEntry定义的服务)、路由规则(hosts相匹配,且应用到此网关的VirtualService)匹配。
下面的例子,允许网格外部发送针对gmem.cc的HTTPS请求:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: mesh.gmem.cc spec: # 标签列表,指定应用此网关配置的Pod/VM具有的标签集 # 具有匹配标签的Pod(通常是ingressgateway/egressgateway)的Envoy接收配置 # 目标Pod必须是以proxy模式运行的Pilot agent,也就是说你不能随意指定标签选择器 selector: istio: ingressgateway # 在这里定义所有的服务器,其端口都将成为网关Pod的物理监听端口 # 对于这个例子,你可以使用*.mesh.gmem.cc域名,访问istio-ingressgateway的任意Pod的443端口 servers: # 多个服务器可以分开配置,也可以配置在同一个Gateway资源中 - port: # 此Gateway创建后,ingressgateway的Envoy会立即添加一个0.0.0.0_443监听器 # 并且引用名为https.443.https的路由 number: 443 name: https protocol: HTTPS hosts: # 可以使用通配符,这样可以任何子域名访问网关 - *.mesh.gmem.cc tls: mode: SIMPLE # 服务器数字证书,必须挂载到istio-ingressgateway serverCertificate: /etc/istio/ingressgateway-certs/tls.crt # 服务器私钥,必须挂载到istio-ingressgateway privateKey: /etc/istio/ingressgateway-certs/tls.key # 如果客户端发送HTTP请求,则将其重定向到HTTPS httpsRedirect: true # 下面是一个TCP网关,使用MongoDB协议 - port: number: 2379 name: mongo protocol: MONGO hosts: [ "*" ] |
必须把上述网关绑定到VirtualService,它才能工作,精确的说,是网关产生的物理监听器才拥有了路由表:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: ubuntu.mesh.gmem.cc spec: hosts: # 域名必须和绑定的网关的域名兼容,例如是它的子集 # 这里的hosts+绑定网关的hosts,生成Envoy路由表的virtualHosts.domains字段 - ubuntu.mesh.gmem.cc # 将虚拟服务绑定到网关 # 指定需要应用此虚拟服务(定义的路由规则)的Gateway(所选择的Pod)或Sidecar # 保留字mesh表示,网格中所有Sidecar使用此虚拟服务定义的路由规则 # 如果要同时为Gateway和Sidecar应用此虚拟服务,传入mesh+相关Gateway的名字 gateways: - mesh.gmem.cc http: - match: - uri: prefix: /blog route: - destination: # 生成Envoy路由表的virtualHosts[0].routes[0].route.cluster # 例如 outbound|8086||ubuntu.default.svc.k8s.gmem.cc host: ubuntu port: number: 8000 # 端口可以自动选择,也可以明确指定 # 这里明确指定了8000,生成Envoy Cluster # outbound|8000||ubuntu.default.svc.k8s.gmem.cc # 注意,不能随意指定,必须在default命名空间有个叫ubuntu的服务,在暴露其8000端口才可以 |
为集群内的TCP服务建立入口Gateway时,hosts字段被忽略:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: zookeeper spec: selector: istio: ingressgateway servers: - hosts: # 这边写什么都无所谓,只认客户端的目的IP地址 - 'zk.mesh.gmem.cc' port: name: zookeeper number: 2181 protocol: TCP |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: zk.mesh.gmem.cc spec: gateways: - zookeeper hosts: - zk.mesh.gmem.cc tcp: - route: - destination: host: zk |
需要以下细节:
- 上述虚拟服务和网关配对后,istio-ingressgateway的Pod立刻开始监听2181端口
- 但是,istio-ingressgateway服务默认只配置了80、443端口
这意味着上述网关配置不可用,你还需要修改istio-ingressgateway服务的配置。
首先,需要在Istio的安装或配置中禁用允许服务直接访问外部服务的能力。这通常通过设置Istio的出站流量策略为 REGISTRY_ONLY 来实现。REGISTRY_ONLY 模式意味着所有出站流量都必须通过Istio的Egress网关,除非流量的目的地已经在Istio的服务注册表中定义。这可以通过在安装Istio时配置meshConfig.outboundTrafficPolicy.mode为REGISTRY_ONLY来完成,或者在已经安装的Istio上通过修改ConfigMap来实现。
接下来,需要配置一个或多个Egress网关来处理出站流量。这通常涉及到创建一个Gateway资源,用于指定Egress网关应该接受哪些出站流量(基于端口、主机名等条件)。
为了允许出站流量访问外部服务,需要为每个外部服务定义一个ServiceEntry资源。ServiceEntry使外部服务在Istio的内部服务注册表中可见,即使这些服务不是网格的一部分。这样,即使启用了REGISTRY_ONLY模式,出站流量也可以被正确地路由到外部服务。
然后,为每个需要通过Egress网关访问的外部服务创建一个VirtualService。VirtualService定义了流量从Egress网关到外部服务的具体路由规则,可以基于URL路径、请求头等条件进行路由。
最后,可能需要为通过Egress网关访问的外部服务配置DestinationRule,以指定流量策略,如负载均衡、TLS设置等。
一个简单的配置示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: istio-egressgateway spec: selector: istio: egressgateway servers: - port: number: 80 name: http protocol: HTTP hosts: - "*.example.com" --- apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: external-svc-example-com spec: hosts: - "example.com" ports: - number: 80 name: http protocol: HTTP resolution: DNS location: MESH_EXTERNAL --- apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: route-via-egressgateway spec: hosts: - "example.com" gateways: - istio-egressgateway - mesh http: - match: - gateways: - mesh route: - destination: host: istio-egressgateway.istio-system.svc.cluster.local subset: v1 port: number: 80 weight: 100 - route: - destination: host: example.com port: number: 80 weight: 100 |
ServiceEntry可以配合Gateway+VirtualService,将出口流量导向一组特殊的Pod —— istio-egressgateway。本节给出一个样例。
首先定义一个ServiceEntry:
1 2 3 4 5 6 7 8 9 10 11 12 |
apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: gmem spec: hosts: - k8s.gmem.cc ports: - number: 80 name: http protocol: HTTP resolution: DNS |
有了这个配置以后,就可以从网格内部访问k8s.gmem.cc了,但是流量直接从Sidecar发往k8s.gmem.cc。
现在,我们要让流量通过istio-egressgateway的Pod转发。首先,定义一个Gateway:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: gmem spec: # 指定哪些虚拟机/Pod应用这个网关配置,在K8S中,可匹配任意命名空间中的Pod selector: # 使用deployment istio-egressgateway定义的Pod,此deployment作为出口网关使用 istio: egressgateway servers: - port: number: 80 name: http protocol: HTTP hosts: - k8s.gmem.cc |
然后,定义一个虚拟服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService spec: hosts: - k8s.gmem.cc gateways: # 此虚拟服务生成的Envoy配置(监听器路由),将应用到网关gmem所应用到的Pod,以及所有sidecar - gmem - mesh http: - match: # 如果HTTP请求从mesh发出,也就是从sidecar发出 - gateways: - mesh port: 80 route: # 那么将流量转发到istio-egressgateway,也就是出口网关服务 - destination: host: istio-egressgateway.istio-system.svc.k8s.gmem.cc port: number: 80 weight: 100 - match: # 如果HTTP请求从gmem网关所应用到的Pod,也就是istio-egressgateway对应的Pod发出 - gateways: - gmem port: 80 route: # 那么将流量直接发往外部 - destination: host: k8s.gmem.cc port: number: 80 weight: 100 |
1.6新增,让运维人员能够描述单个非K8S工作负载,例如物理机或虚拟机,的属性。
必须伴随ServiceEntry一起使用,ServiceEntry使用选择器来匹配工作负载(包括Pod以及虚拟机、物理机),产生MESH_INTERNAL类型的服务。
当工作负载连接到istiod后,WorkloadEntry的Status会发生变化,反应工作负载的状态和一些其它细节,类似于K8S更新Pod的状态。
下面是一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
apiVersion: networking.istio.io/v1beta1 kind: WorkloadEntry metadata: name: details-svc spec: # 此工作负载具有Sidecar,且Sidecar使用该SA启动 serviceAccount: details-legacy # 工作负载的IP地址 address: 2.2.2.2 # 此工作负载的标签 labels: app: details-legacy instance-id: vm1 |
对应的ServiceEntry:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
apiVersion: networking.istio.io/v1beta1 kind: ServiceEntry metadata: name: details-svc spec: hosts: - details.bookinfo.com # 内部服务 location: MESH_INTERNAL ports: - number: 80 name: http protocol: HTTP resolution: STATIC # 此服务匹配的工作负载,不管它是Pod还是WorkloadEntry workloadSelector: labels: app: details-legacy |
WorkloadEntry的address也可以指定为FQDN,对应的ServiceEntry的解析策略应该设置为 resolution: DNS。
定义 ServiceEntry 来允许访问外部 HTTP 服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: external-svc-httpbin spec: hosts: - httpbin.org ports: - number: 80 name: http protocol: HTTP resolution: DNS location: MESH_EXTERNAL |
注意:
- 没有 ServiceEntry,网格中的服务无法通过 Istio 的 DNS 发现机制解析外部服务的名称。但是底层DNS机制还是能够解析域名的
- 如果你需要对某些外部服务应用特定的路由规则,或者需要 Istio 对这些流量进行更细粒度的管理(例如,只允许访问特定的外部服务),则 ServiceEntry 是必需的
- 如果你需要详细控制如何访问特定的外部服务,包括服务发现、安全、和流量管理,那么 ServiceEntry 是必要的
如果你只是定义了一个 ServiceEntry 来允许访问外部服务,而没有配置流量经过 Egress Gateway,那么流量将直接从 Pod 的 Sidecar 代理出去,直接到达外部服务。ServiceEntry 使得服务网格内的服务能够访问和调用网格外的服务,但它本身并不强制流量经过 Egress Gateway。
定义 Gateway 作为 Egress Gateway:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: istio-egressgateway spec: selector: istio: egressgateway servers: - port: number: 80 name: http protocol: HTTP hosts: - httpbin.org |
注意:
- Gateway定义的是一个网关规则,而不是定义网关
- 网关其实就是运行Envoy的容器,它可以作为独立Pod运行,用作边界网关;也可以作为Sidecar运行,作为每个工作负载和外部通信的网关
- selector选择器决定了这个规则运用到哪些网关或者说Envoy/Istio Proxy容器。这里的含义是仅仅应用到出口网关
- 这个网关中定义的域名httpbin.org用前面的ServiceEntry负责,即方访问外部
定义 VirtualService 将流量从 Sidecar 代理路由到 Egress Gateway,然后到外部服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: route-via-egressgateway spec: hosts: - httpbin.org gateways: - mesh - istio-egressgateway http: - match: - gateways: - mesh port: 80 route: - destination: host: istio-egressgateway.istio-system.svc.cluster.local port: number: 80 weight: 100 - match: - gateways: - istio-egressgateway port: 80 route: - destination: host: httpbin.org port: number: 80 weight: 100 |
注意:
- gateways定义了这个虚拟服务应用到哪些网关,这里是应用到:
- mesh 特殊关键字,代表所有Sidecar
- 特定网关,具体来说,是VirtualService所在命名空间中,名为istio-egressgateway的网关。如果要应用到其它命名空间中运行的网关,使用gateway-namespace/gateway-name这种形式
- 路由规则:
- 对于来自mesh内部也就是sidecar的流量,转发给istio-egressgateway这个网关
- 对于来自istio-egressgateway的流量,发送给httpbin.org前面已经定义了对应的Gateway,负责发送到网格外部
Istio提供了一个灵活的模型,用以收集网格中服务的各种指标(telemetry)。指标收集工作主要由Mixer负责。
从逻辑上说,每次:
- 请求发起前,消费者的Envoy都会调用Mixer,执行前提条件检查(Check)
- 每次请求处理完成后,都会调用Mixer,发送监控指标(Report)
Sidecar在本地具有缓存,大部分前提条件检查都在本地完成。此外监控指标也具有本地缓冲,不会频繁的发送给Mixer。
Istio的策略和监控特性基于一个公共的模型来配置,你需要配置三类资源:
- Handlers:决定使用哪些适配器,这些适配器如何运作
- Instances:定义如何把请求属性映射为适配器的输入。Instance代表一个或多个适配器需要处理的数据块
- Rules:决定何时调用适配器,传递什么instance给它