Per-machine supervisor. Exactly one Hyper.Node runs per BEAM node; it owns
every microVM scheduled onto this machine.
Children:
VM routing lives in
Hyper.Cluster.Routing(a cluster-wide CRDT started byHyper.Cluster, above this supervisor), not here - this node only owns the local processes that run its microVMs.Hyper.Node.ImageStore- a node-local content-addressed blob cache. Started before the VM supervisor so VMs can pull base images on boot.Hyper.Node.VMSupervisor- a localDynamicSupervisorthat starts oneHyper.Node.FireVMMper VM. Local on purpose: a firecracker VM is pinned to this machine's kernel/rootfs/cgroup/tap devices and cannot migrate, so we deliberately avoidHorde.DynamicSupervisor(which would try to restart VMs on a surviving node - cold-booting a ghost).Hyper.Node.Users- manages an availability pool of users. Each VM gets its own user id and group id.Hyper.Node.Budget.Supervisor- the node's resource budget: hard memory/disk accounting (Hyper.Node.Budget.Hard) plus theSys.Monreal-time monitors backing the soft budget (Hyper.Node.Budget.Soft). Lives here, not at the application root, because both are per-node and only meaningful while this node hosts VMs.Hyper.Node.Reaper- a periodic, liveness-aware GC for per-VM host resources (orphaned firecracker cgroups andhyper-rw-*dm volumes) stranded by an unclean death whose vm_id never reboots. Started last so the VM supervisor it consults for liveness is already up.
Summary
Functions
Returns a specification to start this module under a supervisor.
Run argv in a VM hosted on this node, identified by vm_id, via its
relay/agent. The node-local half of Hyper.exec/3, which resolves the owning
node and :erpc-calls this function there.
Fork the running VM parent_vm_id onto this node: budget-admitted like any
placement (try_run/3), erroring with the node's admission verdict
(:mem_exhausted, :cpu_saturated, ...) instead of falling back elsewhere.
The guest is asked to sync first (best-effort); the snapshot is otherwise
crash-consistent, like a power cut.
Publish the running VM vm_id's disk state as a new derived image and return
what a remote node needs to boot a fork of it: the new img_id plus the
parent's instance type, arch, and boot_args. The slow-fork half of
Hyper.Vm.fork/1; the parent keeps running throughout.
Boot a fork of the running parent VM (described by its start Opts) on this
node: same image lineage, rootfs snapshotted COW-style from the parent's
mutable layer.
Boot an image-backed VM on this node: claim a uid, build the mutable rootfs layer, resolve the kernel, and start the VM supervisor. The uid is freed and the mutable layer torn down automatically when the VM supervisor dies.
Start a microVM on this node.
Tear down an image-backed VM started by start_image_vm/2.
Start a VM here and confirm its budget.
CPU time accrued by vm_id's meter on this node but not yet flushed to
the usage table. The node-local half of Hyper.usage/1, which resolves the
owning node and :erpc-calls this function there.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec exec(Hyper.Vm.Id.t(), [String.t()], keyword()) :: {:ok, %{stdout: binary(), stderr: binary(), exit_code: integer()}} | {:error, term()}
Run argv in a VM hosted on this node, identified by vm_id, via its
relay/agent. The node-local half of Hyper.exec/3, which resolves the owning
node and :erpc-calls this function there.
@spec fork_vm_local(Hyper.Vm.Id.t()) :: {:ok, pid()} | {:error, term()}
Fork the running VM parent_vm_id onto this node: budget-admitted like any
placement (try_run/3), erroring with the node's admission verdict
(:mem_exhausted, :cpu_saturated, ...) instead of falling back elsewhere.
The guest is asked to sync first (best-effort); the snapshot is otherwise
crash-consistent, like a power cut.
@spec publish_fork_image(Hyper.Vm.Id.t()) :: {:ok, %{ img_id: Hyper.Img.id(), type: Hyper.Vm.Instance.t(), arch: Hyper.Vm.Instance.arch(), boot_args: String.t() | nil }} | {:error, term()}
Publish the running VM vm_id's disk state as a new derived image and return
what a remote node needs to boot a fork of it: the new img_id plus the
parent's instance type, arch, and boot_args. The slow-fork half of
Hyper.Vm.fork/1; the parent keeps running throughout.
@spec start_forked_vm(Hyper.Vm.Id.t(), Hyper.Node.FireVMM.Opts.t()) :: {:ok, pid()} | {:error, term()}
Boot a fork of the running parent VM (described by its start Opts) on this
node: same image lineage, rootfs snapshotted COW-style from the parent's
mutable layer.
@spec start_image_vm(Hyper.Vm.Id.t(), Hyper.Vm.Spec.t()) :: {:ok, pid()} | {:error, term()}
Boot an image-backed VM on this node: claim a uid, build the mutable rootfs layer, resolve the kernel, and start the VM supervisor. The uid is freed and the mutable layer torn down automatically when the VM supervisor dies.
@spec start_vm(Hyper.Node.FireVMM.Opts.t()) :: DynamicSupervisor.on_start_child()
Start a microVM on this node.
@spec stop_image_vm(pid()) :: :ok
Tear down an image-backed VM started by start_image_vm/2.
@spec test_system() :: :ok | {:error, term()}
@spec try_run( Hyper.Vm.Instance.Spec.t(), (-> {:ok, pid()} | {:error, term()}), (pid() -> :ok) ) :: {:ok, pid()} | {:error, term()}
Start a VM here and confirm its budget.
start_fun boots the VM and returns {:ok, vm_pid}; the reservation is held
against vm_pid and released when it dies. If the reserve loses a race (the
node filled up since the scheduler's snapshot) the just-started VM is torn down
via stop_fun and {:error, reason} is returned.
@spec unflushed_usage(Hyper.Vm.Id.t()) :: Unit.Time.t()
CPU time accrued by vm_id's meter on this node but not yet flushed to
the usage table. The node-local half of Hyper.usage/1, which resolves the
owning node and :erpc-calls this function there.