上游集群
4 分钟阅读
简要概述
管理上游集群的配置,可以包含任意数量独立配置的上游集群,集群管理器处理了哪些主机可用和健康、负载平衡、线程本地存储上游连接数据、上游连接类型(TCP/IP、UDP)、上游协议在适用的情况下(HTTP/1.1、HTTP/2、HTTP/3)等所有复杂性。
集群管理配置可支持:
- 静态配置
- 集群发现服务(CDS)API
集群配置
静态配置
static_resources:
listeners:
- name: listener_0
......
clusters:
- name: oneops-syncds-v1
type: LOGICAL_DNS
dns_lookup_family: V4_ONLY
load_assignment:
cluster_name: oneops-syncds-v1
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 192.168.0.1
port_value: 8080
服务发现
在配置中定义上游集群时,Envoy需要知道如何解析集群的成员。这被称为服务发现。
在 envoy 上最佳案例是使用最终一致的服务发现以及主动健康检查(Envoy 显式地对上游集群成员进行健康检查)来确定集群健康,而不是类似其他Zookeeper、etcd、Consul 等做一致性协调。
STATIC
静态是最简单的服务发现类型。配置明确指定了每个上游主机的解析网络名称(IP地址/端口、Unix域套接字等)。
clusters:
- name: oneops-syncds-v1
type: STATIC
dns_lookup_family: V4_ONLY
load_assignment:
cluster_name: oneops-syncds-v1
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 192.168.0.1
port_value: 8080
STRICT_DNS
clusters:
- name: oneops-syncds-v1
type: STRICT_DNS
dns_lookup_family: V4_ONLY
load_assignment:
cluster_name: oneops-syncds-v1
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: example.com
port_value: 80
注意需配合主机头,否则会出现 403 错误等。
LOGICAL_DNS
TODO;
EDS
clusters:
- name: oneops-syncds-v1
type: EDS
dns_lookup_family: V4_ONLY
eds_cluster_config:
eds_config:
resource_api_version: V3
api_config_source:
# 通过 http 接口
api_type: REST
transport_api_version: V3
refresh_delay: 2s
cluster_names:
- eds_cluster
# 通过 grpc 接口
#api_type: GRPC
#grpc_services:
#- envoy_grpc:
# cluster_name: eds_cluster
- name: eds_cluster
type: STATIC
dns_lookup_family: V4_ONLY
load_assignment:
cluster_name: eds_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 10.5.17.224
port_value: 80
以上配置含义,对使用到上游集群名为 “oneops-syncds-v1” 的路由,获取端点的方式是通过 REST 接口访问 “xds_cluster” 配置的 “10.5.17.224:80” 地址,接口为:
POST /v3/discovery:endpoints
ORIGINAL_DST
TODO;
健康检查
HTTP 主动健康检查
通过在 “clusters[_].health_checks” 添加参数,示例如下,更多可参考 Bootstrap 中 core.v3.HealthCheck 配置。
clusters:
- name: oneops-syncds-v2
type: STATIC
dns_lookup_family: V4_ONLY
lb_policy: round_robin
load_assignment:
cluster_name: test1
endpoints:
- lb_endpoints:
- endpoint:
# 针对各端点单独的健康检查配置
#health_check_config:
# 上游端口健康检查的服务端口,有时候业务端口与健康检查端口分开
# port_value: 8080
# address:
# socket_address:
# address: 192.168.0.1
# port_value: 8000
address:
socket_address:
protocol: TCP
address: 192.168.0.1
port_value: 8080
- endpoint:
address:
socket_address:
protocol: TCP
address: 192.168.0.1
port_value: 8000
health_checks:
- timeout: 1s
interval: 2s
unhealthy_threshold: 1
healthy_threshold: 1
http_health_check:
path: "/health"
event_log_path: /var/log/envoy.log
always_log_health_check_failures: true
这里上游端点 192.168.0.1:8080 与 192.168.0.1:8080 在 envoy 首次启动时,使用 HTTP 健康检查访问 “/health” 必须可用,否则即使探测失败,也是不会移除不可用端点的。
也就是所有上游端点健康检查失败情况下,则 envoy 行为是把所有端口都对外提供服务了。
HTTP 健康检查过滤器
在大规模网络中,如果上游服务对外提供 HTTP 健康检查地址(如:"/ping"),下游探测器(如:prometheus)请求这类地址可能会产生较大的健康检查流量,你可以通过 HTTP 健康检查过滤器(配置在 http_filters 中)适当缓存以降低这种流量回到上游服务中,支持以下几种模式:
- 不透传(No pass through)
下游请求这类健康检查 URL 地址永远不会传递到上游服务,根据服务器当前的状态,Envoy 将回复 200 或 503。
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
http_filters:
- name: envoy.extensions.filters.http.health_check
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck
pass_through_mode: false
headers:
- name: ":method"
string_match:
exact: "GET"
- name: ":path"
prefix_match: "/ping"
- 不透传,根据上游集群健康计算 (No pass through, computed from upstream cluster health):
下游请求这类健康检查 URL 地址将根据一个或多个上游集群中至少指定百分比的服务器是否可用(健康 + 降级),返回 200 或 503,但是如果 Envoy 服务器处于排空状态,它将无论上游集群的健康状态如何都会回复503。
TODO;
- 透传 (Pass through):
下游请求这类健康检查 URL 地址将会传递到上游服务,上游服务根据其健康状态返回 200 或 503。
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
http_filters:
- name: envoy.extensions.filters.http.health_check
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck
pass_through_mode: true
headers:
- name: ":method"
string_match:
exact: "GET"
- name: ":path"
prefix_match: "/ping"
- 透传跟缓存 (Pass through with caching)
下游请求这类健康检查 URL 地址将会传递给上游服务,envoy 服务器会将结果缓存一段时间,随后的请求将在缓存时间内返回缓存值,当缓存时间达到时,下一个健康检查请求将被传递给下游服务。
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
http_filters:
- name: envoy.extensions.filters.http.health_check
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck
pass_through_mode: true
cache_time: 30s
headers:
- name: ":method"
string_match:
exact: "GET"
- name: ":path"
prefix_match: "/ping"
以上几种模式,均需注意下配置下 “headers” 过滤特定的健康检查请求,否则会把所有请求均人为健康探测流量被缓存,从而影响了业务流量。
异常值检测
它也称为被动健康检查,在 config.cluster.v3.OutlierDetection 配置,区别于主动健康检查,在 config.core.v3.HealthCheck,它们可以配合使用也可以单独使用。
异常值检测是动态确定上游集群中某些主机的性能是否与其他主机不同,并将其从健康负载均衡集中移除的过程。常见的异常值包含:连续的故障、时间成功率、时间延迟等,它需要过滤器来报告数据,当前仅以下过滤器支持:[http router],[tcp proxy],[redis proxy],[thrift proxy]
检测错误分为两类:
- 外部生成的错误
外部生成的错误是事务特定的,发生在上游服务器对接收到的请求做出响应时。例如,HTTP服务器返回错误代码500或者redis服务器返回无法解码的有效负载。这些错误是在Envoy成功连接到上游主机之后由上游主机生成的。
- 本地生成的错误
由 Envoy 生成的,响应于中断或阻止与上游主机通信的事件。本地生成错误的例子包括超时、TCP复位、无法连接到指定端口等。
连续的 5xx 错误
TODO;
网关连续故障
TODO;
连续本地起源失败
TODO;
成功率
TODO;
失败率
TODO;
异常检测日志
TODO;
负载均衡
TODO;
支持的类型
在单个上游集群中存在多个主机,需要把下游客户端请求负载均衡至各个主机,以有效的利用资源,envoy 这里分为两类:全局负载均衡和分布式负载均衡。
- 分布式负载均衡
是指 Envoy 根据上游主机的位置来确定如何将负载分配给端点。
在这种策略下,Envoy 可以根据上游主机的位置信息做出更智能的负载均衡决策。
这通常包括考虑主机的地理位置、网络延迟等因素,以优化流量的分发。这种方式使Envoy能够更好地适应不同主机之间的差异,提高系统的性能和稳定性。
激活的健康检查(Active health checking):通过对上游主机进行健康检查,Envoy可以调整优先级和本地性的权重,以适应不可用主机。
区域感知路由(Zone aware routing):这可以使Envoy在不需要在控制平面中明确配置优先级的情况下,更喜欢较近的端点。
负载均衡算法:Envoy可以使用多种不同的算法,利用提供的权重来确定选择哪个主机。这些算法有助于更精细地控制流量的分发,以满足系统的性能和可靠性需求。
- 全局负载均衡
是指有一个单一权威的数据来决定如何在主机之间分配负载,既控制平面完成,它可以通过指定各种参数(如优先级、本地性权重、端点权重和端点健康状况)来调整应用于单个端点的负载。
大多数复杂的部署都会同时使用这两类特性。例如,可以使用全局负载均衡来定义高级别的路由优先级和权重,同时使用分布式负载均衡来响应系统的变化(例如,使用主动健康检查)。通过结合使用这两种方式,可以充分发挥两者的优势:在宏观层面上,可以通过一个全局意识的权威来控制流量的流向,同时在微观层面上,个体代理可以对系统变化做出反应。
策略:加权轮询
加权轮询(Weighted Round Robin)是一种简单的策略,它按照轮询的顺序选择每个可用的上游主机,如果在某个地区给端点分配了权重,那么将使用加权轮询的调度表,其中具有较高权重的端点将更频繁地出现在轮换中,以实现有效的加权。
clusters:
- name: oneops-syncds-v2
type: STATIC
dns_lookup_family: V4_ONLY
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: test1
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
protocol: TCP
address: 192.168.0.1
port_value: 8080
load_balancing_weight: 1
- endpoint:
address:
socket_address:
protocol: TCP
address: 192.168.0.1
port_value: 8000
load_balancing_weight: 9
以上配置 “lb_policy” 与 “load_balancing_weight” 分布对应该策略与对应端点的权重。
策略:加权最小请求数
加权最小请求数(Weighted Least Request)取决于主机是否具有相同的权重,有以下区别:
- 所有权重相等
这是一种O(1)算法,根据配置选择 N 个随机的可用主机(默认为2),然后选择具有最少活动请求的主机,权重会随着这些主机上的活动请求数量的增加而减小。
- 所有权重不相等
如果集群中多个主机具有不同的权重,负载均衡器将使用加权轮询调度表,在请求时动态调整权重,权重是在选择主机时使用以下公式计算的:
weight = load_balancing_weight / (active_requests + 1)^active_request_bias.
active_request_bias
可以通过运行时进行配置,默认值为 1.0 且必须大于或等于 0.0。由公式知活动请求数越大,则相应的有效权重越低。同时 “active_request_bias=0” 时,等同于加权轮询。
clusters:
- name: oneops-syncds-v2
type: STATIC
dns_lookup_family: V4_ONLY
lb_policy: LEAST_REQUEST
load_assignment:
cluster_name: test1
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
protocol: TCP
address: 192.168.0.1
port_value: 8080
load_balancing_weight: 2
- endpoint:
address:
socket_address:
protocol: TCP
address: 192.168.0.1
port_value: 8000
load_balancing_weight: 1
TODO;
策略:环形哈希
环形哈希(Ring hash)实现了一致性哈希到上游主机,每个主机通过对其地址进行哈希映射到一个圆上(即由哈希组成的"环"),然后通过对请求的某个属性进行哈希,找到顺时针方向上最近的相应主机来路由每个请求。
如果希望使用主机地址以外的其他内容作为哈希键去组成圆环,则可以在 “envoy.lb” 的 LbEndpoint.Metadata 中指定,例如:
clusters:
- name: oneops-syncds-v2
type: STATIC
dns_lookup_family: V4_ONLY
#lb_policy: ROUND_ROBIN
lb_policy: RING_HASH
ring_hash_lb_config:
minimum_ring_size: 2
maximum_ring_size: 2
#hash_function: MURMUR_HASH_2
#hash_function: XX_HASH
load_assignment:
cluster_name: test1
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
protocol: TCP
address: 192.168.0.1
port_value: 8080
#load_balancing_weight: 1
#metadata:
# filter_metadata:
# envoy.lb:
# hash_key: "test1"
- endpoint:
address:
socket_address:
protocol: TCP
address: 192.168.0.1
port_value: 8000
#load_balancing_weight: 1
#metadata:
# filter_metadata:
# envoy.lb:
# hash_key: "test2"
TODO;
策略:Maglev
Maglev 算法是一种一致性哈希算法,可以作为环形哈希负载均衡器的替代方案,它提供更好的负载分布和可扩展性。
clusters:
- name: oneops-syncds-v2
type: STATIC
dns_lookup_family: V4_ONLY
lb_policy: MAGLEV
load_assignment:
cluster_name: test1
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
protocol: TCP
address: 192.168.0.1
port_value: 8080
- endpoint:
address:
socket_address:
protocol: TCP
address: 192.168.0.1
port_value: 8000
TODO;
策略:随机
随机(Random)负载均衡器是一种简单的策略,它从可用的上游主机中随机选择一个,与轮询策略相比,随机选择通常在没有健康检查策略的情况下表现更好。
clusters:
- name: oneops-syncds-v2
type: STATIC
dns_lookup_family: V4_ONLY
lb_policy: MAGLEV
load_assignment:
cluster_name: test1
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
protocol: TCP
address: 192.168.0.1
port_value: 8080
- endpoint:
address:
socket_address:
protocol: TCP
address: 192.168.0.1
port_value: 8000
TODO;
负载优先级
在负载均衡期间,如果上游集群存在多个 “lb_endpoints” 配置,则 envoy 只会考虑把流量转发至最高优先级级别一组主机。
对于每个 “lb_endpoints” config.endpoint.v3.LocalityLbEndpoints 可以指定一个可选的参数 “priority” 表示优先级,从数字 0 至 N 代表从高至低,当最高优先级级别的主机(P=0)健康时,所有流量都会落在该优先级级别的端点上,仅当最高优先级级别的端点变得不健康时,流量将开始流向较低的优先级级别调度,这个记得需配合健康检查使用。
clusters:
- name: oneops-syncds-v2
type: STATIC
dns_lookup_family: V4_ONLY
lb_policy: RANDOM
load_assignment:
cluster_name: test1
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
protocol: TCP
address: 192.168.0.1
port_value: 8080
priority: 1
- lb_endpoints:
- endpoint:
address:
socket_address:
protocol: TCP
address: 192.168.0.1
port_value: 8000
priority: 10
health_checks:
- timeout: 1s
interval: 2s
unhealthy_threshold: 1
healthy_threshold: 1
http_health_check:
path: "/health"
event_log_path: /var/log/envoy.log
always_log_health_check_failures: true
以上端点 “192.168.0.1:8080” 如果正常,则所有流量均到该节点,仅当健康检测失败后才会被流量转发至 “192.168.0.1:8000” 端点,详细的算法调度表可参考 Priority levels。
聚合集群
TODO;