Envoy’s HTTP Connection Manager doesn’t route traffic in the traditional sense; it directs it based on rules you define.
Let’s watch it in action. Imagine we have two backend services: service-a on port 8080 and service-b on 8081. We want Envoy to send requests to /a/* to service-a and requests to /b/* to service-b.
Here’s a snippet of an Envoy configuration that achieves this:
static_resources:
listeners:
- name: listener_0
address:
socket_address:
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
codec_type: AUTO
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match:
prefix: "/a/"
route:
cluster: service_a
- match:
prefix: "/b/"
route:
cluster: service_b
http_filters:
- name: envoy.filters.http.router
typed_config: {}
clusters:
- name: service_a
connect_timeout: 0.25s
type: LOGICAL_DNS
lb_policy: ROUND_ROBIN
dns_lookup_family: V4_ONLY
load_assignment:
cluster_name: service_a
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 127.0.0.1
port_value: 8080
- name: service_b
connect_timeout: 0.25s
type: LOGICAL_DNS
lb_policy: ROUND_ROBIN
dns_lookup_family: V4_ONLY
load_assignment:
cluster_name: service_b
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 127.0.0.1
port_value: 8081
When a request arrives at port 10000, the http_connection_manager filter intercepts it. It then consults its route_config. Within route_config, it looks at virtual_hosts. Here, we have one virtual host that matches any incoming domain (domains: ["*"]). Inside this virtual host, it checks the routes.
The first route has a match of prefix: "/a/". If the request URI starts with /a/, Envoy will route it to the cluster named service_a. If it doesn’t match the first route, it checks the next one. The second route has a match of prefix: "/b/". If the request URI starts with /b/, it’s routed to the cluster named service_b.
The clusters section defines where Envoy can send traffic. service_a is configured to send traffic to 127.0.0.1:8080, and service_b to 127.0.0.1:8081. The http_filters list ensures that the router filter is enabled, which is the filter responsible for actually performing the upstream connection and forwarding the request based on the routing decisions made by the http_connection_manager.
The core of routing in Envoy’s HTTP Connection Manager lies in the route_config, specifically the virtual_hosts and routes sections. Each route can have various matchers, not just prefixes. You can match by exact path, regular expressions, or even HTTP headers. This flexibility allows for sophisticated traffic splitting, A/B testing, and canary deployments by defining multiple routes within a virtual host, often with different weights or conditions. The route also specifies the cluster to send the traffic to, which is a logical group of upstream endpoints.
One critical aspect often overlooked is the order of routes within a virtual_host. Envoy evaluates routes sequentially from top to bottom. The first route that matches the incoming request is used, and no further routes are considered. This means more specific routes should generally be placed before more general ones to ensure they are matched correctly. For example, a route matching an exact path like /a/specific should come before a route matching the prefix /a/.
The journey doesn’t end with routing; the router filter is what executes the actual upstream request. It takes the cluster name determined by the http_connection_manager and initiates a connection to one of the endpoints within that cluster, respecting load balancing policies.
The next logical step after mastering basic routing is to explore advanced routing features like weighted routing for traffic shifting and header-based routing for fine-grained control.