Open-Source Wikis

/

Istio

/

Systems

/

Service discovery

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 aggregator

Key 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 Service and WorkloadInstance objects. Multi-cluster works by spawning one per remote cluster (see Multi-cluster).
  • ServiceEntry controller — backed by Istio's ServiceEntry and WorkloadEntry CRs. 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 ServiceEntry can use a workloadSelector to attach to Pod-derived WorkloadInstances. This means Pods feed into ServiceEntry.ServiceInstances.
  • A Kubernetes Service can have a WorkloadEntry selector — Istio maps WorkloadEntry into Kube Controller.ServiceInstances so 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:

  • serviceregistry reports endpoint changes directly to xds.DiscoveryServer.EDSCacheUpdate.
  • The push pipeline issues an EDS-only push without rebuilding the full snapshot.
  • proxy_dependencies.go filters 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:

  • Workload collection — every workload (Pod or WE) labeled for ambient.
  • Service collection — services with their ambient mode resolved.
  • Address collection — pre-computed WorkloadAddress and ServiceAddress types served via the workload API.
  • Authorization collection — 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:

  1. Builds a kube client for the remote cluster.
  2. Spawns a per-cluster kube.Controller instance.
  3. Plumbs its events into the aggregate registry with the clusterID set.

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.ConfigUpdate to trigger a push.

Entry points for modification

  • New service source → implement serviceregistry.Instance and add to the aggregate. Inspect pilot/pkg/serviceregistry/memory/ as the simplest example.
  • New CR kind → add a schema entry in pkg/config/schema/, regenerate, add an analyzer in pkg/config/analysis/analyzers/, and (if it affects xDS output) update the relevant generator in pilot/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.go and pilot/pkg/serviceregistry/serviceentry/controller.go together. 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

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Service discovery – Istio wiki | Factory