669 lines
21 KiB
Plaintext
669 lines
21 KiB
Plaintext
---
|
|
layout: docs
|
|
page_title: 'Configuration Entry Kind: Service Router'
|
|
description: >-
|
|
The service-router config entry kind controls Connect traffic routing and
|
|
manipulation at networking layer 7 (e.g. HTTP).
|
|
---
|
|
|
|
# Service Router
|
|
|
|
-> **v1.8.4+:** On Kubernetes, the `ServiceRouter` custom resource is supported in Consul versions 1.8.4+.<br />
|
|
**v1.6.0+:** On other platforms, this config entry is supported in Consul versions 1.6.0+.
|
|
|
|
The `service-router` config entry kind (`ServiceRouter` on Kubernetes) controls Connect traffic routing and
|
|
manipulation at networking layer 7 (e.g. HTTP).
|
|
|
|
If a router is not explicitly configured or is configured with no routes then
|
|
the system behaves as if a router were configured sending all traffic to a
|
|
service of the same name.
|
|
|
|
## Interaction with other Config Entries
|
|
|
|
- Service router config entries are a component of [L7 Traffic
|
|
Management](/docs/connect/l7-traffic-management).
|
|
|
|
- Service router config entries are restricted to only services that define
|
|
their protocol as HTTP-based via a corresponding
|
|
[`service-defaults`](/docs/connect/config-entries/service-defaults) config
|
|
entry or globally via
|
|
[`proxy-defaults`](/docs/connect/config-entries/proxy-defaults) .
|
|
|
|
- Any route destination that omits the `ServiceSubset` field is eligible for
|
|
splitting via a
|
|
[`service-splitter`](/docs/connect/config-entries/service-splitter) should
|
|
one be configured for that service, otherwise resolution proceeds according
|
|
to any configured
|
|
[`service-resolver`](/docs/connect/config-entries/service-resolver).
|
|
|
|
## Sample Config Entries
|
|
|
|
### Path prefix matching
|
|
|
|
Route HTTP requests with a path starting with `/admin` to a different service:
|
|
|
|
<CodeTabs tabs={[ "HCL", "Kubernetes YAML", "JSON" ]}>
|
|
|
|
```hcl
|
|
Kind = "service-router"
|
|
Name = "web"
|
|
Routes = [
|
|
{
|
|
Match {
|
|
HTTP {
|
|
PathPrefix = "/admin"
|
|
}
|
|
}
|
|
|
|
Destination {
|
|
Service = "admin"
|
|
}
|
|
},
|
|
# NOTE: a default catch-all will send unmatched traffic to "web"
|
|
]
|
|
```
|
|
|
|
```yaml
|
|
apiVersion: consul.hashicorp.com/v1alpha1
|
|
kind: ServiceRouter
|
|
metadata:
|
|
name: web
|
|
spec:
|
|
routes:
|
|
- match:
|
|
http:
|
|
pathPrefix: /admin
|
|
destination:
|
|
service: admin
|
|
# NOTE: a default catch-all will send unmatched traffic to "web"
|
|
```
|
|
|
|
```json
|
|
{
|
|
"Kind": "service-router",
|
|
"Name": "web",
|
|
"Routes": [
|
|
{
|
|
"Match": {
|
|
"HTTP": {
|
|
"PathPrefix": "/admin"
|
|
}
|
|
},
|
|
"Destination": {
|
|
"Service": "admin"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
</CodeTabs>
|
|
|
|
### Header/query parameter matching
|
|
|
|
Route HTTP requests with a special URL parameter or header to a canary subset:
|
|
|
|
<CodeTabs tabs={[ "HCL", "Kubernetes YAML", "JSON" ]}>
|
|
|
|
```hcl
|
|
Kind = "service-router"
|
|
Name = "web"
|
|
Routes = [
|
|
{
|
|
Match {
|
|
HTTP {
|
|
Header = [
|
|
{
|
|
Name = "x-debug"
|
|
Exact = "1"
|
|
},
|
|
]
|
|
}
|
|
}
|
|
Destination {
|
|
Service = "web"
|
|
ServiceSubset = "canary"
|
|
}
|
|
},
|
|
{
|
|
Match {
|
|
HTTP {
|
|
QueryParam = [
|
|
{
|
|
Name = "x-debug"
|
|
Exact = "1"
|
|
},
|
|
]
|
|
}
|
|
}
|
|
Destination {
|
|
Service = "web"
|
|
ServiceSubset = "canary"
|
|
}
|
|
},
|
|
# NOTE: a default catch-all will send unmatched traffic to "web"
|
|
]
|
|
```
|
|
|
|
```yaml
|
|
apiVersion: consul.hashicorp.com/v1alpha1
|
|
kind: ServiceRouter
|
|
metadata:
|
|
name: web
|
|
spec:
|
|
routes:
|
|
- match:
|
|
http:
|
|
header:
|
|
- name: x-debug
|
|
exact: '1'
|
|
destination:
|
|
service: web
|
|
serviceSubset: canary
|
|
- match:
|
|
http:
|
|
queryParam:
|
|
- name: x-debug
|
|
exact: '1'
|
|
destination:
|
|
service: web
|
|
serviceSubset: canary
|
|
# NOTE: a default catch-all will send unmatched traffic to "web"
|
|
```
|
|
|
|
```json
|
|
{
|
|
"Kind": "service-router",
|
|
"Name": "web",
|
|
"Routes": [
|
|
{
|
|
"Match": {
|
|
"HTTP": {
|
|
"Header": [
|
|
{
|
|
"Name": "x-debug",
|
|
"Exact": "1"
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"Destination": {
|
|
"Service": "web",
|
|
"ServiceSubset": "canary"
|
|
}
|
|
},
|
|
{
|
|
"Match": {
|
|
"HTTP": {
|
|
"QueryParam": [
|
|
{
|
|
"Name": "x-debug",
|
|
"Exact": "1"
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"Destination": {
|
|
"Service": "web",
|
|
"ServiceSubset": "canary"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
</CodeTabs>
|
|
|
|
### gRPC routing
|
|
|
|
Re-route a gRPC method to another service. Since gRPC method calls [are
|
|
HTTP/2](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md), we can use an HTTP path match rule to re-route traffic:
|
|
|
|
<CodeTabs tabs={[ "HCL", "Kubernetes YAML", "JSON" ]}>
|
|
|
|
```hcl
|
|
Kind = "service-router"
|
|
Name = "billing"
|
|
Routes = [
|
|
{
|
|
Match {
|
|
HTTP {
|
|
PathExact = "/mycompany.BillingService/GenerateInvoice"
|
|
}
|
|
}
|
|
|
|
Destination {
|
|
Service = "invoice-generator"
|
|
}
|
|
},
|
|
# NOTE: a default catch-all will send unmatched traffic to "billing"
|
|
]
|
|
```
|
|
|
|
```yaml
|
|
apiVersion: consul.hashicorp.com/v1alpha1
|
|
kind: ServiceRouter
|
|
metadata:
|
|
name: billing
|
|
spec:
|
|
routes:
|
|
- match:
|
|
http:
|
|
pathExact: /mycompany.BillingService/GenerateInvoice
|
|
destination:
|
|
service: invoice-generator
|
|
# NOTE: a default catch-all will send unmatched traffic to "billing"
|
|
```
|
|
|
|
```json
|
|
{
|
|
"Kind": "service-router",
|
|
"Name": "billing",
|
|
"Routes": [
|
|
{
|
|
"Match": {
|
|
"HTTP": {
|
|
"PathExact": "/mycompany.BillingService/GenerateInvoice"
|
|
}
|
|
},
|
|
"Destination": {
|
|
"Service": "invoice-generator"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
</CodeTabs>
|
|
|
|
## Available Fields
|
|
|
|
<ConfigEntryReference
|
|
keys={[
|
|
{
|
|
name: 'apiVersion',
|
|
description: 'Must be set to `consul.hashicorp.com/v1alpha1`',
|
|
hcl: false,
|
|
},
|
|
{
|
|
name: 'Kind',
|
|
description: {
|
|
hcl: 'Must be set to `service-router`',
|
|
yaml: 'Must be set to `ServiceRouter`',
|
|
},
|
|
},
|
|
{
|
|
name: 'Name',
|
|
description: 'Set to the name of the service being configured.',
|
|
type: 'string: <required>',
|
|
yaml: false,
|
|
},
|
|
{
|
|
name: 'Namespace',
|
|
type: `string: "default"`,
|
|
enterprise: true,
|
|
description: 'Specifies the namespace the config entry will apply to.',
|
|
yaml: false,
|
|
},
|
|
{
|
|
name: 'Meta',
|
|
type: 'map<string|string>: nil',
|
|
description:
|
|
'Specifies arbitrary KV metadata pairs. Added in Consul 1.8.4.',
|
|
yaml: false,
|
|
},
|
|
{
|
|
name: 'metadata',
|
|
children: [
|
|
{
|
|
name: 'name',
|
|
description: 'Set to the name of the service being configured.',
|
|
},
|
|
{
|
|
name: 'namespace',
|
|
description:
|
|
'If running Consul Open Source, the namespace is ignored (see [Kubernetes Namespaces in Consul OSS](/docs/k8s/crds#consul-oss)). If running Consul Enterprise see [Kubernetes Namespaces in Consul Enterprise](/docs/k8s/crds#consul-enterprise) for more details.',
|
|
},
|
|
],
|
|
hcl: false,
|
|
},
|
|
{
|
|
name: 'Routes',
|
|
type: 'array<ServiceRoute>',
|
|
description: `The list of routes to consider when
|
|
processing L7 requests. The first route to match in the list is terminal and
|
|
stops further evaluation. Traffic that fails to match any of the provided
|
|
routes will be routed to the default service.`,
|
|
children: [
|
|
{
|
|
name: 'Match',
|
|
type: 'ServiceRouteMatch: <optional>',
|
|
description: `A set of criteria that can
|
|
match incoming L7 requests. If empty or omitted it acts as a catch-all.`,
|
|
children: [
|
|
{
|
|
name: 'HTTP',
|
|
type: 'ServiceRouteHTTPMatch: <optional>',
|
|
description: `A set of [HTTP-specific match criteria](#serviceroutehttpmatch).`,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: 'Destination',
|
|
type: 'ServiceRouteDestination: <optional>',
|
|
description: `Controls [how to proxy](#serviceroutedestination) the matching request(s) to a service.`,
|
|
},
|
|
],
|
|
},
|
|
]}
|
|
/>
|
|
|
|
### `ServiceRouteHTTPMatch`
|
|
|
|
<ConfigEntryReference
|
|
topLevel={false}
|
|
keys={[
|
|
{
|
|
name: 'PathExact',
|
|
type: 'string: ""',
|
|
description: {
|
|
hcl:
|
|
'Exact path to match on the HTTP request path.<br><br> At most only one of `PathExact`, `PathPrefix`, or `PathRegex` may be configured.',
|
|
yaml:
|
|
'Exact path to match on the HTTP request path.<br><br> At most only one of `pathExact`, `pathPrefix`, or `pathRegex` may be configured.',
|
|
},
|
|
},
|
|
{
|
|
name: 'PathPrefix',
|
|
type: 'string: ""',
|
|
description: {
|
|
hcl:
|
|
'Path prefix to match on the HTTP request path.<br><br> At most only one of `PathExact`, `PathPrefix`, or `PathRegex` may be configured.',
|
|
yaml:
|
|
'Path prefix to match on the HTTP request path.<br><br> At most only one of `pathExact`, `pathPrefix`, or `pathRegex` may be configured.',
|
|
},
|
|
},
|
|
{
|
|
name: 'PathRegex',
|
|
type: 'string: ""',
|
|
description: {
|
|
hcl:
|
|
'Regular expression to match on the HTTP request path.<br><br>The syntax is [described below](#regular-expression-syntax).<br><br> At most only one of `PathExact`, `PathPrefix`, or `PathRegex` may be configured.',
|
|
yaml:
|
|
'Regular expression to match on the HTTP request path.<br><br>The syntax is [described below](#regular-expression-syntax).<br><br> At most only one of `pathExact`, `pathPrefix`, or `pathRegex` may be configured.',
|
|
},
|
|
},
|
|
{
|
|
name: 'Methods',
|
|
type: 'array<string>',
|
|
description:
|
|
'A list of HTTP methods for which this match applies. If unspecified all HTTP methods are matched. If provided the names must be a valid [method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods).',
|
|
},
|
|
{
|
|
name: 'Header',
|
|
type: 'array<ServiceRouteHTTPMatchHeader>)',
|
|
description:
|
|
'A set of criteria that can match on HTTP request headers. If more than one is configured all must match for the overall match to apply.',
|
|
children: [
|
|
{
|
|
name: 'Name',
|
|
type: 'string: <required>',
|
|
description: 'Name of the header to match.',
|
|
},
|
|
{
|
|
name: 'Present',
|
|
type: 'bool: false',
|
|
description: {
|
|
hcl:
|
|
'Match if the header with the given name is present with any value.<br><br> At most only one of `Exact`, `Prefix`, `Suffix`, `Regex`, or `Present` may be configured.',
|
|
yaml:
|
|
'Match if the header with the given name is present with any value.<br><br> At most only one of `exact`, `prefix`, `suffix`, `regex`, or `present` may be configured.',
|
|
},
|
|
},
|
|
{
|
|
name: 'Exact',
|
|
type: 'string: ""',
|
|
description: {
|
|
hcl:
|
|
'Match if the header with the given name is this value.<br><br> At most only one of `Exact`, `Prefix`, `Suffix`, `Regex`, or `Present` may be configured.',
|
|
yaml:
|
|
'Match if the header with the given name is this value.<br><br> At most only one of `exact`, `prefix`, `suffix`, `regex`, or `present` may be configured.',
|
|
},
|
|
},
|
|
{
|
|
name: 'Prefix',
|
|
type: 'string: ""',
|
|
description: {
|
|
hcl:
|
|
'Match if the header with the given name has this prefix.<br><br> At most only one of `Exact`, `Prefix`, `Suffix`, `Regex`, or `Present` may be configured.',
|
|
yaml:
|
|
'Match if the header with the given name has this prefix.<br><br> At most only one of `exact`, `prefix`, `suffix`, `regex`, or `present` may be configured.',
|
|
},
|
|
},
|
|
{
|
|
name: 'Suffix',
|
|
type: 'string: ""',
|
|
description: {
|
|
hcl:
|
|
'Match if the header with the given name has this suffix.<br><br> At most only one of `Exact`, `Prefix`, `Suffix`, `Regex`, or `Present` may be configured.',
|
|
yaml:
|
|
'Match if the header with the given name has this suffix.<br><br> At most only one of `exact`, `prefix`, `suffix`, `regex`, or `present` may be configured.',
|
|
},
|
|
},
|
|
{
|
|
name: 'Regex',
|
|
type: 'string: ""',
|
|
description: {
|
|
hcl:
|
|
'Match if the header with the given name matches this pattern.<br><br>The syntax is [described below](#regular-expression-syntax).<br><br> At most only one of `Exact`, `Prefix`, `Suffix`, `Regex`, or `Present` may be configured.',
|
|
yaml:
|
|
'Match if the header with the given name matches this pattern.<br><br>The syntax is [described below](#regular-expression-syntax).<br><br> At most only one of `exact`, `prefix`, `suffix`, `regex`, or `present` may be configured.',
|
|
},
|
|
},
|
|
{
|
|
name: 'Invert',
|
|
type: 'bool: false',
|
|
description: 'Inverts the logic of the match',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: 'QueryParam',
|
|
type: 'array<ServiceRouteHTTPMatchQueryParam>)',
|
|
description:
|
|
'A set of criteria that can match on HTTP query parameters. If more than one is configured all must match for the overall match to apply.',
|
|
children: [
|
|
{
|
|
name: 'Name',
|
|
type: 'string: <required>',
|
|
description: 'The name of the query parameter to match on.',
|
|
},
|
|
{
|
|
name: 'Present',
|
|
type: 'bool: false',
|
|
description: {
|
|
hcl:
|
|
'Match if the query parameter with the given name is present with any value.<br><br>At most only one of `Exact`, `Regex`, or `Present` may be configured.',
|
|
yaml:
|
|
'Match if the query parameter with the given name is present with any value.<br><br>At most only one of `exact`, `regex`, or `present` may be configured.',
|
|
},
|
|
},
|
|
{
|
|
name: 'Exact',
|
|
type: 'string: ""',
|
|
description: {
|
|
hcl:
|
|
'Match if the query parameter with the given name is this value.<br><br>At most only one of `Exact`, `Regex`, or `Present` may be configured.',
|
|
yaml:
|
|
'Match if the query parameter with the given name is this value.<br><br>At most only one of `exact`, `regex`, or `present` may be configured.',
|
|
},
|
|
},
|
|
{
|
|
name: 'Regex',
|
|
type: 'string: ""',
|
|
description: {
|
|
hcl:
|
|
'Match if the query parameter with the given name matches this pattern.<br><br>The syntax is [described below](#regular-expression-syntax).<br><br>At most only one of `Exact`, `Regex`, or `Present` may be configured.',
|
|
yaml:
|
|
'Match if the query parameter with the given name matches this pattern.<br><br>The syntax is [described below](#regular-expression-syntax).<br><br>At most only one of `exact`, `regex`, or `present` may be configured.',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
]}
|
|
/>
|
|
|
|
### `ServiceRouteDestination`
|
|
|
|
<ConfigEntryReference
|
|
topLevel={false}
|
|
keys={[
|
|
{
|
|
name: 'Service',
|
|
type: 'string: ""',
|
|
description:
|
|
'The service to resolve instead of the default service. If empty then the default service name is used.',
|
|
},
|
|
{
|
|
name: 'ServiceSubset',
|
|
type: 'string: ""',
|
|
description: {
|
|
hcl:
|
|
"A named subset of the given service to resolve instead of the one defined as that service's `DefaultSubset`. If empty, the default subset is used.",
|
|
yaml:
|
|
"A named subset of the given service to resolve instead of the one defined as that service's `defaultSubset`. If empty, the default subset is used.",
|
|
},
|
|
},
|
|
{
|
|
name: 'Namespace',
|
|
type: 'string: ""',
|
|
description:
|
|
'The Consul namespace to resolve the service from instead of the current namespace. If empty the current namespace is assumed.',
|
|
enterprise: true,
|
|
},
|
|
{
|
|
name: 'PrefixRewrite',
|
|
type: 'string: ""',
|
|
description: {
|
|
hcl:
|
|
'Defines how to rewrite the HTTP request path before proxying it to its final destination.<br><br> This requires that either `Match.HTTP.PathPrefix` or `Match.HTTP.PathExact` be configured on this route.',
|
|
yaml:
|
|
'Defines how to rewrite the HTTP request path before proxying it to its final destination.<br><br> This requires that either `match.http.pathPrefix` or `match.http.pathExact` be configured on this route.',
|
|
},
|
|
},
|
|
{
|
|
name: 'RequestTimeout',
|
|
type: 'duration: 0',
|
|
description:
|
|
'The total amount of time permitted for the entire downstream request (and retries) to be processed.',
|
|
},
|
|
{
|
|
name: 'NumRetries',
|
|
type: 'int: 0',
|
|
description:
|
|
'The number of times to retry the request when a retryable result occurs.',
|
|
},
|
|
{
|
|
name: 'RetryOnConnectFailure',
|
|
type: 'bool: false',
|
|
description: 'Allows for connection failure errors to trigger a retry.',
|
|
},
|
|
{
|
|
name: 'RetryOnStatusCodes',
|
|
type: 'array<int>',
|
|
description:
|
|
'A list of HTTP response status codes that are eligible for retry.',
|
|
},
|
|
{
|
|
yaml: false,
|
|
name: 'RequestHeaders',
|
|
type: 'HTTPHeaderModifiers: <optional>',
|
|
description: `A set of [HTTP-specific header modification rules](/docs/connect/config-entries/service-router#httpheadermodifiers)
|
|
that will be applied to requests routed to this service.
|
|
This cannot be used with a \`tcp\` listener.`,
|
|
},
|
|
{
|
|
yaml: false,
|
|
name: 'ResponseHeaders',
|
|
type: 'HTTPHeaderModifiers: <optional>',
|
|
description: `A set of [HTTP-specific header modification rules](/docs/connect/config-entries/service-router#httpheadermodifiers)
|
|
that will be applied to responses from this service.
|
|
This cannot be used with a \`tcp\` listener.`,
|
|
},
|
|
]}
|
|
/>
|
|
|
|
|
|
### `HTTPHeaderModifiers`
|
|
|
|
<ConfigEntryReference
|
|
topLevel={false}
|
|
yaml={false}
|
|
keys={[
|
|
{
|
|
hcl: false,
|
|
name: 'Unsupported',
|
|
type: '',
|
|
description: `HTTP Header modification is not yet supported in our Kubernetes CRDs.`,
|
|
},
|
|
{
|
|
yaml: false,
|
|
name: 'Add',
|
|
type: 'map<string|string>: optional',
|
|
description: `The set of key/value pairs that specify header values to add.
|
|
Use header names as keys. Header names are _not_ case-sensitive.
|
|
If header values with the same name already exist, the value set here will
|
|
be appended and both will be present.
|
|
If Envoy is used as the proxy, the value may contain
|
|
[variable placeholders](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#custom-request-response-headers) for example
|
|
\`%DOWNSTREAM_REMOTE_ADDRESS%\` to interpolate dynamic request
|
|
metadata into the value added.`,
|
|
},
|
|
{
|
|
yaml: false,
|
|
name: 'Set',
|
|
type: 'map<string|string>: optional',
|
|
description: `The set of key/value pairs that specify header values to add.
|
|
Use header names as keys. Header names are _not_ case-sensitive.
|
|
If header values with the same name already exist, the value set here will
|
|
_replace_ them.
|
|
If Envoy is used as the proxy, the value may contain
|
|
[variable placeholders](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#custom-request-response-headers) for example
|
|
\`%DOWNSTREAM_REMOTE_ADDRESS%\` to interpolate dynamic request
|
|
metadata into the value added.`,
|
|
},
|
|
{
|
|
yaml: false,
|
|
name: 'Remove',
|
|
type: 'array<string>: optional',
|
|
description: `The set of header names to remove. Only headers
|
|
whose names are a <i>case-insensitive</i> exact match will be removed`,
|
|
},
|
|
]}
|
|
/>
|
|
|
|
## ACLs
|
|
|
|
Configuration entries may be protected by [ACLs](/docs/security/acl).
|
|
|
|
Reading a `service-router` config entry requires `service:read` on the resource.
|
|
|
|
Creating, updating, or deleting a `service-router` config entry requires
|
|
`service:write` on the resource and `service:read` on any other service referenced by
|
|
name in these fields:
|
|
|
|
- [`Routes[].Destination.Service`](#service)
|
|
|
|
## Regular Expression Syntax
|
|
|
|
The actual syntax of the regular expression fields described here is entirely
|
|
proxy-specific.
|
|
|
|
When using [Envoy](/docs/connect/proxies/envoy) as a proxy (the only supported proxy in Kubernetes),
|
|
the syntax for these fields is version specific:
|
|
|
|
| Envoy Version | Syntax |
|
|
| --------------- | ------------------------------------------------------------------- |
|
|
| 1.11.2 or newer | [documentation](https://github.com/google/re2/wiki/Syntax) |
|
|
| 1.11.1 or older | [documentation](https://en.cppreference.com/w/cpp/regex/ecmascript) |
|