Skip to main content

Expose the gateway on a generic cluster

On a cluster that is not AKS, EKS, or GKE, tetrate agentrouter dataplane ingress install has no cloud provider to drive; the data plane is exposed through whatever already terminates traffic at the cluster edge, typically an Ingress controller. The mechanics are ordinary Kubernetes, except for two details that are not obvious from the chart: which Service to target, and the NetworkPolicy rule the Ingress controller needs.

This page applies to self-managed clusters: k3s, kubeadm, OpenShift, or any distribution where inbound traffic arrives through an Ingress controller, a tunnel, or a bare NodePort rather than a provisioned cloud load balancer. The data plane itself is installed first, through the Helm or GitOps path.

Where the Service is​

The data plane's gateway is a Gateway API Gateway named egress in the tars-dataplane namespace, and Envoy Gateway serves it through a Service of the same name:

  • Service: egress
  • Namespace: tars-dataplane
  • Port: 10080, plain HTTP inside the cluster; TLS terminates at the Ingress controller in front of it

This is the Service named in Step 10 of the Helm installation guide and used as the Ingress backend in the cloud guides. Confirm the Service before wiring anything; the label selector lists the generated Service with the namespace and name it landed with:

kubectl get svc -A -l gateway.envoyproxy.io/owning-gateway-name=egress

Nothing needs to be pinned. The chart-owned EnvoyProxy has no value for the Service name, and an edit made to it by hand is reverted by the next helm upgrade, so the name is not something to change.

When the data plane terminates TLS itself, with one HTTPS listener per published hostname, the endpoint to expose is different: see Terminate TLS on the data plane.

Point the edge at the Service​

The wiring is standard:

  • Same namespace: an Ingress rule in tars-dataplane with the egress Service as its backend, on port 10080.
  • Ingress controller in another namespace: an ExternalName Service in the controller's namespace resolving to egress.tars-dataplane.svc.cluster.local, with the Ingress backend pointing at the ExternalName. The Ingress controller must be configured to pass the Host header through unchanged, since hostname matching on the gateway's routes is exact.
  • A tunnel in front (for example an outbound-only edge): terminate the tunnel on the Ingress controller and treat everything below it identically.

The public hostname that results is declared in the data plane's own chart, under global.gateway.hostnames or with tetrate agentrouter dataplane install --gateway-hostname, the same declaration every path uses; see Declare the hostnames a data plane serves. The data plane reports it to the management plane, and a data plane older than 0.7.0 keeps an address set through the deprecated SetDataplaneURL admin API instead. The readiness probe contract is standard port 443 only; a listener on a non-standard port needs a standard-port endpoint in front before DNS health checks can target it (see Fail over between data planes).

On a cluster that enforces NetworkPolicy, the Ingress controller needs an egress rule that names the gateway pods, not the Service in front of them. The policy is evaluated against the destination pod after DNAT, so a rule written for the ExternalName Service, or for the Service's cluster IP, covers nothing: the connection is refused at the pod, and the only visible symptom is a 502 in the Ingress controller's log with connection refused against a cluster IP.

egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: tars-dataplane
podSelector:
matchLabels:
gateway.envoyproxy.io/owning-gateway-name: egress

A 502 with connection refused from the Ingress controller, while kubectl exec from another pod in an unrestricted namespace reaches the gateway fine, means this rule is missing.

Verify​

  1. The root of the gateway endpoint answers with the JSON status document, no API key needed; "status": "serving" means routes are deployed. See Health and status visibility.
  2. GET /v1/models with an API key returns the models the key can reach. An empty list at this point is configuration, not exposure: see the note on model grants in the installation guides.
  3. The declared hostname answers the readiness probe green (GET /healthz/membership returns 200 with "status":"ready") before DNS is pointed at it. A 404 there means the Host the request carried is not one the data plane declares.