ljzsdut
GitHubToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

11 Http流量管理

Envoy高级路由

Envoy基于HTTP router过滤器基于路由表完成多种高级路由机制,例如

  • 将域名映射到虚拟主机

  • path的前缀(prefix)匹配、精确匹配或正则表达式匹配

  • 虚拟主机级别的TLS重定向;

  • 路由级别的path/host重定向,以及由Envoy直接生成响应报文;

  • 显式host重定向、前缀重定向;

  • 基于HTTP标头或路由配置的请求重试与请求超时;

  • 通过“运行时参数值”从一个集群迁移至另一个集群(流量迁移)

  • 通过“基于权重/百分比”的路由跨多个集群进行流量分割

  • 基于任意标头匹配路由规则;

  • 基于优先级的路由;

  • 基于hash策略的路由;

  • ……

虚机主机及路由配置概述

虚拟主机

路由配置中的顶级元素是虚拟主机。每个虚拟主机都有一个逻辑名称以及一组域名,请求报文中的HOST头将根据此处的域名进行路由;单个侦听器可以服务于多个顶级域。基于域名选择虚拟主机后,将基于配置的路由机制完成请求路由或进行重定向;

虚拟主机的配置字段:

{
    "name": "...",
    "domains": [],
    "routes": [],
    "require_tls": "...",
    "virtual_clusters": [],
    "rate_limits": [], 
    "request_headers_to_add": [], 
    "request_headers_to_remove": [], 
    "response_headers_to_add": [], 
    "response_headers_to_remove": [], 
    "cors": "{...}",
    "per_filter_config": "{...}", 
    "typed_per_filter_config": "{...}", 
    "include_request_attempt_count": "...", 
    "retry_policy": "{...}",
    "hedge_policy": "{...}"
}

虚拟主机级别的路由策略用于为相关的路由属性提供默认配置,用户也可在路由配置上自定义用到的路由属性,例如限流、CORS和重试机制等;

Envoy匹配路由时,它基于如下工作过程进行:

  1. 检测HTTP请求的host标头或:authority(即USER@HOST),并将其同路由配置中定义的虚拟主机作匹配检查;

  2. 在匹配到的虚拟主机配置中按顺序检查虚拟主机中的每个路由条目中的匹配条件,直到第一个路由条目匹配的为止(短路);

  3. 如果定义了虚拟集群,按顺序检查虚拟主机中的每个虚拟集群,直到第一个匹配的为止;

listeners:
- name:
	address: {...} 
	filter_chians: [] 
	- filters:
		- name: envoy.http_connection_manager
			config:
				...
				route_config:
					name: ... 
					virutal_hosts: [] 
					- name: ...
						domains: []    # 虚拟主机的域名,路由匹配时将请求报文中的host标头值与此处列表项进行匹配检测;
						routes: []			# 路由条目,匹配到当前虚拟主机的请求中的path匹配检测将针对各route中由match定义条件进行;
						- name: ...
							match: {...}
								prefix|path|regex: ... # 基于路径前缀、路径或正则表达式三者之一定义匹配条件;
							route: {...}
								cluster|cluster_header|weighted_cluster: ... # 基于集群、请求报文中的集群标头或加权集群(流量分割)定义路由目标;
						virtual_clusters: [] # 为此虚拟主机定义的用于收集统计信息的虚拟集群列表;
						...
					...

域名搜索顺序

将请求报文中的host标头值依次与路由表中定义的各Virtualhost的domain属性值进行比较,并于第一次匹配时终止搜索:

domain搜索顺序:

  • Exact domain names: www.foo.com
  • Prefix domain wildcards: *.foo.com or *-bar.foo.com
  • Suffix domain wildcards: foo.* or foo-*
  • Special wildcard * matching any domain.

匹配时,先将HOST值与与所有的domain属性值中的精确域名进行精确匹配,如果匹配到则终止匹配;如果没有匹配到,再去匹配所有的前缀域名,如果匹配到则终止匹配;如果没有匹配到,则去匹配后缀域名,如果匹配到则终止匹配;如果没有匹配到,再去看看是否有"*",如果有则匹配,如果没有则匹配不到任何的域名。

符合匹配条件的请求要由如下三种方式之一处理

  • route:路由到指定位置
  • redirect:重定向到指定位置
  • direct_response:直接以给定的内容进行响应

路由中也可按需在请求及响应报文中添加或删除响应标头:

{
    "name": "...",
    "match": "{...}",
    "route": "{...}",
    "redirect": "{...}",
    "direct_response": "{...}",
    "metadata": "{...}",
    "decorator": "{...}",
    "per_filter_config": "{...}",
    "typed_per_filter_config": "{...}",
    "request_headers_to_add": [],
    "request_headers_to_remove": [],
    "response_headers_to_add": [],
    "response_headers_to_remove": [],
    "tracing": "{...}"
}

路由配置

路由匹配

Snipaste_2022-04-30_20-35-58

匹配条件是定义的检测机制,用于过滤出符合条件的请求并对其作出所需的处理 ,例如路由、重定向或直接响应等;

基于path匹配

  1. 基础匹配:必须要定义prefix、path和regex三种匹配条件中的一种形式(三选一)
  2. 除了必须设置上述三者其中之一外,还可额外完成如下限定(与上面匹配条件是”与“关系):
    1. 区分字符大小写(case_sensitive)
    2. 匹配指定的运行键值表示的比例进行流量迁移(runtime_fraction); 不断地修改运行时键值完成流量迁移
    3. 基于标头的路由:匹配指定的一组标头(headers);
    4. 基于参数的路由:匹配指定的一组URL查询参数(query_parameters);
    5. 仅匹配grpc流量(grpc);
{
  "prefix": "...",
  "path": "...",
  "regex": "...", 
  "case_sensitive": "{...}",
  "runtime_fraction": "{...}", 
  "headers": [], 
  "query_parameters": [], 
  "grpc": "{...}"
}

示例:

          route_config:
            name: local_route
            virtual_hosts:
            - name: vh_001
              domains: ["ilinux.io", "*.ilinux.io", "ilinux.*"]
              routes:
              - match:
                  path: "/service/blue"
                route:
                  cluster: blue
              - match:
                  regex: "^/service/.*blue$"
                redirect:
                  path_redirect: "/service/blue"
              - match:
                  prefix: "/service/yellow"
                direct_response:
                  status: 200
                  body:
                    inline_string: "This page will be provided soon later.\n"
              - match:
                  prefix: "/"
                route:
                  cluster: red
            - name: vh_002
              domains: ["*"]
              routes:
              - match:
                  prefix: "/"
                route:
                  cluster: gray

基于标头匹配

Snipaste_2022-04-30_20-44-32

基于参数匹配

Snipaste_2022-04-30_20-49-23

示例:

          route_config:
            name: local_route
            virtual_hosts:
            - name: vh_001
              domains: ["*"]
              routes:
              - match:
                  prefix: "/"
                  headers:   # 基于header匹配
                  - name: X-Canary
                    exact_match: "true"
                route:
                  cluster: ver-1.7-pre
              - match:
                  prefix: "/"
                  query_parameters:  #基于参数匹配
                  - name: "username"
                    string_match:
                      prefix: "vip_"
                route:
                  cluster: ver-1.6
              - match:
                  prefix: "/"
                route:
                  cluster: ver-1.5

路由目标

符合匹配条件的请求要由如下三种方式之一处理:

  • route:路由到指定位置
  • redirect:重定向到指定位置
  • direct_response:直接以给定的内容进行响应

路由

路由到指定的集群。

匹配到的流量可路由至如下三种目标之一:

  • cluster:指定的上游集群;
  • cluster_header:请求标头中的cluster_header的值指定的上游集群;
  • weighted_clusters:基于权重将请求路由至多个上游集群,进行流量分割;

路由过滤器额外还可执行如下操作(”高级流量管理“部分详解):

  • matadata_match:子集负载均衡器使用的端点元数据匹配条件;
  • prefix_rewrite:前缀重写,即将下游请求的path转发至上游主机时重写为另一个path;
  • host_rewrite:主机头重写;
  • auto_host_rewrite:自动主机头重写,仅适用于strict_dns或logical_dns类型的集群;
  • timeout:上游超时时长,默认为15s;
  • idle_timeout:路由的空间超时时长,未指定时表示不超时;
  • retry_policy:重试策略,优先于虚拟主机级别的重试策略;
  • request_mirror_policy:启用流量镜像策略,也称为流量复制或影子镜像;
  • cors:跨域资源共享(Cross Origin Resource Sharing);
  • priority:路由优先级;
  • rate_limits:速率限制;
  • hash_policy:上游集群使用环哈希算法时为其指定用于环形哈希负载均衡的哈希策略表; 通常哈希计算的目标 是指定的标头、cookie或者请求报文的源IP地址;
{
  "cluster": "...",   # 路由到指定的目标集群
  "cluster_header": "...",
  "weighted_clusters": "{...}",  #将流量路由并按权重比例分配到多个上游集群;
  "cluster_not_found_response_code": "...",
  "metadata_match": "{...}",
  "prefix_rewrite": "...",
  "host_rewrite": "...",
  "auto_host_rewrite": "{...}",
  "auto_host_rewrite_header": "...",
  "timeout": "{...}",
  "idle_timeout": "{...}",
  "retry_policy": "{...}",
  "request_mirror_policy": "{...}",
  "priority": "...",
  "rate_limits": [],
  "include_vh_rate_limits": "{...}",
  "hash_policy": [],
  "cors": "{...}",
  "max_grpc_timeout": "{...}",
  "grpc_timeout_offset": "{...}",
  "upgrade_configs": [],
  "internal_redirect_action": "...",
  "hedge_policy": "{...}"
}

重定向

为请求响应一个301应答,从而将请求从一个URL永久重定向至另一个URL。

Envoy支持如下重定向行为:

  • 协议重定向:https_redirect或scheme_redirect二者只能使用其一;
  • 主机重定向:host_redirect
  • 端口重定向:port_redirect
  • 路径重定向:path_redirect
  • 路径前缀重定向:prefix_redirect
  • 重设响应码:response_code,默认为301;
  • strip_query:是否在重定向期间删除URL中的查询参数,默认为false;
{
    "https_redirect": "...",
    "scheme_redirect": "...",
    "host_redirect": "...",
    "port_redirect": "...",
    "path_redirect": "...",
    "prefix_rewrite": "...",
    "response_code": "...",
    "strip_query": "..."
}

直接响应

Envoy还可以直接响应请求而不将其代理至上游主机:

{
  "status": "...",
  "body": "{...}" 
}

响应码可由status直接给出;

响应正文可省略,默认为空;需要指定时应该由body通过如下三种方式之一给出数据源:

{
	"filename": "...", 
  "inline_bytes": "...", 
  "inline_string": "..."
}
  • filename:本地文件数据源;
  • inline_bytes:直接给出的内联字节;
  • inline_string:直接给出的内联字符串;

路由策略

基础路由配置:

  • 在match中简单通过prefix、path或regex指定匹配条件
  • 将匹配到的请求进行重定向、直接响应或路由到指定目标集群

高级路由策略:

  • 在match中通过prefix、path或regex指定匹配条件,并使用高级匹配机制

    • 结合runtime_fraction按比例切割流量
    • 结合headers按指定的标头路由,例如基于cookie进行,将其值分组后路由到不同目标;
    • 结合query_parameters按指定的参数路由,例如基于参数group进行,将其值分组后路由到不同的目标;
    • 提示:可灵活组合多种条件构建复杂的匹配机制
  • 复杂路由目标

    • 结合请求报文标头中cluster_header的值进行定向路由
    • weighted_clusters:将请求根据目标集群权重进行流量分割
    • 配置高级路由属性,例如重试、超时、CORS、限速等;

演示示例

root@istio:~/ServiceMesh_in_Practise/http-connection-manager/httproute-simple-match# ls
docker-compose.yaml  front-envoy.yaml

root@istio:~/ServiceMesh_in_Practise/http-connection-manager/httproute-header-match# ls
docker-compose.yaml  front-envoy.yaml

高级流量管理

流量迁移

灰度策略

常用的策略类型大体可分为“基于请求内容发布”和“基于流量比例发布”两种类型:

  • 基于请求内容发布:配置相应的请求内容规则,满足相应规则服务流量会路由到灰度版 本;例如对于http请求,通过匹配特定的Cookie标头值完成引流

  • cookie内容:

    • 完全匹配:当且仅当表达式完全符合此情况时,流量才会走到这个版本;
      • 正则匹配:此处需要您使用正则表达式来匹配相应的规则;
    • 自定义Header:
    • 完全匹配:当且仅当表达式完全符合此情况时,流量才会走到这个版本;
      • 正则匹配:此处需要您使用正则表达式来匹配相应的规则;
  • 可以自定义请求头的key和value,value支持完全匹配和正则匹配;

  • 基于流量比例发布:对灰度版本配置期望的流量权重,将服务流量以指定权重比例引流到灰度版本。例如10%的流量分配为新版本,90%的流量保持在老版本,所有版本的权重之和为100,这种灰度策略也可以称为AB测试;

流量迁移配置

通过在路由中配置“运行时对象”选择特定路由以及相应集群的概率的变动,从而实现将虚拟主机中特定路由的流量逐渐从一个集群迁移到另一个集群;

routes:
- match:			# 定义路由匹配参数;
		prefix|path|regex: ... 		# 流量过滤条件,三者必须定义其中之一;也可结合基于header和parameter的过滤条件
		runtime_fraction:					# 额外匹配指定的运行时键值,每次评估匹配路径时,它必需低于此字段指示的匹配百分比;支持渐进式修改;
			default_value:          # 运行时键值不可用时,则使用此默认值;
				numerator:						# 指定分子,默认为0;
				denominator:					# 指定分母,小于分子时,最终百分比为1;分母可固定使用HUNDRED(默认)、TEN_THOUSAND和MILLION;
			runtime_key: routing.traffic_shift.app1   # 指定要使用的运行时键,键值需要用户自定义,用于运行时修改分子的值;
	route:
		custer: app1_v1
- match:
		prefix|path|regex: ...      # 此处的匹配条件应该与前一个路由匹配条件相同,以确保能够分割流量;
	route:
		cluster: app1_v2            # 此处的集群通常是前一个路由项目中的目标集群应用程序的不同版本;	

Envoy基于首次匹配以短路机制工作,若相应的路由存在runtime_fraction对象,则指定比例之外的其他请求,会另外匹配其它路由条目;这意味着上述示例中,如果两个路由条目match条件一样,则runtime_fraction对象定义的百分比之外的流量将由第二个路由条目精确捕获;

用户可以通过不断地修改runtime_fraction对象的值完成流量迁移:

# curl -X POST 'http://envoy_ip:admin_port/runtime_modify?key1=val1&key2=val2'
例如:
# curl -X POST 'http://172.16.0.2:9901/runtime_modify?routing.traffic_shift.app1=30'

演示示例

root@istio:~/ServiceMesh_in_Practise/http-connection-manager/http-traffic-shifting# ls
docker-compose.yaml  front-envoy.yaml  send-request.sh

流量分割

HTTP router过滤器支持在一个路由中指定多个上游具有权重属性的集群 ,而后将流量基于权重调度至此些集群其中之一;

routes
- match: {...}
	route:
		weight_clusters: {...}
			clusters: []  # 与当前路由关联的一个或多个集群,必须参数
			- name: ...
				weight: ...   #集群权重,取值范围:0至total_weight
				metadata_match: {...}  # 子集负载均衡器使用的端点元数据匹配条件,可选参数,仅用于上游集群中具有与此字段中设置的元数据匹配的元数端点以进行流量分配;
			total_weight: ...  # 总权重值,默认为100;
			runtime_key_prefix: ... # 可选参数,用于设定键前缀,从而每个集群以“runtime_key_prefix+.+cluster[i].name”为其键名,并能够以运行时键值的方式为每个集群提供权重;其中cluster[i].name表示列表中第i个集群名称。
			...

分配给每个集群的权重同样可以使用运行时参数进行调整:curl -X POST 'http://envoy_ip:admin_port/runtime_modify?key1=val1&key2=val2'

演示示例

root@istio:~/ServiceMesh_in_Practise/http-connection-manager/http-traffic-splitting# ls
docker-compose.yaml  front-envoy.yaml  send-request.sh

流量镜像

将流量转发至一个集群(主集群)的同时再转发到另一个集群(影子集群),无须等待影子集群返回响应。支持收集影子集群的常规统计信息,常用于测试。

默认情况下,路由器会镜像所有请求;也可使用如下两个参数配置转发的流量 比例:

  • runtime_key:运行时键,用于明确定义向影子集群转发的流量的百分比,取值范围为0- 10000,每个数字表示0.01%的请求比例;定义了此键却未指定其值时,默认为0;(即将废弃)
  • runtime_fraction:转发的流量比例小于N/D,优先级高于runtime_key;
route:
  cluster: myapp-v1.5
  request_mirror_policy:
    cluster: myapp-v1.6  # 影子集群名称
    #runtime_key: "..."  # 备注:即将废弃
    runtime_fraction:
      default_value:
        numerator: 10
        denominator: HUNDRED
      runtime_key: routing.request_mirror.myapp

演示示例

root@istio:~/ServiceMesh_in_Practise/http-connection-manager/http-request-mirror# ls
docker-compose.yaml  front-envoy.yaml  send-request.sh

故障注入

故障注入在实现上同重定向、重写和重试相似,不过是修改HTTP请求或应答的内容而已;由专用的故障注入过滤器(envoy.fault)实现,用于测试微服务对不同形式的故障韧性;

  • 通过用户指定的错误代码注入延迟(delay)和请求中止(abort),从而模拟出分阶段的不同故障情形

  • 故障范围仅限于通过网络进行通信的应用程序可观察到的范围,不支持模拟本地主机上的CPU和磁盘故障;

过滤器配置:

{
"delay": "{...}", # 注入延迟,延迟和请求中止至少要定义一个; 
"abort": "{...}", # 注入请求中止
"upstream_cluster": "...",  # 过路器适配的上游集群,即仅生效于指定的目标集群;
"headers": [], # 过滤器适配的请求报文标头列表,匹配检测时,各标头间为“与”关系; 
"downstream_nodes": [], # 要注入故障的下游主机列表,未指定时将匹配所有主机;
"max_active_faults": "{...}",  # 在同一个时间点所允许的最大活动故障数,默认为不限制;可以被
                                # 运行时参数config_http_filters_fault_injection_runtime所覆盖;
"response_rate_limit": "{...}" # 响应速率限制,可以被运行时参数fault.http.rate_limit.response_percent所覆盖;此为单流或连接级别的限制
}

延迟

http_filters:
- name: envoy.fault
  config:
    delay:
      type: fixed
      fixed_delay: 10s
      percentage:
        numerator: 10
        denominator: HUNDRED

请求终止

http_filters:
- name: envoy.fault
  config:
    abort:
      http_status: 503
      percentage:
        numerator: 10
        denominator: HUNDRED

演示示例

root@istio:~/ServiceMesh_in_Practise/http-connection-manager/fault-injection# ls
docker-compose.yaml  front-envoy.yaml  service-envoy-fault-injection-abort.yaml  service-envoy-fault-injection-delay.yaml  service-envoy.yaml

超时和重试

请求重试:

请求重试

主机断言:

Snipaste_2022-05-01_13-43-40

重试条件(同x-envoy-retry-on标头):

  • 5xx:上游主机返回5xx响应码,或者根本未予响应(断开/重置/读取超时)
  • gateway-error:网关错误,类似于5xx策略,但仅为502、503或504的应用进行重试
  • connection-failure:在TCP级别与上游服务建立连接失败时进行重试
  • retriable-4xx:上游服务器返回可重复的4xx响应码时进行重试
  • refused-stream:上游服器务使用REFUSED——STREAM错误码重置时进行重试
  • retriable-status-codes:上游服务器的响应码与重试策略或x-envoy-retriable-status-codes 标头值中定义的响应码匹配时进行重试

重试条件2(同x-envoy-retry-grpc-on标头):

  • cancelled:gRPC应答标头中的状态码是“cancelled”时进行重试
  • deadline-exceeded:gRPC应答标头中的状态码是“deadline-exceeded”时进行重试
  • internal:gRPC应答标头中的状态码是“internal”时进行重试
  • resource-exhausted:gRPC应答标头中的状态码是“resource-exhausted”时进行重试
  • unavailable:gRPC应答标头中的状态码是“unavailable”时进行重试

默认情况下,Envoy不会进行任何类型的重试操作,除非明确定义

routes:
- match:
    prefix: "/service/blue"
  route:
    cluster: blue_abort
    retry_policy:
      retry_on: "5xx"  # 重试
      num_retries: 3
- match:
    prefix: "/service/red"
  route:
    cluster: red_delay
    timeout: 1s         # 超时
- match:
    prefix: "/service/green"
  route:
    cluster: green
- match:
    prefix: "/"
  route:
    cluster: mycluster

演示示例

root@istio:~/ServiceMesh_in_Practise/http-connection-manager/timeout-retries# ls
docker-compose.yaml  front-envoy.yaml  service-envoy-fault-injection-abort.yaml  service-envoy-fault-injection-delay.yaml  service-envoy.yaml

CORS(跨域资源共享)

CORS

​ CORS是一种通过指定能够访问当前域的那些特定的或所有路由而来的外部域来强制对资源进行客户端访问控制的方法;出于安全方面的原因,浏览器会限制从脚本发起的跨域HTTP请求,因而,浏览器使用HTTP标头的存在来确定是否允许来自不同来源的响应;

​ Envoy的cors过滤器用于许可Web应用服务器进行跨域访问控制;通过在HTTP标头中追加一些额外的信息来通知浏览器允许跨域访问;

​ 在虚拟主机或路由级别可配置的CORS策略相关控制参数如下:

CORS1

CORS过滤器支持两种运行时设定,过滤器启用与否,可通过admin接口的/runtime获取:

  • filter_enabled:在请求上启用过滤器的百分比,默认为100/HUNDRED;设置filter_enabled参数的runtime_key的值可在运行时启用或禁用CROS过滤器;
  • shadow_enabled:于影子模式下在请求上启用CORS过滤器的百分比,默认值 为0;用于评估请求的Origin以确定它是否有效,但并不会强制任何策略;设置shadow_enabled参数的runtime_key的值可在运行时启用或禁用CROS过滤器;

Envoy的cors机制需要按如下方式配置:

  • 在过路器列表中定义cors过滤器;
  • 在虚拟主机或路由中配置cors策略:
    • 禁用:禁用CORS
    • 启用:启用CORS,且允许的请求者为*;
    • 受限:启用CORS,且允许的请求者需要自定义

image-20220501161537882

              routes:
              - match:
                  prefix: "/cors/open"
                route:
                  cluster: backend_service
              - match:
                  prefix: "/cors/disabled"
                route:
                  cluster: backend_service
                  cors:
                    filter_enabled:
                      default_value:
                        numerator: 0
                        denominator: HUNDRED
              - match:
                  prefix: "/cors/restricted"
                route:
                  cluster: backend_service
                  cors:
                    allow_origin:
                    - "envoyproxy.io"
                    allow_methods: "GET"
              - match:
                  prefix: "/"
                route:
                  cluster: backend_service
          http_filters:
          - name: envoy.cors
            typed_config: {}
          - name: envoy.router
            typed_config: {}

演示示例

root@istio:~/ServiceMesh_in_Practise/http-connection-manager/cors# ls
README.md  backend  frontend