Envoy 上游集群异常值检测
异常值检测
- 异常值检测和弹出是动态确定上游集群中的某些主机是否与其他主机不同,并将它们从健康 负载平衡集中删除的过程。性能可能沿着不同的轴,例如连续失败、时间成功率、时间延迟等。异常值检测是被动健康检查的一种形式。Envoy 还支持主动健康检查。被动和主动健康检查可以一起或单独启用,并构成整体上游健康检查解决方案的基础。异常值检测是集群配置的一部分 它需要过滤器来报告错误、超时和重置。目前,以下过滤器支持异常值检测:http router、 tcp proxy、 redis proxy和thrift proxy。
- 检测到的错误分为两类:外部错误和本地错误。外部生成的错误是特定于事务的,并在上游服务器上响应接收到的请求而发生。例如,HTTP 服务器返回错误代码 500 或 redis 服务器返回无法解码的有效负载。在 Envoy 成功连接到上游主机后,这些错误会在上游主机上生成。Envoy 会生成本地错误,以响应中断或阻止与上游主机通信的事件。本地错误的示例包括超时、TCP 重置、无法连接到指定端口等。
- 检测到的错误类型取决于过滤器类型。例如,http 路由器过滤器检测本地产生的错误(超时、重置 - 与上游主机连接相关的错误),并且因为它也理解 HTTP 协议,所以它报告 HTTP 服务器返回的错误(外部生成的错误)。在这种情况下,即使与上游 HTTP 服务器的连接成功,与服务器的事务也可能会失败。相比之下,tcp 代理过滤器不理解 TCP 层之上的任何协议,并且只报告本地产生的错误。
- 在默认配置下(outlier_detection.split_external_local_origin_errors为false)本地产生的错误与外部生成的(事务)错误没有区别,最终都在同一个桶中,并与 outlier_detection.consecutive_5xx、 outlier_detection.consecutive_gateway_failure和 outlier_detection.success_rate_stdev_factor 配置进行比较项目。例如,如果与上游 HTTP 服务器的连接因超时而失败两次,然后在成功连接后,服务器返回错误代码 500,则错误总数将为 3。
- 异常值检测也可以配置为区分本地产生的错误和外部产生的(事务)错误。它是通过 outlier_detection.split_external_local_origin_errors配置项完成的。在该模式中,本地产生的错误由单独的计数器跟踪,而不是外部产生的(事务)错误,并且异常值检测器可以配置为对本地产生的错误做出反应并忽略外部产生的错误,反之亦然。
- 重要的是要了解一个集群可以在多个过滤器链之间共享。如果一个过滤器链根据其异常检测类型弹出主机,则其他过滤器链也会受到影响,即使它们的异常检测类型不会弹出该主机。
弹射算法
根据异常值检测的类型,弹出或者内联运行(例如在连续 5xx 的情况下)或以指定的间隔运行(例如在周期性成功率的情况下)。弹出算法的工作原理如下:
-
主机被确定为异常值。
-
如果没有主机被弹出,Envoy 将立即弹出主机。否则,它会检查以确保弹出的主机数量低于允许的阈值(通过 outlier_detection.max_ejection_percent 设置指定)。如果弹出的主机数量超过阈值,则不会弹出主机。
-
主机被弹出若干毫秒。弹出意味着主机被标记为不健康,并且不会在负载均衡期间使用,除非负载均衡器处于 紧急情况。毫秒数等于outlier_detection.base_ejection_time值乘以主机连续被弹出的次数。如果主机继续失败,这会导致主机被弹出的时间越来越长。当弹出时间达到 outlier_detection.max_ejection_time时,它不再增加。当宿主变得健康时,弹出时间乘数会随着时间的推移而减少。主机的健康状况检查间隔等于 outlier_detection.interval. 如果主机在该检查期间是健康的,则弹出时间乘数会减少。假设主机保持健康,大约需要outlier_detection.max_ejection_time / outlier_detection.base_ejection_time * outlier_detection.interval秒才能将弹出时间降低到最小值outlier_detection.base_ejection_time。
-
在满足弹出时间后,被弹出的主机将自动重新投入使用。通常,异常值检测与主动健康检查一起使用,以获得全面的健康检查解决方案。
检测类型
Envoy 支持以下异常检测类型:
连续 5xx
- 在默认模式下(outlier_detection.split_external_local_origin_errors为false),此检测类型考虑了所有生成的错误:本地起源和外部起源(事务)错误。由非 HTTP 过滤器(如tcp 代理或 redis 代理)生成的错误在内部映射到 HTTP 5xx 代码并按此处理。
- 在拆分模式(outlier_detection.split_external_local_origin_errors为true)中,此检测类型仅考虑外部起源(事务)错误,而忽略本地起源的错误。如果上游主机是 HTTP 服务器,则仅考虑 5xx 类型的错误(有关例外情况,请参阅连续网关故障)。对于 redis 服务器,通过 redis 代理提供的服务只考虑来自服务器的格式错误的响应。格式正确的响应,即使它们带有操作错误(如未找到索引、拒绝访问)也不会被考虑在内。
- 如果上游主机返回一些被视为连续 5xx 类型错误的错误,它将被弹出。弹出所需的连续 5xx 的数量由outlier_detection.consecutive_5xx值控制。
连续网关故障
- 在默认模式下(outlier_detection.split_external_local_origin_errors为false),此检测类型考虑了 5xx 错误的子集,称为“网关错误”(502、503 或 504 状态代码)和本地源故障,例如超时、TCP 重置等。
- 在拆分模式下(outlier_detection.split_external_local_origin_errors为true),此检测类型考虑了 5xx 错误的子集,称为“网关错误”(502、503 或 504 状态代码),并且仅受http 路由器支持。
- 如果上游主机返回一定数量的连续“网关错误”(502、503 或 504 状态代码),它将被弹出。弹出所需的连续网关故障数由outlier_detection.consecutive_gateway_failure值控制。
连续的本地源失败
- 仅当outlier_detection.split_external_local_origin_errors为true并且仅考虑本地产生的错误(超时、重置等)时才启用此检测类型。如果 Envoy 反复无法连接到上游主机或与上游主机的通信反复中断,它将被弹出。检测到各种本地引发的问题:超时、TCP 重置、ICMP 错误等。弹出所需的连续本地引发的故障数由outlier_detection.consecutive_local_origin_failure值控制。http 路由器、 tcp 代理 和redis 代理都支持这种检测类型。
成功率
- 基于成功率的异常值检测聚合来自集群中每个主机的成功率数据。然后在给定的时间间隔根据统计异常值检测弹出主机。如果主机在聚合间隔内的请求量小于 outlier_detection.success_rate_request_volume 值,则不会为主机计算成功率异常值检测。此外,如果某个时间间隔内所需请求量最小的主机数量小于 outlier_detection.success_rate_minimum_hosts 值,则不会对集群进行检测。
- 在默认配置模式下(outlier_detection.split_external_local_origin_errors为false)此检测类型考虑了所有类型的错误:本地和外部起源。outlier_detection.enforcing_local_origin_success 配置项被忽略。
- 在拆分模式下(outlier_detection.split_external_local_origin_errors为true),本地产生的错误和外部产生的(事务)错误被分别计算和处理。大多数配置项,即 outlier_detection.success_rate_minimum_hosts、 outlier_detection.success_rate_request_volume、 outlier_detection.success_rate_stdev_factor适用于这两种类型的错误,但outlier_detection.enforcing_success_rate仅适用于外部错误,而outlier_detection.enforcing_local_origin_success_rate仅 适用于本地错误。
失败率
- 基于故障百分比的异常值检测功能类似于成功率检测,因为它依赖于来自集群中每个主机的成功率数据。但是,不是将这些值与整个集群的平均成功率进行比较,而是将它们与用户配置的平坦阈值进行比较。此阈值通过 outlier_detection.failure_percentage_threshold 字段配置。
- 基于失败百分比的检测的其他配置字段与成功率检测的字段类似。基于失败百分比的检测也遵循 outlier_detection.split_external_local_origin_errors;外部和本地引发的错误的执行百分比分别由 outlier_detection.enforcing_failure_percentage 和 outlier_detection.enforcing_failure_percentage_local_origin控制。与成功率检测一样,如果主机在聚合间隔内的请求量小于 outlier_detection.failure_percentage_request_volume ,则不会对主机执行检测 价值。如果某个时间间隔内具有最小所需请求量的主机数量小于 outlier_detection.failure_percentage_minimum_hosts 值,则也不会对集群执行检测。
gRPC
- 对于 gRPC 请求,异常值检测将使用从grpc-status响应标头映射的 HTTP 状态。
异常探测配置格式
查看代码
clusters:
- name: ...
...
outlier_detection:
consecutive_5xx: ... # 因连续5xx错误而弹出主机之前允许出现的连续5xx响应或本地原始错误的数量,默认为5;
interval: ... # 弹射分析扫描之间的时间间隔,默认为10000ms或10s;
base_ejection_time: ... # 主机被弹出的基准时长,实际时长等于基准时长乘以主机已经弹出的次数;默认为30000ms或30s;
max_ejection_percent: ... # 因异常探测而允许弹出的上游集群中的主机数量百分比,默认为10%;不过,无论如何,至少要弹出一个主机;
enforcing_consecutive_5xx: ... # 基于连续的5xx检测到主机异常时主机将被弹出的几率,可用于禁止弹出或缓慢弹出;默认为100;
enforcing_success_rate: ... # 基于成功率检测到主机异常时主机将被弹出的几率,可用于禁止弹出或缓慢弹出;默认为100;
success_rate_minimum_hosts: ... # 对集群启动成功率异常检测的最少主机数,默认值为5;
success_rate_request_volume: ... # 在检测的一次时间间隔中必须收集的总请求的最小值,默认值为100;
success_rate_stdev_factor: ... # 用确定成功率异常值弹出的弹射阈值的因子;弹射阈值=均值-(因子*平均成功率标准差);不过,此处设置的值需要除以1000以得到因子,例如,需要使用1.3为因子时,需要将该参数值设定为1300;
consecutive_gateway_failure: ... # 因连续网关故障而弹出主机的最少连续故障数,默认为5;
enforcing_consecutive_gateway_failure: ... # 基于连续网关故障检测到异常状态时而弹出主机的几率的百分比,默认为0;
split_external_local_origin_errors: ... # 是否区分本地原因而导致的故障和外部故障,默认为false;此项设置为true时,以下三项方能生效;
consecutive_local_origin_failure: ... # 因本地原因的故障而弹出主机的最少故障次数,默认为5;
enforcing_consecutive_local_origin_failure: ... # 基于连续的本地故障检测到异常状态而弹出主机的几率百分比,默认为100;
enforcing_local_origin_success_rate: ... # 基于本地故障检测的成功率统计检测到异常状态而弹出主机的几率,默认为100;
failure_percentage_threshold: {…} # 确定基于故障百分比的离群值检测时要使用的故障百分比,如果给定主机的故障百分比大于或等于该值,它将被弹出;默认为85;
enforcing_failure_percentage: {…} # 基于故障百分比统计信息检测到异常状态时,实际弹出主机的几率的百分比;此设置可用于禁用弹出或使其缓慢上升;默认为0;
enforcing_failure_percentage_local_origin: {…} #基于本地故障百分比统计信息检测到异常状态时,实际主机的概率的百分比;默认为0;
failure_percentage_minimum_hosts: {…} # 集群中执行基于故障百分比的弹出的主机的最小数量;若集群中的主机总数小于此值,将不会执行基于故障百分比的弹出;默认为5;
failure_percentage_request_volume: {…} #必须在一个时间间隔(由上面的时间间隔持续时间定义)中收集总请求的最小数量,以对此主机执行基于故障百分比的弹出;如果数量低于此设置,则不会对此主机执行基于故障百分比的弹出;默认为50;
max_ejection_time: {…} # 主机弹出的最长时间;如果未指定,则使用默认值(300000ms或300s)或 base_ejection_time值中的大者;
max_ejection_time_jitter: {...} # 添加到弹出时间的最大抖动量,以防止所有代理尝试同时重新连接到主机的“雷声”效应。默认为 0s。
使用异常探测
- 同主动健康检查一样,异常检测也要配置在集群级别;下面的示例用于配置在返回3个连续5xx错误时将主机弹出30秒:
clusters:
- name: ...
...
outlier_detection:
consecutive_5xx: "3"
base_ejection_time: "30s"
- 在新服务上启用异常检测时应该从不太严格的规则集开始,以便仅弹出具有网关连接错误的主机(HTTP 503),并且仅在10%的时间内弹出它们
clusters:
- name: ...
...
outlier_detection:
consecutive_gateway_failure: "3"
base_ejection_time: "30s"
enforcing_consecutive_gateway_failure: "10"
- 同时,高流量、稳定的服务可以使用统计信息来弹出频繁异常容的主机;下面的配置示例将弹出错误率低于群集平均值1个标准差的任何端点,统计信息每10秒进行一次评估,并且算法不会针对任何在10秒内少于500个请求的主机运行
clusters:
- name: ...
...
outlier_detection:
interval: "10s"
base_ejection_time: "30s"
success_rate_minimum_hosts: "10"
success_rate_request_volume: "500"
success_rate_stdev_factor: "1000“ # divided by 1000 to get a double
异常探测配置示例
查看代码
admin:
profile_path: /tmp/envoy.prof
access_log_path: /tmp/admin_access.log
address:
socket_address: { address: 0.0.0.0, port_value: 9901 }
static_resources:
listeners:
- name: listener_0
address:
socket_address: { address: 0.0.0.0, port_value: 80 }
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
codec_type: AUTO
route_config:
name: local_route
virtual_hosts:
- name: webservice
domains: ["*"]
routes:
- match: { prefix: "/" }
route: { cluster: web_cluster_01 }
http_filters:
- name: envoy.filters.http.router
clusters:
- name: web_cluster_01
connect_timeout: 0.25s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: web_cluster_01
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: { address: myservice, port_value: 80 }
outlier_detection:
consecutive_5xx: 3
base_ejection_time: 10s
max_ejection_percent: 10
参考文档
https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/outlier
https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/cluster/v3/outlier_detection.proto#envoy-v3-api-msg-config-cluster-v3-outlierdetection