diff --git a/guides/service-to-service.mdx b/guides/service-to-service.mdx index feba0d5d..b8dcb2cd 100644 --- a/guides/service-to-service.mdx +++ b/guides/service-to-service.mdx @@ -51,6 +51,8 @@ http://WORKLOAD_NAME.GVC_NAME.cpln.local[:PORT] - Headers may optionally be used on the receiving workload to verify that the communication was encrypted. - Only the ports listed in the workload containers array will be made accessible to other workloads. +For a caller in the same GVC, `spec.networking.internal.routing` on the target workload decides whether these requests stay in the caller's location or fail over to another location when no local replica can serve them. See [Internal Routing and Location Failover](/reference/workload/general#internal-routing-and-location-failover). + The syntax above load-balances across all replicas. To reach an individual replica directly, or to find the hostnames assigned to a given replica, see [Finding Replica Hostnames](/reference/workload/general#finding-replica-hostnames) in the Workload reference. ## Configure internal firewall using the Console diff --git a/llms.txt b/llms.txt index 2a8c259e..a5ed0b38 100644 --- a/llms.txt +++ b/llms.txt @@ -170,6 +170,8 @@ Policies must use the GVC-scoped form for identities: `//gvc/GVC_NAME/identity/N **Internal DNS**: Workloads in the same GVC communicate at `WORKLOAD_NAME.GVC_NAME.cpln.local:PORT`. All internal traffic is automatically mTLS-encrypted — no configuration required. Every workload receives a `CPLN_TOKEN` env var (auto-rotated JWT, valid only from inside that workload). +**Internal failover**: `spec.networking.internal.routing` on the target workload controls same-GVC `cpln.local` calls. `locationEndpoint` keeps them on replicas in the caller's location. `gvcEndpoint` prefers local replicas and fails over to the canonical endpoint (another location that passes its health check) as local replicas become unavailable: fully when none are ready, partially when only some are unhealthy. Failover traffic is billed as egress. New workloads default to `gvcEndpoint`. Workloads created before the field existed have no value, which means `locationEndpoint`. + **Public canonical endpoint** depends on the GVC's `endpointNamingFormat`: - `org` (default for new GVCs): `{workload}-{gvcAlias}.{orgEndpointPrefix}.cpln.app` - `default`: `{workload}-{gvcAlias}.cpln.app` diff --git a/reference/workload/general.mdx b/reference/workload/general.mdx index 2cb23cbe..88776d1f 100644 --- a/reference/workload/general.mdx +++ b/reference/workload/general.mdx @@ -182,6 +182,49 @@ Typically, the workload endpoint is used. For stateful workloads with `replicaDi - workload: `$workloadName.$gvcName.cpln.local:$port` - replica-direct: `replica-$replicaIndex.$workloadName.$locationName.$gvcName.cpln.local:$port` +#### Internal Routing and Location Failover + +`spec.networking.internal.routing` controls where requests to `$workloadName.$gvcName.cpln.local` go when they come from a workload in the **same** [GVC](/reference/gvc). Set it on the workload being called. + +| Value | Behavior | +| :-- | :-- | +| `locationEndpoint` | Requests are served only by replicas in the caller's location. If no local replica can serve them, the request fails. This is also the behavior when the field is not set. | +| `gvcEndpoint` | Requests are served by local replicas while they are healthy. As local replicas become unavailable, requests fail over to the workload's [canonical endpoint](#canonical-endpoint-global), which routes to another location that passes its health check. | + +```yaml Workload manifest +kind: workload +name: WORKLOAD +spec: + networking: + internal: + routing: gvcEndpoint +``` + +**Defaults:** + +- New workloads default to `gvcEndpoint`. +- Workloads created before this field was introduced have no value, which behaves as `locationEndpoint`. They are not changed automatically. Set `gvcEndpoint` to opt in. +- To see a workload's current setting, run `cpln workload get WORKLOAD --gvc GVC -o json` and check `spec.networking.internal.routing`. +- An explicit value is always kept. Re-applying a manifest that omits the field with `cpln apply` keeps the stored value. + +**When failover happens:** local replicas are preferred. + +- **Full failover:** all requests go to another location when the workload has no ready replicas in the caller's location, for example when it is suspended there with [`spec.localOptions`](#location-specific-options). +- **Partial failover:** when only some local replicas are unhealthy (not ready, or temporarily removed from load balancing after returning errors), a share of requests can go to another location while the rest stay local. +- **Recovery:** traffic returns to local replicas once they are healthy again. A replica removed after returning errors is retried after about 30 seconds. + +Failover traffic stays on the service mesh and uses mutual TLS, just like calls between GVCs. The target's [internal firewall](/reference/workload/firewall#internal) still applies in the remote location, so a caller that is not allowed is rejected there with `403 Forbidden`. + +**Requirements and limits:** + +- The setting only takes effect when the GVC has more than one location and the workload is deployed in at least one other location. +- Callers in a **different** GVC already reach the workload through its canonical endpoint, so this setting does not change their routing. +- Outbound ports that the caller excludes from the service mesh with `cpln/disableServiceMeshOutboundPort` bypass mesh routing, so requests on those ports never fail over. +- Requests can fail for a brief period before failover completes, while the failure is being detected. +- For workloads that only expose TCP ports, location health is checked at the TCP level. A location that has no ready replicas but is not suspended can still be selected. + +Requests that fail over leave the caller's location and are billed as egress. New workloads default to `gvcEndpoint`, so set `locationEndpoint` if a workload's internal traffic must stay in one location. + ### Finding Replica Hostnames When `replicaDirect` is enabled on a stateful workload, each replica is reachable at its own stable hostname, letting you target a specific replica instead of load-balancing across all of them: diff --git a/skill.md b/skill.md index 53337477..6b123dcf 100644 --- a/skill.md +++ b/skill.md @@ -479,6 +479,7 @@ For Terraform / Pulumi / Kubernetes Operator deployment patterns, fetch the rele **Endpoints:** - Internal DNS (same-GVC): `WORKLOAD.GVC.cpln.local:PORT` + - `spec.networking.internal.routing` on the target: `locationEndpoint` (caller's location only) or `gvcEndpoint` (prefer local replicas, fail over to the location the canonical endpoint selects as local replicas become unavailable; billed as egress). New workloads default to `gvcEndpoint`; older workloads have no value, which means `locationEndpoint`. - Public canonical (set by GVC `endpointNamingFormat`): - `org` (default for new GVCs): `{workload}-{gvcAlias}.{orgEndpointPrefix}.cpln.app` - `default`: `{workload}-{gvcAlias}.cpln.app`