Envoy is an open source edge and service agent designed for cloud-native applications, and the default data plane for Istio Service Mesh. In this article, we introduce the basic use of Envoy with a simple example.

1. Configuration

Creating a proxy configuration

Envoy uses YAML configuration files to control the behavior of the proxy. In the following steps we will build the configuration using a static configuration interface, which means that all settings are predefined in the configuration file. In addition Envoy also supports dynamic configurations, so that settings can be automatically discovered by some external source.

Resources

The first line of the Envoy configuration defines the interface configuration being used, where we will be configuring the static API, so the first line should be static_resources.

1
static_resources:

Listeners

Listeners are defined at the beginning of the configuration. Listeners are the network configurations that Envoy listens to for requests, such as IP addresses and ports. Here we have Envoy running inside a Docker container, so it needs to listen on IP address 0.0.0.0, in which case Envoy will listen on port 10000.

Here is the configuration to define the listener.

1
2
3
4
5
static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address: { address: 0.0.0.0, port_value: 10000 }

Filters

Listening to incoming traffic through Envoy, the next step is to define how to handle these requests. Each listener has a set of filters, and different listeners can have a different set of filters.

In our example, we proxy all traffic to baidu.com and once configured we should be able to see the Baidu homepage directly by requesting Envoy’s endpoint without having to change the URL address.

Filters are defined via filter_chains, and the purpose of each filter is to find a match for the incoming request to match the destination address with.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address: { address: 0.0.0.0, port_value: 10000 }

    filter_chains:
    - filters:
      - name: envoy.http_connection_manager
        config:
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_service
              domains: ["*"]
              routes:
              - match: { prefix: "/" }
                route: { host_rewrite: www.baidu.com, cluster: service_baidu }
          http_filters:
          - name: envoy.router

This filter uses envoy.http_connection_manager, which is a built-in filter designed for HTTP connections:

  • stat_prefix : A prefix to be used when issuing statistics for the connection manager.
  • route_config : A route configuration that checks the route if the virtual host matches. In our configuration here, route_config matches all incoming HTTP requests, regardless of the request’s host domain.
  • routes: If the URL prefix matches, then a set of routing rules defines what will happen next. / means match the root route.
  • host_rewrite : Changes the inbound Host header information for HTTP requests.
  • cluster : The name of the cluster where the request will be processed, with the corresponding implementation below.
  • http_filters : This filter allows Envoy to adapt and modify the request as it is processed.

Clustering

When a request matches a filter, the request will be passed to the cluster. The following configuration defines the host as the baidu.com domain for accessing HTTPS, and if multiple hosts are defined, Envoy will implement a Round Robin policy. The configuration is shown below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address: { address: 0.0.0.0, port_value: 10000 }

    filter_chains:
    - filters:
      - name: envoy.http_connection_manager
        config:
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_service
              domains: ["*"]
              routes:
              - match: { prefix: "/" }
                route: { host_rewrite: www.baidu.com, cluster: service_baidu }
          http_filters:
          - name: envoy.router

  clusters:
  - name: service_baidu
    connect_timeout: 0.25s
    type: LOGICAL_DNS
    dns_lookup_family: V4_ONLY
    lb_policy: ROUND_ROBIN
    hosts: [{ socket_address: { address: www.baidu.com, port_value: 443 }}]
    tls_context: { sni: baidu.com }

Administration

Finally, an administration module needs to be configured.

1
2
3
4
admin:
  access_log_path: /tmp/admin_access.log
  address:
    socket_address: { address: 0.0.0.0, port_value: 9901 }

The above configuration defines a static configuration template for Envoy, the listener defines the port and IP address of Envoy, the listener has a set of filters to match incoming requests, and after matching the request, forwards the request to the cluster.

2. Turn on the proxy

After the configuration is done, you can start Envoy via Docker container by mounting the above configuration file via Volume to the /etc/envoy/envoy.yaml file in the container.

Then start the Envoy container bound to port 80 using the following command.

1
2
3
4
$ docker run --name=envoy -d \
  -p 80:10000 \
  -v $(pwd)/manifests/1.getting-started/envoy.yaml:/etc/envoy/envoy.yaml \
  envoyproxy/envoy:latest

Once started, we can access the application curl localhost on port 80 locally to test the success of the proxy. We can also check by accessing localhost in our local browser to see if.

You can see that the request is being proxied to baidu.com, and you should also see that the URL address has not changed, it is still localhost.

3. Management View

Envoy provides an administrative view that allows us to view configuration, statistics, logs and other data inside Envoy.

We can configure admin by adding additional resource definitions, where we can also define the port for the admin view, but we need to be careful that the port does not conflict with other listener configurations.

1
2
3
4
admin:
  access_log_path: /tmp/admin_access.log
  address:
    socket_address: { address: 0.0.0.0, port_value: 9901 }

Of course, we can also expose the management port to external users through the Docker container. The above configuration will expose the admin page to external users, of course we only use it here for demonstration is OK, if you are used for online environment also need to do some security measures, you can check Envoy’s related documentation for more security configuration.

To expose the administration page to external users as well, we use the following command to run another container.

1
2
3
4
5
$ docker run --name=envoy-with-admin -d \
    -p 9901:9901 \
    -p 10000:10000 \
    -v $(pwd)/manifests/1.getting-started/envoy.yaml:/etc/envoy/envoy.yaml \
    envoyproxy/envoy:latest

After running successfully, we can now access the Envoy administration page by typing localhost:9901 in our browser: localhost:9901.

It is important to note that the current administration page not only allows to perform some destructive operations (e.g., shutting down services), but also may expose some private information (e.g., statistics, cluster names, certificate information, etc.). Therefore, access to the administration page should be allowed only through the secure network.

Of course there are many more uses for Envoy, and this article is just the easiest way to get started, so we’ll dive in later.