赞
踩
xDS是一类发现服务的总称,包含LDS、RDS、CDS、EDS以及SDS。Envoy通过xDS API可以动态获取Listener(监听器)、Route(路由)、Cluster(集群)、Endpoint(集群成员)以及Secret(证书)配置
LDS:Listener发现服务。Listener监听器控制Envoy启动端口监听(目前只支持TCP协议),并配置L3/L4层过滤器,当网络连接达到后,配置好的网络过滤器堆栈开始处理后续事件。这种通用的监听器体系结构用于执行大多数不同的代理任务(限流、客户端认证、HTTP连接管理、TCP代理等)
RDS:Route发现服务,用于HTTP连接管理过滤器动态获取路由配置。路由配置包含HTTP头部修改(增加、删除HTTP头部键值)、Virtual Hosts(虚拟主机)以及Virtual Hosts定义的各个路由条目
CDS:Cluster发现服务,用于动态获取Cluster信息。Envoy Cluster管理器管理着所有的上游Cluster。鉴于上游Cluster或者主机可用于任何代理转发任务,所以上游Cluster一般从Listener或Route中抽象出来
EDS:Endpoint发现服务。在Envoy术语中,Cluster成员就叫Endpoint,对于每个Cluster,Envoy通过EDS API动态获取Endpoint。EDS作为首选的服务发现的原因有两点:
SDS:Secret发现服务,用于运行时动态获取TLS证书。若没有SDS特性,在Kubernetes环境中,必须创建包含证书的Secret,代理启动前Secret必须挂载到Sidecar容器中,如果证书过期,则需要重新部署。使用SDS,集中式的SDS服务器将证书分发给所有的Envoy实例,如果证书过期,服务器会将新的证书分发,Envoy接收到新的证书后重新加载即可,不用重新部署
1)Why ADS
Istio 0.8以前,Pilot提供的单一资源的DS
带来的挑战:
综合以上两个问题,很容易出现配置更新过程中网络流量丢失带来网络错误
ADS是一种xDS的实现,它基于gRPC长连接,允许通过一条连接(gRPC的同一stream),发送多种资源的请求和响应
2)ADS最终一致性的考量
xDS是一种最终一致的协议,所以在配置更新过程中流量会丢失
例如,如果通过CDS/EDS获得Cluster X,一条指向Cluster X的RouteConfiguration刚好调整为指向Cluster Y,但是在CDS、EDS还没来得及下发Cluster Y的配置的情况下,路由到Cluster Y的流量会全部被丢弃,并且返回给客户端状态码503
在某些场景下,流量丢弃是不可接受的。Istio通过遵循make before break模型,调整配置更新顺序可以完全避免流量丢失
Envoy通过gRPC与某个特定的Pilot实例建立连接。Pilot通过简单的串行分发配置方式保证xDS的更新顺序按照CDS->EDS->LDS->RDS的顺序进行,Envoy以相同的顺序加载配置
基于Istio 1.18.0版本源码
Pilot-Discovery是Istio控制面的核心,负责服务网格中的流量管理以及控制面和数据面之间的配置下发
Pilot-Discovery从注册中心(如Kubernetes)获取服务信息并汇集,从Kubernetes API Server中获取配置规则,将服务信息和配置数据转换为xDS接口的标准数据结构,通过GRPC下发到数据面的Envoy
Pilot Server中主要包含三部分逻辑:
ConfigController为每种Config资源都创建了一个Informer,用于监听所有Config资源并注册EventHandler
完整的Config事件处理流程如下图所示:
ServiceController为4种资源分别创建了Informer,用于监听Kubernetes资源的更新,并为其注册EventHandler
当监听到Service、Endpoint、Pod、Node资源更新时,EventHandler会创建资源处理任务并将其推送到任务队列,然后由任务处理协程阻塞式地接收任务对象,最终调用任务处理函数完成对资源对象的事件处理
1)DiscoveryService初始化
StreamAggregatedResources接收DiscoveryRequest,返回DiscoveryResponse流,包含全量的xDS数据
2)DiscoveryServer启动
DiscoveryServer的Start()
方法启动两个重要的goroutine:handleUpdates和sendPushes
Config、Service、Endpoint对资源的处理最后都会调用ConfigUpdate()
方法向DiscoveryServer的pushChannel队列发送PushRequest实现的,处理流程如下:
DiscoveryServer首先通过handleUpdates协程阻塞式地接收并处理更新请求,并将PushRequest发送到DiscoveryServer的pushQueue中,然后由sendPushes协程并发地将PushRequest发送给每一条连接的pushChannel,最后由DiscoveryServer的流处理接口处理分发请求
3)handleUpdates
DiscoveryServer.Push方法会一直往下调用,直到把数据推入到DiscoveryServer的pushQueue管道中,代码调用逻辑如下:
4)sendPushes
doSendPushes()
方法内启动了一个无限循环,在default代码块中实现了主要的功能逻辑:
每个客户端在通过pushConnection将本次xDS推送完后,都会调用pushEv.done()
方法,通知semaphore
从pushQueue到最终推送xDS配置流程如下图:
5)xDS配置的生成与分发
pushConnection()
方法核心逻辑如下:
6)小结
配置变化后向Envoy推送xDS时序:
响应Envoy主动发起的xDS时序:
基于Istio 1.10版本落地问题总结
1)Istio在大规模场景下xDS性能瓶颈
当前Istio下发xDS使用的是全量下发策略,也就是网格里的所有Sidecar内存里都会有整个网格内所有的服务发现数据。比如下图,虽然workload1在业务逻辑上只依赖service2,但是Istiod会把全量的服务发现数据(service2、3、4)都发送给workload1
这样的结果是,每个Sidecar内存都会随着网格规模增长而增长,如果网格规模超过1万个实例,单个Envoy的内存超过了250MiB,而整个网格的开销还要再乘以网格规模大小
2)Istio当前优化方案
针对这个问题,社区提供了一个方案,就是Sidecar这个CRD,这个配置可以显式的定义服务之间的依赖关系,或者说可见性关系。比如下图这个配置的意思就是workload1只依赖service2,这样配置以后,Istiod只会下发service2的信息给workload1
这个方案本身是有效的。但这种方式在大规模场景下很难落地:首先这个方案需要用户提前配置服务间完整的依赖关系,大规模场景下的服务依赖关系很难梳理清楚,而且通常依赖关系也会随着业务的变化而变化
3)Aeraki Lazy xDS
针对上述问题,腾讯开源的Aeraki Lazy xDS和网易开源的Slime lazyload都是基于相同思路解决的,以Aeraki Lazy xDS为例,实现细节如下:
在网格里增加2个组件,一个是Lazy xDS Egress,Egress充当类似网格模型中默认网关角色,另一个是Lazy xDS Controller,用来分析并补全服务间的依赖关系
这个方案的好处:
目前只支持七层协议服务的按需加载,原因是流量在Egress这里中转的时候,Egress需要通过七层协议里的header判断原始目的地。纯TCP协议是没有办法设置额外的header。不过因为Istio主要目的就是为了做七层流量的治理,当网格的大部分请求都是七层的,这个情况目前可以接受的
相关资料:
变更EnvoyFilter时xDS推送范围:变更istio-system namespace(root namespace)下的EnvoyFilter会触发全局xDS推送(全局生效),变更namespaceA下的EnvoyFilter会触发namespaceA下所有边车的xDS推送(同namespace下生效)
问题点:在Pod所在的namespace添加只针对单个Pod生效的EnvoyFilter(通过workLoadSelector限制),但实际会触发同namespace下所有Pod边车的xDS推送
使用场景:
针对我们的使用场景,频繁修改只针对单个Pod生效的EnvoyFilter,但每次修改会触发同namespace下所有边车的xDS推送是不可接受的,所以进行了定制化改造,让带workLoadSelector的EnvoyFilter只触发workLoadSelector范围内的边车的xDS推送
原始逻辑:Istiod在推送xDS前,会遍历所有的Envoy,判断是否为该Envoy推送xDS(pilot/pkg/model/sidecar.go
中的DependsOnConfig()
方法),判断逻辑如下图:
改造后逻辑:这里最重要的是要考虑EnvoyFilter的workLoadSelector变更的场景,如果EnvoyFilter的workLoadSelector变更时保持原始推送判断逻辑(root namespace全局xDS推送,其他namespace同namespace下xDS推送)
问题点:
edsUpdateByKeys()
方法edsUpdateByKeys()
方法后,会触发mayBeRefreshIndexs()
方法(refreshIndexes=true时才会执行,处理逻辑:遍历所有ServiceEntry和对应Instance数据转换为ServiceInstance。执行一次耗时2s左右)改造后逻辑:
PeriodicSyncServiceEntry()
方法PeriodicSyncServiceEntry()
方法取出所有toBeSyncedServiceEntry中的ServiceEntry(处理过程中加锁),设置refreshIndexes=true,调用edsUpdateByKeys()
方法批量处理取出的ServiceEntry,这样无需每个ServiceEntry处理时都触发mayBeRefreshIndexs()
方法,而是每次取出一批ServiceEntry只会触发一次mayBeRefreshIndexs()
方法注意:Istiod需要等待PeriodicSyncServiceEntry()
方法处理完第一次list到的所有ServiceEntry后,才启动DiscoveryService接收来自Envoy端的连接请求,否则会导致Envoy拿不到未处理完的ServiceEntry相关数据(主要是CDS和EDS),导致Envoy调用下游发生503(response_flag=NC)
解决方案:
PeriodicSyncServiceEntry()
方法处理完第一次list到的所有ServiceEntry后将标志位设置为true,ServiceEntryStore的HasSynced()
方法中返回标志位状态pilot/pkg/bootstrap/server.go
中的waitForCacheSync()
方法判断所有的registry(处理ServiceEntry的为serviceEntryStore)都返回true后,才会启动DiscoveryService接收来自Envoy端的连接请求问题点:由于Pod内容器按照spec.containers
中的声明顺序依次启动,而initContainers会在所有容器启动前执行,所以容器的启动顺序是这样的:istio-init修改Pod内iptables使得istio-proxy接管Pod内所有流量(使应用容器内的所有网络请求都需要经过istio-proxy),而应用容器启动的过程中如果发起网络请求(请求配置中心获取配置等),此时istio-proxy有可能还没有启动完成,导致网络异常
>=Istio 1.7版本的解决方案:
方式一:安装Istio时全局设置
# 默认使用default profile(可通过--set profile=default|demo|...调整)
istioctl install
# 设置sidecar优先启动(且sidecar启动成功后再启动其他应用容器)-1.7新特性
--set values.global.proxy.holdApplicationUntilProxyStarts=true
方式二:在应用Deployment通过annotation设置
annotations:
# 重点:配置proxy-设置proxy启动成功后再启动其他应用
proxy.istio.io/config: |
holdApplicationUntilProxyStarts: true
holdApplicationUntilProxyStarts启用效果:
spec:
initContainers:
- name: istio-init
...
containers:
- name: istio-proxy
...
lifecycle:
postStart:
exec:
command:
- pilot-agent
- wait
问题点:Istio使用Envoy作为数据面转发HTTP请求,而Envoy默认要求使用HTTP/1.1或HTTP/2,当客户端使用HTTP/1.0时就会返回426 Upgrade Required
解决方案:编辑istioctl安装配置项文件
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
values:
pilot:
env:
PILOT_HTTP10: 1
...
istio-proxy容器中会添加环境变量ISTIO_META_HTTP10=1
使得Envoy支持HTTP/1.0
问题点:Envoy支持HTTP/1.0后,会有delay close timeout 1s的问题(默认情况HTTP/1.0的请求有1s的延迟)
解决方案:通过EnvoyFilter修改delayed_close_timeout为0s
apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: name: delayed-close-timeout-filter namespace: istio-system spec: configPatches: - applyTo: NETWORK_FILTER match: listener: filterChain: filter: name: envoy.filters.network.http_connection_manager patch: operation: MERGE value: typed_config: '@type': >- type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager delayed_close_timeout: 0s
相关资料:
Istio 常见问题 - Istio 支持 HTTP/1.0
问题点:
Envoy默认会将HTTP header统一转换为小写,例如有一个HTTP header为X-UserId: 12345
,经过Envoy代理后会变成x-userid: 12345
。HTTP的RFC规范也要求应用不能对header大小写敏感,但有些应用没有遵循RFC规范,对大小写敏感了,导致开启Mesh后报错
解决方案:通过EnvoyFilter让Envoy保留HTTP header大小写
apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: name: header-casing-filter namespace: istio-system spec: configPatches: - applyTo: CLUSTER match: context: SIDECAR_INBOUND patch: operation: MERGE value: typed_extension_protocol_options: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: '@type': >- type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions commonHttpProtocolOptions: idleTimeout: 20s use_downstream_protocol_config: http_protocol_options: header_key_format: stateful_formatter: name: preserve_case typed_config: '@type': >- type.googleapis.com/envoy.extensions.http.header_formatters.preserve_case.v3.PreserveCaseFormatterConfig - applyTo: CLUSTER match: context: SIDECAR_OUTBOUND patch: operation: MERGE value: typed_extension_protocol_options: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: '@type': >- type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions commonHttpProtocolOptions: idleTimeout: 20s use_downstream_protocol_config: http_protocol_options: header_key_format: stateful_formatter: name: preserve_case typed_config: '@type': >- type.googleapis.com/envoy.extensions.http.header_formatters.preserve_case.v3.PreserveCaseFormatterConfig - applyTo: NETWORK_FILTER match: listener: filterChain: filter: name: envoy.filters.network.http_connection_manager patch: operation: MERGE value: typed_config: '@type': >- type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager http_protocol_options: header_key_format: stateful_formatter: name: preserve_case typed_config: '@type': >- type.googleapis.com/envoy.extensions.http.header_formatters.preserve_case.v3.PreserveCaseFormatterConfig
问题点:
未开启Mesh时,Spring MVC遇到重复Content-Type情况下取第一个
开启Mesh后,Envoy把重复Content-Type使用逗号拼接,Spring MVC不识别拼接后的Content-Type,报错415 Unsupported Media Type
解决方案:通过EnvoyFilter当出现两个Content-Type时仅保留一个
apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: name: double-header-filter namespace: istio-system spec: configPatches: - applyTo: HTTP_FILTER match: context: SIDECAR_INBOUND listener: filterChain: filter: name: envoy.filters.network.http_connection_manager subFilter: name: envoy.filters.http.router patch: operation: INSERT_BEFORE value: name: envoy.lua typed_config: '@type': type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua inlineCode: | function envoy_on_request(request_handle) local function is_empty(input) return input == nil or input == '' end origin_type = request_handle:headers():get("content-type") if not is_empty(origin_type) then local type_table = {} for type in origin_type:gmatch("([^,]+),?") do table.insert(type_table, type) end if table.getn(type_table)>0 then request_handle:headers():replace("content-type", type_table[1]) end type_table = nil end end - applyTo: HTTP_FILTER match: context: SIDECAR_OUTBOUND listener: filterChain: filter: name: envoy.filters.network.http_connection_manager subFilter: name: envoy.filters.http.router patch: operation: INSERT_BEFORE value: name: envoy.lua typed_config: '@type': type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua inlineCode: | function envoy_on_request(request_handle) local function is_empty(input) return input == nil or input == '' end origin_type = request_handle:headers():get("content-type") if not is_empty(origin_type) then local type_table = {} for type in origin_type:gmatch("([^,]+),?") do table.insert(type_table, type) end if table.getn(type_table)>0 then request_handle:headers():replace("content-type", type_table[1]) end type_table = nil end end
问题点:部分站点开启Mesh后,边车内存缓慢持续增长且不释放,这些站点的特点是接入的sdk是存在定期的Passthrough调用
对同一个站点Passthrough调用eureka的频率进行调整,做对比,结果如下:
调用eureka的频率 | 不调用 | 1s调用一次 | 30s调用一次 |
---|---|---|---|
是否会导致边车内存缓慢持续增长 | 不会 | 不会 | 会 |
根因:边车对于走Passthrough的下游,超过5s未调用边车会清理下游主机、连接数据等,清理过程中存在bug会导致内存泄漏
GitHub issue:https://github.com/envoyproxy/envoy/issues/22218
解决方案:调大清理间隔
apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: name: cleanup-interval-filter namespace: istio-system spec: configPatches: - applyTo: CLUSTER match: cluster: name: PassthroughCluster context: SIDECAR_OUTBOUND patch: operation: MERGE value: cleanup_interval: 1h - applyTo: CLUSTER match: context: SIDECAR_INBOUND patch: operation: MERGE value: cleanup_interval: 1h
相关资料:
问题点:客户端直接访问服务端正常,但开启Mesh后经过Envoy访问服务端则会出现一定几率的503错误(response code=503, response flag=UC)
根因:
Envoy的HTTP Router会在第一次和Upstream建立TCP连接并使用后将连接释放到一个连接池中,而不是直接关闭该连接。这样下次downstream请求相同的Upstream host时可以重用该连接,可以避免频繁创建/关闭连接带来的开销
当连接被Envoy放入连接池后,连接中不再转发来自downstream数据,即连接处于空闲状态。连接对端的应用程序会检查连接的空闲状态,并在空闲期间通过TCP keepalive packet来侦测对端状态。由于空闲的连接也会占用资源,因此应用并不会无限制地在一个空闲连接上进行等待。几乎所有语言/框架在创建TCP服务器时都会设置一个keepalive timeout选项,如果在keepalive timeout的时间内没有收到新的TCP数据包,应用就会关闭该连接
在应用端关闭连接后的极短时间内,Envoy侧尚未感知到该连接的状态变化,如果此时Envoy收到了来自downstream的请求并将该连接从连接池中取出来使用,就会出现503 UC upstream_reset_before_response_started{connection_termination}
异常
总结:Envoy Inbound和应用的HttpServer(Tomcat或者其他HttpServer)在Http1.1 keep-alive的情况下,HttpServer的空闲连接超时释放时间比Envoy默认的要短(Envoy默认1小时),当Envoy正在使用连接时应用的HttpServer要释放空闲连接时会产生冲突造成503
解决方案:调整Inbound的idleTimeout为20s,短于HttpServer的idleTimeout
apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: name: http-idle-timeout namespace: istio-system spec: configPatches: - applyTo: CLUSTER match: context: SIDECAR_INBOUND patch: operation: MERGE value: typedExtensionProtocolOptions: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: '@type': >- type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions commonHttpProtocolOptions: idleTimeout: 20s
相关资料:
关于 istio-proxy 503/504等5xx问题排查
细说Http中的Keep-Alive和Java Http中的Keep-Alive机制
503 UC upstream_reset_before_response_started
问题点:
开启Mesh后,应用无法获取客户端的源IP地址,获取到的地址为127.0.0.6
方案一:使用TPROXY代理
Istio支持两种拦截模式:
使用TPROXY代理方式可以通过getRemoteAddr的方式获取对端IP
方案二:设置XFF头
七层的客户端源IP都是通过HTTP XFF(X-Forwarded-For)头实现的,XFF保存原始客户端的源IP并透传到后端,应用可以解析XFF,得到客户端的源IP
Envoy提供了设置XFF的方法:https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#x-forwarded-for
为了防止对其他七层代理的XFF产生影响,通过EnvoyFilter写Lua脚本实现Inbound中XFF的设置,设置XFF逻辑:如果XFF为空,设置客户端源IP;如果XFF不为空,不修改XFF
apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: name: add-header-filter namespace: istio-system spec: configPatches: - applyTo: HTTP_FILTER match: context: SIDECAR_INBOUND listener: filterChain: filter: name: envoy.filters.network.http_connection_manager patch: operation: INSERT_BEFORE value: name: envoy.lua typed_config: '@type': type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua inlineCode: | function envoy_on_request(handle) local xff = handle:headers():get("X-FORWARDED-FOR") local remote_address = handle:streamInfo():downstreamDirectRemoteAddress() if((remote_address ~= nil and remote_address ~= "") and (xff == nil or xff == "")) then local parts = {} for part in string.gmatch(remote_address, "[^:,]+") do table.insert(parts, part) end local client_ip = parts[1] handle:headers():add("X-FORWARDED-FOR", client_ip) end end
相关资料:
1)各场景边车限流准确性验证
验证点:在每秒限流100qps时,以每秒200qps请求,验证边车限流是否精准
预期:请求成功率稳定为50%
场景1:针对整个Inbound限流
直接使用本地限流器,EnvoyFilter示例如下:
apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: name: instanceA.instanceA-namespace.ratelimit namespace: instanceA-namespace spec: configPatches: - applyTo: HTTP_FILTER match: context: SIDECAR_INBOUND listener: filterChain: filter: name: envoy.filters.network.http_connection_manager patch: operation: INSERT_BEFORE value: name: envoy.filters.http.local_ratelimit typed_config: '@type': type.googleapis.com/udpa.type.v1.TypedStruct type_url: >- type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit value: filter_enabled: default_value: denominator: HUNDRED numerator: 100 runtime_key: local_rate_limit_enabled filter_enforced: default_value: denominator: HUNDRED numerator: 100 runtime_key: local_rate_limit_enforced stat_prefix: http_local_rate_limiter token_bucket: # 每秒限流100qps fill_interval: 1s max_tokens: 100 tokens_per_fill: 100 workloadSelector: labels: instance: instanceA
结果:请求成功率能稳定在50%
结论:使用本地限流器针对整个Inbound限流时限流精准✅
场景2:针对指定请求路径限流
使用descriptors精细化限流,EnvoyFilter示例如下:
apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: name: instanceA.instanceA-namespace.ratelimit namespace: instanceA-namespace spec: configPatches: - applyTo: HTTP_ROUTE match: context: SIDECAR_INBOUND patch: operation: MERGE value: route: rate_limits: - actions: - request_headers: descriptor_key: path header_name: ':path' - applyTo: HTTP_FILTER match: context: SIDECAR_INBOUND listener: filterChain: filter: name: envoy.filters.network.http_connection_manager patch: operation: INSERT_BEFORE value: name: envoy.filters.http.local_ratelimit typed_config: '@type': type.googleapis.com/udpa.type.v1.TypedStruct type_url: >- type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit value: descriptors: - entries: - key: path value: /api/test token_bucket: # 每秒限流100qps fill_interval: 1s max_tokens: 100 tokens_per_fill: 100 filter_enabled: default_value: denominator: HUNDRED numerator: 100 runtime_key: local_rate_limit_enabled filter_enforced: default_value: denominator: HUNDRED numerator: 100 runtime_key: local_rate_limit_enforced stat_prefix: http_local_rate_limiter token_bucket: fill_interval: 1s max_tokens: 100000 tokens_per_fill: 100000 workloadSelector: labels: instance: instanceA
结果:请求成功率不能稳定在50%
结论:使用descriptors精细化限流,针对指定请求路径限流不准❎
2)使用descriptors精细化限流时,限流不准问题分析
根因:descriptors精细化限流实现时使用的全局计时器具有一定的不确定性+/-O(10ms),所以在再填充时间N和N+1处记录的时钟时间很可能小于间隔本身。比如每秒限流100qps,实际令牌桶填充令牌的时间间隔不能稳定在1s
Github地址:https://github.com/envoyproxy/envoy/pull/21327
解决方案:该问题官方已修复,Istio 1.18.2版本已包含该commit,基于Istio 1.18.2版本验证descriptors精细化限流,限流功能正常,可升级至Istio 1.18.2以上版本
推荐阅读:
各公司Istio落地实践:
Istio Pilot源码:
Istio Pilot源码学习(一):Pilot-Discovery启动流程、ConfigController配置规则发现
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。