1. 程式人生 > >eShopOnContainers 知多少[12]:Envoy gateways

eShopOnContainers 知多少[12]:Envoy gateways

![](https://upload-images.jianshu.io/upload_images/2799767-907fa556a105bc40.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) # 1. 引言 在最新的eShopOnContainers 3.0 中Ocelot 閘道器被Envoy Proxy 替換。下面就來簡要帶大家瞭解下Envoy,並嘗試梳理下為什麼要使用Envoy替代Ocelot。 # 2. Hello Envoy >**[ENVOY](https://www.envoyproxy.io)** 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管理頁面,如下圖所示: ![envoy admin page](https://upload-images.jianshu.io/upload_images/2799767-799930f950c87a20.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ## 2.2. 配置簡介 第一次看Envoy的配置檔案,和第一次接觸Nginx的配置檔案一樣,絕對一臉懵逼。沒關係,咱們來理一理。 作為一個代理,不管是Nginx、HAProxy,還是Envoy,其處理流程都是一樣的。其首先都是要監聽指定埠獲取請求流量,然後分析請求資料,進行請求轉發。腦補完大致流程後,再來看 Envoy 是如何組織配置資訊的。先來了幾個核心配置: - **listener** : Envoy 的監聽地址,用來接收請求,處理入站請求。Envoy 會暴露一個或多個 Listener 來監聽客戶端的請求。 - **filter** : 過濾器是處理入站和出站流量的鏈式結構的一部分。在過濾器鏈上可以整合很多特定功能的過濾器,例如,通過整合 GZip 過濾器可以在資料傳送到客戶端之前壓縮資料。 - **route_config** : 路由規則配置。即將請求路由到後端的哪個叢集。 - **cluster** : 叢集定義了流量的目標端點,同時還包括一些其他可選配置,如負載均衡策略等。 整體流程如下圖所示: ![圖片來源:https://fuckcloudnative.io/envoy-handbook/docs/gettingstarted/quick-start/](https://upload-images.jianshu.io/upload_images/2799767-51ed0f50d336c308.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ## 2.3. 代理 ASP.NET Core WebApi 有了上面的基礎,下面嘗試使用Envoy代理ASP.NET Core WebApi。 首先建立兩個簡單API,然後建立一個Envoy配置檔案,最後通過docker compose啟動三個容器進行測試。由於專案檔案結構簡單,這裡不再過多闡述,主要包含四個部分: 1. City Api 2. Weather Api 3. Envoy 代理配置 4. docker compose 配置 整體解決方案如下圖所示。原始碼路徑:[K8S.NET.Envoy](https://github.com/sheng-jie/dotnet.on.k8s/tree/master/K8S.NET.Envoy)。 ![](https://upload-images.jianshu.io/upload_images/2799767-dcab57e426d6dcc0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 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" ``` 從上可以看到,主要用來啟動三個服務: 1. envoy gateway:其中將專案路徑下`/Envoy/envoy.yaml`掛載到容器目錄`/etc/envoy/envoy.yaml`。同時暴露2個埠,9903,10003。 2. city api 3. weather api 因此最終可以通過以下路徑進行訪問: 1. http://localhost:10003/c 訪問city api。 2. 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,服務整體通訊架構如下圖所示: ![](https://upload-images.jianshu.io/upload_images/2799767-47be05d0bd32c83d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 有兩點需要補充說明: 1. [Linkerd](https://linkerd.io)是一種Service Mesh,其核心思想是藉助**Sidecar**模式無侵入式對應用進行服務治理,包括服務發現、流量管理、負載均衡、路由等。 2. 瞭解過Istio(目前比較流行的Service Mesh)應該知道,Envoy在Istio中作為Sidecar而存在,而在eShopOnContainers中Envoy被充當API Gateways。 基於上面的基礎,再來看eShopOnContainers中的配置,其實就很明白了,主要是配置檔案從Ocelot 轉變到envoy.yaml,配置如下圖所示。 ![eShopOnContainers envoy proxy configuration](https://upload-images.jianshu.io/upload_images/2799767-5425c94996b90bd5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 路由配置如下: 1. /m/ 、/marketing-api/ 路由至:marketing api 2. /c/、/catalog-api/ 路由至:catalog api 3. /o/、/ordering-api/ 路由至:ordering api 4. /b/、/basket-api/ 路由至:basket api 5. / 路由至:web bff aggregator api 部署時,基於helm將`envoy.yaml`儲存至`ConfigMap`,在基於`envoyproxy/enovy`映象構建容器,將配置從`ConfigMap`掛載到容器中,容器內部即可基於配置啟動Envoy 網關了。 ![](https://upload-images.jianshu.io/upload_images/2799767-7166fb7fafa25e35.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) # 4. Why Envoy 經過上面的瞭解發現,Envoy還是充當的閘道器角色,那為什麼要替換呢? 先來了解下Envoy的優勢: * **非侵入式架構** : `Envoy` 基於`Sidecar`模式,是一個獨立程序,對應用透明。(在eShopOnContainer中還是獨立的閘道器專案,並非以`Sidecar`模式注入到服務中。) ![](https://upload-images.jianshu.io/upload_images/2799767-d4f4df96d0ad4b1a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) * **基於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://www.grpc.io/) 使用 `HTTP/2` 作為底層多路複用傳輸協議)。 * **服務發現和動態配置** : 與 `Nginx` 等代理的熱載入不同,`Envoy` 可以通過 `API` 介面動態更新配置,無需重啟代理。 * **特殊協議支援** : Envoy 支援對特殊協議在 L7 進行嗅探和統計,包括:[MongoDB](https://www.envoyproxy.io/docs/envoy/latest/configuration/listeners/network_filters/mongo_proxy_filter#)、[DynamoDB](https://www.servicemesher.com/envoy/intro/arch_overview/dynamo.html#arch-overview-dynamo) 等。 * **可觀測性** : `Envoy` 內建 `stats` 模組,可以整合諸如 `prometheus/statsd` 等監控方案。還可以整合分散式追蹤系統,對請求進行追蹤。 再來看下Ocelot:其本質還是ASP.NET Core中的一個請求中介軟體。只能進行7層代理,不支援 gRPC,不支援監控。因此總體而言,Envoy更契合雲原生對網路代理的訴求。 # 5. 總結 本文簡要梳理了Envoy的基本用法,以及其在eShopOnContainers中的運用。Envoy作為一個比肩Nginx的服務代理,其特性在Service Mesh中有著靈活的運用。本文就講到這裡了,下次有機會在和大家分享下Envoy在Service Mesh中的應用。 >參考資料: >1. [Envoy 介紹 - Envoy 中文指南]( https://fuckcloudnative.io/envoy-handbook/docs/overview/overview/) > 2. [Build an API Gateway with Envoy and use with .NET Core APIs](https://www.youtube.com/watch?v=UsoH5cqE1OA)