02 使用入门
启动Envoy时,需要通过“-c”选项为其指定初始配置文件,以提供引导配置( Bootstrap configuration),这也是使用v2 API的必然要求;
~]$ envoy -c <path_to_config>.{json,yaml,pb,pb_text} #扩展名代表了配置信息的组织格式;
引导配置是Envoy配置信息的基点,用于承载Envoy的初始配置,它可能包括静态资源和动态资源的定义:
- 静态资源(static_resources)在启动时直接加载
- 动态资源(dynamic_resources)则需要通过配置的xDS服务获取并生成
通常,Listener和Cluster是Envoy得以运行的基础,而二者的配置可以全部为静态格式, 也可以混合使用动态及静态方式提供,或者全部配置为动态;
一个yaml格式纯静态的基础配置框架类似如下所示:
static_resources:
listeners:
- name: ...
address: {}
filter_chains: [] # 过滤器链是listener的重要组成部分
clusters:
- name: ...
type: ... #指定集群内部成员的生成方式
connect_timeout: {}
lb_policy: ...
load_assignment: {}
详细配置文件详见官方文档
侦听器主要用于定义Envoy监听的用于接收Downstreams请求的套接字、 用于处理请求时调用的过滤器链及相关的其它配置属性;
listeners:
- name:
address:
socket_address: { address: ..., port_value: ..., protocol: ... }
filter_chains:
- filters:
- name:
config:
示例:
下面是一个最简单的静态侦听器配置示例envoy.yaml,主要有3部分组成:name、address、fileter_chains:
static_resources:
listeners:
- name: listener_0
address:
socket_address:
address: 0.0.0.0
port_value: 15001
filter_chains:
- filters:
- name: envoy.echo
echo过滤器:是L4过滤器中的一种,主要用于演示网络过滤器API的功能,它会回显接收到的所有数据至下游的请求者;在配置文件中调用时其名称为envoy.echo;
基于envoy的预制docker镜像启动实例时,需要额外自定义配置文件,而后将其焙进新的镜像中或以存储卷的方式向容器提供以启动容器;
下面以二次打包镜像的方 式进行测试:
- 创建构建docker镜像的工作目录,例如envoy.echo
- 将前面的侦听器示例保存为此目录下的envoy.yaml配置文件
static_resources:
listeners:
- name: listener_0
address:
socket_address:
address: 0.0.0.0
port_value: 15001
filter_chains:
- filters:
- name: envoy.echo
- 在此目录下创建Dockerfile文件,内容如下所示:
FROM envoyproxy/envoy-alpine:v1.11.1
ADD envoy.yaml /etc/envoy/envoy.yaml
- 构建新的docker镜像
# docker image build . -t echo:v0.1
- 启动容器
# docker run --name echo --net bridge -d -p 15001 echo:v0.1
- 测试
# nc 172.17.0.2 15001
Envoy的核心功能在于代理请求及负载均衡,因此,定义上游cluster,并设置其代理和路由机制方为常规使用逻辑;
通常,集群代表了一组提供相同服务的上游服务器(端点)的组合,它可由用户静态配置,也能够通过CDS动态获取;
集群需要在“预热”环节完成之后方能转为可用状态,这意味着集群管理器通过DNS解析或EDS服务完成端点初始化,以及健康状态检测成功之后才可用;
clusters:
- name: ... # 集群的惟一名称,且未提供alt_stat_name时将会被用于统计信息中;
alt_stat_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: ... # 协议类型;
- 严格 DNS(strict_dns): 当使用严格 DNS 服务发现时,Envoy 将持续并异步地解析指定的 DNS 目标。 DNS 结果中的每个返回的IP地址将被视为上游群集中的显式主机。 这意味着如果查询返回三个 IP 地址,Envoy 将假定集群有三个主机,并且三个主机都应该负载均衡。 简单直白一点: 如果上游集群有多个 IP 地址,那么基于轮询算法,每隔一段时间都会连接到不同的 IP。
- 逻辑 DNS(logical_dns): 与严格 DNS 服务发现类似,但在需要初始化新连接时仅使用返回的第一个 IP 地址。 简单直白一点:即使上游集群有多个 IP 地址,相关联的连接每次都会连接到相同的 IP,直到连接被关闭。
静态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 }
每个过滤器可能有自己专用的配置。
tcp_proxy过滤器在下游客户端及上游集群之间执行1:1网络连接代理。它可以单独用作隧道替换,也可以同其他过滤器(如MongoDB过滤器或速率限制过滤器)结合使用;
TCP代理过滤器严格执行由全局资源管理于为每个上游集群的全局资源管理器设定的连接限制,TCP代理过滤器检查上游集群的资源管理器是否可以在不超过该集群的最大连接数的情况下创建连接;
TCP代理过滤器可直接将请求路由至指定的集群,也能够在多个目标集群间基于权重进 行调度转发;
配置语法:
- filters:
- name: envoy.tcp_proxy
typed_config:
"stat_prefix": "...", # 用于统计数据中输出时使用的前缀字符;
"cluster": "...", # 路由到的目标集群标识;
"weighted_clusters": "{...}",
"metadata_match": "{...}",
"idle_timeout": "{...}", # 上下游连接间的超时时长,即没有发送和接收报文的超时时长;
"access_log": [], # 访问日志;
"max_connect_attempts": "{...}" # 最大连接尝试次数;
下面的示例基于TCP代理将下游用户(本机)请求代理至外部的(egress)两个 web服务器:
static_resources:
listeners:
- name: listener_0
address:
socket_address: { address: 0.0.0.0, port_value: 80 }
filter_chains:
- filters:
- name: envoy.tcp_proxy #tcp_proxy过滤器的名字
typed_config:
"@type": type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy
stat_prefix: tcp
cluster: test_cluster #转发至目标cluster
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 }
root@ubuntu1:~/envoy.echo# cat docker-compose.yaml
version: "3"
services:
envoy:
image: envoyproxy/envoy:v1.11.1
container_name: envoy
network_mode: "service:mainserver" #共享mainserver的网络名称空间
volumes:
- ./envoy.yaml:/etc/envoy/envoy.yaml:ro
restart: always
environment:
TZ: "Asia/Shanghai"
depends_on:
- mainserver
mainserver:
image: ikubernetes/mini-http-server:v0.3
networks:
envoymesh:
aliases:
- webserver #mainserver在当前网络中的dns解析名
- httpserver
networks:
envoymesh: {} #创建一个桥接网络
root@ubuntu1:~/envoy.echo# cat envoy.yaml
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.config.filter.network.tcp_proxy.v2.TcpProxy
stat_prefix: tcp
cluster: test_cluster #转发至目标cluster
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: 127.0.0.1, port_value: 8081 }
Envoy通过内置的L4过滤器http_connection_manager将原始字节转换为HTTP应用层协议级别的消息和事件,例如接收到的Header和主体等,以及处理所有HTTP连接和请求共有的功能,包括访问日志、生成和跟踪请求ID,请求/响应头处理、路由表管理和统计信息等;
http_connection_manager通过引入**L7过滤器链(http_filters)**实现了对http协议的操纵,这些http过滤器大体可分为编码器、解码器、遍/解码器三类。 其中router过滤器用于配置路由转发以及处理重试操作和生成统计信息等:
listeners:
- name:
address:
socket_address: { address: ..., port_value: ..., protocol: ... }
filter_chains:
- filters:
- name: envoy.http_connection_manager
config:
condec_type: ... # 连接管理器使用的编解码器类型,可用值有AUTO、HTTP1和HTTP2;
stat_prefix: ... # 统计信息中使用的易读性的信息前缀;
route_config: # 静态路由配置;动态配置应该使用rds字段进行指定;
name: ... # 路由配置的名称;
virtual_hosts: # 虚拟主机列表,用于构成路由表;
- name: ... # 虚拟主机的逻辑名称,用于统计信息,与路由无关;
domains: [] # 当前虚拟主机匹配的域名列表,支持使用“*”通配符;匹配搜索次序为精确匹配、前缀通配、后缀通配及完全通配;
routes: [] # 路由列表,按顺序搜索,第一个匹配到路由信息。下游的客户端请求路由至合适的上游集群中某Server上;
http_filters: # 定义http过滤器链(L7)
- name: envoy.router # 调用的过滤器为envoy.router
http请求处理逻辑:
- 处理请求时,Envoy首先根据下游客户端请求的“host”头部来搜索虚拟主机列表中各virtual_host中的domains列表中的定义,第一个匹配到的Domain的定义所属的virtual_host即可处理请求的虚拟主机;
- 而后搜索当前虚拟主机中的routes列表中的路由列表中各路由条目的match的定义,第一个匹配到的match后的路由机制(route、redirect或direct_response)即生效;
route_config.virtual_hosts.routes配置的路由信息用于将下游的客户端请求路由至合适的上游集群中某Server上;
其路由方式是将url匹配match字段的定义,match字段可通过prefix(前缀)、path(路径)或regex(正则表达式)三者之一来表示匹配模式;
与match相关的请求将由route、redirect或direct_response三个字段其中之一完成路由;
由route定义的路由目标必须是cluster(上游集群名称)、cluster_header(根据请求标头中的cluster_header的值确定目标集群)或weighted_clusters(路由目标有多个集群,每个集群拥有一定的权重)其中之一;
routes:
- name: ... # 此路由条目的名称;
match:
prefix: ... # 请求的URL的前缀;
route: # 路由条目;
cluster: # 目标下游集群;
下面是一个egress类型的Envoy配置示例,它定义了两个virtual_host,不过,发往第二个 virtual_host的请求将被重定向至第一个;
static_resources:
listeners:
- name: listener_0
address:
socket_address: { address: 127.0.0.1, port_value: 80 }
filter_chains:
- filters:
- name: envoy.http_connection_manager
config:
stat_prefix: egress_http
codec_type: AUTO
route_config:
name: test_route
virtual_hosts: #定义了2个virtual_host
- name: web_service_1
domains: ["*.ik8s.io", "ik8s.io"]
routes:
- match: { prefix: "/" }
route: { cluster: web_cluster_1 }
- name: web_service_2
domains: ["*.k8scast.cn","k8scast.cn"]
routes:
- match: { prefix: "/" }
redirect:
host_redirect: "www.ik8s.io"
http_filters:
- name: envoy.router
clusters:
...
root@ubuntu1:~/admin_interface# cat Dockerfile
FROM envoyproxy/envoy-alpine:v1.11.1
RUN apk update && apk --no-cache add curl
root@ubuntu1:~/http_egress# cat envoy.yaml
static_resources:
listeners:
- name: listener_0
address:
socket_address: { address: 127.0.0.1, port_value: 80 }
filter_chains:
- filters:
- name: envoy.http_connection_manager
config:
stat_prefix: egress_http
codec_type: AUTO
route_config:
name: test_route
virtual_hosts:
- name: web_service_1
domains: ["*.ik8s.io", "ik8s.io"]
routes:
- match: { prefix: "/" }
route: { cluster: web_cluster_1 }
- name: web_service_2
domains: ["*.k8scast.cn","k8scast.cn"]
routes:
- match: { prefix: "/" }
route: { cluster: web_cluster_2 }
http_filters:
- name: envoy.router
clusters:
- name: web_cluster_1
connect_timeout: 0.25s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: web_cluster_1
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: { address: myservice, port_value: 8081 }
- name: web_cluster_2
connect_timeout: 0.25s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: web_cluster_2
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: { address: webserver1, port_value: 8081 }
root@ubuntu1:~/http_egress# cat docker-compose.yaml
version: "3"
services:
envoy:
#image: envoyproxy/envoy:v1.11.1
build:
dockerfile: Dockerfile
context: .
container_name: envoy
networks:
envoymesh:
aliases:
- envoy
volumes:
- ./envoy.yaml:/etc/envoy/envoy.yaml:ro
restart: always
environment:
TZ: "Asia/Shanghai"
depends_on:
- webserver1
- webserver2
expose:
- "80"
ports:
- 8080:80
webserver1:
image: ikubernetes/mini-http-server:v0.3
networks:
envoymesh:
aliases:
- webserver1
- myservice
expose:
- "8081"
webserver2:
image: ikubernetes/mini-http-server:v0.3
networks:
envoymesh:
aliases:
- webserver2
- myservice
expose:
- "8081"
networks:
envoymesh: {} #创建一个桥接网络
测试:
root@ubuntu1:~# docker exec -it envoy bash
root@1ee9196d7496:/# curl --header "host: www.ik8s.io" 127.0.0.1
This is a website server by a Go HTTP server.
root@1ee9196d7496:/# curl --header "host: www.ik8s.io" 127.0.0.1
This is a website server by a Go HTTP server.
root@1ee9196d7496:/# curl --header "host: www.ik8s.io" 127.0.0.1/hostname
Hostname: 1cfa6cd83fa6.
root@1ee9196d7496:/# curl --header "host: www.ik8s.io" 127.0.0.1/hostname
Hostname: 40e6675b91db.
root@1ee9196d7496:/# curl --header "host: www.k8scast.cn" 127.0.0.1/hostname
Hostname: 40e6675b91db.
root@1ee9196d7496:/# curl --header "host: www.k8scast.cn" 127.0.0.1/hostname
Hostname: 40e6675b91db.
root@1ee9196d7496:/# curl --header "host: www.k8scast.cn" 127.0.0.1/hostname
Hostname: 40e6675b91db.
镜像ikubernetes/envoy_ingress_demo:v0.1内建了envoy和主程序,后者监听于 “127.0.0.1:8080”,可由同一容器的envoy程序代理后暴露给外部访问;
root@ubuntu1:~/http_ingress# cat envoy.yaml
static_resources:
listeners:
- name: listener_0
address:
socket_address: { address: 0.0.0.0, port_value: 80 }
filter_chains:
- filters:
- name: envoy.http_connection_manager
config:
stat_prefix: igress_http
codec_type: AUTO
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match: { prefix: "/" }
route: { cluster: local_service }
http_filters:
- name: envoy.router
clusters:
- name: local_service
connect_timeout: 0.25s
type: STATIC
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: local_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: { address: 127.0.0.1, port_value: 8081 }
root@ubuntu1:~/http_ingress# cat docker-compose.yaml
version: "3"
services:
envoy:
image: envoyproxy/envoy:v1.11.1
container_name: envoy
network_mode: "service:mainserver" #共享mainserver的网络名称空间
volumes:
- ./envoy.yaml:/etc/envoy/envoy.yaml:ro
restart: always
environment:
TZ: "Asia/Shanghai"
depends_on:
- mainserver
mainserver:
image: ikubernetes/mini-http-server:v0.3
networks:
envoymesh:
aliases:
- webserver #mainserver在当前网络中的dns解析名
- httpserver
networks:
envoymesh: {} #创建一个桥接网络
测试:
root@ubuntu1:~# curl http://172.22.0.2
This is a website server by a Go HTTP server.
root@ubuntu1:~# curl http://172.22.0.2/hostname
Hostname: 0ffb4da19982.