实战名称 |
---|
实战:前端代理-2023.11.2(测试成功) |
实战:流量镜像-2023.11.4(测试成功) |
实战:故障注入过滤器-2023.11.4(测试成功) |
实战:MySQL 过滤器-2023.11.4(测试成功) |
实战:Golang HTTP 过滤器-2023.11.4(测试成功) |
前面我们已经学习了 Envoy 的基础知识,接下来我们就可以来学习如何使用 Envoy 来进行流量管理了。Envoy 作为一个高性能的边缘和内部代理,可以用于许多不同的场景。在本节中,我们将介绍 Envoy 的流量管理功能,包括路由、负载平衡、故障转移、故障注入、速率限制和等方面。
这些案例都位于 Envoy 官方代码仓库中的 examples 目录下,我们可以在这里找到各种使用案例。
所以我们可以先 Clone 一份 Envoy 的代码仓库到本地,然后在本地的 examples 目录下面找到对应的案例进行学习。
$ git clone https://github.com/envoyproxy/envoy.git
Envoy 的路由器可以将流量拆分到跨两个或多个上游集群的虚拟主机中的路由,有两种常见的用例。
路由配置中的运行时对象决定选择特定路由的概率。通过使用 runtime_fraction
配置,可以逐渐将虚拟主机中特定路由的流量从一个群集转移到另一个群集。比如以下示例配置,其中在 Envoy 配置文件中声明了名为 helloworld
的服务的两个版本 helloworld_v1
和 helloworld_v2
。
virtual_hosts:
- name: www2
domains:
- "*"
routes:
- match:
prefix: /
runtime_fraction: # 额外匹配指定的运行时键值,每次评估匹配路径时,它必需低于此字段指示的匹配百分比;支持渐进式修改;
default_value:
numerator: 50
denominator: HUNDRED
runtime_key: routing.traffic_shift.helloworld
route:
cluster: helloworld_v1
- match:
prefix: /
route:
cluster: helloworld_v2
Envoy 使用首个匹配策略来匹配路由。如果路由具有 runtime_fraction
对象,则将基于 runtime_fraction
值额外匹配请求(如果未指定值,则为默认值)。上面的配置中我们可以看到在第一条路由中指定了 runtime_fraction
对象,可以通过更改 runtime_fraction
值来实现流量转移。
首先,将 routing.traffic_shift.helloworld
设置为 100
,这样对于 helloworld
虚拟主机的所有请求,都将与 v1
路由匹配,并由 helloworld_v1
集群提供服务。
要开始转移流量到 helloworld_v2
集群,将 routing.traffic_shift.helloworld
的值设置为 0 < x < 100
。例如设置为 90
,此时到 helloworld
虚拟主机的每 10 个请求中有 1 个请求将与 v1
路由不匹配,并将进入 v2
路由。
逐渐减少 routing.traffic_shift.helloworld
的值以便更大比例的请求与 v2
路由匹配。
当 routing.traffic_shift.helloworld
的值设置为 0 时,没有到 helloworld
虚拟主机的请求与 v1 路由匹配。所有流量此时会进入 v2
路由,由 helloworld_v2
集群提供服务。
现在我们有三个(v1、v2 和 v3)而不是两个版本。可以使用 weighted_clusters
选项可以用来指定每个上游集群的权重来在三个版本之间平均分配流量(比如 33%、33%、34%)。
与前面的示例不同,一个路由条目就够了。路由中的 weighted_clusters
配置块可用于指定多个上游集群以及每个上游集群的权重。
virtual_hosts:
- name: www2
domains:
- "*"
routes:
- match: { prefix: / }
route:
weighted_clusters:
runtime_key_prefix: routing.traffic_split.helloworld
clusters:
- name: helloworld_v1
weight: 33
- name: helloworld_v2
weight: 33
- name: helloworld_v3
weight: 34
默认情况下,权重的和必须等于 100。然后就可以通过运行时变量
routing.traffic_split.helloworld.helloworld_v1
、
routing.traffic_split.helloworld.helloworld_v2
和
routing.traffic_split.helloworld.helloworld_v3
对每个集群的权重进行动态地调整。
实验环境:
Docker Compose version v2.23.0
docker 20.10.21-ce(具有docker环境)
实验软件:
链接:https://pan.baidu.com/s/1RB2YMVeNDkX18YFQQ1suZg?pwd=0820
提取码:0820
2023.11.4-Day4-2023.10.30-Envoy使用案例
实验步骤:
graph LR
A[实战步骤] -->B(1️⃣ 拉取测试demo)
A[实战步骤] -->C(2️⃣ 熟悉demo)
A[实战步骤] -->D(3️⃣ 启动服务)
A[实战步骤] -->E(4️⃣ 测试)
A[实战步骤] -->F(5️⃣ 测试 Envoy 的负载均衡功能)
这些案例大部分都是基于官方提供的一个 Front proxy 来进行演示的,该示例可以使用 Docker Compose 进行管理,其将部署一个前置 Envoy 和几个后端服务(简单的 aiohttp
应用程序),如下图所示:
所有传入请求都通过前端 Envoy 进行路由,前端 Envoy 充当位于 envoymesh
网络边缘的反向代理。 Docker Compose 将端口 8080、8443 和 8001 三个端口,分别处理对服务的 HTTP、HTTPS 调用和对 /admin
的请求,docker-compose.yml
文件内容如下所示:
services:
front-envoy:
build:
context: .
dockerfile: ../shared/envoy/Dockerfile
depends_on:
service-envoy-1:
condition: service_healthy
service-envoy-2:
condition: service_healthy
ports:
- "${PORT_PROXY:-8080}:8080"
- "${PORT_HTTPS:-8443}:8443"
- "${PORT_STATS:-8001}:8001"
service-envoy-1:
build:
context: .
dockerfile: ../shared/envoy/Dockerfile
target: envoy-admin
args:
ENVOY_CONFIG: ./service-envoy.yaml
ENVOY_ADMIN_PORT: 8001
depends_on:
service1:
condition: service_healthy
service1:
build:
context: ../shared/python
target: aiohttp-tracing-service2
environment:
- SERVICE_NAME=1
service-envoy-2:
build:
context: .
dockerfile: ../shared/envoy/Dockerfile
target: envoy-admin
args:
ENVOY_ADMIN_PORT: 8001
ENVOY_CONFIG: ./service-envoy-2.yaml
depends_on:
service2:
condition: service_healthy
service2:
build:
context: ../shared/python
target: aiohttp-tracing-service2
environment:
- SERVICE_NAME=2
Front Envoy 路由到服务容器的所有流量实际上都被路由到 Service Envoy 上,对应的 envoy.yaml
配置如下所示:
# envoy.yaml
static_resources:
listeners:
- address:
socket_address:
address: 0.0.0.0
port_value: 8080
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
codec_type: AUTO
stat_prefix: ingress_http
route_config:
name: local_route
virtual_hosts:
- name: backend
domains:
- "*"
routes:
- match:
prefix: "/service/1"
route:
cluster: service1-envoy
- match:
prefix: "/service/2"
route:
cluster: service2-envoy
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
- address:
socket_address:
address: 0.0.0.0
port_value: 8443
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
codec_type: AUTO
stat_prefix: ingress_http
route_config:
name: local_route
virtual_hosts:
- name: backend
domains:
- "*"
routes:
- match:
prefix: "/service/1"
route:
cluster: service1-envoy
- match:
prefix: "/service/2"
route:
cluster: service2-envoy
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
common_tls_context:
tls_certificates:
# The following self-signed certificate pair is generated using:
# $ openssl req -x509 -newkey rsa:2048 -keyout a/front-proxy-key.pem -out a/front-proxy-crt.pem -days 3650 -nodes -subj '/CN=front-envoy'
#
# Instead of feeding it as an inline_string, certificate pair can also be fed to Envoy
# via filename. Reference: https://envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/base.proto#config-core-v3-datasource.
#
# Or in a dynamic configuration scenario, certificate pair can be fetched remotely via
# Secret Discovery Service (SDS). Reference: https://envoyproxy.io/docs/envoy/latest/configuration/security/secret.
- certificate_chain:
inline_string: |
-----BEGIN CERTIFICATE-----
MIICqDCCAZACCQCquzpHNpqBcDANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDDAtm
cm9udC1lbnZveTAeFw0yMDA3MDgwMTMxNDZaFw0zMDA3MDYwMTMxNDZaMBYxFDAS
BgNVBAMMC2Zyb250LWVudm95MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAthnYkqVQBX+Wg7aQWyCCb87hBce1hAFhbRM8Y9dQTqxoMXZiA2n8G089hUou
oQpEdJgitXVS6YMFPFUUWfwcqxYAynLK4X5im26Yfa1eO8La8sZUS+4Bjao1gF5/
VJxSEo2yZ7fFBo8M4E44ZehIIocipCRS+YZehFs6dmHoq/MGvh2eAHIa+O9xssPt
ofFcQMR8rwBHVbKy484O10tNCouX4yUkyQXqCRy6HRu7kSjOjNKSGtjfG+h5M8bh
10W7ZrsJ1hWhzBulSaMZaUY3vh5ngpws1JATQVSK1Jm/dmMRciwlTK7KfzgxHlSX
58ENpS7yPTISkEICcLbXkkKGEQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCmj6Hg
vwOxWz0xu+6fSfRL6PGJUGq6wghCfUvjfwZ7zppDUqU47fk+yqPIOzuGZMdAqi7N
v1DXkeO4A3hnMD22Rlqt25vfogAaZVToBeQxCPd/ALBLFrvLUFYuSlS3zXSBpQqQ
Ny2IKFYsMllz5RSROONHBjaJOn5OwqenJ91MPmTAG7ujXKN6INSBM0PjX9Jy4Xb9
zT+I85jRDQHnTFce1WICBDCYidTIvJtdSSokGSuy4/xyxAAc/BpZAfOjBQ4G1QRe
9XwOi790LyNUYFJVyeOvNJwveloWuPLHb9idmY5YABwikUY6QNcXwyHTbRCkPB2I
m+/R4XnmL4cKQ+5Z
-----END CERTIFICATE-----
private_key:
inline_string: |
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC2GdiSpVAFf5aD
tpBbIIJvzuEFx7WEAWFtEzxj11BOrGgxdmIDafwbTz2FSi6hCkR0mCK1dVLpgwU8
VRRZ/ByrFgDKcsrhfmKbbph9rV47wtryxlRL7gGNqjWAXn9UnFISjbJnt8UGjwzg
Tjhl6EgihyKkJFL5hl6EWzp2Yeir8wa+HZ4Achr473Gyw+2h8VxAxHyvAEdVsrLj
zg7XS00Ki5fjJSTJBeoJHLodG7uRKM6M0pIa2N8b6HkzxuHXRbtmuwnWFaHMG6VJ
oxlpRje+HmeCnCzUkBNBVIrUmb92YxFyLCVMrsp/ODEeVJfnwQ2lLvI9MhKQQgJw
tteSQoYRAgMBAAECggEAeDGdEkYNCGQLe8pvg8Z0ccoSGpeTxpqGrNEKhjfi6NrB
NwyVav10iq4FxEmPd3nobzDPkAftfvWc6hKaCT7vyTkPspCMOsQJ39/ixOk+jqFx
lNa1YxyoZ9IV2DIHR1iaj2Z5gB367PZUoGTgstrbafbaNY9IOSyojCIO935ubbcx
DWwL24XAf51ez6sXnI8V5tXmrFlNXhbhJdH8iIxNyM45HrnlUlOk0lCK4gmLJjy9
10IS2H2Wh3M5zsTpihH1JvM56oAH1ahrhMXs/rVFXXkg50yD1KV+HQiEbglYKUxO
eMYtfaY9i2CuLwhDnWp3oxP3HfgQQhD09OEN3e0IlQKBgQDZ/3poG9TiMZSjfKqL
xnCABMXGVQsfFWNC8THoW6RRx5Rqi8q08yJrmhCu32YKvccsOljDQJQQJdQO1g09
e/adJmCnTrqxNtjPkX9txV23Lp6Ak7emjiQ5ICu7iWxrcO3zf7hmKtj7z+av8sjO
mDI7NkX5vnlE74nztBEjp3eC0wKBgQDV2GeJV028RW3b/QyP3Gwmax2+cKLR9PKR
nJnmO5bxAT0nQ3xuJEAqMIss/Rfb/macWc2N/6CWJCRT6a2vgy6xBW+bqG6RdQMB
xEZXFZl+sSKhXPkc5Wjb4lQ14YWyRPrTjMlwez3k4UolIJhJmwl+D7OkMRrOUERO
EtUvc7odCwKBgBi+nhdZKWXveM7B5N3uzXBKmmRz3MpPdC/yDtcwJ8u8msUpTv4R
JxQNrd0bsIqBli0YBmFLYEMg+BwjAee7vXeDFq+HCTv6XMva2RsNryCO4yD3I359
XfE6DJzB8ZOUgv4Dvluie3TB2Y6ZQV/p+LGt7G13yG4hvofyJYvlg3RPAoGAcjDg
+OH5zLN2eqah8qBN0CYa9/rFt0AJ19+7/smLTJ7QvQq4g0gwS1couplcCEnNGWiK
72y1n/ckvvplmPeAE19HveMvR9UoCeV5ej86fACy8V/oVpnaaLBvL2aCMjPLjPP9
DWeCIZp8MV86cvOrGfngf6kJG2qZTueXl4NAuwkCgYEArKkhlZVXjwBoVvtHYmN2
o+F6cGMlRJTLhNc391WApsgDZfTZSdeJsBsvvzS/Nc0burrufJg0wYioTlpReSy4
ohhtprnQQAddfjHP7rh2LGt+irFzhdXXQ1ybGaGM9D764KUNCXLuwdly0vzXU4HU
q5sGxGrC1RECGB5Zwx2S2ZY=
-----END PRIVATE KEY-----
clusters:
- name: service1-envoy
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: service1-envoy
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: service-envoy-1
port_value: 8000
- name: service2-envoy
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: service2-envoy
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: service-envoy-2
port_value: 8000
admin:
address:
socket_address:
address: 0.0.0.0
port_value: 8001
layered_runtime: # 用于设置 Envoy 的运行时参数
layers:
- name: static_layer_0
static_layer: # 静态层,用于设置 Envoy 的运行时参数
envoy:
resource_limits:
listener:
example_listener_name:
connection_limit: 10000
上面的配置文件我们可以看到 Envoy 中定义了两个 Listener,分别监听 8080 和 8443 端口,分别对应 http 和 https 请求,我们可以看到 8443 这个监听器配置了 TLS 证书,这个证书是自签名的。然后根据路由规则,将匹配路由 /service/1
的请求路由到 service1-envoy
这个集群中,将匹配路由 /service/2
的请求路由到 service2-envoy
这个集群中。
这两个集群分别对应了 service-envoy-1
和 service-envoy-2
这两个后端服务,需要注意的是这里的两个后端服务并不是最终的 aiohttp
应用程序,而是 aiohttp
应用前面的 Service Envoy 代理,这样所有的请求都会先经过 Envoy 代理,然后再路由到最终的 aiohttp
应用程序中。
下面是 service1
这个服务前面的 Envoy 代理的配置文件:
# service-envoy.yaml
static_resources:
listeners:
- address:
socket_address:
address: 0.0.0.0
port_value: 8000
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
codec_type: AUTO
stat_prefix: service_envoy_1
route_config:
name: local_route
virtual_hosts:
- name: backend
domains:
- "*"
routes:
- match:
prefix: "/service/1"
route:
cluster: service1
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
clusters:
- name: service1
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: service1
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: service1
port_value: 8080
admin:
address:
socket_address:
address: 0.0.0.0
port_value: 8001
我们可以看到收到的请求都会被路由到 service1
这个集群中,这个集群对应的是 service1
这个服务,这个服务就是一个 真正的后端 aiohttp
应用程序了,Service Envoy 相当于充当应用程序的 Sidecar,这样所有请求均由 Envoy 处理,然后有效路由到您的服务。
下面我们路由到 examples/front-proxy
目录,然后使用 Docker Compose 启动这个案例:
这个是要安装docker-compose的。
$ pwd
/Users/cnych/Documents/course/istio/manifests/envoy/examples/front-proxy
$ docker-compose up --build -d
#docker compose up --build -d
$ docker-compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
front-proxy-front-envoy-1 front-proxy-front-envoy "/docker-entrypoint.sh /usr/local/bin/envoy -c /etc/envoy.yaml" front-envoy About a minute ago Up 35 seconds 0.0.0.0:8001->8001/tcp, :::8001->8001/tcp, 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp, 0.0.0.0:8443->8443/tcp, :::8443->8443/tcp, 10000/tcp
front-proxy-service-envoy-1-1 front-proxy-service-envoy-1 "/docker-entrypoint.sh /usr/local/bin/envoy -c /etc/envoy.yaml" service-envoy-1 About a minute ago Up 37 seconds (healthy) 10000/tcp
front-proxy-service-envoy-2-1 front-proxy-service-envoy-2 "/docker-entrypoint.sh /usr/local/bin/envoy -c /etc/envoy.yaml" service-envoy-2 About a minute ago Up 37 seconds (healthy) 10000/tcp
front-proxy-service1-1 front-proxy-service1 "python3 /code/service.py" service1 About a minute ago Up 38 seconds (healthy)
front-proxy-service2-1 front-proxy-service2 "python3 /code/service.py" service2 About a minute ago Up 38 seconds (healthy)
在使用docker-compose启动的时候报错了:。。。
汇总踩坑如下:
1.要升级docker-compse版本新一点
2.要手动拉取2个镜像才行
⚠️ 注意:
安装docker compose出问题了。。。
老师当时测试都是没问题的。
[root@docker front-proxy]#docker-compose --version
docker-compose version 1.26.2, build eefe0d31
docker-ce 20.10.21
[root@docker front-proxy]#docker-compose --version
Docker Compose version v2.23.0
这次就可以了,估计还是和自己的docker-compose版本有关。
第一次拉取失败:。。。
第2次拉取失败:。。。
估计还是自己linux虚机无法访问外网导致的。。。
export https_proxy=http://127.0.0.1:33210 http_proxy=http://127.0.0.1:33210 all_proxy=socks5://127.0.0.1:33211
不行呀。。。。
[service2 python-base 1/3] FROM docker.io/library/python:3.11.5-slim-bullseye@sha256:9f35f3a6420693c209c11bba63dcf103d88e47ebe0b205336b5168c122967edf
可以单独拉取成功的:
哦,这边可以正常下载了。。。
怎么还一直卡在这里呀。。。。
执行命令后会启动 5 个容器,其中 3 个是 Envoy 代理,2 个是 aiohttp
应用程序:
启动完成后,我们现在可以通过前端代理向两个服务发送请求来测试 Envoy 的路由功能了,我们可以使用 curl 命令来测试:
service1
[root@docker front-proxy]#curl -v localhost:8080/service/1
* About to connect() to localhost port 8080 (#0)
* Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> GET /service/1 HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:8080
> Accept: */*
>
< HTTP/1.1 200 OK
< content-type: text/plain; charset=utf-8
< content-length: 79
< date: Fri, 03 Nov 2023 22:51:21 GMT
< server: envoy
< x-envoy-upstream-service-time: 4
<
Hello from behind Envoy (service 1)! hostname 221f01bef482 resolved 172.19.0.3
* Connection #0 to host localhost left intact
service2
[root@docker front-proxy]#curl -v localhost:8080/service/2
* About to connect() to localhost port 8080 (#0)
* Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> GET /service/2 HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:8080
> Accept: */*
>
< HTTP/1.1 200 OK
< content-type: text/plain; charset=utf-8
< content-length: 79
< date: Fri, 03 Nov 2023 22:51:41 GMT
< server: envoy
< x-envoy-upstream-service-time: 4
<
Hello from behind Envoy (service 2)! hostname eb0e9d483971 resolved 172.19.0.2
* Connection #0 to host localhost left intact
可以看到每个请求我们都是直接发送到 Front Envoy,然后 Front Envoy 再根据路由规则将请求路由到对应的 Service Envoy,然后再路由到最终的
aiohttp
应用程序中的。
此外还可以使用 HTTPS 来调用 Envoy 后面的服务,例如调用 service1:
[root@docker front-proxy]#curl https://localhost:8443/service/1 -k -v
* About to connect() to localhost port 8443 (#0)
* Trying ::1...
* Connected to localhost (::1) port 8443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* skipping SSL peer certificate verification
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
* subject: CN=front-envoy
* start date: 7月 08 01:31:46 2020 GMT
* expire date: 7月 06 01:31:46 2030 GMT
* common name: front-envoy
* issuer: CN=front-envoy
> GET /service/1 HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:8443
> Accept: */*
>
< HTTP/1.1 200 OK
< content-type: text/plain; charset=utf-8
< content-length: 79
< date: Fri, 03 Nov 2023 22:52:35 GMT
< server: envoy
< x-envoy-upstream-service-time: 3
<
Hello from behind Envoy (service 1)! hostname 221f01bef482 resolved 172.19.0.3
* Connection #0 to host localhost left intact
接下来我们还可以继续测试 Envoy 的负载均衡功能,我们可以将 service1
服务扩展到 3 个实例:
[root@docker front-proxy]#docker-compose scale service1=3
[+] Running 3/3
✔ Container front-proxy-service1-1 Running 0.0s
✔ Container front-proxy-service1-3 Started 0.0s
✔ Container front-proxy-service1-2 Started 0.0s
然后我们多次向 service1
发送请求,前端 Envoy 将通过对三台 service1
机器进行轮询来对请求进行负载平衡:
[root@docker front-proxy]#curl localhost:8080/service/1
Hello from behind Envoy (service 1)! hostname 221f01bef482 resolved 172.19.0.3
[root@docker front-proxy]#curl localhost:8080/service/1
Hello from behind Envoy (service 1)! hostname 221f01bef482 resolved 172.19.0.3
[root@docker front-proxy]#curl localhost:8080/service/1
Hello from behind Envoy (service 1)! hostname 4f39d0e0afc7 resolved 172.19.0.7
[root@docker front-proxy]#curl localhost:8080/service/1
Hello from behind Envoy (service 1)! hostname f6699d0a5de9 resolved 172.19.0.8
[root@docker front-proxy]#curl localhost:8080/service/1
Hello from behind Envoy (service 1)! hostname 4f39d0e0afc7 resolved 172.19.0.7
[root@docker front-proxy]#curl localhost:8080/service/1
Hello from behind Envoy (service 1)! hostname f6699d0a5de9 resolved 172.19.0.8
[root@docker front-proxy]#curl localhost:8080/service/1
Hello from behind Envoy (service 1)! hostname 221f01bef482 resolved 172.19.0.3
测试完成后可以执行 docker-compose down
命令来停止所有容器。
[root@docker front-proxy]#docker-compose down
[+] Running 8/8
✔ Container front-proxy-front-envoy-1 Removed 0.2s
✔ Container front-proxy-service-envoy-2-1 Removed 0.2s
✔ Container front-proxy-service-envoy-1-1 Removed 0.2s
✔ Container front-proxy-service2-1 Removed 0.3s
✔ Container front-proxy-service1-1 Removed 0.4s
✔ Container front-proxy-service1-3 Removed 0.4s
✔ Container front-proxy-service1-2 Removed 0.3s
✔ Network front-proxy_default Removed 0.1s
[root@docker front-proxy]#docker-compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
[root@docker front-proxy]#docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
测试结束。
这里说的集群都是envoy集群。
流量镜像功能允许您将流量复制到另一个集群,而不会影响主要流量。这对于测试新版本的服务或将流量发送到另一个集群以进行分析非常有用。
实验环境:
Docker Compose version v2.23.0
docker 20.10.21-ce(具有docker环境)
实验软件:
链接:https://pan.baidu.com/s/1RB2YMVeNDkX18YFQQ1suZg?pwd=0820
提取码:0820
2023.11.4-Day4-2023.10.30-Envoy使用案例
实验步骤:
graph LR
A[实战步骤] -->B(1️⃣ 拉取测试demo)
A[实战步骤] -->C(2️⃣ 熟悉demo)
A[实战步骤] -->D(3️⃣ 启动服务)
A[实战步骤] -->E(4️⃣ 测试流量镜像功能)
这里的演示应用一共包含 5 个容器,对应的 docker-compose.yml
文件内容如下所示:
services:
envoy-front-proxy:
build:
context: .
dockerfile: ../shared/envoy/Dockerfile
ports:
- "${PORT_PROXY:-10000}:10000"
depends_on:
service1:
condition: service_healthy
service1-mirror:
condition: service_healthy
service2:
condition: service_healthy
service2-mirror:
condition: service_healthy
service1:
build:
context: ../shared/python
target: aiohttp-tracing-service
environment:
- SERVICE_NAME=1
service1-mirror:
build:
context: ../shared/python
target: aiohttp-tracing-service
environment:
- SERVICE_NAME=1
service2:
build:
context: ../shared/python
target: aiohttp-tracing-service
environment:
- SERVICE_NAME=2
service2-mirror:
build:
context: ../shared/python
target: aiohttp-tracing-service
environment:
- SERVICE_NAME=2
输入请求由 envoy-front-proxy
服务接收,路径为 /service/1
的请求被静态复制。每个请求由 service1
集群处理,并且额外转发到 service1-mirror
集群:
对于路径 /service/2
的请求将根据 x-mirror-cluster
标头的存在和值进行动态镜像。此路径的所有请求都会转发到 service2
集群,并且还会镜像到标头中指定的集群。
例如,如果我们发送带有头信息 x-mirror-cluster: service2-mirror
的请求,则该请求将被镜像转发到 service2-mirror
集群。
另外需要注意 Envoy 只会将从主集群接收到的响应返回给客户端。比如我们这里来自
service1
和service2
集群的响应将会发送给客户端。而来自service1-mirror
或service2-mirror
集群的响应则不会被发送回客户端。这也意味着在镜像集群中请求处理过程中的任何问题或延迟都不会影响客户端接收到的响应。
现在切换到 examples/route-mirror
目录,然后启动应用:
# examples/route-mirror 路径
$ docker-compose up --build -d
$ docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
route-mirror-envoy-front-proxy-1 route-mirror-envoy-front-proxy "/docker-entrypoint.sh /usr/local/bin/envoy -c /etc/envoy.yaml" envoy-front-proxy 6 seconds ago Up 4 seconds 0.0.0.0:10000->10000/tcp, :::10000->10000/tcp
route-mirror-service1-1 route-mirror-service1 "python3 /code/service.py" service1 6 seconds ago Up 5 seconds (healthy)
route-mirror-service1-mirror-1 route-mirror-service1-mirror "python3 /code/service.py" service1-mirror 6 seconds ago Up 5 seconds (healthy)
route-mirror-service2-1 route-mirror-service2 "python3 /code/service.py" service2 6 seconds ago Up 5 seconds (healthy)
route-mirror-service2-mirror-1 route-mirror-service2-mirror "python3 /code/service.py" service2-mirror 6 seconds ago Up 5 seconds (healthy)
现在让我们向 envoy-front-proxy
服务发送一个请求,该服务将请求转发到 service1
,并将请求镜像发送到服 service1-mirror
:
$ curl localhost:10000/service/1
Hello from behind Envoy (service 1)!
然后我们可以分别查看 service1
和 service1-mirror
服务的日志:
$ docker-compose logs service1
$ docker-compose logs service1-mirror
或者:
docker logs -f route-mirror-service1-1
docker logs -f route-mirror-service1-mirror-1
而且对于对 service1-mirror
服务的请求,Envoy 将 Host 头信息修改为在主机名中添加了 -shadow
后缀。
如果在对 service2
的请求中没有指定 x-mirror-cluster
,或者指定未知的集群,则该请求将不会被镜像,而是会以正常方式处理。
$ curl localhost:10000/service/2
Hello from behind Envoy (service 2)!
$ curl --header "x-mirror-cluster: service2-mirror-non-existent" localhost:10000/service/2
Hello from behind Envoy (service 2)!
我们可以查看日志来验证结果:
$ docker compose logs service2
route-mirror-service2-1 | DEBUG:asyncio:Using selector: EpollSelector
route-mirror-service2-1 | ======== Running on http://0.0.0.0:8080 ========
route-mirror-service2-1 | (Press CTRL+C to quit)
route-mirror-service2-1 | Host: localhost:10000
route-mirror-service2-1 | INFO:aiohttp.access:192.168.227.6 [30/Oct/2023:09:21:03 +0000] "GET /service/2 HTTP/1.1" 200 189 "-" "curl/7.87.0"
route-mirror-service2-1 | Host: localhost:10000
route-mirror-service2-1 | INFO:aiohttp.access:192.168.227.6 [30/Oct/2023:09:23:06 +0000] "GET /service/2 HTTP/1.1" 200 189 "-" "curl/7.87.0"
route-mirror-service2-1 | Host: localhost:10000
route-mirror-service2-1 | INFO:aiohttp.access:192.168.227.6 [30/Oct/2023:09:23:11 +0000] "GET /service/2 HTTP/1.1" 200 189 "-" "curl/7.87.0"
$ docker compose logs service2-mirror
route-mirror-service2-mirror-1 | DEBUG:asyncio:Using selector: EpollSelector
route-mirror-service2-mirror-1 | ======== Running on http://0.0.0.0:8080 ========
route-mirror-service2-mirror-1 | (Press CTRL+C to quit)
route-mirror-service2-mirror-1 | Host: localhost-shadow:10000
route-mirror-service2-mirror-1 | INFO:aiohttp.access:192.168.227.6 [30/Oct/2023:09:21:03 +0000] "GET /service/2 HTTP/1.1" 200 189 "-" "curl/7.87.0"
可以看到 service2
中有对应的请求日志,但是 service2-mirror
中并没有。
接下来我们再发送一个请求,这次我们将请求发送到 service2
服务,但是我们在请求头中添加了 x-mirror-cluster: service2-mirror
这个头信息,这样 Envoy 将会将请求镜像到 service2-mirror
集群:
$ curl --header "x-mirror-cluster: service2-mirror" localhost:10000/service/2
Hello from behind Envoy (service 2)!
请求后查看这两个服务的日志来验证请求是否已被镜像:
同样的对于对 service2-mirror
服务的请求,Envoy 将 Host 头信息修改为在主机名中添加了 -shadow
后缀。
停掉测试容器:
[root@docker route-mirror]#docker-compose down
[+] Running 6/5
? Container route-mirror-envoy-front-proxy-1 Removed 0.1s
? Container route-mirror-service2-1 Removed 0.3s
? Container route-mirror-service2-mirror-1 Removed 0.3s
? Container route-mirror-service1-1 Removed 0.2s
? Container route-mirror-service1-mirror-1 Removed 0.3s
? Network route-mirror_default Removed 0.0s
测试完成。
实验环境:
Docker Compose version v2.23.0
docker 20.10.21-ce(具有docker环境)
实验软件:
链接:https://pan.baidu.com/s/1RB2YMVeNDkX18YFQQ1suZg?pwd=0820
提取码:0820
2023.11.4-Day4-2023.10.30-Envoy使用案例
实验步骤:
graph LR
A[实战步骤] -->B(1️⃣ 拉取测试demo)
A[实战步骤] -->C(2️⃣ 熟悉demo)
A[实战步骤] -->D(3️⃣ 启动服务)
A[实战步骤] -->E(4️⃣ 故障注入过滤器)
这里我们可以使用 Envoy 的故障注入过滤器来模拟故障,例如模请求中止、失败以及延迟响应等,这样我们就可以测试服务的容错能力了。该功能主要利用 Envoy 的运行时支持来控制故障注入的能力。
这里我们的入口 Envoy 的配置如下所示:
# envoy.yaml
static_resources:
listeners:
- address:
socket_address:
address: 0.0.0.0
port_value: 9211
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
codec_type: AUTO
stat_prefix: ingress_http
access_log:
- name: envoy.access_loggers.stdout
typed_config:
"@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
route_config:
name: local_route
virtual_hosts:
- name: service
domains:
- "*"
routes:
- match:
prefix: /
route:
cluster: local_service
http_filters:
- name: envoy.filters.http.fault
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.fault.v3.HTTPFault
abort:
http_status: 503
percentage:
numerator: 0
denominator: HUNDRED
delay:
fixed_delay: 3s
percentage:
numerator: 0
denominator: HUNDRED
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
clusters:
- name: local_service
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: local_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: backend
port_value: 80
layered_runtime:
layers:
- name: disk_layer_0
disk_layer:
symlink_root: /srv/runtime/current
subdirectory: envoy
上面的配置文件中我们使用了一个 layered_runtime
属性,这个属性用于运行时配置提供程序的配置,这里我们使用了 disk_layer
,这个参数用于设置 Envoy 的运行时参数存储在哪个目录下,这里我们设置为 /srv/runtime/current
,子目录为 envoy
,然后就可以这这个目录下面创建配置文件。
注意下环境:
奇怪:(没关系的,不影响测试效果。)
为了实现故障注入功能,我们这里需要添加一个 envoy.filters.http.fault
的过滤器,这个过滤器用于实现故障注入功能,我们可以通过 typed_config
属性来配置故障注入的功能,这里我们配置了 abort
和 delay
两个功能,abort
用于模拟请求中止,delay
用于模拟延迟响应,这两个参数至少需要配置一个。
- name: envoy.filters.http.fault
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.fault.v3.HTTPFault
abort:
http_status: 503
percentage:
numerator: 0
denominator: HUNDRED
delay:
fixed_delay: 3s
percentage:
numerator: 0 # 分子
denominator: HUNDRED # 分母
delay
:如果指定了此参数,该过滤器将根据对象中的值注入延迟。
fixed_delay
:在向上游转发操作之前添加固定延迟,对于 HTTP / Mongo,指定的延迟将在发出新请求/操作之前注入。header_delay
:故障延迟通过一个 HTTP 头部来控制(如果适用)。percentage
:注入延迟的操作/连接/请求的百分比。abort
:如果指定,过滤器将根据对象中的值中止请求。
http_status
:用于中止 HTTP 请求的 HTTP 状态码。grpc_status
:用于中止 gRPC 请求的 gRPC 状态码。header_abort
:故障中止通过一个 HTTP 头部来控制(如果适用)。percentage
:注入中止的操作/连接/请求的百分比。另外需要注意如果存在以下运行时配置值,则故障过滤器的值会被覆盖。
fault.http.abort.abort_percent
fault.http.abort.http_status
fault.http.delay.fixed_delay_percent
fault.http.delay.fixed_duration_ms
我们这里就是通过 disk_layer
方式来配置运行时参数的,我们可以在 所以我们需要在 /srv/runtime/current/envoy
目录下添加配置来控制故障注入的功能。
下面我们来测试下故障注入的功能,我们路由到 examples/fault-injection
目录,然后使用 Docker Compose 启动这个案例:
$ pwd
/Users/cnych/Documents/course/istio/manifests/envoy/examples/fault-injection
$ docker-compose up --build -d
$ docker-compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
fault-injection-backend-1 fault-injection-backend "gunicorn -b 0.0.0.0:80 httpbin:app -k gevent" backend 9 seconds ago Up 7 seconds 0.0.0.0:8080->80/tcp, :::8080->80/tcp
fault-injection-envoy-1 fault-injection-envoy "/docker-entrypoint.sh /usr/local/bin/envoy -c /etc/envoy.yaml" envoy 9 seconds ago Up 7 seconds 0.0.0.0:9211->9211/tcp, :::9211->9211/tcp, 10000/tcp
启动后我们开始发送连续的 HTTP 请求,重新开一个终端窗口,然后执行下面的命令:
# examples/fault-injection 目录下
$ docker-compose exec envoy bash
bash send_request.sh
这里的 send_request.sh
脚本我们将会向 Envoy 一直发送 HTTP 请求,Envoy 将请求转发到后端容器,直到我们手动停止脚本,脚本内容如下所示:
#!/usr/bin/env bash
set -ex
while :; do
curl -v localhost:9211/status/200
sleep 1
done
虽然我们已经在 Envoy 中配置了故障注入功能,但是现在我们还没有启用它,但是目前的配置的分子都为 0,所以我们的请求都是正常的,我们可以看到请求的状态码都是 200
。
下面我们再开一个新的终端,通过以下命令,在运行时打开中断故障注入。
# examples/fault-injection 目录下
$ docker compose exec envoy bash
$ bash enable_abort_fault_injection.sh
这里执行的 enable_abort_fault_injection.sh
脚本可以对 100%的请求进行 HTTP 中止,对应的内容如下所示:
#!/usr/bin/env bash
set -ex
mkdir -p /srv/runtime/v1/envoy/fault/http/abort
echo '100' > /srv/runtime/v1/envoy/fault/http/abort/abort_percent
echo '503' > /srv/runtime/v1/envoy/fault/http/abort/http_status
pushd /srv/runtime
ln -s /srv/runtime/v1 new && mv -Tf new current
popd
当我们执行了该脚本后前面一直发送的请求就会出现中断的情况,我们可以看到请求的状态码都是 503
。
在容器里执行。
这是因为我们在 Envoy 中配置了故障注入功能,然后通过运行时配置将故障注入功能打开了,配置 abort_percent
为 100%,这样所有请求都会被中断了。
可以再次执行 disable_abort_fault_injection.sh
脚本来关闭中断功能。
bash disable_abort_fault_injection.sh
该脚本实现也非常简单,只需要将上面的运行时配置文件删除即可,脚本内容如下所示:
#!/usr/bin/env bash
set -ex
rm /srv/runtime/v1/envoy/fault/http/abort/abort_percent
rm /srv/runtime/v1/envoy/fault/http/abort/http_status
pushd /srv/runtime
ln -s /srv/runtime/v1 new && mv -Tf new current
popd
正常现在我们的请求就恢复正常的响应了。
用同样的方式我们还可以通过一下命令通过运行时打开延迟故障注入。
# examples/fault-injection 目录下
$ docker compose exec envoy bash
$ bash enable_delay_fault_injection.sh
这里的 enable_delay_fault_injection.sh
脚本和前面一样,只是将 abort
改为了 delay
,将 fixed_delay_percent
设置为 50,fixed_duration_ms
设置为 3000,表示 50% 的请求会被延迟 3 秒钟,脚本内容如下所示:
#!/usr/bin/env bash
set -ex
mkdir -p /srv/runtime/v1/envoy/fault/http/delay
echo '50' > /srv/runtime/v1/envoy/fault/http/delay/fixed_delay_percent
echo '3000' > /srv/runtime/v1/envoy/fault/http/delay/fixed_duration_ms
pushd /srv/runtime
ln -s /srv/runtime/v1 new && mv -Tf new current
popd
上面脚本执行后,可以看到所有请求的响应码都为 200
,但是其中一半的请求将需要 3 秒钟才能完成。
同样要关闭延迟故障注入功能,只需要执行 disable_delay_fault_injection.sh
脚本即可
bash disable_delay_fault_injection.sh
最后我们可以通过查看当前运行时文件系统来了解当前的运行时配置。
$ tree /srv/runtime
/srv/runtime
|-- current -> /srv/runtime/v1
`-- v1
`-- envoy
`-- fault
`-- http
|-- abort
`-- delay
7 directories, 0 files
到这里我们就验证了 Envoy 的故障注入功能。
终止docker-compose:
[root@docker fault-injection]#docker-compose down
[+] Running 3/2
? Container fault-injection-backend-1 Removed 0.9s
? Container fault-injection-envoy-1 Removed 0.1s
? Network fault-injection_default Removed 0.0s
测试完成。
实验环境:
Docker Compose version v2.23.0
docker 20.10.21-ce(具有docker环境)
实验软件:
链接:https://pan.baidu.com/s/1RB2YMVeNDkX18YFQQ1suZg?pwd=0820
提取码:0820
2023.11.4-Day4-2023.10.30-Envoy使用案例
实验步骤:
graph LR
A[实战步骤] -->B(1️⃣ 拉取测试demo)
A[实战步骤] -->C(2️⃣ 熟悉demo)
A[实战步骤] -->D(3️⃣ 启动服务)
A[实战步骤] -->E(4️⃣ MySQL 过滤器)
这里我们将来展示如何在 Envoy 代理中使用 MySQL 过滤器。Envoy 代理配置包括一个 MySQL 过滤器,用于解析查询并收集 MySQL 特定的指标。
首先切换到 examples/mysql
目录下面,然后启动服务:
# examples/mysql 目录
$ docker-compose up --build -d
$ docker-compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
mysql-mysql-1 mysql-mysql "docker-entrypoint.sh mysqld" mysql 14 seconds ago Up 13 seconds 3306/tcp, 33060/tcp
mysql-proxy-1 mysql-proxy "/docker-entrypoint.sh /usr/local/bin/envoy -c /etc/envoy.yaml" proxy 14 seconds ago Up 13 seconds 0.0.0.0:8001->8001/tcp, :::8001->8001/tcp, 10000/tcp
记得要手动拉取2个镜像才行:
docker pull docker.io/library/mysql:8.2.0@sha256:1773f3c7aa9522f0014d0ad2bbdaf597ea3b1643c64c8ccc2123c64afd8b82b1
docker pull docker.io/envoyproxy/envoy:contrib-dev
这个应用中我们通过 Envoy 来代理 MySQL 的请求,然后我们可以通过 mysql
客户端来连接 Envoy 代理,然后执行一些命令,这些命令将会被 Envoy 代理转发到后端的 MySQL 服务。对应的 Envoy 配置如下所示:
static_resources:
listeners:
- name: mysql_listener
address:
socket_address:
address: 0.0.0.0
port_value: 1999
filter_chains:
- filters:
- name: envoy.filters.network.mysql_proxy
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.mysql_proxy.v3.MySQLProxy
stat_prefix: egress_mysql
- name: envoy.filters.network.tcp_proxy
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy
stat_prefix: mysql_tcp
cluster: mysql_cluster
clusters:
- name: mysql_cluster
type: STRICT_DNS
load_assignment:
cluster_name: mysql_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: mysql
port_value: 3306
admin:
address:
socket_address:
address: 0.0.0.0
port_value: 8001
由于 MySQL 服务是 TCP 协议了,所有我们现在配置的是全新的过滤器链,这里我们使用的是 envoy.filters.network.mysql_proxy
以及 envoy.filters.network.tcp_proxy
两个过滤器。
MySQL 代理过滤器解码 MySQL 客户端和服务器之间的网络协议。它解码载荷中的 SQL 查询,解码的信息被发出作为动态元数据,可以与访问日志过滤器结合使用,以获取有关访问表以及对每个表执行的操作的详细信息。它是一个透明的过滤器,所以不会影响客户端和服务器的正常进度。此外 MySQL 代理过滤器应与 TCP 代理过滤器链接在一起。
上面的配置就是通过 Envoy 的 1999 端口来接收 MySQL 的代理请求,然后转发给 mysql_cluster
集群下面配置的 mysql 后端服务。
下面我们来验证下 MySQL 代理过滤器的功能,我们可以通过 mysql
客户端来连接 Envoy 代理:
$ docker run --rm -it --platform=linux/amd64 --network mysql_default mysql:5.7 mysql -h proxy -P 1999 -u root --skip-ssl
# ......
mysql> CREATE DATABASE test;
Query OK, 1 row affected (0.00 sec)
mysql> USE test;
Database changed
mysql> CREATE TABLE test ( text VARCHAR(255) );
Query OK, 0 rows affected (0.01 sec)
mysql> SELECT COUNT(*) FROM test;
+----------+
| COUNT(*) |
+----------+
| 0 |
+----------+
1 row in set (0.01 sec)
mysql> INSERT INTO test VALUES ('hello, world!');
Query OK, 1 row affected (0.00 sec)
mysql> SELECT COUNT(*) FROM test;
+----------+
| COUNT(*) |
+----------+
| 1 |
+----------+
1 row in set (0.00 sec)
mysql> exit
Bye
然后我们可以检查下流量出口的数据统计:
$ curl -s "http://localhost:8001/stats?filter=egress_mysql"
mysql.egress_mysql.auth_switch_request: 1
mysql.egress_mysql.decoder_errors: 0
mysql.egress_mysql.login_attempts: 1
mysql.egress_mysql.login_failures: 0
mysql.egress_mysql.protocol_errors: 0
mysql.egress_mysql.queries_parse_error: 2
mysql.egress_mysql.queries_parsed: 7
mysql.egress_mysql.sessions: 1
mysql.egress_mysql.upgraded_to_ssl: 0
可以看到已经有相关的数据了。同样还可以查看下入口的 TCP 的统计数据:
curl -s "http://localhost:8001/stats?filter=mysql_tcp"
tcp.mysql_tcp.downstream_cx_no_route: 0
tcp.mysql_tcp.downstream_cx_rx_bytes_buffered: 0
tcp.mysql_tcp.downstream_cx_rx_bytes_total: 451
tcp.mysql_tcp.downstream_cx_total: 1
tcp.mysql_tcp.downstream_cx_tx_bytes_buffered: 0
tcp.mysql_tcp.downstream_cx_tx_bytes_total: 679
tcp.mysql_tcp.downstream_flow_control_paused_reading_total: 0
tcp.mysql_tcp.downstream_flow_control_resumed_reading_total: 0
tcp.mysql_tcp.idle_timeout: 0
tcp.mysql_tcp.max_downstream_connection_duration: 0
tcp.mysql_tcp.upstream_flush_active: 0
tcp.mysql_tcp.upstream_flush_total: 0
这样我们就实现了通过 Envoy 来代理 MySQL 的请求。
停止docker-comose:
[root@docker mysql]#docker-compose down
[+] Running 3/2
✔ Container mysql-proxy-1 Removed 0.1s
✔ Container mysql-mysql-1 Removed 1.2s
✔ Network mysql_default Removed 0.0s
测试结束。
实验环境:
Docker Compose version v2.23.0
docker 20.10.21-ce(具有docker环境)
实验软件:
链接:https://pan.baidu.com/s/1RB2YMVeNDkX18YFQQ1suZg?pwd=0820
提取码:0820
2023.11.4-Day4-2023.10.30-Envoy使用案例
实验步骤:
graph LR
A[实战步骤] -->B(1️⃣ 拉取测试demo)
A[实战步骤] -->C(2️⃣ 熟悉demo)
A[实战步骤] -->D(3️⃣ 启动服务)
A[实战步骤] -->E(4️⃣ Golang HTTP 过滤器)
HTTP Golang 过滤器允许在请求和响应流期间运行 Golang,并简化了对 Envoy 的扩展。这个过滤器使用的 Go 插件可以独立于 Envoy 重新编译,这使得在不重新编译 Envoy 的情况下更新插件变得容易。
另外需要注意 Envoy 的 Go 插件必须实现 StreamFilter API。构建 Go 插件动态库时,必须使用与 Envoy 的 glibc
版本一致的 Go 版本。
下面我们就来演示下如何使用 Golang HTTP 过滤器,这里的示例将展示一个 Go 插件,它可以直接响应请求,并且还可以更新上游服务器提供的响应。
同样定位到 examples/golang-http
目录,首先构建 go 插件库:
# examples/golang-http 目录
$ docker-compose -f docker-compose-go.yaml run --rm go_plugin_compile
docker pull docker.io/library/golang:1.21.3-bullseye@sha256:26c7537d6ac3827eb4638034d16edc64de57bb011c8cc8fe301ac13a6568f6f4
⚠️ 注意:
记得配置这个参数,否则可能下载不了go包:
编译后的库文件会出现在 lib
文件夹中。
$ ls lib
simple.so
然后接下来可以直接启动我们的服务:
$ docker-compose up --build -d
$ docker-compose ps
NAME COMMAND SERVICE STATUS PORTS
golang-http-helloworld_service-1 "python3 /code/servi…" helloworld_service running (healthy)
golang-http-proxy-1 "/docker-entrypoint.…" proxy running 0.0.0.0:10000->10000/tcp, :::10000->10000/tcp
这里我们同样要重点了解下 Envoy 代理的配置文件:
static_resources:
listeners:
- name: listener_0
address:
socket_address:
address: 0.0.0.0
port_value: 10000
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
http_filters:
- name: envoy.filters.http.golang
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.golang.v3alpha.Config
library_id: simple
library_path: "lib/simple.so"
plugin_name: simple
plugin_config:
"@type": type.googleapis.com/xds.type.v3.TypedStruct
value:
prefix_localreply_body: "Configured local reply from go"
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match:
prefix: "/"
route:
cluster: helloworld_service_cluster
clusters:
- name: helloworld_service_cluster
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: helloworld_service_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: helloworld_service
port_value: 8080
整体上和之前的配置文件差不多,会将所有的请求都转发到后端的 helloworld_service
服务,但是这里我们在 http_filters
中还添加了一个 envoy.filters.http.golang
过滤器,这个过滤器就是我们编译的 go 插件,这个过滤器的配置如下所示:
- name: envoy.filters.http.golang
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.golang.v3alpha.Config
library_id: simple
library_path: "lib/simple.so"
plugin_name: simple
plugin_config:
"@type": type.googleapis.com/xds.type.v3.TypedStruct
value:
prefix_localreply_body: "Configured local reply from go"
这里我们配置了 go 插件的路径,然后还配置了 plugin_config
,这个配置会传递给 go 插件,这里我们配置了 prefix_localreply_body
,这个配置会在 go 插件中使用,我们可以在 go 插件中获取到这个配置,然后将其添加到响应体中。
下面我们可以使用 curl
命令来测试下 go 插件的功能:
curl -v localhost:10000 2>&1
正常我们可以看到由 Go 插件添加的 rsp-header-from-go: bar-test
这个 Header 头信息。
然后我们再发出一个由上游处理并由 Go 插件更新的请求:
$ curl localhost:10000/update_upstream_response 2>&1
upstream response body updated by the simple plugin%
该请求的响应结果是由 Go 插件更新的。
最后我们使用自定义配置进行处理的 Go 插件进行请求。
$ curl localhost:10000/localreply_by_config 2>&1
Configured local reply from go, path: /localreply_by_config
得到的结果是由 Go 插件提供的包含 prefix_localreply_body
值的 body。
当然这些都是我们在插件里面去实现的,具体要实现什么样的功能完全取决于我们自己。
测试结束。
我们这里只是挑选了几个过滤器来进行演示,实际上 Envoy 还有很多其他的过滤器,这里就不一一介绍了,如果想了解更多的过滤器可以参考 Envoy 官方文档 了解更多使用方式。
最重要的是我们要了解 Envoy 的过滤器的工作原理,了解 Envoy 的配置流程,这样我们才能更好的使用 Envoy。
我的博客主旨:
微信二维码
x2675263825 (舍得), qq:2675263825。
微信公众号
《云原生架构师实战》
个人博客站点
http://onedayxyy.cn/
语雀
https://www.yuque.com/xyy-onlyone
csdn
https://blog.csdn.net/weixin_39246554?spm=1010.2135.3001.5421
知乎
https://www.zhihu.com/people/foryouone
好了,关于本次就到这里了,感谢大家阅读,最后祝大家生活快乐,每天都过的有意义哦,我们下期见!