Envoy’s listener and cluster configuration isn’t just a list of rules; it’s a dynamic, programmable network fabric that can adapt to changing conditions in real-time.
Let’s watch Envoy in action. Imagine we have a simple setup: a front-end web application running on 10.0.0.1:8080 and a back-end API on 10.0.0.2:9000. We want Envoy to act as a reverse proxy, directing incoming traffic to the web app, which in turn calls the API.
Here’s a snippet of a basic Envoy configuration (envoy.yaml):
static_resources:
listeners:
- name: listener_0
address:
socket_address:
address: 0.0.0.0
port_value: 80
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:
cluster: web_app_cluster
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
clusters:
- name: web_app_cluster
connect_timeout: 0.25s
type: LOGICAL_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: web_app_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 10.0.0.1
port_value: 8080
- name: api_cluster
connect_timeout: 0.25s
type: STATIC
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: api_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 10.0.0.2
port_value: 9000
When a request hits Envoy on port 80, the listener_0 intercepts it. The http_connection_manager filter, acting as the first layer, inspects the HTTP request. Based on the route_config, it matches the incoming request (in this case, any request due to domains: ["*"] and prefix: "/"). The router then forwards the request to the cluster named web_app_cluster.
The web_app_cluster is configured to resolve LOGICAL_DNS to 10.0.0.1:8080. Envoy establishes a connection to this address. If the web app needs to call the API, it would typically do so with a request that Envoy’s web_app_cluster would then route to the api_cluster (though this isn’t explicitly shown in this simplified example, a more complex route_config would handle this). The api_cluster is STATIC, meaning its endpoint 10.0.0.2:9000 is hardcoded.
This setup, while basic, demonstrates the core components:
- Listeners: The entry points for network traffic. They define the address and port Envoy listens on and the initial set of filters to apply. In our example,
listener_0listens on0.0.0.0:80. - Filter Chains: A sequence of network filters that process traffic arriving at a listener. The
http_connection_manageris a powerful filter that handles HTTP-specific logic, including routing. - Route Configuration: Within the
http_connection_manager, this defines how requests are routed. It usesvirtual_hosts(matching based on hostnames) androutes(matching based on URL paths or other request properties) to direct traffic to specific clusters. - Clusters: A logical group of upstream endpoints that Envoy can send traffic to. Each cluster has a name (e.g.,
web_app_cluster), a load balancing policy (ROUND_ROBIN), and a mechanism to discover endpoints.LOGICAL_DNSmeans Envoy will resolve the hostname dynamically, whileSTATICmeans the endpoint is fixed. - Endpoints: The actual network addresses of the upstream services.
The truly remarkable aspect is that these configurations don’t need to be reloaded by restarting Envoy. This is where xDS (Discovery Service) comes in. Instead of embedding static configurations, Envoy can dynamically fetch its listeners, clusters, routes, and endpoints from management servers. This allows for near real-time updates to the proxy’s behavior without service disruption. You can add new services, change routing rules, or update endpoints on the fly.
The most surprising thing about xDS is how it enables Envoy to act not just as a proxy, but as a programmable data plane for a service mesh. It decouples the network configuration from the application itself, allowing infrastructure operators to manage network policies, traffic shaping, and resilience strategies centrally. This means you can change how traffic flows between thousands of services by updating a few configuration resources on a management server, and Envoy proxies across your fleet will pick up those changes automatically.
The connect_timeout of 0.25s on a cluster dictates how long Envoy will wait for a connection to an upstream host to be established. If this timeout is exceeded, Envoy will mark the endpoint as unhealthy for a period (defined by outlier detection settings, if configured) and try another endpoint in the cluster, if available. This is crucial for maintaining application availability even when individual upstream instances are slow to respond.
When a request is made to http://<envoy-ip>/some/path, Envoy’s listener_0 accepts it. The http_connection_manager looks at its route_config. It checks virtual_hosts for a match on the incoming request’s host header. If a match is found (e.g., domains: ["*"]), it then checks the routes within that virtual host. The prefix: "/" route is a catch-all. This route points to cluster: web_app_cluster. Envoy then consults the configuration for web_app_cluster. If it’s LOGICAL_DNS, it resolves the hostname. If STATIC, it uses the provided IP. It then attempts to establish a connection to the resolved endpoint (10.0.0.1:8080 in this case) and forwards the request.
The next concept to explore is how Envoy handles TLS termination and origination, and the role of CDS (Cluster Discovery Service) in dynamically managing upstream services.