pkgs.writeErofsLayers,profiles/ch-runner: MVE

$ nix-build -A examples.dummy.config.debug.closure.erofs.list | xargs
cat | xargs du -h
749M    /nix/store/bzfv5x6lycq6hzhjv6d6vlk1q8fdg9di-base0.erofs
24M     /nix/store/hp41jfq36y0mmjrzqilyh3jfsvqic3kb-nixos.erofs

$ nix run -f . examples.dummy.config.uvms.cloud-hypervisor.runner
...
<<< Welcome to NixOS 25.11pre-git (x86_64) - ttyS0 >>>

nixos login:

The definition of the `pkgs` fixpoint was moved to pkgs/default.nix.
For that, dirToAttrs was moved to lib/, imported ad hoc
This commit is contained in:
Else, Someone 2025-09-19 16:28:48 +03:00
parent 1828835a1d
commit 28d3f89ad4
12 changed files with 428 additions and 77 deletions

114
profiles/ch-runner.nix Normal file
View file

@ -0,0 +1,114 @@
{
config,
lib,
pkgs,
...
}:
# It is not the intent to stick to the microvm.nix-like static interface,
# but we shall begin by reproducing at least some of their work.
let
cfg = config.uvms.cloud-hypervisor;
inherit (config.debug.closure.erofs) layers;
in
{
options = {
uvms.cloud-hypervisor.enable = lib.mkEnableOption "Configure guest (e.g. fileSystems)";
uvms.cloud-hypervisor.runner = lib.mkOption {
type = lib.types.package;
description = "A naive script for running this system in cloud-hypervisor";
};
uvms.cloud-hypervisor.extraArgv = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
};
uvms.cloud-hypervisor.argv = lib.mkOption {
type = lib.types.listOf lib.types.str;
};
uvms.cloud-hypervisor.extraCmdline = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
};
uvms.cloud-hypervisor.cmdline = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [
"earlyprintk=ttyS0"
"console=ttyS0"
"reboot=t"
"panic=-1"
"init=${config.system.build.toplevel}/init"
]
++ config.boot.kernelParams
++ config.uvms.cloud-hypervisor.extraCmdline;
};
};
config = lib.mkMerge [
{
uvms.cloud-hypervisor.argv = lib.mkBefore (
[
(lib.getExe pkgs.cloud-hypervisor)
"--cmdline=${lib.concatStringsSep " " cfg.cmdline}"
"--kernel=${config.boot.kernelPackages.kernel}/${pkgs.stdenv.hostPlatform.linux-kernel.target}"
"--initramfs=${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}"
"--vsock=cid=4,socket=CONNECT.sock"
"--api-socket=ch.sock"
"--serial=tty"
"--console=null"
"--watchdog"
"--seccomp=true"
]
++ cfg.extraArgv
);
uvms.cloud-hypervisor.runner = pkgs.writeShellScriptBin "run-${config.networking.hostName}" ''
set -euo pipefail
GUESTNAME=${config.networking.hostName}
args=(
${lib.concatMapStringsSep "\n" lib.escapeShellArg cfg.argv}
)
mkdir -p "$HOME/uvms/$GUESTNAME"
cd "$HOME/uvms/$GUESTNAME"
cleanup() {
rm "$HOME/uvms/$GUESTNAME"/{ch,CONNECT}.sock
}
exec -a "uuvm/$GUESTNAME" "''${args[@]}"
'';
}
(lib.mkIf cfg.enable {
boot.initrd.availableKernelModules = [
"erofs"
"overlay"
"virtio_mmio"
"virtio_pci"
"virtio_blk"
# "9pnet_virtio"
# "9p"
"virtiofs"
];
boot.initrd.systemd.enable = true;
fileSystems = {
"/nix/store" = {
fsType = "overlay";
overlay.lowerdir = map (img: "/nix/.ro-stores/${toString img.seq}") layers;
neededForBoot = true;
};
}
// lib.listToAttrs (
map (
img:
lib.nameValuePair "/nix/.ro-stores/${toString img.seq}" {
device = "/dev/disk/by-label/${img.label}";
neededForBoot = true;
options = [ "x-systemd.device-timeout=5" ];
}
) layers
);
uvms.cloud-hypervisor.argv = [
"--memory=size=1536M,hotplug_size=1536M,hotplugged_size=512M,hotplug_method=virtio-mem,mergeable=on,shared=on"
"--cpus=boot=4"
"--disk"
]
++ map (img: "path=${img},readonly=true,id=${toString img.label}") layers;
})
];
}

View file

@ -0,0 +1,85 @@
{
config,
lib,
pkgs,
modulesPath,
...
}:
let
cfg = config.debug.closure;
inherit (lib.types) package;
inherit (config.system.build) toplevel;
ps = pkgs.callPackage ../pkgs { };
inherit (ps) writeErofsLayers;
emptySystem = import (pkgs.path + "/nixos/lib/eval-config.nix") {
modules = [
(modulesPath + "/profiles/minimal.nix")
{
system.stateVersion = config.system.stateVersion;
fileSystems."/".fsType = "tmpfs";
boot.loader.grub.enable = false;
networking.hostName = "base";
networking.nftables.enable = true;
networking.useNetworkd = true;
systemd.network.enable = true;
}
];
};
in
{
options.debug.closure = {
info = lib.mkOption {
type = package;
description = "closureInfo { rootPaths = [ toplevel ]; }";
};
write = lib.mkOption {
type = package;
description = "writeClosure [ toplevel ]";
};
extraLayers = lib.mkOption {
type = lib.types.listOf package;
description = "Roots from which to generate `erofs.layers`";
defaultText = ''
[
(nixosSystem { modules = [ ]; }).config.system.build.toplevel
# implicitly appended: config.system.build.toplevel
]
'';
};
erofs.layers = lib.mkOption {
type = lib.types.listOf package;
description = "writeErofs [ ... ]";
readOnly = true;
};
erofs.list = lib.mkOption {
type = package;
description = "writeClosure [ (writeErofs [ ... ])... ]";
};
};
config.debug.closure = {
info = pkgs.closureInfo { rootPaths = [ toplevel ]; };
write = pkgs.writeClosure [ toplevel ];
erofs.list =
pkgs.runCommand "layers"
{
__structuredAttrs = true;
inherit (cfg.erofs) layers;
}
''
printf "%s\n" "''${layers[@]}" > "$out"
'';
extraLayers = lib.mkMerge [
(lib.mkBefore [ emptySystem.config.system.build.toplevel ])
# (lib.mkAfter [config.system.build.toplevel])
];
erofs.layers = writeErofsLayers {
layers = cfg.extraLayers ++ [
toplevel
];
labelUpper = config.networking.hostName;
};
};
}

View file

@ -6,7 +6,7 @@
# zswap is said to be more reliable than zram
boot.kernelParams = lib.optionals (!config.zramSwap.enable) [ "zswap.enabled=1" ];
}
(lib.optionalAttrs (options ? microvm) {
(lib.optionalAttrs (options ? "microvm" && config.microvm.guest.enable) {
microvm = {
hypervisor = lib.mkDefault "cloud-hypervisor";
graphics.enable = lib.mkDefault true;

View file

@ -1,47 +1,42 @@
{
options,
config,
lib,
pkgs,
...
}:
let
mkIfGuest = import ../lib/mkIfMicrovmGuest.nix { inherit options config lib; };
in
{
imports = [
./vsock-connect-guest.nix
./uvms-users.nix
];
config = lib.optionalAttrs (options ? microvm) {
microvm = {
hypervisor = "cloud-hypervisor";
volumes = [
config = lib.mkMerge [
(mkIfGuest {
microvm = {
hypervisor = "cloud-hypervisor";
volumes = [
{
image = "swapfile.img";
mountPoint = "/var/swapfiles";
size = 1024;
}
];
};
swapDevices = [
{
image = "swapfile.img";
mountPoint = "/var/swapfiles";
size = 1024;
device = "/var/swapfiles/swap0";
size = 768;
}
];
cloud-hypervisor.extraArgs = [
"--api-socket=ch.sock"
];
};
swapDevices = [
{
device = "/var/swapfiles/swap0";
size = 768;
}
];
})
{
boot.kernelParams = [ "zswap.enabled=1" ];
zramSwap.enable = false;
boot.kernelParams = [ "zswap.enabled=1" ];
zramSwap.enable = false;
systemd.services."microvm@".serviceConfig.ExecStartPost =
pkgs.writeShellScript "microvm-fix-umask" ''
if [[ -e CONNECT.vsock ]] ; then
chmod g+r CONNECT.vsock
fi
'';
systemd.tmpfiles.settings."10-muvm" = {
"/var/lib/microvms/*/CONNECT.sock".z.mode = "660";
};
};
}
];
}

View file

@ -1,18 +1,27 @@
{ options, lib, ... }:
{
config =
lib.optionalAttrs (options ? "microvm") {
options,
config,
lib,
...
}:
let
mkIfGuest = import ../lib/mkIfMicrovmGuest.nix { inherit options config lib; };
in
{
config = lib.mkMerge [
(mkIfGuest {
microvm.cloud-hypervisor.extraArgs = [
"--vsock"
"cid=4,socket=CONNECT.sock"
];
}
// {
})
{
# Somehow, sshd calls to PAM with PAM_RHOST="UNKNOWN",
# prompting a slow DNS look-up each time...
#
# https://mastodon.acm.org/@nobody/115108458851355328
# https://github.com/linux-pam/linux-pam/issues/885#issuecomment-3030698895
networking.hosts."100::" = [ "UNKNOWN" ];
};
}
];
}