consul/website/content/docs/connect/config-entries/service-resolver.mdx

804 lines
24 KiB
Plaintext

---
layout: docs
page_title: 'Configuration Entry Kind: Service Resolver'
description: >-
The `service-resolver` config entry kind controls which service instances
should satisfy Connect upstream discovery requests for a given service name.
---
# Service Resolver
-> **v1.8.4+:** On Kubernetes, the `ServiceResolver` 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-resolver` config entry kind (`ServiceResolver` on Kubernetes) controls which service instances
should satisfy Connect upstream discovery requests for a given service name.
If no resolver config is defined the chain assumes 100% of traffic goes to the
healthy instances of the default service in the current datacenter+namespace
and discovery terminates.
## Interaction with other Config Entries
- Service resolver config entries are a component of [L7 Traffic
Management](/docs/connect/l7-traffic).
## UI
Once a `service-resolver` is successfully entered, you can view it in the UI. Service routers, service splitters, and service resolvers can all be viewed by clicking on your service then switching to the *routing* tab.
![screenshot of service resolver in the UI](/img/l7-routing/Resolver.png)
## Sample Config Entries
### Filter on service version
Create service subsets based on a version metadata and override the defaults:
<CodeTabs tabs={[ "HCL", "Kubernetes YAML", "JSON" ]}>
```hcl
Kind = "service-resolver"
Name = "web"
DefaultSubset = "v1"
Subsets = {
v1 = {
Filter = "Service.Meta.version == v1"
}
v2 = {
Filter = "Service.Meta.version == v2"
}
}
```
```yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceResolver
metadata:
name: web
spec:
defaultSubset: v1
subsets:
v1:
filter: 'Service.Meta.version == v1'
v2:
filter: 'Service.Meta.version == v2'
```
```json
{
"Kind": "service-resolver",
"Name": "web",
"DefaultSubset": "v1",
"Subsets": {
"v1": {
"Filter": "Service.Meta.version == v1"
},
"v2": {
"Filter": "Service.Meta.version == v2"
}
}
}
```
</CodeTabs>
### Other datacenters
Expose a set of services in another datacenter as a virtual service:
<CodeTabs tabs={[ "HCL", "Kubernetes YAML", "JSON" ]}>
```hcl
Kind = "service-resolver"
Name = "web-dc2"
Redirect {
Service = "web"
Datacenter = "dc2"
}
```
```yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceResolver
metadata:
name: web-dc2
spec:
redirect:
service: web
datacenter: dc2
```
```json
{
"Kind": "service-resolver",
"Name": "web-dc2",
"Redirect": {
"Service": "web",
"Datacenter": "dc2"
}
}
```
</CodeTabs>
### Failover
Enable failover for subset `v2` to `dc2`, and all other subsets to `dc3` or `dc4`:
<CodeTabs tabs={[ "HCL", "Kubernetes YAML", "JSON" ]}>
```hcl
Kind = "service-resolver"
Name = "web"
ConnectTimeout = "15s"
Failover = {
v2 = {
Datacenters = ["dc2"]
}
"*" = {
Datacenters = ["dc3", "dc4"]
}
}
```
```yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceResolver
metadata:
name: web
spec:
connectTimeout: 15s
failover:
v2:
datacenters: ['dc2']
'*':
datacenters: ['dc3', 'dc4']
```
```json
{
"Kind": "service-resolver",
"Name": "web",
"ConnectTimeout": "15s",
"Failover": {
"v2": {
"Datacenters": ["dc2"]
},
"*": {
"Datacenters": ["dc3", "dc4"]
}
}
}
```
</CodeTabs>
Enable failover to a WAN federated datacenter and then a cluster peer for all
service subsets.
<CodeTabs tabs={[ "HCL", "Kubernetes YAML", "JSON" ]}>
```hcl
Kind = "service-resolver"
Name = "web"
ConnectTimeout = "15s"
Failover = {
"*" = {
Targets = [
{Datacenter = "dc3"},
{Peer = "peer-01"}
]
}
}
```
```yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceResolver
metadata:
name: web
spec:
connectTimeout: 15s
failover:
'*':
targets:
- datacenter: "dc3"
- peer: "peer-01"
```
```json
{
"Kind": "service-resolver",
"Name": "web",
"ConnectTimeout": "15s",
"Failover": {
"*": {
"Targets": [
{"Datacenter": "dc3"},
{"Peer": "peer-01"}
]
}
}
}
```
</CodeTabs>
<EnterpriseAlert product="consul">
Failover to another datacenter and namespace for all service subsets.
</EnterpriseAlert>
<CodeTabs tabs={[ "HCL", "Kubernetes YAML", "JSON" ]}>
```hcl
Kind = "service-resolver"
Name = "product-api"
Namespace = "primary"
ConnectTimeout = "0s"
Failover = {
"*" = {
Datacenters = ["dc2"]
Namespace = "secondary"
}
}
```
```yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceResolver
metadata:
name: product-api
namespace: primary
spec:
connectTimeout: 0s
failover:
namespace: 'secondary'
datacenters: ['dc2']
```
```json
{
"Kind": "service-resolver",
"Name": "product-api",
"Namespace": "primary",
"ConnectTimeout": "0s",
"Failover": {
"*": {
"Datacenters": ["dc2"],
"Namespace": "secondary"
}
}
}
```
</CodeTabs>
<EnterpriseAlert product="consul">
Failover within a datacenter and a different namespace.
</EnterpriseAlert>
<CodeTabs tabs={[ "HCL", "Kubernetes YAML", "JSON" ]}>
```hcl
Kind = "service-resolver"
Name = "product-api"
Namespace = "primary"
ConnectTimeout = "0s"
Failover = {
"*" = {
Service = "product-api-backup"
Namespace = "secondary"
}
}
```
```yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceResolver
metadata:
name: product-api
namespace: primary
spec:
connectTimeout: 0s
failover:
service: 'product-api-backup'
namespace: 'secondary'
```
```json
{
"Kind": "service-resolver",
"Name": "product-api",
"Namespace": "primary",
"ConnectTimeout": "0s",
"Failover": {
"*": {
"Service": "product-api-backup",
"Namespace": "secondary"
}
}
}
```
</CodeTabs>
### Consistent load balancing
Apply consistent load balancing for requests based on `x-user-id` header:
<CodeTabs tabs={[ "HCL", "Kubernetes YAML", "JSON" ]}>
```hcl
Kind = "service-resolver"
Name = "web"
LoadBalancer = {
Policy = "maglev"
HashPolicies = [
{
Field = "header"
FieldValue = "x-user-id"
}
]
}
```
```yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceResolver
metadata:
name: web
spec:
loadBalancer:
policy: maglev
hashPolicies:
- field: header
fieldValue: x-user-id
```
```json
{
"Kind": "service-resolver",
"Name": "web",
"LoadBalancer": {
"Policy": "maglev",
"HashPolicies": [
{
"Field": "header",
"FieldValue": "x-user-id"
}
]
}
}
```
</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-resolver`',
yaml: 'Must be set to `ServiceResolver`',
},
},
{
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 in which the configuration entry will apply.',
yaml: false,
},
{
name: 'Partition',
type: `string: "default"`,
enterprise: true,
description:
'Specifies the admin partition in which the configuration entry will apply.',
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: 'ConnectTimeout',
type: 'duration: 0s',
description:
'The timeout for establishing new network connections to this service.',
},
{
name: 'DefaultSubset',
type: 'string: ""',
description:
'The subset to use when no explicit subset is requested. If empty, the unnamed subset is used.',
},
{
name: 'Subsets',
type: 'map[string]ServiceResolverSubset',
description:
'A map of subset name to subset definition for all usable named subsets of this service. The map key is the name of the subset and all names must be valid DNS subdomain elements.<br><br>This may be empty, in which case only the unnamed default subset will be usable.',
children: [
{
name: 'Filter',
type: 'string: ""',
description: `The [filter expression](/api-docs/features/filtering) to be used for selecting
instances of the requested service. If empty, all healthy instances are
returned. This expression can filter on the same selectors as the
[Health API endpoint](/api-docs/health#filtering-2).`,
},
{
name: 'OnlyPassing',
type: 'bool: false',
description: `Specifies the behavior of the resolver's
health check interpretation. If this is set to false, instances with checks
in the passing as well as the warning states will be considered healthy. If
this is set to true, only instances with checks in the passing state will
be considered healthy.`,
},
],
},
{
name: 'Redirect',
type: 'ServiceResolverRedirect: <optional>',
description: {
hcl: `When configured, all attempts to resolve the service this resolver defines will be substituted for the supplied redirect EXCEPT when the redirect has already been applied.
<br><br>
When \`Redirect\` is set, all other fields besides \`Kind\`, \`Name\`, \`Namespace\` and \`Redirect\` will be ignored.`,
yaml: `When configured, all attempts to resolve the service this resolver defines will be substituted for the supplied redirect EXCEPT when the redirect has already been applied.
<br><br>
When \`redirect\` is set, all other fields besides \`redirect\` will be ignored.`,
},
children: [
{
name: 'Service',
type: 'string: ""',
description: 'A service to resolve instead of the current service.',
},
{
name: 'ServiceSubset',
type: 'string: ""',
description: {
hcl: `A named subset of the given service to
resolve instead of one defined as that service's \`DefaultSubset\`. If empty, the
default subset is used.
<br><br>
If this is specified at least one of \`Service\`, \`Datacenter\`, or \`Namespace\`
should be configured.`,
yaml: `A named subset of the given service to
resolve instead of one defined as that service's \`defaultSubset\`. If empty, the
default subset is used.
<br><br>
If this is specified at least one of \`service\`, \`datacenter\`, or \`namespace\`
should be configured.`,
},
},
{
name: 'Namespace',
enterprise: true,
type: 'string: ""',
description:
'Specifies the destination namespace to resolve the service from.',
},
{
name: 'Partition',
enterprise: true,
type: 'string: ""',
description:
'Specifies the destination admin partition to resolve the service from.',
},
{
name: 'Datacenter',
type: 'string: ""',
description:
'Specifies the destination datacenter to resolve the service from.',
},
{
name: 'Peer',
type: 'string: ""',
description:
`Specifies the destination cluster peer to resolve the target service name from.
Ensure that [intentions are defined](/docs/connect/cluster-peering/create-manage-peering#authorize-services-for-peers)
on the peered cluster to allow the source service to access this redirect target service as an upstream. When
the peer name is specified, Consul uses Envoy'services outlier detection to determine
the health of the redirect target based on whether communication attempts to the
redirect target are generally successful. Service health checks imported from a peer
are not considered in the health of a redirect target because they do not account for service
routers, splitters, and resolvers that may be defined in the peer for the target service.`,
},
],
},
{
name: 'Failover',
type: 'map[string]ServiceResolverFailover',
description: {
hcl: `Controls when and how to
reroute traffic to an alternate pool of service instances.
<br><br>
The map is keyed by the service subset it applies to and the special
string \`"*"\` is a wildcard that applies to any subset not otherwise
specified here.
<br><br>
\`Service\`, \`ServiceSubset\`, \`Namespace\`, \`Targets\`, and
\`Datacenters\` cannot all be empty at once.`,
yaml: `Controls when and how to
reroute traffic to an alternate pool of service instances.
<br><br>
The map is keyed by the service subset it applies to and the special
string \`"*"\` is a wildcard that applies to any subset not otherwise
specified here.
<br><br>
\`service\`, \`serviceSubset\`, \`namespace\`, \`targets\` and
\`datacenters\` cannot all be empty at once.`,
},
children: [
{
name: 'Service',
type: 'string: ""',
description:
'The service to resolve instead of the default as the failover group of instances during failover.',
},
{
name: 'ServiceSubset',
type: 'string: ""',
description:
'The named subset of the requested service to resolve as the failover group of instances. If empty, the default subset for the requested service is used.',
},
{
name: 'Namespace',
enterprise: true,
type: 'string: ""',
description:
'The namespace to resolve the requested service from to form the failover group of instances. If empty, the current namespace is used.',
},
{
name: 'Datacenters',
type: 'array<string>',
description: 'A fixed list of datacenters to try during failover.',
},
{
name: 'Targets',
type: 'array<ServiceResolverFailoverTarget>',
description: `A fixed list of failover targets to try during
failover. It allows operators to express complicated failover
scenarios such as between cluster peers, WAN federated datacenters, and local admin partitions.`,
children: [
{
name: 'Service',
type: 'string: ""',
description:
'The service name to use for the failover target. If empty, the current service name is used.',
},
{
name: 'ServiceSubset',
type: 'string: ""',
description:
'The named subset to use for the failover target. If empty, the default subset for the requested service name is used.',
},
{
name: 'Namespace',
enterprise: true,
type: 'string: ""',
description:
`The namespace to use for the failover target. If empty, the \`default\` namespace is used.`,
},
{
name: 'Partition',
enterprise: true,
type: 'string: ""',
description:
`The admin partition within the same datacenter to use for the failover target.
If empty, the \`default\` partition is used.
To use an admin partition in a different datacenter for the failover target,
use the \`Peer\` field instead.`,
},
{
name: 'Datacenter',
type: 'string: ""',
description:
`The WAN federated datacenter to use for the failover target.
If empty, the current datacenter is used.
To use a datacenter for the failover target that is connected
with a cluster peering relationship rather than WAN federation,
use the \`Peer\` field instead.`,
},
{
name: 'Peer',
type: 'string: ""',
description:
`Specifies the destination cluster peer to resolve the target service name from.
Ensure that [intentions are defined](/docs/connect/cluster-peering/create-manage-peering#authorize-services-for-peers)
on the peered cluster to allow the source service to access this failover target service as an upstream.
When the peer name is specified, Consul uses Envoy's outlier detection
to determine the health of the failover target based on
whether communication attempts to the failover target are generally successful.
Service health checks imported from a peer are not considered in the health
of a failover target because they do not account for service routers, splitters, and resolvers
that may be defined in the peer for the target service.`,
},
],
},
],
},
{
name: 'LoadBalancer',
type: 'LoadBalancer',
description:
'Determines the load balancing policy and configuration for services issuing requests to this upstream. This option is available in Consul versions 1.9.0 and newer.',
children: [
{
name: 'Policy',
type: 'string: ""',
description:
'The load balancing policy used to select a host. One of: `random`, `round_robin`, `least_request`, `ring_hash`, `maglev`.',
},
{
name: 'RingHashConfig',
type: 'RingHashConfig',
description: 'Configuration for the `ring_hash` policy type.',
children: [
{
name: 'MinimumRingSize',
type: 'int: 1024',
description:
'Determines the minimum number of entries in the hash ring.',
},
{
name: 'MaximumRingSize',
type: 'int: 8192',
description:
'Determines the maximum number of entries in the hash ring.',
},
],
},
{
name: 'LeastRequestConfig',
type: 'LeastRequestConfig',
description: 'Configuration for the `least_request` policy type.',
children: [
{
name: 'ChoiceCount',
type: 'int: 2',
description:
'Determines the number of random healthy hosts from which to select the one with the least requests.',
},
],
},
{
name: 'HashPolicies',
type: 'array<HashPolicies>',
description: `List of hash policies to use for
hashing load balancing algorithms. Hash policies are evaluated individually
and combined such that identical lists result in the same hash.
If no hash policies are present, or none are successfully evaluated,
then a random backend host will be selected.`,
children: [
{
name: 'Field',
type: 'string: ""',
description: {
hcl:
'The attribute type to hash on. Must be one of `header`, `cookie`, or `query_parameter`. Cannot be specified along with `SourceIP`.',
yaml:
'The attribute type to hash on. Must be one of `header`, `cookie`, or `query_parameter`. Cannot be specified along with `sourceIP`.',
},
},
{
name: 'FieldValue',
type: 'string: ""',
description: {
hcl:
'The value to hash. ie. header name, cookie name, URL query parameter name. Cannot be specified along with `SourceIP`.',
yaml:
'The value to hash. ie. header name, cookie name, URL query parameter name. Cannot be specified along with `sourceIP`.',
},
},
{
name: 'CookieConfig',
type: 'CookieConfig',
description:
'Additional configuration for the "cookie" hash policy type. This is specified to have Envoy generate a cookie for a client on its first request.',
children: [
{
name: 'Session',
type: 'bool: false',
description: 'Generates a session cookie with no expiration.',
},
{
name: 'TTL',
type: 'duration: 0s',
description:
'TTL for generated cookies. Cannot be specified for session cookies.',
},
{
name: 'Path',
type: 'string: ""',
description: 'The path to set for the cookie.',
},
],
},
{
name: 'SourceIP',
type: 'bool: false',
description: {
hcl:
'Determines whether the hash should be of the source IP address rather than of a field and field value. Cannot be specified along with `Field` or `FieldValue`.',
yaml:
'Determines whether the hash should be of the source IP address rather than of a field and field value. Cannot be specified along with `field` or `fieldValue`.',
},
},
{
name: 'Terminal',
type: 'bool: false',
description:
'Will short circuit the computation of the hash when multiple hash policies are present. If a hash is computed when a Terminal policy is evaluated, then that hash will be used and subsequent hash policies will be ignored.',
},
],
},
],
},
]}
/>
## Service Subsets
A service subset assigns a concrete name to a specific subset of discoverable
service instances within a datacenter, such as `"version2"` or `"canary"`.
A service subset name is useful only when composed with an actual service name,
a specific datacenter, and namespace.
All services have an unnamed default subset that will return all healthy
instances unfiltered.
Subsets are defined in `service-resolver` configuration entries, but are
referenced by their names throughout the other configuration entry kinds.
## ACLs
Configuration entries may be protected by [ACLs](/docs/security/acl).
Reading a `service-resolver` config entry requires `service:read` on the resource.
Creating, updating, or deleting a `service-resolver` config entry requires
`service:write` on the resource and `service:read` on any other service referenced by
name in these fields:
- [`Redirect.Service`](#service)
- [`Failover[].Service`](#service-1)