Envoy项目为多种平台(例如amd64和arm64等)维护有相应的Docker Image,我们可按需猎取相应镜像后以容器形式运行Envoy,而且它们存在以下几种变化形式
◼ envoy:基于Ubuntu Bionic制作的Docker Image
◼ envoy-alpine和envoy-alpine-dev:基于alpine制作的Docker Image
◼ envoy-debug和envoy-debug-dev:基于Ubuntu制作的带有debug环境的Docker Image
◼ envoy-windows和envoy-windows-dev:基于Windows 1809制作的Docker Image
- Get Envoy项目为多个主流的Linux发行版(例如Ubuntu、CentOS和RHEL等)维护了二进制的发行版,配置相应的仓库后,即可使用系统的包管理器进行安装;
◼ Ubuntu
◆ https://dl.bintray.com/tetrate/getenvoy-deb
◼ CentOS
◆ https://tetrate.bintray.com/getenvoy-rpm/centos/
◼ RHEL
◆ https://tetrate.bintray.com/getenvoy-rpm/rhel/
- 部署文档(建议使用稳定版)
◼ Installing Envoy — envoy 1.21.0-dev-c7dfbb documentation
部署Envoy,以Debian/Ubuntu Linux发行版为例
运行Envoy
◼ 检查envoy的版本
◆ envoy --version
◼ 获取帮助
◆ envoy --help
◼ 检查配置文件语法
◆ envoy --mode validate -c /path/to/my-envoy-config.yaml
◼ 运行Envoy,并指定自定义的日志路径
◆ envoy -c envoy-demo.yaml --log-path logs/custom.log
◆ 示例配置文件(提示:被代理的服务是Google,建议修改为其他可用地址)
启动Envoy
启动Envoy时,需要通过“-c”选项为其指定初始配置文件以提供引导配置(Bootstrap configuration),这也是使用v3 API的必然要求;
◼ ~]$ envoy -c .{json,yaml,pb,pb_text}
◆ 扩展名代表了配置信息的组织格式;
◼ 引导配置是Envoy配置信息的基点,用于承载Envoy的初始配置,它可能包括静态资源和动态资源的定义
◆静态资源(static_resources)于启动直接加载
◆动态资源(dynamic_resources)则需要通过配置的xDS服务获取并生成
◼ 通常,Listener和Cluster是Envoy得以运行的基础,而二者的配置可以全部为静态格式,也可以混合使用动态及静态方式提供,或者全部配置为动态;
◆ 例如,一个yaml格式纯静态的基础配置框架:
static_resources:
listeners:
- name: ...
address: {}
filter_chains: []
clusters:
- name: ...
type: ...
connect_timeout: {}
dns_lookup_family: V4_ONLY
load_assignment: {}
静态资源和动态资源
纯静态资源配置方式主是直接在配置文件中通过static_resources配置参数明确定义listeners、
clusters和secrets的配置方式,各配置参数的数据类型如下面的配置所示;
◼ 其中,listeners用于配置纯静态类型的侦听器列表,clusters用于定义可用的集群列表及每个集群的端点,而可选的secrets用于定义TLS通信中用到数字证书等配置信息
◼ 具体使用时,admin和static_resources两参数即可提供一个最小化的资源配置,甚至admin也可省略
{
"listeners": [],
"clusters": [],
"secrets": []
}
动态资源,是指由envoy通过xDS协议发现所需要的各项配置的机制,相关的配置信息保存于称之为管理服务器(Management Server)的主机上,经由xDS API向外暴露;下面是一个纯动态资源的基础配置框架;
{
"lds_config": "{...}",
"cds_config": "{...}",
"ads_config": "{...}"
}
listener的简易静态配置
侦听器主要用于定义Envoy监听的用于接收Downstreams请求的套接字、用于处理请求时调用的过滤器链及相关的其它配置属性;
listeners:
- name:
address:
socket_address: { address: ..., port_value: ..., protocol: ... }
filter_chains: - filters:
- name:
config:
L4过滤器echo主要用于演示网络过滤器API的功能,它会回显接收到的所有数据至下游的请求者;在配置文件中调用时其名称为envoy.echo; 下面是一个最简单的静态侦听器配置示例
static_resources:
listeners:
name: listener_0
address:
socket_address:
address: 0.0.0.0
port_value: 15001
filter_chains: - filters:
- name: envoy.echo
启动第一个自定义的Envoy实例
基于直接部署二进制程序启动运行envoy实例时,直接指定要使用的配置文件即可:
-
为envoy创建配置文件专用的存储目录,例如/etc/envoy/
-
将前面的侦听器示例保存为此目录下的envoy-echo-demo.yaml配置文件
-
测试配置文件语法
# envoy --mode validate -c /etc/envoy/envoy-echo-demo.yaml
- 若语法测试不存在问题,即可直接启动Envoy示例
# envoy -c /etc/envoy/envoy-echo-demo.yaml
- 使用nc命令发起测试,键入的任何内容将会由envoy.echo过滤器直接echo回来
# nc 127.0.0.1 15001
提示:Envoy的核心功能在于代理请求及负载均衡,因此,定义上游cluster,并设置其代理和路由机制是为常规使用逻辑,本示例仅用于简单演示如何启动envoy进程
启动第一个自定义的Envoy容器实例
基于envoy的预制docker镜像启动实例时,需要额外自定义配置文件,而后将其焙进新的镜像中或以存储卷的方式向容器提供以启动容器;下面以二次打包镜像的方式进行测试:
-
为envoy容器创建专用的工作目录,例如/applications/envoy/
-
将前面的侦听器示例保存为此目录下的echo-demo/子目录中,文件名为envoy.yaml
◼ 提示:完整的文件路径为/applications/envoy/echo-demo/envoy.yaml;
- 测试配置文件语法
# cd /applications/envoy/
# docker run --name echo-demo --rm -v $(pwd)/echo-demo/envoy.yaml:/etc/envoy/envoy.yaml envoyproxy/envoy-alpine:v1.18-latest --mode validate -c /etc/envoy/envoy.yaml
- 若语法测试不存在问题,即可直接启动Envoy示例;下面第一个命令用于了解echo-demo容器的IP地址
# docker run --name echo-demo --rm -v $(pwd)/echo-demo/envoy.yaml:/etc/envoy/envoy.yaml envoyproxy/envoy-alpine:v1.18-latest -c /etc/envoy/envoy.yaml
- 使用nc命令发起测试,键入的任何内容将会由envoy.echo过滤器直接echo回来
# containerIP=$(docker container inspect --format="{{.NetworkSettings.IPAddress}}" echo-demo)
# nc $containerIP 15001
Cluster简易静态配置
通常,集群代表了一组提供相同服务的上游服务器(端点)的组合,它可由用户静态配置,也能够通过CDS动态获取;
集群需要在“预热”环节完成之后方能转为可用状态,这意味着集群管理器通过DNS解析或EDS服务完成端点初始化,以及健康状态检测成功之后才可用;
clusters:
- name: ... # 集群的惟一名称,且未提供alt_stat_name时将会被用于统计信息中;
alt_state_name: ... # 统计信息中使用的集群代名称;
type: ... # 用于解析集群(生成集群端点)时使用的服务发现类型,可用值有STATIC、STRICT_DNS、LOGICAL_DNS、ORIGINAL_DST和EDS等;
lb_policy: # 负载均衡算法,支持ROUND_ROBIN、LEAST_REQUEST、RING_HASH、RANDOM、MAGLEV和CLUSTER_PROVIDED;
load_assignment: # 为STATIC、STRICT_DNS或LOGICAL_DNS类型的集群指定成员获取方式;EDS类型的集成要使用eds_cluster_config字段配置;
cluster_name: ... # 集群名称;
endpoints: # 端点列表;
- locality: {} # 标识上游主机所处的位置,通常以region、zone等进行标识;
lb_endpoints: # 属于指定位置的端点列表;
- endpoint_name: ... # 端点的名称;
endpoint: # 端点定义;
socket_adddress: # 端点地址标识;
address: ... # 端点地址;
port_value: ... # 端点端口;
protocol: ... # 协议类型;
cluster 配置示例
静态Cluster的各端点可以在配置中直接给出,也可借助DNS服务进行动态发现;
下面的示例直接给出了两个端点地址
clusters:
- name: test_cluster
connect_timeout: 0.25s
type: STATIC
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: test_cluster
endpoints:
- lb_endpoints: - endpoint:
address:
socket_address: { address: 172.17.0.3, port_value: 80 }
- endpoint:
address:
socket_address: { address: 172.17.0.4, port_value: 80 }
L4过滤器tcp_proxy
TCP代理过滤器在下游客户端及上游集群之间执行1:1网络连接代理
◼ 它可以单独用作隧道替换,也可以同其他过滤器(如MongoDB过滤器或速率限制过滤器)结合使用;
◼ TCP代理过滤器严格执行由全局资源管理于为每个上游集群的全局资源管理器设定的连接限制
◆ TCP代理过滤器检查上游集群的资源管理器是否可以在不超过该集群的最大连接数的情况下创建连接;
◼ TCP代理过滤器可直接将请求路由至指定的集群,也能够在多个目标集群间基于权重进行调度转发;
配置语法:
{
"stat_prefix": "...", # 用于统计数据中输出时使用的前缀字符;
"cluster": "...", # 路由到的目标集群标识;
"weighted_clusters": "{...}",
"metadata_match": "{...}",
"idle_timeout": "{...}", # 上下游连接间的超时时长,即没有发送和接收报文的超时时长;
"access_log": [], # 访问日志;
"max_connect_attempts": "{...}" # 最大连接尝试次数;
}
TCP代理配置示例(Front Proxy)
static_resources:
listeners:
name: listener_0
address:
socket_address: { address: 0.0.0.0, port_value: 80 }
filter_chains: - filters:
- name: envoy.tcp_proxy
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy
stat_prefix: tcp
cluster: local_cluster
clusters:
- name: local_cluster
connect_timeout: 0.25s
type: STATIC
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: local_cluster
endpoints:
- lb_endpoints: - endpoint:
address:
socket_address: { address: 172.31.0.11, port_value: 8080 }
- endpoint:
address:
socket_address: { address: 172.31.0.12, port_value: 8080 }
L4过滤器http_connection_manager
http_connection_manager通过引入L7过滤器链实现了对http协议的操纵,其中router过滤器用于配置路由转发;
listeners:
- name:
address:
socket_address: { address: ..., port_value: ..., protocol: ... }
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: ... # 统计信息中使用的易读性的信息前缀;
route_config: # 静态路由配置;动态配置应该使用rds字段进行指定;
name: ... # 路由配置的名称;
virtual_hosts: # 虚拟主机列表,用于构成路由表;
- name: ... # 虚拟主机的逻辑名称,用于统计信息,与路由无关;
domains: [] # 当前虚拟主机匹配的域名列表,支持使用“*”通配符;匹配搜索次序为精确匹配、前缀通配、后缀通配及完全通配;
routes: [] # 指定的域名下的路由列表,执行时按顺序搜索,第一个匹配到路由信息即为使用的路由机制;
http_filters: # 定义http过滤器链
- name: envoy.filters.http.router # 调用7层的路由过滤器
提示:
处理请求时,Envoy首先根据下游客户端请求的“host”来搜索虚拟主机列表中各virtual_host中的domains列表中的定义,第一个匹配到的Domain的定义所属的virtual_host即可处理请求的虚拟主机;
◼而后搜索当前虚拟主机中的routes列表中的路由列表中各路由条目的match的定义,第一个匹配到的match后的路由机制(route、redirect或direct_response)即生效;
HTTP L7路由基础配置
route_config.virtual_hosts.routes配置的路由信息用于将下游的客户端请求路由至合适的上游集群中某Server上;
◼ 其路由方式是将url匹配match字段的定义
◆ match字段可通过prefix(前缀)、path(路径)或safe_regex(正则表达式)三者之一来表示匹配模式;
◼ 与match相关的请求将由route(路由规则)、redirect(重定向规则)或direct_response(直接响应)三个字段其中之一完成路由;
◼ 由route定义的路由目标必须是cluster(上游集群名称)、cluster_header(根据请求标头中的cluster_header的值确定目标集群)或weighted_clusters(路由目标有多个集群,每个集群拥有一定的权重)其中之一;
routes:
- name: ... # 此路由条目的名称;
match:
prefix: ... # 请求的URL的前缀;
route: # 路由条目;
cluster: # 目标下游集群;
路由基础配置概览
match
◼ 基于prefix、path或regex三者其中任何一个进行URL匹配
✓ 提示:regex将会被safe_regex取代;
◼ 可额外根据headers和query_parameters完成报文匹配
◼ 匹配的到报文可有三种路由机制
◆ redirect
◆ direct_response
◆ route
route
◼ 支持cluster、weighted_clusters和cluster_header三者之一定义目标路由
◼ 转发期间可根据prefix_rewrite和host_rewrite完成URL重写;
◼ 可额外配置流量管理机制,例如timeout、retry_policy、
cors、request_mirror_policy和rate_limits等;
L7 Front Proxy 代理配置示例
下面是一个egress类型的Envoy配置示例,它定义了两个virtual_host,不过,发往第二个virtual_host的请求将被重定向至第一个;
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: web_service_1
domains: ["*.ik8s.io", "ik8s.io"]
routes:
- match: { prefix: "/" }
route: { cluster: local_cluster }
- name: web_service_2
domains: ["*.magedu.com",“magedu.com"]
routes:
- match: { prefix: "/" }
redirect:
host_redirect: "www.ik8s.io"
http_filters:
- name: envoy.filters.http.router
clusters:
- name: local_cluster
connect_timeout: 0.25s
type: STATIC
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: local_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: { address: 172.31.2.11, port_value: 8080 }
- endpoint:
address:
socket_address: { address: 172.31.2.12, port_value: 8080 }
前面给出了Listener的配置,Cluster配置我们采用前面tcp-proxy示例中的配置;
而后于主机上使用curl命令即可发起测试
◼ 由下面的命令及结果可知,发往第一个“domain”的请求被调度至后端主机的各端点之上
◼ 发往第二个“domain”的请求被重定向至“http://www.ik8s.io”,而该Location又对应于上面的第一个domain中的主机
~$ curl -H "host: www.ik8s.io" 172.31.0.2
iKubernetes demoapp v1.0 !! ClientIP: 172.31.0.2, ServerName: webserver01, ServerIP: 172.31.2.11!
~$ curl -H "host: www.ik8s.io" 172.31.0.2
iKubernetes demoapp v1.0 !! ClientIP: 172.31.0.2, ServerName: webserver02, ServerIP: 172.31.2.12!
~$ curl -I -H "host: www.magedu.com" 172.31.0.2
HTTP/1.1 301 Moved Permanently
location: http://www.ik8s.io/
date: Tue, 18 May 2021 03:14:37 GMT
server: envoy
transfer-encoding: chunked
L7 Ingress Listener代理配置示例
- ingress 监听器会从其他节点接收请求,并转发到本地应用。本地应用的响应会经过Envoy发回下游。
- Egress 监听器会从本地应用接收请求,并转发到网络的其他节点。这些接收请求的节点通常也会运行Envoy,并接收经过它们的ingress 监听器的请求。
将运行的envoy和demoapp两个应用的容器设定为共享同一个Network Namespace来模拟Ingress代理的场景
◼ envoy容器(Sidecar):监听于0.0.0.0:80套接字,接收客户端请求,并代理至demoapp容器
◼ demoapp容器:监听于127.0.0.1:8080套接字,接收来自于同一网络名称空间中的envoy代理的请求
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: web_service_1
domains: ["*"]
routes:
- match: { prefix: "/" }
route: { cluster: local_cluster }
http_filters: - name: envoy.filters.http.router
clusters:
- name: local_cluster
connect_timeout: 0.25s
type: STATIC
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: local_cluster
endpoints:
- lb_endpoints: - endpoint:
address:
socket_address: { address: 127.0.0.1, port_value: 8080 }
~$ curl 172.31.0.2
iKubernetes demoapp v1.0 !! ClientIP: 127.0.0.1, ServerName: d7cc61138f2c, ServerIP: 172.31.0.2!
~$ curl -I 172.31.0.2
HTTP/1.1 200 OK
content-type: text/html; charset=utf-8
content-length: 97
server: envoy
date: Tue, 18 May 2021 03:59:39 GMT
x-envoy-upstream-service-time: 1
L7 Egress Listener代理配置示例
将运行的envoy和client两个应用的容器设定为共享同一个Network Namespace来模拟Egress代理的场景
◼ envoy容器(Sidecar):监听于127.0.0.1:80套接字,接收客户端请求,并代理至demoapp容器
◼ client容器:直接向同一网络名称空间中的envoy代理的127.0.0.1:80套接字发请求
下面,我们测试于client容器的交互式接口中请求Egress Sidecar上的服务
static_resources:
listeners:
- name: listener_0
address:
socket_address: { address: 127.0.0.1, 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
route_config:
name: local_route
virtual_hosts: - name: web_service_1
domains: ["*"]
routes:
- match: { prefix: "/" }
route: { cluster: web_cluster }
http_filters: - name: envoy.filters.http.router
clusters:
- name: web_cluster
connect_timeout: 0.25s
type: STATIC
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: web_cluster
endpoints:
- lb_endpoints: - endpoint:
address:
socket_address: { address: 172.31.4.11, port_value: 80 }
- endpoint:
address:
socket_address: { address: 172.31.4.12, port_value: 80 }
~$ docker exec -it http-egress_client_1 /bin/sh
[root@2e41a86dbac1 /]# curl 127.0.0.1/hostname
ServerName: webserver02
[root@2e41a86dbac1 /]# curl 127.0.0.1/hostname
ServerName: webserver01