Skip to content

Architecture⚓︎

xCore is a Kubernetes operator with a single custom resource. This page explains what the operator creates from a Site, how the network model maps 3GPP interfaces onto cluster networking, and how the operator reports state. Read it before writing your first Site definition; the field-level detail is on the Site definition page.

Site, operator, network functions and the Agent

The operator model⚓︎

An xCore installation is one Helm release in one namespace. The release contains the operator, the Expeto Agent and, unless the cluster already has them, the MariaDB and Dragonfly operators and the netcfg node-configuration operator, which is shared by every Expeto product on the cluster. The cluster-scoped CustomResourceDefinitions are applied by the chart before the rest of the release on every install and upgrade, and shared by every release on the cluster.

The operator reconciles exactly one Site in its namespace. From that document it creates and owns:

  • a child resource per network function, each reconciled into Deployments or StatefulSets, Services and ConfigMaps: Smf, one Upf per user plane, Mm (the AMF), Mme (the MME and its S1 transport controller), Sdm (the subscriber store), Nfcm and Udsf;
  • the data backends: an Apache Ignite cluster for session state, a MariaDB instance or Galera cluster for the SMF and the subscriber store, a Dragonfly cache for identifier allocation and timers, and separate Dragonfly session stores for the MME and the subscriber store;
  • one NetworkAttachmentDefinition per declared attachment, plus MetalLB and FRR-K8s resources when the Site uses BGP or service addresses;
  • optionally a node-tuning DaemonSet (or a MachineConfig on OpenShift) from the Site's nodeConfig.

The Agent is the one component the Helm chart installs directly rather than the operator: its identity (Site ID and token) is installation configuration, not topology. The operator watches the Agent's readiness and folds it into the Site's status.

Every object the operator creates carries the label expeto.io/managed-by=taiga and is guarded by a validating admission webhook that rejects edits from anyone but the operator. Configuration changes are made on the Site; kubectl edit on a generated ConfigMap or Deployment is refused with a message pointing back at the field that owns it.

Multiple cores on one cluster are separate installations in separate namespaces. A second Site applied into a namespace that already has one is rejected with the reason RejectedExtraSite.

Network model⚓︎

The Site describes networking in two layers.

Attachments (spec.network.attachments) are the identity carrier. Each one becomes a NetworkAttachmentDefinition whose CNI plugin chain is rendered verbatim, so any plugin the nodes support works: macvlan or ipvlan on a VLAN interface, SR-IOV, a route-override chain, with static, host-local or whereabouts address management. An attachment is a segment: "the radio VLAN", "the corporate breakout".

Interfaces in the topology reference attachments by name and pin the address a pod uses on that segment as a host address with prefix length (transitIpRange: 10.0.3.10/24). The mapping to 3GPP interfaces:

Site field 5G 4G Carried by
controlPlane.amf.n2[] N2 (NGAP over SCTP) AMF
controlPlane.mme.s1[] S1-MME (S1AP over SCTP) MME transport controller
userPlanes[].n3[] N3 S1-U UPF, radio side
userPlanes[].n6[] N6 SGi UPF, data-network side; carries the UE pools
userPlanes[].n9[] N9 S5-U UPF to UPF; an attachment or the pod network
controlPlane.n16[] N16 S5-C / S8 (GTP-C) SMF, roaming or upstream control plane

Interfaces that face other clusters or cloud networks can be realized as LoadBalancer addresses drawn from a service range (spec.network.serviceRanges) instead of an attachment, for N2, S1, N3 and N16. The address is allocated by MetalLB and advertised over BGP or layer 2. On a cluster with neither MetalLB nor a suitable network interface, N2 and S1 can also be served on a node port of every node.

UE address pools live on the N6 interface that serves them. Each pool declares how its traffic reaches the outside: source NAT on the UPF (the default), static routes that the network team installs (the operator lists the required routes in the Site's status), or BGP advertisement over a named peering with the UPF's N6 transit address as next hop.

BGP routers are declared in spec.network.bgp, each with a local ASN and named peerings that ride an attachment. A Site has at most one MetalLB router: the operator emits its FRR-K8s configuration and MetalLB peers into MetalLB's namespace and cleans them up through a finalizer when the Site is deleted. A GoBGP router instead runs as a sidecar in the dataplane pods of the user planes whose UE pools name its peerings, and announces those pools from the user plane itself; see BGP.

User planes and routing⚓︎

Each entry in spec.topology.userPlanes is one UPF, and its mode and the interfaces it declares determine its 3GPP role. A user plane with N6 is an anchor: it carries the systemId of an xControl System, and the SMF routes a subscriber to the anchor whose systemId matches the System the Mobile Asset was provisioned into. A user plane without N6 is a front: it serves every System on the Site, carries no systemId, and hands traffic over N9 to the anchors named in its peerUpfs.

Interfaces Mode 5G Mode 4G Mode Dual
N3 and N9, no N6 (front) intermediate UPF SGW-U SGW-U and intermediate UPF
N9 and N6 (anchor) session anchor behind an intermediate UPF PGW-U PGW-U and 5G session anchor, behind a front
N3 and N6 (anchor) session anchor (PSA-UPF) none: 4G sessions are refused 5G session anchor; 4G sessions are refused

No user plane is both the SGW-U and the PGW-U of a 4G session: the SMF refuses a session that would use one user plane for both. 4G radios therefore always connect to a front, which forwards over N9 (S5-U) to the anchors.

A public core declares one SGW-U with N3 and N9 and one PGW-U per network segment with N9 and N6. A private 4G core declares an SGW-U in front of one or more PGW-Us; a private 5G core can be a single PSA-UPF with N3 and N6. A combined core declares a front in mode Dual and one anchor in mode Dual per System, so that each System serves both radio technologies from one address pool. numberOfSessions sizes each user plane's session table and is fixed once the user plane exists.

Control plane⚓︎

spec.topology.controlPlane declares the PLMN and the mobility and subscriber functions:

  • plmnId is the PLMN the mobility functions serve and the home PLMN the subscriber store keys Mobile Assets under. A Site with an MME needs a 3-digit MNC.
  • amf deploys the AMF (three microservices) with its N2 endpoint, its tracking areas and, optionally, its identity and NAS security preferences. slices sets the S-NSSAIs it advertises.
  • mme deploys the MME and its S1 transport controller with the same vocabulary, plus its NAS and mobility timers, the S-GW and P-GW lists (default: the co-located SMF) and the Diameter realm (default: derived from the PLMN). The Agent registers the MME, so xControl shows its eNodeBs and the Expeto CLI reads its sessions; the MME's configuration stays with the operator.
  • subscriberManagement deploys the subscriber store: UDR, UDM and AUSF in mode 5G, the HSS in mode 4G, all four in mode Dual. The AMF needs mode 5G or Dual, the MME 4G or Dual. The Agent provisions Mobile Assets into whichever store is present.
  • dataNetworks lists the DNNs or APNs the core serves; the first is the AMF's default DNN and each one becomes a P-GW association on the MME.
  • usageReporting sets how often the SMF collects session usage from the user planes for xControl.

Declaring both amf and mme gives a combined core, and the two identities must not collide. There is no N26 interface between the AMF and the MME. When every anchor is in mode Dual, so that one anchor serves both radio technologies for its System, the operator configures both mobility functions for interworking without N26 and they tell dual-mode UEs so; a session then follows the UE between 4G and 5G and keeps its address. Otherwise a UE that changes radio technology attaches or registers afresh. In both cases the HSS and the UDM of the dual-mode store cancel each other's registration of a UE that has moved.

Status model⚓︎

The Site carries standard Kubernetes conditions. Ready rolls up the Agent, every user plane and every control-plane function, and is the condition every validation failure is reported on; the subsystem conditions GatewaysReady, ControlPlaneReady and PeeringReady say which part is waiting, and InfrastructureReady is always True. Each False condition names its cause in reason (TopologyMissingNADs, NetworkSpecInvalid, MariaDbOperatorMissing, GatewayNotReady, ...), and status.lastReconcileError carries the last error verbatim, so kubectl describe site is the first and usually last place to look. The child resources (smf, upf, mm, mme, sdm) carry their own NetworkConfigured and DatabaseReady. The troubleshooting page lists every reason.

The operator also publishes the gauge taiga_site_condition{namespace,site,type} (1 true, 0 false, -1 unknown) on its metrics endpoint, which is what an alert on type="Ready" watches.

Change management⚓︎

The Site definition is agreed with Expeto Support during design, and later changes are reviewed with them too: its data model moves between releases, and Expeto intends the Agent to write the Site from the configuration held in xControl. See Who writes the Site. Edits to the Site fall into two classes. Live-reload fields (routes, MTU, BGP peers, pool reachability) are applied by the running pods. Restart-required fields (image tags, topology bindings, storage shape, sysctls) roll the affected pods. Setting spec.maintenanceMode: false makes the webhook reject restart-required edits until you flip it back, so a change window can be enforced on the resource itself.

Two decisions are fixed at creation: the MariaDB topology (standalone or Galera; changing between them on an existing database is refused) and each user plane's numberOfSessions.