Istio 详解

Envoy 详解

Envoy 是专为大型现代 SOA(面向服务架构)架构设计的 L7 代理和通信总线,它既可以作为 Service Mesh 中的数据面使用,也可以作为入口网关层使用,可以通过 xDS API 控制 Envoy 的监听、路由、负载均衡等行为。

Envoy 核心功能

  1. 高性能设计:采用 C++ 编写,拥有良好的四层、七层代理性能
  2. Filter 架构:可以在四、七层编写 Filter 以扩展 Envoy 的功能,eg: 监听过滤器、四层网络过滤器,以及七层过滤器。不过 Envoy 支持最完善的还是 HTTP过滤器,支持了限流、路由转发、故障注入等多种服务治理功能,
  3. 良好的 HTTP/2 支持:随着 gRPC 框架的流行以及边缘层网络性能的要求提升,HTTP/2 越来越被重视。Envoy 原生支持 HTTP/2,可以在 HTTPHTTP/2 之间做转换。比如在 Sidecar 模式中,无论应用协议是 HTTP 还是 HTTP/2Envoy 之间默认使用 HTTP/2 通信,这样极大提升了服务性能和稳定性,避免了 HTTP 频繁建立连接带来的消耗和不稳定性。
  4. 多种协议支持:Thrift、gRPC、MongoDB、Redis、MySQL 等多种网络协议都被支持,甚至可以使用 EnvoyRedisMesh 方案,用来代替流行的 Redis 中间件。
  5. 可观测性:支持强大的统计系统。日志、Metrics、链路追踪都有良好的支持。
  6. 边缘网关:Envoy 本身就是一个高性能的网络代理组件,完全可以作为入口网关层使用,在 Kubernetes 中,也可以作为 EgressIngress 使用。
  7. 服务发现:和其他常见的网络代理软件不同,Envoy 默认支持服务发现组件。Envoy 使用了一套 xDS 的动态 API,获取服务的后端节点并实时更新,结合 Envoy 强大的负载均衡器,可以做到最终一致性。
  8. Wasm 扩展:Wasm 全称为 WebAssembly,最早用在浏览器端用来解决 JavaScript 性能问题和大型项目团队协作问题。近些年,它开始在一些后端技术上使用,用来代替 Lua,作为核心系统的扩展方式。因为 Wasm 可以使用多种语言进行开发,所以方便对核心系统进行扩展,不用担心语言问题。当然相对于原生的 C++ 扩展方式,它大概有 3 成的性能损耗。

Envoy 架构设计

Envoy

  1. Iptable:通过 Iptable 劫持,将入口和出口流量都转发到 Envoy 上,达到劫持流量的目的。
  2. ListenerEnvoy 通过建立多个监听器提供不同的服务。比如通过监听的两个端口分别负责 Sidecar 模式的出流量和入流量,Sidecar 多使用这种设计,这样可以简化编程逻辑,也可以增强 Filter 的通用性。如果提供不同协议,Envoy 也会建立不同的端口来提供服务。
  3. Worker:每个 Listener 维护一个对应的 Worker PoolEnvoy 为每个逻辑处理器创建一个 Worker 线程,当我们在一个新的端口启动一个新的 server 时,Envoy 也会根据 -concurrency 创建对应的 Worker 线程,要注意启动太多的 worker 线程并不一定是好事,特别是在 Sidecar 模式,我们并不会分配过多的逻辑核心给到 Sidecar,创建过多的 Worker 线程可能导致每个 Worker 线程维护的连接变多,Upstream 压力过大。
  4. Filters:可以理解为中间件,通过 Filter,可以做到四层和七层的流量过滤,支持服务治理需要的限流、熔断等功能。
  5. Cluster Manager:流量经过 Router,识别出需要转发的 Cluster,通过 Cluster Manager 进行服务发现和负载均衡等功能。
  6. UpstreamUpstream 维护了 EndPoint 的连接池,通过负载具衡器,将流量转发到合适的 EndPoint 上面。

过程示例

Productpage 服务通过 HTTP 协议,调用 review 服务的过程。

Envoy过程

  1. 通过 Iptables 对流量进行劫持,将 Productpage 访问 Reviews 的流量转发到 Envoy 的出流量 15001 端口上。

  2. Envoy 先根据 virtual_hosts 进行匹配,再通过路由匹配,发现路由对应的 Cluster,通过服务发现找到 Cluster 对应的 EndPoint,将流量转发到 10.40.0.15:9080Pod 上。

  3. ReviewsPod 通过 Iptables 对流量进行劫持,将流量劫持到 Envoy 的入流量端口 15006 上。

  4. Envoy 将本地流量转发到对应的本地地址 127.0.0.1:9080,这里不需要对流量进行识别,因为流量被转发到入流量端口 15006 上,这个端口的配置用于本地流量的转发。

  5. 到这里整个 Sidecar 的流量出入过程就结束了。出入流量都经由 Envoy,最终被正确的转发到了 ReviewsPod 上面。

Envoy 配置

Envoy 的配置分为静态配置和动态配置

  1. 静态配置 : 手动填写的配置
  2. 动态配置 : xDS API 获取的配置

静态配置

eg:
定义 Listener 监听器,监听端口为 10000virtual_hosts 匹配所有域名,Routes 的匹配规则为所有 Path,也就是所有访问 10000 端口的请求都会被转发到 service_google 服务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
listeners:
- name: listener_0
address:
socket_address: { address: 0.0.0.0, port_value: 10000 }
filter_chains:
- filters:
- name: envoy.http_connection_manager
config:
stat_prefix: ingress_http
codec_type: AUTO
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"] // 匹配所有域名
routes:
- match: { prefix: "/" }// 匹配所有 path
route: { host_rewrite: www.google.com, cluster: service_google } // 将流量转发到 service_google 服务
http_filters:
- name: envoy.router

service_google

1
2
3
4
5
6
7
8
9
clusters:
- name: service_google
connect_timeout: 0.25s
type: LOGICAL_DNS
# Comment out the following line to test on v6 networks
dns_lookup_family: V4_ONLY
lb_policy: ROUND_ROBIN
hosts: [{ socket_address: { address: google.com, port_value: 443 }}]
tls_context: { sni: www.google.com }

动态配置

通过查询一个或多个管理服务器 获取数据 以发现动态资源变更,比如 RouterClusterEndPoint 等,我们把这些发现服务及其对应的 API 称为 xDSxDS 最大的价值就是定义了一套可扩展的通用微服务控制 API,这些 API 不仅可以做到服务发现,也可以做到路由发现、集群发现,可以说所有配置都能通过发现的方式解决

xDS 协议: Service Mesh 控制面和数据面的通信桥梁

通过 xDS 协议可以做到 discovery everything,所有配置都可以通过发现的方式解决,这是 Envoy xDS 架构为微服务世界带来的重大变革。

xDS 概念介绍

xDS 包含:

  1. LDS(监听器发现服务)
  2. CDS(集群发现服务)
  3. EDS(节点发现服务)
  4. SDS(密钥发现服务)
  5. RDS(路由发现服务)

xDS 中每种类型对应一个发现的资源,这些类型数据存储在 xDS 协议的 Discovery RequestDiscovery ResponseTypeUrl 字段中, 这个字段按照以下格式存储: type.googleapis.com/<resource type>

eg: type.googleapis.com/envoy.api.v2.Cluster 就表明是 Cluster 类型的资源,需要按照 Cluster 类型处理数据

LDS

envoy.api.v2.Listener(LDS):对应 Listener 数据类型,包含了监听器的名称、监听端口、监听地址等信息,通过动态更新此类型,可以动态新增监听器或者更新监听器的地址端口等信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"name": "...",
"address": "{...}",
"filter_chains": [],
"use_original_dst": "{...}",
"per_connection_buffer_limit_bytes": "{...}",
"metadata": "{...}",
"drain_type": "...",
"listener_filters": [],
"listener_filters_timeout": "{...}",
"continue_on_listener_filters_timeout": "...",
"transparent": "{...}",
"freebind": "{...}",
"socket_options": [],
"tcp_fast_open_queue_length": "{...}",
"traffic_direction": "...",
"udp_listener_config": "{...}",
"api_listener": "{...}",
"connection_balance_config": "{...}",
"reuse_port": "...",
"access_log": []
}

RDS

envoy.api.v2.RouteConfigurationRDS):对应 Envoy 中的 Route 类型,用于更新 virtual_hosts,以及 virtual_hosts 包含的路由表信息、路由规则、针对路由的限流、路由级别的插件等,包括路由匹配到的 Cluster

1
2
3
4
5
6
7
8
9
10
11
12
{
"name": "...",
"virtual_hosts": [],
"vhds": "{...}",
"internal_only_headers": [],
"response_headers_to_add": [],
"response_headers_to_remove": [],
"request_headers_to_add": [],
"request_headers_to_remove": [],
"most_specific_header_mutations_wins": "...",
"validate_clusters": "{...}"
}

CDS

envoy.api.v2.Cluster(CDS):对应 Envoy 中的 Cluster 类型,包含了 Cluster 是采用静态配置数据,还是采用动态 EDS 发现的方式,包括 Cluster 的负载均衡策略、健康检查配置等,以及服务级别的插件设置

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
{
"transport_socket_matches": [],
"name": "...",
"alt_stat_name": "...",
"type": "...",
"cluster_type": "{...}",
"eds_cluster_config": "{...}",
"connect_timeout": "{...}",
"per_connection_buffer_limit_bytes": "{...}",
"lb_policy": "...",
"hosts": [],
"load_assignment": "{...}",
"health_checks": [],
"max_requests_per_connection": "{...}",
"circuit_breakers": "{...}",
"tls_context": "{...}",
"upstream_http_protocol_options": "{...}",
"common_http_protocol_options": "{...}",
"http_protocol_options": "{...}",
"http2_protocol_options": "{...}",
"extension_protocol_options": "{...}",
"typed_extension_protocol_options": "{...}",
"dns_refresh_rate": "{...}",
"dns_failure_refresh_rate": "{...}",
"respect_dns_ttl": "...",
"dns_lookup_family": "...",
"dns_resolvers": [],
"use_tcp_for_dns_lookups": "...",
"outlier_detection": "{...}",
"cleanup_interval": "{...}",
"upstream_bind_config": "{...}",
"lb_subset_config": "{...}",
"ring_hash_lb_config": "{...}",
"original_dst_lb_config": "{...}",
"least_request_lb_config": "{...}",
"common_lb_config": "{...}",
"transport_socket": "{...}",
"metadata": "{...}",
"protocol_selection": "...",
"upstream_connection_options": "{...}",
"close_connections_on_host_health_failure": "...",
"drain_connections_on_host_removal": "...",
"filters": [],
"track_timeout_budgets": "..."
}
EDS

envoy.api.v2.ClusterLoadAssignment(EDS) :常说的服务发现。包含服务名、节点信息和 LB 策略等数据。

1
2
3
4
5
{
"cluster_name": "...",
"endpoints": [],
"policy": "{...}"
}

SDS

envoy.api.v2.Auth.Secret(SDS):用于发现证书信息,以动态更新证书。

istio 可以通过 SDS 即可动态更新证书
{
“name”: “…”,
“tls_certificate”: “{…}”,
“session_ticket_keys”: “{…}”,
“validation_context”: “{…}”,
“generic_secret”: “{…}”
}

gRPC 流式订阅
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
grpcurl -v -plaintext admin-rpc-service.eve-cn-infra-dev:30200   list

grpcurl -v -plaintext auth-rpc-service.eve-cn-infra-dev:10200 list

grpcurl -v -plaintext chat-rpc-service.eve-cn-infra-dev:30300 list

grpcurl -v -plaintext conversation-rpc-service.eve-cn-infra-dev:10220 list

grpcurl -v -plaintext friend-rpc-service.eve-cn-infra-dev:10240 list

grpcurl -v -plaintext group-rpc-service.eve-cn-infra-dev:10260 list

grpcurl -v -plaintext msg-rpc-service.eve-cn-infra-dev:10280 list

grpcurl -v -plaintext push-rpc-service.eve-cn-infra-dev:10170 list

grpcurl -v -plaintext user-rpc-service.eve-cn-infra-dev:10320 list

需要给 grpc 的 svc 添加appProtocol: http2

参考链接 https://istio.io/latest/docs/ops/configuration/traffic-management/protocol-selection/

1
2
3
4
5
6
7
8
9
10
11
12
13
for svc in \
admin-rpc-service \
auth-rpc-service \
chat-rpc-service \
conversation-rpc-service \
friend-rpc-service \
group-rpc-service \
msg-rpc-service \
push-rpc-service \
user-rpc-service; do
kubectl patch svc $svc -n eve-cn-infra-dev --type=json \
-p='[{"op":"add","path":"/spec/ports/0/appProtocol","value":"http2"}]'
done
API 请求顺序

典型的 HTTP 路由场景,客户端需要先获取 Listener 资源,通过 Listener 资源拿到 Route 的配置。Route 中包含一个或者多个 Cluster 集群资源,通过 Cluster 集群的信息再获取集群节点的信息,这样整个请求链路就完成了

全量请求和增量请求

当出现新的资源时,只需向 Management Server 发送新增的资源,Management Server 也只会返回新增资源的数据。

多条请求流和单条请求流

xDS 协议并不约束在请求多个资源时,多个资源使用同一个请求流,还是每个资源各使用一个请求流,Management Server 应该同时支持这两种模式。

在一个连接请求多个资源

支持在一条连接中按照顺序获取 xDS 中的各种 API,比如先请求 CDS,然后请求 EDS

xDS 协议详解

请求信息示例:

1
2
3
4
5
6
7
version_info:   # version_info 为空,表示这是连接中的第一个请求流
node: { id: envoy } # Node 中的 ID 则表明机器信息,需要传递机器的唯一标识
resource_names:
- foo
- bar
type_url: type.googleapis.com/envoy.api.v2.ClusterLoadAssignment # EDS
response_nonce:

回应信息示例:

1
2
3
4
5
6
version_info: X
resources:
- foo ClusterLoadAssignment proto encoding
- bar ClusterLoadAssignment proto encoding
type_url: type.googleapis.com/envoy.api.v2.ClusterLoadAssignment
nonce: A # ACK NACK 或者区分资源更新到底是响应哪个推送数据。

在收到 Management Server 推送的新版本数据后,Envoy 会响应 ACK 或者 NACK 告知 Management Server 是否更新版本成功。ACK 代表更新版本成功,这时会携带 Management Server 推送的最新版本号发送 ACK 信息;NACK 代表更新版本失败,这时会携带旧的版本号发送 NACK 信息。

补 边缘代理模式

Envoy 不仅可以用于 Sidecar 模式,也可以用作边缘网关,但是用作边缘网关层我们有一些注意事项,这些问题不仅在 Envoy 中存在,在其他的边缘网关中也应该处理。

  1. HTTP 标头清理 : use_remote_address 设置为 true 来清理 HTTP 标头。
  2. 超时控制 : 连接超时、流超时和路由超时。
  3. 连接限制