Architecture
Big picture
Clusternet has a parent cluster that holds all the APIs and state, plus one agent in each child cluster. The parent runs four components, each a separate binary under src/cmd: clusternet-hub, clusternet-scheduler, clusternet-controller-manager, and clusternet-agent (the agent runs on the child). The README and official docs describe the same four-component layout (Introduction). Distribution flows through a chain of CRDs (Custom Resource Definitions): a user-authored Subscription is scheduled to a set of clusters, expanded into a Base per cluster, rendered into a Description per cluster, and finally applied to each child.
Components
clusternet-hub
The hub is an aggregated API server. It serves the shadow API, proxies kubectl traffic to child clusters, and approves cluster registration requests. Its package tree is src/pkg/hub, and the binary entry point is main() at src/cmd/clusternet-hub/hub.go:31, which builds the command via app.NewClusternetHubCmd.
clusternet-scheduler
The scheduler decides which clusters a Subscription goes to. It is a port of the Kubernetes scheduler framework that schedules clusters rather than nodes, living under src/pkg/scheduler. Its per-item loop is scheduleOne at src/pkg/scheduler/scheduler.go:287.
clusternet-controller-manager
The controller manager turns a scheduled Subscription into per-cluster Base and Description objects in each child's dedicated namespace. The deployer logic is in src/pkg/controllermanager/deployer/deployer.go, starting from handleSubscription at src/pkg/controllermanager/deployer/deployer.go:273.
clusternet-agent
The agent is the only Clusternet process that runs in a child cluster. It registers the child with the parent through a ClusterRegistrationRequest plus a CSR (CertificateSigningRequest), reports heartbeat and health, and in Pull mode applies the Descriptions the parent produced. Its generic deployer entry is handleDescription at src/pkg/agent/deployer/generic/generic.go:123.
How a request flows
Trace one Subscription from creation to running pods on children.
- A user creates a Subscription in the parent. The subscription controller picks it up at
handle(src/pkg/controllers/apps/subscription/subscription.go:176), injects a finalizer (src/pkg/controllers/apps/subscription/subscription.go:203), then delegates to an injected handler atsrc/pkg/controllers/apps/subscription/subscription.go:230. clusternet-schedulerpops the Subscription from its queue inscheduleOne(src/pkg/scheduler/scheduler.go:287), where the pop happens atsrc/pkg/scheduler/scheduler.go:288. It calls the scheduling algorithm atsrc/pkg/scheduler/scheduler.go:346and binds asynchronously atsrc/pkg/scheduler/scheduler.go:415.- The default binder writes the chosen clusters back to the Subscription status rather than creating any Base, which keeps the scheduler and deployer loosely coupled. See
DefaultBinder.Bindatsrc/pkg/scheduler/framework/plugins/defaultbinder/default_binder.go:57and the status assignment atsrc/pkg/scheduler/framework/plugins/defaultbinder/default_binder.go:65. clusternet-controller-managerobserves the status change and creates one Base per binding cluster inpopulateBasesAndLocalizations(src/pkg/controllermanager/deployer/deployer.go:322), then renders each Base into a Description inpopulateDescriptions(src/pkg/controllermanager/deployer/deployer.go:755).- In Pull mode the child's
clusternet-agentwatches its namespace's Description and applies it atsrc/pkg/agent/deployer/generic/generic.go:131. In Push mode the hub applies the same objects over the WebSocket tunnel.
The Internals page walks these hops with quoted code.
Key design decisions
The scheduler does not produce Base objects; it only records Status.BindingClusters on the Subscription (src/pkg/scheduler/framework/plugins/defaultbinder/default_binder.go:65). The controller manager reacts to that status. This status-based handoff decouples scheduling from deployment, so each can be reasoned about and scaled on its own.
Children choose a sync mode. The mode is a typed string with three values defined at src/pkg/apis/clusters/v1beta1/types.go:39: Push (src/pkg/apis/clusters/v1beta1/types.go:44), where the hub pushes changes to the child; Pull (src/pkg/apis/clusters/v1beta1/types.go:48), where the child's agent watches the parent and applies changes itself; and Dual (src/pkg/apis/clusters/v1beta1/types.go:51), which does both. Pull mode means the parent never needs inbound reachability to the child.
A Subscription picks a scheduling strategy, defaulting to Replication and offering Dividing as the alternative (src/pkg/apis/apps/v1alpha1/subscription.go:65). Replication places a full copy on each matched cluster; Dividing splits replicas across clusters by predicted capacity.
Extension points
- CRDs are the primary public surface: Subscription, Base, Description, ManagedCluster, FeedInventory, Localization, Globalization, Manifest, HelmChart, and HelmRelease, all under
src/pkg/apis. - The scheduler is a plugin framework with filter, score, predict, and bind extension points under
src/pkg/scheduler/framework/plugins, mirroring the Kubernetes scheduler framework. - The shadow API in
src/pkg/hub/registry/shadowlets ordinary Kubernetes objects submitted withkubectlbecome distribution material without new resource types. - The CRDs and clientsets are published separately as
github.com/clusternet/apisfor third-party integration (README).