eShopOnContainers 知多少[12]:Envoy gateways

微服务知多少

共 11637字,需浏览 24分钟

 ·

2020-10-20 04:04

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.10.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 : 集群定义了流量的目标端点,同时还包括一些其他可选配置,如负载均衡策略等。

整体流程如下图所示:

图片来源:https://fuckcloudnative.io/envoy-handbook/docs/gettingstarted/quick-start/

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。

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,服务整体通信架构如下图所示:

有两点需要补充说明:

  1. Linkerd是一种Service Mesh,其核心思想是借助Sidecar模式无侵入式对应用进行服务治理,包括服务发现、流量管理、负载均衡、路由等。
  2. 了解过Istio(目前比较流行的Service Mesh)应该知道,Envoy在Istio中作为Sidecar而存在,而在eShopOnContainers中Envoy被充当API Gateways。

基于上面的基础,再来看eShopOnContainers中的配置,其实就很明白了,主要是配置文件从Ocelot 转变到envoy.yaml,配置如下图所示。

路由配置如下:

  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 网关了。

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中的应用。

参考资料:

  1. Envoy 介绍 - Envoy 中文指南
  2. Build an API Gateway with Envoy and use with .NET Core APIs


浏览 60
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报