Skip to content

Architecture ​

Big picture ​

The README describes the system in four parts (README.md:95 onward): the Clusterpedia APIServer, the ClusterSynchro Manager, a Storage Layer interface, and a concrete Storage Component such as MySQL or PostgreSQL. The APIServer is the read path; it registers as a Kubernetes Aggregated API and answers cross-cluster queries. The ClusterSynchro Manager is the write path; it watches each registered cluster and pushes observed resources into storage. The Storage Layer is an interface both sides talk to, so the backing database is pluggable.

A cluster is registered through the PediaCluster CRD (Custom Resource Definition) in API group cluster.clusterpedia.io/v1alpha2. That object carries both the credentials to reach the member cluster and the declaration of which resources to synchronize.

Components ​

Clusterpedia APIServer ​

Registers with the host Kubernetes APIServer through aggregation and provides the unified query entrance (README.md:95 onward). The runtime scheme is installed in an init block via install.Install(Scheme) (pkg/apiserver/apiserver.go:44), the API group is wired into the generic server with genericServer.InstallAPIGroup(&apiGroupInfo) (pkg/apiserver/apiserver.go:156), and the server starts with server.GenericAPIServer.PrepareRun().RunWithContext(ctx) (pkg/apiserver/apiserver.go:173). The binary is built from cmd/apiserver.

ClusterSynchro Manager ​

Manages the per-cluster synchros that copy resources into storage (README.md:95 onward). Each synchro is created by New (pkg/synchromanager/clustersynchro/cluster_synchro.go:84), which builds a dynamic discovery manager for the cluster and reads existing resource versions from storage before starting informers. The binary is built from cmd/clustersynchro-manager.

Storage Layer ​

An interface that both the read and write paths use, declared in pkg/storage/storage.go. StorageFactory (pkg/storage/storage.go:20) creates per-resource storages and manages cluster lifecycle; ResourceStorage (pkg/storage/storage.go:39) defines Get, List, Watch, Create, Update, and Delete. The default implementation lives under pkg/storage/internalstorage.

Storage Component ​

The concrete database. The default storage layer connects to MySQL or PostgreSQL (README.md:95 onward). Storage layers register themselves by name; internalstorage calls storage.RegisterStorageFactoryFunc(StorageName, NewStorageFactory) from its init (pkg/storage/internalstorage/register.go:28), where the registry function is RegisterStorageFactoryFunc (pkg/storage/register.go:9).

A fifth binary, binding-apiserver, runs the APIServer and the synchro manager in one process: it builds the manager with synchromanager.NewManager(...) and starts it with go synchromanager.Run(1, ctx.Done()) (cmd/binding-apiserver/app/binding_apiserver.go:73).

How a request flows ​

Trace kubectl get deployments -A across clusters, from the aggregated HTTP request to the database SELECT.

  1. ResourceHandler.ServeHTTP (pkg/kubeapiserver/resource_handler.go:42) is the entry point. It branches on requestInfo.Verb (pkg/kubeapiserver/resource_handler.go:53); for list and watch it inspects the label selector to decide whether to forward the request (pkg/kubeapiserver/resource_handler.go:54).
  2. If a cluster name was given, the PediaCluster lister checks the cluster exists (pkg/kubeapiserver/resource_handler.go:91). If the resource is not enabled in discovery, the request is passed to the delegate (pkg/kubeapiserver/resource_handler.go:107).
  3. The GroupVersionResource (GVR) plus subresource resolve a REST storage through r.rest.GetResourceREST(...) (pkg/kubeapiserver/resource_handler.go:117).
  4. For verb == list, it delegates to the upstream handler handlers.ListResource(storage, nil, reqScope, false, r.minRequestTimeout) (pkg/kubeapiserver/resource_handler.go:154). Reusing the standard handler is what makes responses kubectl-compatible.
  5. The upstream handler calls RESTStorage.List (pkg/kubeapiserver/resourcerest/storage.go:110). It first runs resolveListOptions (pkg/kubeapiserver/resourcerest/storage.go:77), which decodes the URL query into an internal.ListOptions (pkg/kubeapiserver/resourcerest/storage.go:80) and rejects owner search unless exactly one cluster is named (pkg/kubeapiserver/resourcerest/storage.go:92).
  6. It then calls s.Storage.List(ctx, objs, options) (pkg/kubeapiserver/resourcerest/storage.go:145), crossing into the storage layer interface.
  7. The internalstorage implementation ResourceStorage.List (pkg/storage/internalstorage/resource_storage.go:222) builds the query in genListObjectsQuery (pkg/storage/internalstorage/resource_storage.go:203) and reads rows with result.From(query) (pkg/storage/internalstorage/resource_storage.go:234), then decodes each stored JSON blob back into a runtime object.

The write path is the mirror image: an informer event becomes ResourceStorage.Create (pkg/storage/internalstorage/resource_storage.go:67) or ResourceStorage.Update (pkg/storage/internalstorage/resource_storage.go:110), which write the same Resource row.

Key design decisions ​

The defining choice is to store each resource as one JSON column rather than a normalized per-kind schema, and to translate label and field selectors into database JSON-path predicates at query time. The whole object is kept in Object datatypes.JSON (pkg/storage/internalstorage/types.go:105), and selectors are turned into predicates by the JSONQuery builder (pkg/storage/internalstorage/json_builder.go:54). One table fits every resource kind, and arbitrary field search reuses the same mechanism. The cost is dependence on per-dialect JSON functions and weaker index use, covered in Internals.

Clusterpedia deliberately leaves network connectivity between clusters out of scope. The README states it does not solve multi-cluster networking and expects tools such as Submariner, Skupper, or tower to be used alongside it. The scope is collecting state centrally and serving cross-cluster reads.

A smaller decision: there is no default ORDER BY, for performance reasons, with a comment pointing at pull request #44 (pkg/storage/internalstorage/util.go:324).

Extension points ​

  • PediaCluster CRD (staging/src/github.com/clusterpedia-io/api/cluster/v1alpha2/types.go:59): how clusters and their synced resources are declared.
  • Storage layer plugins: implement StorageFactory (pkg/storage/storage.go:20) and register a name (pkg/storage/register.go:9); shared-object plugins can also be loaded at runtime via plugin.Open in LoadPlugins (pkg/storage/plugin.go:9).
  • Raw and parameterized SQL (Structured Query Language) search, gated by feature flags read in applyListOptionsToQuery (pkg/storage/internalstorage/util.go:217).