istio/istio
Service discovery
Active contributors: howardjohn, hzxuzhonghu, costinm, stevenctl
Purpose
Istio's service discovery is the layer that turns a heterogeneous set of inputs (Kubernetes Services and Endpoints, Istio ServiceEntry and WorkloadEntry, MCS imports, multi-cluster federations, file-mounted configs) into a unified service catalog that the rest of the control plane treats as a single source of truth.
Directory layout
pilot/pkg/serviceregistry/
├── instance.go # The shared Instance interface
├── aggregate/ # Aggregate registry: merges all sources
├── kube/
│ ├── controller/ # The Kubernetes-source registry: services, endpoints, pods, nodes
│ │ └── ambient/ # Ambient-specific registry built on krt
│ └── ...
├── serviceentry/ # ServiceEntry / WorkloadEntry registry
├── ambient/ # Ambient-shared types and helpers (now used outside kube/)
├── memory/ # In-memory registry for tests
├── mock/ # Test mocks
├── provider/ # Registry-provider enum (kubernetes, mock, ...)
└── util/
pilot/pkg/config/
├── kube/
│ ├── crdclient/ # The Istio CR ConfigStore implementation backed by Kubernetes CRDs
│ ├── gateway/ # Gateway API → Istio internal config conversion
│ ├── ingress/ # Kubernetes Ingress → VirtualService conversion (legacy)
│ └── agentgateway/ # Agent-gateway controller (preview feature)
├── memory/ # In-memory ConfigStore for tests
└── aggregate/ # Multi-source ConfigStore aggregatorKey abstractions
| Symbol | File | Role |
|---|---|---|
serviceregistry.Instance |
pilot/pkg/serviceregistry/instance.go |
The interface every registry implements |
model.ServiceDiscovery |
pilot/pkg/model/service.go |
The high-level API the rest of istiod uses |
aggregate.Controller |
pilot/pkg/serviceregistry/aggregate/controller.go |
Merges per-registry results |
kube.Controller |
pilot/pkg/serviceregistry/kube/controller/controller.go |
Kubernetes registry |
serviceentry.Controller |
pilot/pkg/serviceregistry/serviceentry/controller.go |
ServiceEntry / WorkloadEntry registry |
ambient.Index |
pilot/pkg/serviceregistry/kube/controller/ambient/index.go |
krt-driven Ambient registry |
model.ConfigStore |
pkg/config/store.go |
Get/List interface for Istio CRs |
crdclient.Client |
pilot/pkg/config/kube/crdclient/client.go |
Istiod's actual CR source |
How it works
graph LR
subgraph KubeReg["Kube Controller"]
s[Services] --> ksi[ServiceInstances]
e[Endpoints/Slices] --> ksi
p[Pods] --> kwi[WorkloadInstances]
end
subgraph SEReg["ServiceEntry Controller"]
se[ServiceEntry] --> ssi[ServiceInstances]
we[WorkloadEntry] --> swi[WorkloadInstances]
swi --> ssi
end
kwi --> ssi
swi --> ksi
KubeReg --> Agg[Aggregate]
SEReg --> Agg
Mem[Memory / file] --> Agg
MCS[MCS imports] --> KubeReg
MultiCluster[Other clusters via<br/>multicluster.Controller] --> KubeReg
Agg -->|model.ServiceDiscovery| Push[PushContext build]There are two near-orthogonal registries today:
- Kube controller — backed by Kubernetes Services + Endpoints (or EndpointSlices) + Pods. Builds
ServiceandWorkloadInstanceobjects. Multi-cluster works by spawning one per remote cluster (see Multi-cluster). - ServiceEntry controller — backed by Istio's
ServiceEntryandWorkloadEntryCRs. Provides a way to register external services (ExternalName-style) and workloads outside Kubernetes (VMs, bare metal).
The cross-controller asymmetry is documented in architecture/networking/pilot.md:
- A
ServiceEntrycan use aworkloadSelectorto attach to Pod-derivedWorkloadInstances. This means Pods feed intoServiceEntry.ServiceInstances. - A Kubernetes Service can have a
WorkloadEntryselector — Istio mapsWorkloadEntryintoKube Controller.ServiceInstancesso VM workloads can be reached through a regular Service.
These cross-arrows are why the two controllers cannot be cleanly separated.
Endpoints fast path
Endpoints are by far the most-frequently-changed resource in any cluster. They get a special path that bypasses PushContext rebuilds:
serviceregistryreports endpoint changes directly toxds.DiscoveryServer.EDSCacheUpdate.- The push pipeline issues an EDS-only push without rebuilding the full snapshot.
proxy_dependencies.gofilters by service-of-interest.
This is the "EDS bypass" referred to in architecture/networking/pilot.md.
Ambient registry
The Ambient registry is built on krt (pkg/kube/krt/). It maintains:
Workloadcollection — every workload (Pod or WE) labeled for ambient.Servicecollection — services with their ambient mode resolved.Addresscollection — pre-computedWorkloadAddressandServiceAddresstypes served via the workload API.Authorizationcollection — compiled L4 authz policy.
These collections are joined and re-derived whenever any input changes. The result is shipped over the workload API to the ztunnels. See systems/ambient-mesh for details.
Multi-cluster extension
pkg/kube/multicluster/ watches for secrets in istio-system labeled istio/multiCluster=true. Each secret contains a kubeconfig for a remote cluster. The controller:
- Builds a kube client for the remote cluster.
- Spawns a per-cluster
kube.Controllerinstance. - Plumbs its events into the aggregate registry with the
clusterIDset.
The aggregate then exposes services and endpoints across all clusters; the network ID and cluster ID are propagated through to the proxy so cross-cluster routing is possible.
The wiring lives in pilot/pkg/bootstrap/server.go: search for multicluster.NewController.
ConfigStore
The ConfigStore is the parallel layer for Istio CRs. Implementations:
crdclient— production. Backed by Kubernetes CRD informers. The default for production.memory— tests.file— for cases where Istiod runs without a Kubernetes API (e.g. external istiod with file-based config).xds— Istio's own ADS-as-a-config-source. Used for MCP-style integrations.
aggregate.Controller (under pilot/pkg/config/aggregate/) merges multiple stores into one.
Integration points
- Reads from: Kubernetes API (Services, EndpointSlices/Endpoints, Pods, Nodes, Namespaces), Istio CRs via the ConfigStore.
- Writes to: in-memory state only. Status writers in
pilot/pkg/status/push status conditions back to CRs but the registry itself is read-only. - Notifies: every change calls
xds.ConfigUpdateto trigger a push.
Entry points for modification
- New service source → implement
serviceregistry.Instanceand add to the aggregate. Inspectpilot/pkg/serviceregistry/memory/as the simplest example. - New CR kind → add a schema entry in
pkg/config/schema/, regenerate, add an analyzer inpkg/config/analysis/analyzers/, and (if it affects xDS output) update the relevant generator inpilot/pkg/networking/core/. - Cross-controller dependencies → if your change involves a Pod feeding a ServiceEntry or vice-versa, look at
pilot/pkg/serviceregistry/kube/controller/controller.goandpilot/pkg/serviceregistry/serviceentry/controller.gotogether. The cross-feeding is non-trivial and historically a source of bugs.
Key source files
| File | Purpose |
|---|---|
pilot/pkg/serviceregistry/instance.go |
The Instance interface |
pilot/pkg/serviceregistry/aggregate/controller.go |
Aggregate over registries |
pilot/pkg/serviceregistry/kube/controller/controller.go |
Kubernetes services + endpoints |
pilot/pkg/serviceregistry/serviceentry/controller.go |
ServiceEntry + WorkloadEntry |
pilot/pkg/serviceregistry/kube/controller/ambient/index.go |
Ambient registry (krt) |
pilot/pkg/config/kube/crdclient/client.go |
Istio CR client |
pilot/pkg/config/aggregate/config.go |
ConfigStore aggregator |
pkg/kube/multicluster/secretcontroller.go |
Per-cluster controller spawner |
See also
- systems/xds — the consumer of this layer.
- systems/krt — what the Ambient registry is built on.
- features/multicluster — the user-facing feature.
architecture/networking/pilot.md— cross-references this material in narrative form.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.