Define API gateway routes on virtual machines
This topic describes how to configure HTTP and TCP routes and attach them to Consul API gateway listeners. Routes are rule-based configurations that allow external clients to send requests to services in the mesh.
Overview
The following steps describe the general workflow for defining and deploying routes:
- Define routes in an HTTP or TCP configuration entry. The configuration entry includes rules for routing requests, target services in the mesh for the traffic, and the name of the gateway to attach to.
- Deploy the configuration entry to create the routes and attach them to the gateway.
Routes and the gateways they are attached to are eventually-consistent objects. They provide feedback about their current state through a series of status conditions. As a result, you must manually check the route status to determine if the route is bound to the gateway successfully.
Requirements
The following requirements must be satisfied to use API gateways on VMs:
- Consul 1.15 or later
- A Consul cluster with service mesh enabled. Refer to
connect - Network connectivity between the machine deploying the API Gateway and a Consul cluster agent or server
ACL requirements
If ACLs are enabled, you must present a token with the following permissions to configure Consul and deploy API gateway routes:
Refer Mesh Rules for additional information about configuring policies that enable you to interact with Consul API gateway configurations.
Define the routes
Define route configurations and bind them to listeners configured on the gateway so that Consul can route incoming requests to services in the mesh.
Create a route configuration entry file and specify the following settings:
Kind: Set tohttportcp.Name: Specify a name for the route. The name is metadata that you can use to reference the configuration when performing Consul operations.Parents: Specifies a list of API gateways that the route binds to.Rules: If you are configuring HTTP routes, define a list of routing rules for constructing a routing table that maps listeners to services. Each member of the list is a map that may containing the following fields:
Refer to the HTTP route configuration entry and TCP route configuration entry reference for details about configuring routes.
Configure any additional fields necessary for your use case, such as the namespace or admin partition.
Save the configuration.
The following example routes requests from the listener on the API gateway at port 8443 to services in Consul based on the path of the request. When an incoming request starts at path /, Consul forwards 90 percent of the requests to the ui service and 10 percent to experimental-ui. Consul also forwards requests starting with /api to api.
Kind = "http-route"
Name = "my-http-route"
// Rules define how requests will be routed
Rules = [
// Send all requests to UI services with 10% going to the "experimental" UI
{
Matches = [
{
Path = {
Match = "prefix"
Value = "/"
}
}
]
Services = [
{
Name = "ui"
Weight = 90
},
{
Name = "experimental-ui"
Weight = 10
}
]
},
// Send all requests that start with the path `/api` to the API service
{
Matches = [
{
Path = {
Match = "prefix"
Value = "/api"
}
}
]
Services = [
{
Name = "api"
}
]
}
]
Parents = [
{
Kind = "api-gateway"
Name = "my-gateway"
SectionName = "my-http-listener"
}
]
Route to service subsets
When an http-route sends traffic to a single backend service, Consul API Gateway also evaluates the backend service's discovery chain. When the backend service has a service-router that routes matching requests to a service-resolver subset, the API gateway preserves that subset routing when it builds the gateway discovery chain.
You can expose an HTTP route at the gateway while using Consul service mesh traffic management, such as routing a subset of requests to a canary service version. Consul applies the http-route match and the service-router match. For example, a request must match the gateway route and the service-router rule before Consul routes it to the subset destination.
The following example routes requests with the x-version: canary header to the canary subset of the api service. All other requests that match the route use the stable default subset.
Kind = "service-resolver"
Name = "api"
DefaultSubset = "stable"
Subsets = {
stable = {
Filter = "Service.Meta.version == stable"
}
canary = {
Filter = "Service.Meta.version == canary"
}
}
Kind = "service-router"
Name = "api"
Routes = [
{
Match {
HTTP {
Header = [
{
Name = "x-version"
Exact = "canary"
},
]
}
}
Destination {
Service = "api"
ServiceSubset = "canary"
}
},
]
Kind = "http-route"
Name = "api-route"
Rules = [
{
Matches = [
{
Path = {
Match = "prefix"
Value = "/api"
}
}
]
Services = [
{
Name = "api"
}
]
}
]
Parents = [
{
Kind = "api-gateway"
Name = "my-gateway"
SectionName = "my-http-listener"
}
]
TLS SDS service overrides
Route services can override listener/gateway default certificates by setting TLS.SDS.
HTTP route service override
Use Rules[].Services[].TLS.SDS in http-route.
CertResourceis required whenSDSis set.ClusterNameis optional and inherited from effective listener/gateway SDS cluster when omitted.Hostnamesmust be set on the HTTP route when using serviceTLS.SDSoverrides.
Kind = "http-route"
Name = "api-route"
Parents = [
{
Kind = "api-gateway"
Name = "my-gateway"
SectionName = "my-http-listener"
}
]
Hostnames = ["foo.example.com", "bar.example.com"]
Rules = [
{
Services = [
{
Name = "svc-foo"
TLS = {
SDS = {
CertResource = "foo.example.com"
}
}
},
{
Name = "svc-bar"
}
]
}
]
TCP route service override
Use Services[].TLS.SDS in tcp-route.
CertResourceis required whenSDSis set.ClusterNameis optional and inherited from effective listener/gateway SDS cluster when omitted.- TCP routes currently support at most one service.
- A TCP listener can only have one distinct effective TCP SDS override; conflicting overrides are rejected.
Kind = "tcp-route"
Name = "my-tcp-route"
Parents = [
{
Kind = "api-gateway"
Name = "my-gateway"
SectionName = "my-tcp-listener"
}
]
Services = [
{
Name = "payments"
TLS = {
SDS = {
CertResource = "payments.internal.example.com"
}
}
}
]
Effective precedence
Certificate selection precedence is:
- Route service
TLS.SDS - Listener
TLS.SDS - Gateway top-level
TLS.SDS
Configure passive health checks for routed services
Passive health checks configure Envoy outlier detection on the API gateway. When an instance of a routed service returns consecutive errors, the gateway removes the instance from its load balancing pool for a period of time and sends requests to the remaining healthy instances.
Passive health checks require Consul v2.1 or later. You can configure passive health checks in the following locations:
- Gateway-wide default:
Defaults.PassiveHealthCheckin theapi-gatewayconfiguration entry. The default applies to every service that routes attached to the gateway target. - Service-level override:
Limits.PassiveHealthCheckon a destination service in anhttp-routeortcp-routeconfiguration entry.
When you configure both, the service-level passive health check replaces the gateway default for that service. Consul does not merge individual fields. When multiple routes on the same gateway target the same service, configure the same passive health check on each route because Consul applies only one of them to the service.
Define a gateway-wide default
The following example configures a default passive health check on the api-gateway gateway:
Kind = "api-gateway"
Name = "api-gateway"
Listeners = [
{
Name = "listener-one"
Port = 9999
Protocol = "http"
}
]
Defaults = {
PassiveHealthCheck = {
Interval = "10s"
MaxFailures = 3
EnforcingConsecutive5xx = 80
MaxEjectionPercent = 25
BaseEjectionTime = "30s"
}
}
Override the default for a service
The following example routes /s1 requests to the s1 service, which inherits the gateway default, and routes /s2 requests to the s2 service, which uses its own passive health check:
Kind = "http-route"
Name = "api-gateway-route"
Parents = [
{
Kind = "api-gateway"
Name = "api-gateway"
SectionName = "listener-one"
}
]
Rules = [
{
Matches = [
{
Path = {
Match = "prefix"
Value = "/s1"
}
}
]
Services = [
{
Name = "s1"
}
]
},
{
Matches = [
{
Path = {
Match = "prefix"
Value = "/s2"
}
}
]
Services = [
{
Name = "s2"
Limits = {
PassiveHealthCheck = {
Interval = "20s"
MaxFailures = 5
EnforcingConsecutive5xx = 60
MaxEjectionPercent = 50
BaseEjectionTime = "60s"
}
}
}
]
}
]
You can configure upstream circuit-breaker limits, such as MaxConnections, in the same Defaults and Limits blocks. Unlike passive health checks, Consul applies service-level circuit-breaker limits field by field, and inherits any unset limit from the gateway default.
Verify the passive health check
Complete the following steps to verify that the gateway applies the passive health check:
Read the configuration entries to confirm that Consul stored the passive health check settings:
$ consul config read -kind api-gateway -name api-gateway $ consul config read -kind http-route -name api-gateway-routeOn the machine that runs the API gateway, query the Envoy admin interface for the outlier detection configuration of the
s2cluster. By default, the Envoy admin interface listens onlocalhost:19000.$ curl --silent localhost:19000/config_dump \ | jq '.configs[] | select(."@type" | contains("ClustersConfigDump")) | .dynamic_active_clusters[] | select(.cluster.name | startswith("s2.")) | .cluster.outlier_detection'The output contains the values from the route's passive health check:
{ "consecutive_5xx": 5, "interval": "20s", "base_ejection_time": "60s", "max_ejection_percent": 50, "enforcing_consecutive_5xx": 60 }Run the same command for the
s1cluster to verify that it uses the gateway default values.
Route requests with inverted header matches
Set Invert to true on a header match in an http-route to negate the condition. Inverted header matches let you route requests when a header is absent or when a header value does not equal a specific value. For example, you can send requests without a canary header to a stable service and requests with the header to a canary service.
Inverted header matches require Consul v2.1 or later. Refer to Rules[].Matches[].Headers[].Invert in the HTTP route configuration reference for how Invert affects each match type.
The following example routes requests that do not include the x-canary header to the s2 service, and routes requests that include the header to the s1 service:
Kind = "http-route"
Name = "canary-route"
Parents = [
{
Kind = "api-gateway"
Name = "api-gateway"
SectionName = "listener-one"
}
]
Rules = [
{
Matches = [
{
Headers = [
{
Name = "x-canary"
Match = "present"
Invert = true
}
]
}
]
Services = [
{
Name = "s2"
}
]
},
{
Matches = [
{
Headers = [
{
Name = "x-canary"
Match = "present"
}
]
}
]
Services = [
{
Name = "s1"
}
]
}
]
The following example routes requests to the service-v2 service when the x-version header is present and its value is not v1:
Kind = "http-route"
Name = "version-route"
Parents = [
{
Kind = "api-gateway"
Name = "api-gateway"
SectionName = "listener-one"
}
]
Rules = [
{
Matches = [
{
Headers = [
{
Name = "x-version"
Match = "exact"
Value = "v1"
Invert = true
}
]
}
]
Services = [
{
Name = "service-v2"
}
]
}
]
For exact, prefix, suffix, and regex matches, an inverted condition does not match requests that do not include the header. To match requests that do not include a header, use the present match type with Invert = true.
Verify inverted header matches
Complete the following steps to verify that the gateway applies the inverted header match:
Read the route configuration entry and confirm that the header match includes
"Invert": true. Confirm that the route status shows that the route is bound to the gateway.$ consul config read -kind http-route -name canary-routeOn the machine that runs the API gateway, query the Envoy admin interface for the route configuration. By default, the Envoy admin interface listens on
localhost:19000. The header matcher for the inverted condition includes"invert_match": true.$ curl --silent localhost:19000/config_dump \ | jq '.configs[] | select(."@type" | contains("RoutesConfigDump")) | .dynamic_route_configs[].route_config.virtual_hosts[].routes[].match.headers'Send requests with and without the header. Replace
GATEWAY_ADDRESSwith the address of the API gateway listener. The first request routes to thes2service, and the second request routes to thes1service.$ curl http://GATEWAY_ADDRESS:9999/ $ curl --header "x-canary: true" http://GATEWAY_ADDRESS:9999/
Deploy the route configuration
Run the consul config write command to attach the routes to the specified gateways. The following example writes a configuration called my-http-route.hcl:
$ consul config write my-http-route.hcl