eShopOnContainers 知多少[12]:Envoy gateways
1. 引言
在最新的eShopOnContainers 3.0 中Ocelot 网关被Envoy Proxy 替换。下面就来简要带大家了解下Envoy,并尝试梳理下为什么要使用Envoy替代Ocelot。
2. Hello Envoy
ENVOY IS AN OPEN SOURCE EDGE AND SERVICE PROXY, DESIGNED FOR CLOUD-NATIVE APPLICATIONS.
Enovy(信使) 是一款开源的专为云原生应用设计的服务代理。
2.1. 快速体验
首先基于本地Dockers快速体验以下,先启动本地Docker-Desktop,拉取Envoy镜像:
> docker search envoy-dev NAME DESCRIPTION STARS OFFICIAL AUTOMATED envoyproxy/envoy Images for tagged releases. Use envoy-dev fo… 96 > docker image pull envoyproxy:envoy-dev latest: Pulling from envoyproxy/envoy-dev 171857c49d0f: Pull complete 419640447d26: Pull complete 61e52f862619: Pull complete 3f2a8c910457: Pull complete b2ce823b3fd3: Pull complete ec09faba9bc7: Pull complete b0b9168845d0: Pull complete 39a220277151: Pull complete 9081a11f5983: Pull complete 1880b475bc3a: Pull complete Digest: sha256:cd8dbbbd8ce4c8c6eb52e4f8eebf55f29d1e597ca8311fecf9eda08b8cca813a Status: Downloaded newer image for envoyproxy/envoy-dev:latest docker.io/envoyproxy/envoy-dev:latest
该Docker 镜像将包含最新版本的 Envoy 和一个基本的 Envoy 配置,可以将10000端口的入站请求路由到www.google.com
。
下面启动容器测试:
> docker run -d --name envoy -p 10000:10000 envoyproxy/envoy-dev:latest 27e422f34b389d99e9180e47d8109a19975ccd139f42ac2f4fa9f724906b72f6 > docker ps | findstr 'envoy' 27e422f34b38 envoyproxy/envoy-dev:latest "/docker-entrypoint.?? 2 minutes ago Up 2 minutes 0.0.0.0:10000->10000/tcp envoy > curl -I http://localhost:10000 HTTP/1.1 200 OK content-type: text/html; charset=ISO-8859-1 p3p: CP="This is not a P3P policy! See g.co/p3phelp for more info." date: Sat, 17 Oct 2020 04:38:38 GMT server: envoy x-xss-protection: 0 x-frame-options: SAMEORIGIN expires: Sat, 17 Oct 2020 04:38:38 GMT cache-control: private set-cookie: 1P_JAR=2020-10-17-04; expires=Mon, 16-Nov-2020 04:38:38 GMT; path=/; domain=.google.com; Secure set-cookie: NID=204=h0EoJXNOTbQA11L-tVowqcwloS0-BCTR71IeN4irsmpubdPIIS4sU8Gco79pt1NhONAxxFdUJ46SKvbX4Ni-jKMWbSW0k_kn3fFkVrfLm7OOBbAtUWtxGGOCRJGbSNIRyOPfDB7_wMngEWW3yoFEs9diSCtZK9DWFZdtJJZtWuI; expires=Sun, 18-Apr-2021 04:38:38 GMT; path=/; domain=.google.com; HttpOnly alt-svc: h3-Q050=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-27=":443"; ma=2592000,h3-T051=":443"; ma=2592000,h3-T050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43" x-envoy-upstream-service-time: 37 transfer-encoding: chunked
PS: 请确保本地机器能访问Google,否则curl -I http://localhost:10000
会出错。
接下来我们进入容器内部,查看下配置文件,默认路径为/etc/envoy/envoy.yaml
:
docker exec -it envoy /bin/bash root@27e422f34b38:/# cat /etc/envoy/envoy.yaml admin: access_log_path: /tmp/admin_access.log address: socket_address: protocol: TCP address: 127.0.0.1 port_value: 9901 static_resources: listeners: - name: listener_0 address: socket_address: protocol: TCP 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 route_config: name: local_route virtual_hosts: - name: local_service domains: ["*"] routes: - match: prefix: "/" route: host_rewrite_literal: www.google.com cluster: service_google http_filters: - name: envoy.filters.http.router clusters: - name: service_google connect_timeout: 30s type: LOGICAL_DNS # Comment out the following line to test on v6 networks dns_lookup_family: V4_ONLY lb_policy: ROUND_ROBIN load_assignment: cluster_name: service_google endpoints: - lb_endpoints: - endpoint: address: socket_address: address: www.google.com port_value: 443 transport_socket: name: envoy.transport_sockets.tls typed_config: "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext sni: www.google.com
我们把上面的配置文件拷贝到本地,将上面的www.google.com
改为www.baidu.com
,将admin.address.socket_address.address: 127.0.0.1
该为0.0.0.0
,然后把配置文件命名为envoy-baidu.yaml
,然后挂载到容器的/etc/envoy/envoy.yaml
。
> docker run --rm -d --name envoy-baidu -v $Home/k8s/envoy-baidu.yaml:/etc/envoy/envoy.yaml -p 9901:9901 -p 15001:15001 envoyproxy/envoy-dev:latest > docker ps | findstr 'envoy' f07f6a1e9305 envoyproxy/envoy-dev:latest "/docker-entrypoint.?? 2 minutes ago Up 2 minutes 10000/tcp, 0.0.0.0:9901->9901/tcp, 0.0.0.0:15001->15001/tcp envoy-baidu 3cd12b5f6ddd envoyproxy/envoy-dev:latest "/docker-entrypoint.?? About an hour ago Up About an hour 0.0.0.0:10000->10000/tcp envoy > curl -I http://localhost:15001 HTTP/1.1 200 OK accept-ranges: bytes cache-control: private, no-cache, no-store, proxy-revalidate, no-transform content-length: 277 content-type: text/html date: Sat, 17 Oct 2020 05:41:01 GMT etag: "575e1f65-115" last-modified: Mon, 13 Jun 2016 02:50:13 GMT pragma: no-cache server: envoy x-envoy-upstream-service-time: 24
使用浏览器访问http://localhost:9901即可访问envoy管理页面,如下图所示:
2.2. 配置简介
第一次看Envoy的配置文件,和第一次接触Nginx的配置文件一样,绝对一脸懵逼。没关系,咱们来理一理。
作为一个代理,不管是Nginx、HAProxy,还是Envoy,其处理流程都是一样的。其首先都是要监听指定端口获取请求流量,然后分析请求数据,进行请求转发。脑补完大致流程后,再来看 Envoy 是如何组织配置信息的。先来了几个核心配置:
- listener : Envoy 的监听地址,用来接收请求,处理入站请求。Envoy 会暴露一个或多个 Listener 来监听客户端的请求。
- filter : 过滤器是处理入站和出站流量的链式结构的一部分。在过滤器链上可以集成很多特定功能的过滤器,例如,通过集成 GZip 过滤器可以在数据发送到客户端之前压缩数据。
- route_config : 路由规则配置。即将请求路由到后端的哪个集群。
- cluster : 集群定义了流量的目标端点,同时还包括一些其他可选配置,如负载均衡策略等。
整体流程如下图所示:
2.3. 代理 ASP.NET Core WebApi
有了上面的基础,下面尝试使用Envoy代理ASP.NET Core WebApi。
首先创建两个简单API,然后创建一个Envoy配置文件,最后通过docker compose启动三个容器进行测试。由于项目文件结构简单,这里不再过多阐述,主要包含四个部分:
- City Api
- Weather Api
- Envoy 代理配置
- docker compose 配置
整体解决方案如下图所示。源码路径:K8S.NET.Envoy。
Envoy 代理配置基于第一节的基础上进行修改,如下所示:
admin: access_log_path: /tmp/admin_access.log address: socket_address: protocol: TCP address: 0.0.0.0 port_value: 9903 static_resources: listeners: - name: listener_0 address: socket_address: protocol: TCP address: 0.0.0.0 port_value: 10003 filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager stat_prefix: ingress_http route_config: name: local_route virtual_hosts: - name: local_service domains: ["*"] routes: - match: prefix: "/c" route: prefix_rewrite: "/city" cluster: city_service - match: prefix: "/w" route: prefix_rewrite: "/weather" cluster: weather_service http_filters: - name: envoy.filters.http.router clusters: - name: city_service 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 load_assignment: cluster_name: city_service endpoints: - lb_endpoints: - endpoint: address: socket_address: address: cityapi port_value: 80 - name: weather_service 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 load_assignment: cluster_name: weather_service endpoints: - lb_endpoints: - endpoint: address: socket_address: address: weatherapi port_value: 80
以上配置Envoy监听10003
端口,通过指定prefix_rewrite
重写前缀,将/c
路由至cityapi
的/city
路径,将/w
路由至weatherapi
的/weather
路径。
docker-compose配置如下:
version: '3' services: envoygateway: build: Envoy/ ports: - "9903:9903" - "10003:10003" volumes: - ./Envoy/envoy.yaml:/etc/envoy/envoy.yaml cityapi: build: K8S.NET.CityApi/ ports: - "8080:80" environment: ASPNETCORE_URLS: "http://+" ASPNETCORE_ENVIRONMENT: "Development" weatherapi: build: K8S.NET.WeatherApi/ ports: - "8082:80" environment: ASPNETCORE_URLS: "http://+" ASPNETCORE_ENVIRONMENT: "Development"
从上可以看到,主要用来启动三个服务:
- envoy gateway:其中将项目路径下
/Envoy/envoy.yaml
挂载到容器目录/etc/envoy/envoy.yaml
。同时暴露2个端口,9903,10003。 - city api
- weather api
因此最终可以通过以下路径进行访问:
- http://localhost:10003/c 访问city api。
- http://localhost:10003/w 访问weather api。
执行以下命令,启动应用和代理,并测试:
> docker-compose up -d Starting k8snetenvoy_envoygateway_1 ... done Starting k8snetenvoy_cityapi_1 ... done Starting k8snetenvoy_weatherapi_1 ... done > docker-compose ps Name Command State Ports ----------------------------------------------------------------------------------------------------------------------- k8snetenvoy_cityapi_1 dotnet K8S.NET.CityApi.dll Up 443/tcp, 0.0.0.0:8080->80/tcp k8snetenvoy_envoygateway_1 /docker-entrypoint.sh envo ... Up 10000/tcp, 0.0.0.0:10003->10003/tcp, 0.0.0.0:9903->9903/tcp k8snetenvoy_weatherapi_1 dotnet K8S.NET.WeatherApi.dll Up 443/tcp, 0.0.0.0:8082->80/tcp > curl http://localhost:10003/c Shanghai > curl http://localhost:10003/w Cool
3. eShopOnContainers 中的应用
eShopOnContainer 中主要定义了四个API 网关(BFF 模式),服务间通信方式主要有两种,一种是HTTP,一种是gRPC。如果启用Service Mesh并且部署至K8S,服务整体通信架构如下图所示:
有两点需要补充说明:
- Linkerd是一种Service Mesh,其核心思想是借助Sidecar模式无侵入式对应用进行服务治理,包括服务发现、流量管理、负载均衡、路由等。
- 了解过Istio(目前比较流行的Service Mesh)应该知道,Envoy在Istio中作为Sidecar而存在,而在eShopOnContainers中Envoy被充当API Gateways。
基于上面的基础,再来看eShopOnContainers中的配置,其实就很明白了,主要是配置文件从Ocelot 转变到envoy.yaml,配置如下图所示。
路由配置如下:
- /m/ 、/marketing-api/ 路由至:marketing api
- /c/、/catalog-api/ 路由至:catalog api
- /o/、/ordering-api/ 路由至:ordering api
- /b/、/basket-api/ 路由至:basket api
- / 路由至:web bff aggregator api
部署时,基于helm将envoy.yaml
保存至ConfigMap
,在基于envoyproxy/enovy
镜像构建容器,将配置从ConfigMap
挂载到容器中,容器内部即可基于配置启动Envoy 网关了。
4. Why Envoy
经过上面的了解发现,Envoy还是充当的网关角色,那为什么要替换呢? 先来了解下Envoy的优势:
-
非侵入式架构 :
Envoy
基于Sidecar
模式,是一个独立进程,对应用透明。(在eShopOnContainer中还是独立的网关项目,并非以Sidecar
模式注入到服务中。) -
基于C++开发实现:拥有强大的定制化能力和优异的性能。
-
L3/L4/L7 架构 : 传统的网络代理,要么在
HTTP
层工作,要么在TCP
层工作。而Envoy
同时支持 3/4 层和 7 层代理。 -
顶级 HTTP/2 支持 : 它将
HTTP/2
视为一等公民,并且可以在HTTP/2
和HTTP/1.1
之间相互转换(双向),建议使用HTTP/2
。 -
gRPC 支持 : Envoy 完美支持 HTTP/2,也可以很方便地支持
gRPC
(gRPC 使用HTTP/2
作为底层多路复用传输协议)。 -
服务发现和动态配置 : 与
Nginx
等代理的热加载不同,Envoy
可以通过API
接口动态更新配置,无需重启代理。 -
可观测性 :
Envoy
内置stats
模块,可以集成诸如prometheus/statsd
等监控方案。还可以集成分布式追踪系统,对请求进行追踪。
再来看下Ocelot:其本质还是ASP.NET Core中的一个请求中间件。只能进行7层代理,不支持 gRPC,不支持监控。因此总体而言,Envoy更契合云原生对网络代理的诉求。
5. 总结
本文简要梳理了Envoy的基本用法,以及其在eShopOnContainers中的运用。Envoy作为一个比肩Nginx的服务代理,其特性在Service Mesh中有着灵活的运用。本文就讲到这里了,下次有机会在和大家分享下Envoy在Service Mesh中的应用。
参考资料:
推荐链接:你必须知道的ML.NET开发指南
推荐链接:你必须知道的Office开发指南
推荐链接:你必须知道的IOT开发指南
推荐链接:你必须知道的Azure基础知识
推荐链接:你必须知道的PowerBI基础知识

关注我的公众号『微服务知多少』,我们微信不见不散。
阅罢此文,如果您觉得本文不错并有所收获,请【打赏】或【推荐】,也可【评论】留下您的问题或建议与我交流。 你的支持是我不断创作和分享的不竭动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)