Gateway of. NET architecture through Envoy

What is Gateway

In the microservice architecture, if each microservice usually exposes a set of fine endpoints, there may be the following problems

  1. If there is no API gateway mode, the client application will be coupled with the internal microservice.
  2. In a client application, a single page / screen may need to call multiple services multiple times.  
  3. Without a gateway, all microservices must be exposed to the "outside world".
  4. Every publicly published microservice must deal with issues such as authorization and SSL.

The Gateway can provide a single entry point for the micro service group, and the API Gateway is located between the client application and the micro service. It acts as a reverse proxy, routing requests from clients to services. It can also provide other cross domain capabilities, such as authentication, SSL termination, and caching.

What is Envoy

Envoy is an L7 agent and communication bus designed for large-scale modern SOA (Service-Oriented Architecture). It has the following advantages

  1. C++11, native code, high performance
  2. L3/L4 filter architecture, such as TCP proxy
  3. HTTP L7 filter architecture, cache, speed limit, routing / forwarding
  4. Top HTTP2 and GRPC support
  5. Service discovery and dynamic configuration
  6. health examination
  7. Advanced load balancing

We can implement API Gateway with envoy. Envoy organizes gateway information through yaml configuration files. Let's talk about the core concepts in envoy

Listener

A named network address that can be connected by downstream clients. Its configuration style is as follows:

static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address:
        protocol: TCP
        address: 0.0.0.0
        port_value: 10000

This configuration indicates that Envoy listens on port 10000, and downstream clients can interact with Envoy through this port

L3/L4 Filter

L3/L4 Filter can help us realize functions such as HTTP connection management, speed limit, TCP proxy, etc. its configuration style is as follows:

    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
          scheme_header_transformation:
            scheme_to_overwrite: http
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_service
              domains: ["*"]
              routes:
              - match:
                  prefix: "/"
                route:
                  host_rewrite_literal: 192.168.43.94
                  cluster: service_envoyproxy_io
          http_filters:
          - name: envoy.filters.http.router

This configuration indicates that HTTP requests are accepted through the HttpConnectionManager filter and forwarded to the service through the router filter configuration_ envoyproxy_ IO this upstream cluster

Upstream Cluster

Envoy's cluster manager manages all configured upstream clusters to truly process the requests accepted by envoy. Its configuration style is as follows:

clusters:
  - name: service_envoyproxy_io
    connect_timeout: 30s
    type: strict_dns
    dns_lookup_family: V4_ONLY
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: service_envoyproxy_io
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: 192.168.43.94
                port_value: 5000

This configuration indicates that Envoy will forward the request to the address 192.168.43.94:5000.

The call logic is summarized as follows. The Listener accepts the request and gives it to the filter. After the filter is processed, it forwards the request to the upstream cluster according to the routing rules, and the endpoint in the upstream cluster will actually process the request.

Run Envoy

We run a default Envoy container through docker

docker run --rm -it -p 9901:9901 -p 10000:10000 envoyproxy/envoy-dev

visit http://localhost:10000/ , I found that he jumped to Envoy's official website

 

 

  We enter the container to check its configuration and find that it will eventually forward the request to www.envoyproxy.io

cat /etc/envoy/envoy.yaml
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: www.envoyproxy.io
                port_value: 443

Static file configuration

We now implement our own gateway through Envoy. Static file configuration is that we configure the configuration information in advance. The configuration content cannot be modified after Envoy is started

Preparation services

We prepare two. Net web APIs, server1 and server2, where we create NameController and create a new Get method

Server1

      [HttpGet]
        public string Get()
        {
            _logger.LogInformation("call server1");
            var req = Request;
            return "server1";
        }

Server2

        [HttpGet]
        public string Get()
        {
            _logger.LogInformation("call server2");
            var req = Request;
            return "server2";
        }

Specify the startup port of server1 as 5000 and the startup port of server2 as 5001

Server1

webBuilder.UseUrls("http://*:5555").UseStartup<Startup>();

Server2

webBuilder.UseUrls("http://*:5001/").UseStartup<Startup>();

We start Server1 and Server2

Prepare Envoy configuration

We take the default Envoy configuration file from the container and modify it as follows

admin:
  address:
    socket_address:
      protocol: TCP
      address: 0.0.0.0
      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
          scheme_header_transformation:
            scheme_to_overwrite: http
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_service
              domains: ["*"]
              routes:
              - match:
                  prefix: "/"
                route:
                  host_rewrite_literal: 192.168.43.94
                  cluster: service_envoyproxy_io
          http_filters:
          - name: envoy.filters.http.router
  clusters:
  - name: service_envoyproxy_io
    connect_timeout: 30s
    type: static
    # Comment out the following line to test on v6 networks
    dns_lookup_family: V4_ONLY
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: service_envoyproxy_io
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: 192.168.43.94
                port_value: 5000
        - endpoint:
            address:
              socket_address:
                address: 192.168.43.94
                port_value: 5001

We start Envoy and verify that the configuration is correct

docker run --rm -it -p 9901:9901 -p 10000:10000 -v D:/gateway/envoy/config/static/envoy.yaml:/etc/envoy/envoy.yaml -v D:/gateway/envoy/logs:/logs envoyproxy/envoy-dev  -c /etc/envoy/envoy.yaml --log-path logs/custom.log

Call the api and find that it implements the load

http://localhost:10000/Name

Dynamic file configuration

Dynamic files can help us realize that Envoy will automatically update its configuration when the file changes.

Modify the static file and extract the cluster into the cds.yaml file

resources:
- "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
  name: example_proxy_cluster
  type: STRICT_DNS
  typed_extension_protocol_options:
    envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
      "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
      explicit_http_config:
        http_protocol_options: {}
  load_assignment:
    cluster_name: example_proxy_cluster
    endpoints:
    - lb_endpoints:
      - endpoint:
          address:
            socket_address:
              address: 192.168.43.94
              port_value: 5000

Extract the listener into the lds.yaml file

resources:
- "@type": type.googleapis.com/envoy.config.listener.v3.Listener
  name: listener_0
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 10000
  filter_chains:
  - filters:
    - name: envoy.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.router
        route_config:
          name: local_route
          virtual_hosts:
          - name: local_service
            domains:
            - "*"
            routes:
            - match:
                prefix: "/envoyapi/"
              route:
                prefix_rewrite: "/"
                host_rewrite_literal: 192.168.43.94
                cluster: example_proxy_cluster

Modify envoy.yaml to reference lds.yaml and cds.yaml files

admin:
  address:
    socket_address:
      protocol: TCP
      address: 0.0.0.0
      port_value: 9902
node:
  cluster: test-cluster
  id: test-id
dynamic_resources:
  cds_config:
    path: /etc/envoy/cds.yaml
  lds_config:
    path: /etc/envoy/lds.yaml

Start Envoy

docker run --rm -it -p 9902:9902 -p 10000:10000 -v D:/gateway/envoy/config/dynamic/:/etc/envoy/ -v D:/gateway/envoy/logs:/logs envoyproxy/envoy-dev  -c /etc/envoy/envoy.yaml --log-path logs/custom.log

Call api and find that the call is successful

http://localhost:10000/envoyapi/Name

Modify dynamic file configuration

Modify cds.yaml and set the endpoint port to 5001

resources:
- "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
  name: example_proxy_cluster
  type: STRICT_DNS
  typed_extension_protocol_options:
    envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
      "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
      explicit_http_config:
        http_protocol_options: {}
  load_assignment:
    cluster_name: example_proxy_cluster
    endpoints:
    - lb_endpoints:
      - endpoint:
          address:
            socket_address:
              address: 192.168.43.94
              port_value: 5001

Enter the container to force the update of the file

# cd /etc/envoy
# mv cds.yaml tmp
# mv tmp cds.yaml

Call the api and find that the dynamic update of configuration information is realized without restarting Envoy

 

So far, we have implemented a gateway to proxy our. NET program through Envoy's static configuration and file dynamic configuration

Tags: gateway

Posted on Thu, 28 Oct 2021 18:56:02 -0400 by dizandat