Skip to content
KubeCell logoKubeCell

Introduction

Core concepts

Six ideas carry the whole design. If you understand these, the CRDs and the operational behavior will feel predictable.

Cell

A Cell is the unit of physical capacity: one OCM ManagedCluster bound to one host cluster, plus a single machine profile describing what the machines offer. It is the failure and isolation boundary. The spec has exactly two groups:

  • managedClusterRef.name — the bound OCM ManagedCluster. Once any VirtualCluster has resolved this Cell, the reference is immutable.
  • machineProfile — a name and an inline device contract. Multiple physical machines with the same profile all count as inventory; there is no separate MachineProfile CRD.

Everything else lives in status, filled in by the controller: phase, conditions, managed cluster observations, provider readiness, per-node capacity and allocatable, aggregated inventory, storage inventory, and allocated NodePorts. Never hand-write status.

cell.yaml
apiVersion: kubecell.io/v1alpha1kind: Cellmetadata:  name: cell1  namespace: kubecell-systemspec:  managedClusterRef:    name: cell1              # bound OCM ManagedCluster; immutable once resolved  machineProfile:    name: ascend-910b        # single machine profile per Cell    devices:    - name: ascend      resourceName: huawei.com/Ascend910  # exact host extended-resource key

Device contract, not reservation

The device list is a declarative authorization. The observed allocatable of the resource key is the source of fact. If a Cell declares eight cards but the host only allocates four, the effective capacity is four and a CapabilityMismatch condition is reported.

VirtualNodeClass

A VirtualNodeClass is a cluster-scoped quota tier. One name maps to one entitlement: the hard requests and limits applied to every child cluster that uses it, plus the storage class for child volumes.

  • The class is applied as a Kubernetes ResourceQuota in the child cluster's host namespace, so enforcement happens on the host, not in the child cluster.
  • CPU, memory, and extended resources (accelerators) all follow the same pattern: request equals limit, integer values for devices.
  • Classes are platform-owned. Tenants reference them but never edit them; changing a tier means changing the class, which is a platform action.
virtualnodeclass.yaml
apiVersion: kubecell.io/v1alpha1kind: VirtualNodeClassmetadata:  name: ascend-910b-small    # cluster-scoped; the tier name tenants referencespec:  entitlement:    workloadHard:      requests.cpu: "4"      limits.cpu: "4"      requests.memory: 8Gi      limits.memory: 8Gi      requests.huawei.com/Ascend910: "2"      limits.huawei.com/Ascend910: "2"  storageClassName: topolvm-provisioner

VirtualClusterPlan

A VirtualClusterPlan is a side-effect-free preflight. Its spec is isomorphic to a VirtualCluster, so you can copy a plan file into a creation request once it is accepted. The controller evaluates the request against the current Cell inventory and reports exactly one decision:

DecisionMeaningWhat to do
AcceptedBuildable under the current inventory snapshot.Create the VirtualCluster (rechecked at creation time).
RejectedA required resource is missing: CPU, memory, devices, storage, or ports.Read checks and adjust the request or Cell.
UnknownInventory is unavailable or stale.Fix freshness, then retry once observations recover.

Acceptance is not reservation

Plans evaluate once and never refresh periodically. expiresAt marks when the conclusion becomes void, and the controller re-runs feasibility at real creation time. If someone else consumed capacity in between, creation is blocked and the recheck report appears in status.feasibilityChecks.
plan.yaml
apiVersion: kubecell.io/v1alpha1kind: VirtualClusterPlanmetadata:  name: child-dev-plan  namespace: kubecell-systemspec:  cellRef: {name: cell1}  classRef: {name: ascend-910b-small}status:  decision: Accepted          # Accepted | Rejected | Unknown  expiresAt: "2026-09-22T10:15:00Z"   # stale conclusions are void  checks: []                  # per-resource findings when Rejected

VirtualCluster

A VirtualCluster is the product. Eight lines of spec: which Cell, which class. The controller does the rest:

  1. Resolves the Cell and class, then writes an immutable resolution snapshot.
  2. Re-runs feasibility and stops on a non-accepted conclusion.
  3. Creates the Foundation Work, then the Instance Work once the first is Applied.
  4. Discovers the child API address from host nodes and publishes the admin kubeconfig.
  5. Reports readiness, quota usage, and per-reflected-Pod observations in status.

Key fields are immutable: the Cell reference and class reference cannot be silently changed. To move a workload to different hardware, create a new child cluster. To change the size of a child cluster, switch to a different class.

The logical node

Every child cluster exposes exactly one node, always named kubelet. It is an entitlement view, not a physical machine. This is why there are no horizontal fields — no node count, node pools, or replicas — and why DaemonSet-style semantics are unsupported.

nodes.sh
$ kubectl--kubeconfig child-dev.yaml get nodesNAME      STATUS   ROLES    AGE   VERSIONkubelet   Ready<none>   42s   v1.34.2+k3s1

Reflected Pods and PVCs

K3k mirrors child-cluster Pods and PVCs into the child's host namespace. A reflected Pod is a real host Pod with identical resource requests, so the host scheduler places it and the namespace quota accounts for it. The same is true for storage: a child PVC becomes a host PVC backed by TopoLVM.

  • Scheduling decisions stay in Kubernetes — there is no second scheduler to keep in sync.
  • hostPath volumes are rejected at admission with a replacement hint pointing to the storage class.
  • Accelerator requests must use the identical extended-resource key in the child and on the host.

Foundation Work and Instance Work

The controller renders exactly two OCM ManifestWork objects per child cluster. The split is intentional: guardrails first, workload second.

Foundation Work

  • Host namespace with policy labels and annotations
  • ResourceQuota from the class entitlement
  • LimitRange with 100m CPU / 256Mi memory defaults
  • NetworkPolicy denying cross-namespace traffic
  • Port-forward RBAC for the child cluster

Instance Work

  • K3k Cluster in Shared mode
  • K3k VirtualClusterPolicy
  • Server Pod, data volume, NodePort Service created by K3k
  • Child admin kubeconfig Secret read back through the proxy

Resolution snapshot

VirtualCluster.status.resolved is the sole basis for steady-state rendering. It records the resolved Cell, class entitlement, storage class, and the platform constants that applied at resolution time. If a class changes later, existing child clusters keep the snapshot they were built from until the operator acts — that is how KubeCell avoids surprise in-place mutations.

Platform constants vs. API fields

A field belongs in a CRD only if it has more than one safe value. Everything else is a platform constant owned by the release:

ConcernWhere it lives
K3s, K3k, OCM versionsRelease bundle, verified by the installer
API exposure method and port rangePlatform constant; endpoint is discovered into status
Ingress apps suffixPlatform-global fixed suffix
Default container requestsLimitRange injected by the Foundation Work
Quota tierVirtualNodeClass (the one thing tenants choose)

Glossary

Management clusterSingle-node K3s carrying the OCM Hub and the KubeCell controller; runs no tenant workloads.
ManagedClusterOCM registration object representing one Cell host one-to-one.
CellOne ManagedCluster plus its host K3s cluster; the failure and isolation boundary.
VirtualNodeClassCluster-scoped quota tier: hard-limit numbers plus storage class.
VirtualClusterThe child cluster instance; key fields immutable, rich status.
VirtualClusterPlanSide-effect-free preflight; one-shot evaluation, acceptance is not reservation.
Logical child nodeThe single node in the child API, always named kubelet; a view, not a machine.
Reflected PodHost Pod created by K3k mirroring a child Pod, with identical requests.
Cluster proxy / MSAOCM mechanisms; the only path by which the controller reads host APIs.
appsSuffixPlatform-global external domain suffix; hostnames derive as <ingress>.<vc>.<suffix>.

Next: install the management plane and onboard a host, or jump straight to the API reference for every field.