feat(vms): VM de dev microvm.nix (just vm, nix run .#dev)
vms/dev.nix décrit une VM de dev (autologin marc, console série) construite par l'input microvm ; ses runners sont exposés dans flake.nix pour être lancés directement (`nix run .#dev`) et le Justfile ajoute `just vm` / `just vm-build`, qui gardent l'état de la VM (overlay du store, socket qmp) dans ~/.local/state/microvms/<nom> (supprimer le dossier pour repartir de zéro). Le runner passe par `path:` volontairement : ça marche même quand vms/<nom>.nix n'est pas encore commité.
This commit is contained in:
@@ -13,6 +13,24 @@ home:
|
|||||||
nixos:
|
nixos:
|
||||||
sudo nixos-rebuild switch --impure --flake .#{{env('HOST')}}
|
sudo nixos-rebuild switch --impure --flake .#{{env('HOST')}}
|
||||||
|
|
||||||
|
# VM de dev microvm.nix : construit si besoin puis lance la VM dans le terminal
|
||||||
|
# (console série, autologin marc). L'état (overlay du /nix/store, socket qmp)
|
||||||
|
# vit dans ~/.local/state/microvms/<nom>/, supprimer ce dossier pour repartir
|
||||||
|
# d'une VM vierge. Nom = clé de nixosConfigurations (vms/<nom>.nix), défaut dev.
|
||||||
|
vm name="dev":
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
state="${XDG_STATE_HOME:-$HOME/.local/state}/microvms/{{name}}"
|
||||||
|
mkdir -p "$state"
|
||||||
|
cd "$state"
|
||||||
|
# path: volontaire : marche même si vms/<nom>.nix n'est pas encore commité
|
||||||
|
exec nix run "path:{{justfile_directory()}}#{{name}}"
|
||||||
|
|
||||||
|
# Construit le runner d'une VM sans la lancer (préchauffe, pratique pour voir
|
||||||
|
# les erreurs de build sans VM au milieu)
|
||||||
|
vm-build name="dev":
|
||||||
|
nix build --no-link "path:{{justfile_directory()}}#{{name}}"
|
||||||
|
|
||||||
iso:
|
iso:
|
||||||
nix build .#nixosConfigurations.{{env('HOST')}}-iso.config.system.build.isoImage > nixos.iso
|
nix build .#nixosConfigurations.{{env('HOST')}}-iso.config.system.build.isoImage > nixos.iso
|
||||||
|
|
||||||
|
|||||||
@@ -239,8 +239,22 @@
|
|||||||
./users.nix
|
./users.nix
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# VM de dev microvm.nix (input `microvm`, voir vms/) : `just vm` ou
|
||||||
|
# `nix run .#dev`. Dupliquer vms/dev.nix pour une 2e VM.
|
||||||
|
dev = nixpkgs.lib.nixosSystem {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
specialArgs = {inherit inputs;};
|
||||||
|
modules = [
|
||||||
|
inputs.microvm.nixosModules.microvm
|
||||||
|
./vms/dev.nix
|
||||||
|
];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Runners des VM de dev, lançables directement : `nix run .#dev`.
|
||||||
|
packages.x86_64-linux.dev = self.nixosConfigurations.dev.config.microvm.declaredRunner;
|
||||||
|
|
||||||
nixOnDroidConfigurations.default = inputs.nix-on-droid.lib.nixOnDroidConfiguration {
|
nixOnDroidConfigurations.default = inputs.nix-on-droid.lib.nixOnDroidConfiguration {
|
||||||
# pkgs = import inputs.nixpkgs-droid {
|
# pkgs = import inputs.nixpkgs-droid {
|
||||||
pkgs = import inputs.nix-on-droid.inputs.nixpkgs {
|
pkgs = import inputs.nix-on-droid.inputs.nixpkgs {
|
||||||
|
|||||||
+180
@@ -0,0 +1,180 @@
|
|||||||
|
# Base commune des VM de dev microvm.nix (input `microvm` du flake).
|
||||||
|
#
|
||||||
|
# Une VM = ce module + vms/<nom>.nix + deux entrées dans flake.nix
|
||||||
|
# (nixosConfigurations.<nom> et packages.x86_64-linux.<nom>).
|
||||||
|
# Exemple complet et mode d'emploi : vms/dev.nix.
|
||||||
|
#
|
||||||
|
# Pourquoi le build est rapide :
|
||||||
|
# - le /nix/store de tower est partagé en 9p (lecture seule) : aucune image
|
||||||
|
# squashfs/erofs de store n'est construite, et tout ce que tower a déjà
|
||||||
|
# construit ou téléchargé est directement disponible dans la VM
|
||||||
|
# (`nix shell`, `nix develop`, exécuter un binaire du store : 0 build) ;
|
||||||
|
# - même nixpkgs que l'hôte : le noyau invité est le dérivé de
|
||||||
|
# boot.kernelPackages de tower, donc déjà en store ;
|
||||||
|
# - /nix/store est monté par-dessus un overlay écrivable posé sur un petit
|
||||||
|
# disque (nix-store-overlay.img) : `nix build` fonctionne DANS la VM.
|
||||||
|
# Le reste (/ /var /home) est un tmpfs : chaque démarrage repart propre.
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
microvm = {
|
||||||
|
hypervisor = "qemu";
|
||||||
|
|
||||||
|
# optimize = false : microvm.nix compile sinon une variante de qemu sans
|
||||||
|
# GUI (`minimal = true`) qui n'est dans aucun cache binaire (28 min de
|
||||||
|
# compilation locale) ET qui est compilée sans le réseau user de qemu
|
||||||
|
# (slirp) : `-netdev user` échoue donc au démarrage. Le qemu_kvm standard
|
||||||
|
# (même version, avec slirp) est en cache.nixos.org : rien à construire.
|
||||||
|
optimize.enable = false;
|
||||||
|
|
||||||
|
# Ressources ; surcharger dans vms/<nom>.nix. NE PAS mettre mem = 2048
|
||||||
|
# (valeur exacte qui bloque qemu, cf. microvm.nix#171).
|
||||||
|
vcpu = lib.mkDefault 4;
|
||||||
|
mem = lib.mkDefault 4096;
|
||||||
|
|
||||||
|
shares = [
|
||||||
|
{
|
||||||
|
tag = "ro-store";
|
||||||
|
source = "/nix/store";
|
||||||
|
mountPoint = "/nix/.ro-store";
|
||||||
|
# 9p est intégré à qemu (pas besoin de virtiofsd, contrairement à
|
||||||
|
# proto = "virtiofs" qui suppose le module hôte microvm + systemd).
|
||||||
|
proto = "9p";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
# Overlay écrivable du store : lower = /nix/.ro-store, upper = le disque
|
||||||
|
# ci-dessous. Le disque est créé au premier lancement (chemin relatif au
|
||||||
|
# répertoire d'état depuis lequel tourne le runner, cf. recette `just vm`).
|
||||||
|
writableStoreOverlay = "/nix/.rw-store";
|
||||||
|
volumes = [
|
||||||
|
{
|
||||||
|
image = "nix-store-overlay.img";
|
||||||
|
mountPoint = config.microvm.writableStoreOverlay;
|
||||||
|
size = 8192; # Mo, fichier sparse (n'occupe que ce qui est écrit)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
# Réseau slirp (user) : la VM sort sur Internet, et les ports de
|
||||||
|
# forwardPorts sont redirigés depuis tower (bind 127.0.0.1 uniquement).
|
||||||
|
interfaces = [
|
||||||
|
{
|
||||||
|
type = "user";
|
||||||
|
id = "user";
|
||||||
|
mac = "02:00:00:1f:1d:01"; # changer par VM (cosmétique, réseaux isolés)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
forwardPorts = [
|
||||||
|
# ssh dans la VM depuis tower
|
||||||
|
{
|
||||||
|
from = "host";
|
||||||
|
host.address = "127.0.0.1";
|
||||||
|
host.port = 2222;
|
||||||
|
guest.port = 22;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
# Pas de fenêtre graphique : la console s'affiche en série dans le terminal
|
||||||
|
# qui lance la VM (qemu -nographic), avec autologin de marc.
|
||||||
|
graphics.enable = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
# --- Optimisations de démarrage ---
|
||||||
|
# `microvm.optimize.enable = false` plus haut empêche de récupérer celles du
|
||||||
|
# module microvm (il les couple à un qemu sans slirp) : on les reprend ici
|
||||||
|
# telles quelles, sans toucher au paquet qemu.
|
||||||
|
documentation.enable = lib.mkDefault false; # les docs pèsent lourd, inutiles en VM
|
||||||
|
boot = {
|
||||||
|
initrd.systemd.enable = lib.mkDefault true; # initrd systemd : démarrage bien plus rapide
|
||||||
|
swraid.enable = lib.mkDefault false;
|
||||||
|
kernelParams = ["8250.nr_uarts=1"]; # une seule console série
|
||||||
|
};
|
||||||
|
networking.useNetworkd = lib.mkDefault true; # networkd plutôt que dhcpcd (plus rapide)
|
||||||
|
systemd.network.wait-online.enable = lib.mkDefault false; # bug systemd#29388
|
||||||
|
systemd.tpm2.enable = lib.mkDefault false;
|
||||||
|
|
||||||
|
# --- Système invité minimal ---
|
||||||
|
system.stateVersion = "25.11";
|
||||||
|
networking.hostName = lib.mkDefault "vm-dev"; # vms/<nom>.nix met le vrai nom
|
||||||
|
networking.useDHCP = true; # l'interface slirp a besoin d'un bail DHCP
|
||||||
|
networking.firewall.enable = false; # NAT ; seuls les ports de forwardPorts sont joignables
|
||||||
|
|
||||||
|
time.timeZone = "America/New_York";
|
||||||
|
|
||||||
|
services.openssh = {
|
||||||
|
enable = true;
|
||||||
|
settings.PermitRootLogin = "no";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Console série : shell direct en marc (bac à sable local, jetable).
|
||||||
|
services.getty.autologinUser = "marc";
|
||||||
|
|
||||||
|
users.users.marc = {
|
||||||
|
isNormalUser = true;
|
||||||
|
description = "Marc Partensky";
|
||||||
|
shell = pkgs.zsh;
|
||||||
|
extraGroups = ["wheel" "kvm"];
|
||||||
|
# Même hash que users.nix (celui de tower) : login console par mot de passe
|
||||||
|
# possible si l'autologin est retiré.
|
||||||
|
hashedPassword = "$6$dE28cUDZ9G81wzwo$JoO.6M91zdtR0ja/g1pw/LKE8aAMugmGkIyOiSuqdjEWVKId9F2jxqdvh5Bo69uOC3/g7uP6O3mZmzxqD2pKU1";
|
||||||
|
openssh.authorizedKeys.keys = [
|
||||||
|
# clé de marc (users.nix)
|
||||||
|
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDiUE73uIEgijfmSsDwBvZmecQnqjBUPRKlMDmevsThc1YJNWEHl57NNIUcx6XSCDPKu5azayImLqIBt8wT5xlqtNX20uCnikDfXZ8gFbGlMRTGZKutQZIRmUrUS5mz97S4dVVK+n5WU+OwOfEg/XKXPh4WbTVDpfTTg7RopRAXkma56HV2TJM0ndPRN8VLmBmtnwdQwEpJ0tRRY+KOHmTojsH65eaZ89+BHbto+Kg+lk6x8IH5VDCRQNHgTEccOpOGYBSHRpoZi1a5h3yajf/eGAQ9Cd38DOsfMtm84oFlii7oXPyxwXoM+uH1SDnvLXyheIrV/XLUurSbEb4aJni6Zu79Z9l8xHhUNmVNSZqWOWUvPbAHlDKUzsbxgk9Zs9OTvSDaRzGhViYl4e1Qc993yerGSW1HHIvYUKM7o5nSQqskSOvOI+ahL5fIbgdyVx4FeuURZIyZSxCz4jIJTK15/6pkT/miHKv+vmQhsoLCqgyXY4SG1p9ruzKkzBe03ZQVW5WeFDLYRjZ+Z4Q2IL2K3BmLgp8tInkPJizQ7v5UGSiajJmPxY0j+CqdH9ZlIBdf8GS+run/N4hpMC1/ayUZRbCY5jg4c8bev8dKEZYJKPs/Hq2zLRZe4YtxcKuiGhgIwQOzo/QrCvSM4pVDgo+d2DjEzIdapqE8hF6BHWDg/w== marc.partensky@gmail.com"
|
||||||
|
# clé de l'agent hermes (debug / aide depuis tower)
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKF3huPUXHP6P6SBXHHw9k7HGh6Cs8ntoRk2pnqrG2Hc hermes@tower"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
security.sudo.wheelNeedsPassword = false; # configuration jetable
|
||||||
|
|
||||||
|
programs.zsh.enable = true;
|
||||||
|
users.defaultUserShell = pkgs.zsh;
|
||||||
|
|
||||||
|
nix.settings = {
|
||||||
|
experimental-features = ["nix-command" "flakes"];
|
||||||
|
trusted-users = ["root" "marc"];
|
||||||
|
# ne pas activer auto-optimise-store : incompatible avec writableStoreOverlay
|
||||||
|
};
|
||||||
|
|
||||||
|
# Volontairement léger : tout le reste se récupère depuis le store partagé de
|
||||||
|
# tower (`nix shell`/`nix develop`) ou s'ajoute dans vms/<nom>.nix.
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
curl
|
||||||
|
fd
|
||||||
|
git
|
||||||
|
htop
|
||||||
|
jq
|
||||||
|
just
|
||||||
|
neovim
|
||||||
|
ripgrep
|
||||||
|
rsync
|
||||||
|
tmux
|
||||||
|
tree
|
||||||
|
unzip
|
||||||
|
vim
|
||||||
|
wget
|
||||||
|
];
|
||||||
|
|
||||||
|
# /home est un tmpfs recréé à chaque démarrage : sans ~/.zshrc, zsh lance son
|
||||||
|
# assistant "nouvel utilisateur" à chaque connexion. On dépose un rc minimal.
|
||||||
|
system.activationScripts.vm-zshrc = ''
|
||||||
|
install -D -o marc -g users -m 0644 ${pkgs.writeText "vm-zshrc" "# VM de dev jetable : ports redirigés listés dans le motd\nautoload -Uz compinit && compinit -C\nalias ll='ls -alF'\nexport EDITOR=nvim\n"} /home/marc/.zshrc
|
||||||
|
'';
|
||||||
|
|
||||||
|
# motd généré depuis microvm.forwardPorts : la correspondance des ports
|
||||||
|
# VM -> tower s'affiche à la connexion (et suit toute modif des forwards).
|
||||||
|
# Depuis 26.05 users.motd n'écrit plus /etc/motd : pam_motd l'affiche en
|
||||||
|
# direct, et les modules sshd/login l'activent déjà (showMotd = true).
|
||||||
|
users.motd = let
|
||||||
|
sshRule = lib.findFirst (p: p.guest.port == 22) null config.microvm.forwardPorts;
|
||||||
|
appRules = builtins.filter (p: p.guest.port != 22) config.microvm.forwardPorts;
|
||||||
|
addr = p: if p.host.address == "" then "127.0.0.1" else p.host.address;
|
||||||
|
in ''
|
||||||
|
VM de dev microvm.nix : /nix/store de tower partagé.
|
||||||
|
${lib.optionalString (sshRule != null) " ssh depuis tower : ssh -p ${toString sshRule.host.port} marc@127.0.0.1\n"}${lib.concatMapStringsSep "\n" (p: " port VM ${toString p.guest.port} -> http://${addr p}:${toString p.host.port} sur tower") appRules}
|
||||||
|
code de l'hôte : microvm.shares (voir vms/<nom>.nix)
|
||||||
|
arrêt : poweroff, ou <état>/current/bin/microvm-shutdown
|
||||||
|
'';
|
||||||
|
}
|
||||||
+74
@@ -0,0 +1,74 @@
|
|||||||
|
# VM de dev « dev » : `just vm` (ou `nix run .#dev`).
|
||||||
|
#
|
||||||
|
# Pour créer une autre VM :
|
||||||
|
# 1. cp vms/dev.nix vms/app2.nix, changer networking.hostName (et les ports)
|
||||||
|
# 2. flake.nix : dupliquer le bloc nixosConfigurations.dev en .app2, et
|
||||||
|
# packages.x86_64-linux.app2 = self.nixosConfigurations.app2.config.microvm.declaredRunner;
|
||||||
|
# 3. just vm app2
|
||||||
|
{
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
imports = [./default.nix];
|
||||||
|
|
||||||
|
networking.hostName = "dev";
|
||||||
|
|
||||||
|
microvm = {
|
||||||
|
# tower : 16 cœurs / 62 Go mais machine bien chargée, rester raisonnable.
|
||||||
|
vcpu = 6;
|
||||||
|
mem = 6144;
|
||||||
|
|
||||||
|
# Code de l'hôte visible dans la VM, en lecture/écriture (9p). Le montage
|
||||||
|
# est à l'uid de marc (1000 = le même que dans la VM), donc les fichiers
|
||||||
|
# restent à lui. Réduire à un projet précis si besoin.
|
||||||
|
shares = [
|
||||||
|
{
|
||||||
|
tag = "git";
|
||||||
|
source = "/home/marc/git";
|
||||||
|
mountPoint = "/mnt/host/git";
|
||||||
|
proto = "9p";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
# Ports de dev : le serveur lancé DANS la VM est joignable depuis tower.
|
||||||
|
# Décalés de +10000 pour ne pas entrer en conflit avec un service de tower
|
||||||
|
# (3000 = gitea par ex.) : qemu refuse de démarrer si le port hôte est déjà
|
||||||
|
# pris. Ici : VM:3000 -> tower:13000, VM:5173 -> tower:15173, etc.
|
||||||
|
forwardPorts = [
|
||||||
|
{
|
||||||
|
from = "host";
|
||||||
|
host.address = "127.0.0.1";
|
||||||
|
host.port = 13000;
|
||||||
|
guest.port = 3000;
|
||||||
|
} # node/next
|
||||||
|
{
|
||||||
|
from = "host";
|
||||||
|
host.address = "127.0.0.1";
|
||||||
|
host.port = 14000;
|
||||||
|
guest.port = 4000;
|
||||||
|
} # phoenix
|
||||||
|
{
|
||||||
|
from = "host";
|
||||||
|
host.address = "127.0.0.1";
|
||||||
|
host.port = 15173;
|
||||||
|
guest.port = 5173;
|
||||||
|
} # vite
|
||||||
|
{
|
||||||
|
from = "host";
|
||||||
|
host.address = "127.0.0.1";
|
||||||
|
host.port = 18000;
|
||||||
|
guest.port = 8000;
|
||||||
|
} # django/fastapi
|
||||||
|
{
|
||||||
|
from = "host";
|
||||||
|
host.address = "127.0.0.1";
|
||||||
|
host.port = 18080;
|
||||||
|
guest.port = 8080;
|
||||||
|
} # divers
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Paquets propres à cette VM (la base est dans vms/default.nix) :
|
||||||
|
# environment.systemPackages = with pkgs; [python3 postgresql];
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user