What is Gateway
In the microservice architecture, if each microservice usually exposes a set of fine endpoints, there may be the following problems
- If there is no API gateway mode, the client application will be coupled with the internal microservice.
- In a client application, a single page / screen may need to call multiple services multiple times.
- Without a gateway, all microservices must be exposed to the "outside world".
- 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
- C++11, native code, high performance
- L3/L4 filter architecture, such as TCP proxy
- HTTP L7 filter architecture, cache, speed limit, routing / forwarding
- Top HTTP2 and GRPC support
- Service discovery and dynamic configuration
- health examination
- 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