eShopOnContainers Envoy gateways有多少种用法?

摘要:1. 引言 在最新的eShopOnContainers 3.0 中Ocelot 网关被Envoy Proxy 替换。下面就来简要带大家了解下Envoy,并尝试梳理下为什么要使用Envoy替代Ocelot。 2. Hello Envoy EN
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 支持对特殊协议在 L7 进行嗅探和统计,包括:MongoDB、DynamoDB等。 可观测性:Envoy内置stats模块,可以集成诸如prometheus/statsd等监控方案。还可以集成分布式追踪系统,对请求进行追踪。 再来看下Ocelot:其本质还是ASP.NET Core中的一个请求中间件。只能进行7层代理,不支持 gRPC,不支持监控。因此总体而言,Envoy更契合云原生对网络代理的诉求。 5. 总结 本文简要梳理了Envoy的基本用法,以及其在eShopOnContainers中的运用。Envoy作为一个比肩Nginx的服务代理,其特性在Service Mesh中有着灵活的运用。本文就讲到这里了,下次有机会在和大家分享下Envoy在Service Mesh中的应用。 参考资料: Envoy 介绍 - Envoy 中文指南 Build an API Gateway with Envoy and use with .NET Core APIs