Service, and what it actually does here
The Service names the backend. For CRD-routed traffic it is not in the data path at all.
The router has picked a Service. On most clusters the next thing that happens is that the packet goes to a ClusterIP and kube-proxy DNATs it to a pod. Here, for anything routed through the CRD provider, that is not what happens.
Traefik’s Kubernetes providers resolve a Service to its EndpointSlice addresses and load-balance across pod IPs itself. The ClusterIP is never dialled and kube-proxy is never involved. The Service object is a discovery mechanism — a query for "which pods" — rather than a hop.
This matters in three practical ways. Retries, circuit-breaking and sticky sessions are Traefik’s to configure, not the kernel’s, so they live in middleware rather than in service.spec.sessionAffinity. Load-balancing is per-request rather than per-connection, which is usually what you want and is different from what kube-proxy does. And a debugging session that reaches for curl against a ClusterIP is testing a path that production traffic does not take — if the ClusterIP works and the route does not, the fault is in the EndpointSlice or in the router, not in the Service.
The alternative is nativeLB: true, which tells Traefik to dial the ClusterIP and let kube-proxy do the balancing. Worth knowing about mostly so that a class can recognise which mode a cluster is in from its symptoms.
$ kubectl get svc tenant-acme
NAME TYPE CLUSTER-IP PORT(S)
tenant-acme ClusterIP 10.32.14.201 8080/TCP # never dialled
$ kubectl get endpointslices -l kubernetes.io/service-name=tenant-acme \
-o jsonpath='{.items[*].endpoints[*].addresses[*]}'
10.36.2.17 # this is the hop
# so: no ClusterIP in the path, and no kube-proxy rule involved.
# retries and stickiness are Traefik middleware, not Service fields.
Check yourself
1 question. One attempt each is recorded; the explanation is the point, not the score.
For traffic routed through the CRD provider, what role does the Service’s ClusterIP play?