Neuvième système du catalogue, et le seul dont aucune distribution ne publie d'image cloud : celle d'un tiers est épinglée et sa somme vérifiée à chaque téléchargement. Son seed diffère aussi — networkd prend la clé d'un bloc pour un NOM d'interface là où netplan honore un « match: », et sshd refuse un compte dont le shell n'existe pas, d'où /bin/sh. Ses dépendances se DÉCLARENT dans conf/nixos/erplibre.nix : envfs répond à /usr/bin/env, nix-ld donne aux roues manylinux leur chargeur. Vérifié sur une VM : le verrou de 362 paquets s'installe et Odoo répond. --- EN --- Ninth system of the catalogue, and the only one no distribution publishes a cloud image for: a third party's is pinned and its sum verified at every download. Its seed differs too — networkd reads a block's key as an interface NAME where netplan honours a "match:", and sshd refuses an account whose shell does not exist, hence /bin/sh. Its dependencies are DECLARED in conf/nixos/erplibre.nix: envfs answers /usr/bin/env, nix-ld gives manylinux wheels their loader. Checked on a VM: the 362-package lock installs and Odoo answers. Assisted-by: Claude Opus 5
612 lines
No EOL
29 KiB
Markdown
612 lines
No EOL
29 KiB
Markdown
|
||
# QEMU/KVM — Déploiement de VM Linux
|
||
|
||
`deploy_qemu.py` déploie une VM Linux (libvirt/KVM) à partir d'une image
|
||
cloud officielle, via `qemu-img` + `cloud-init` + `virt-install`. Choisissez
|
||
la distribution avec `--distro` (`ubuntu` par défaut, puis `debian`,
|
||
`fedora`, `almalinux`, `rocky`, `opensuse`, `arch`, `nixos` et `proxmox`) et
|
||
la version avec `--version` ; `--list-images` affiche tout le catalogue avec
|
||
les specs minimales. Il :
|
||
|
||
1. **Télécharge lui-même l'image cloud** (mise en cache, sans double
|
||
téléchargement).
|
||
2. La convertit en un disque de travail qcow2 dédié et le redimensionne.
|
||
3. Génère `user-data` / `meta-data` et construit le `seed.iso` (cloud-init).
|
||
4. Lance `virt-install` en important le disque + le seed en CD-ROM.
|
||
5. Attend le bail DHCP et affiche la commande SSH.
|
||
|
||
## Prérequis
|
||
|
||
- Un hôte disposant de KVM (bare-metal ou virtualisation imbriquée activée).
|
||
- Les droits `sudo` (le déploiement écrit dans `/var/lib/libvirt/images` et
|
||
pilote libvirt).
|
||
|
||
## Installation
|
||
|
||
Le script **installe automatiquement les composants manquants** : au premier
|
||
lancement, il détecte votre gestionnaire de paquets (apt / dnf / pacman /
|
||
zypper / brew), liste les composants absents (les outils clients, **ainsi que
|
||
le démon libvirt et l'émulateur QEMU système**), demande confirmation, les
|
||
installe avec `sudo`, puis active et démarre `libvirtd`. Utilisez `-y` pour
|
||
accepter automatiquement ou `--no-install-deps` pour désactiver ce
|
||
comportement.
|
||
|
||
Pour tout installer manuellement sur Ubuntu/Debian (pile KVM complète
|
||
recommandée) :
|
||
|
||
```bash
|
||
sudo apt install qemu-utils virtinst libvirt-clients cloud-image-utils \
|
||
libvirt-daemon-system qemu-system-x86
|
||
sudo systemctl enable --now libvirtd
|
||
sudo usermod -aG libvirt,kvm "$USER" # re-login / reconnectez-vous
|
||
```
|
||
|
||
`libvirt-daemon-system` fournit le démon `libvirtd` (et le socket
|
||
`/var/run/libvirt/libvirt-sock`) et `qemu-system-x86` l'émulateur — sans eux
|
||
`virt-install` échoue avec *« Failed to connect socket to
|
||
'/var/run/libvirt/libvirt-sock' »*. Le script les installe et les démarre pour
|
||
vous ; cette commande manuelle n'est utile que si vous préférez préparer
|
||
l'hôte vous-même ou utiliser `--no-install-deps`.
|
||
|
||
## Utilisation
|
||
|
||
Forme la plus simple — l'image est téléchargée automatiquement (chemin déduit
|
||
de `--version`, mis en cache dans `/var/lib/libvirt/images/iso`) :
|
||
|
||
```bash
|
||
sudo ./script/qemu/deploy_qemu.py --name test-vm --version 24.04 \
|
||
--ssh-key ~/.ssh/id_ed25519.pub
|
||
```
|
||
|
||
Télécharger (et vérifier) une image sans créer de VM :
|
||
|
||
```bash
|
||
sudo ./script/qemu/deploy_qemu.py --download-only --version 24.04 --verify
|
||
```
|
||
|
||
Déployer avec un mot de passe interactif au lieu d'une clé SSH :
|
||
|
||
```bash
|
||
sudo ./script/qemu/deploy_qemu.py --name test-vm --version 24.04 --ask-password
|
||
```
|
||
|
||
VM plus grande (8 Go RAM, 8 vCPU, disque 120 Go), en écrasant un disque
|
||
existant :
|
||
|
||
```bash
|
||
sudo ./script/qemu/deploy_qemu.py --name test-vm --version 24.04 \
|
||
--memory 8192 --vcpus 8 --disk-size 120G --ask-password --force
|
||
```
|
||
|
||
Prévisualiser ce qui serait fait, sans rien exécuter (sans sudo, sans
|
||
téléchargement) :
|
||
|
||
```bash
|
||
./script/qemu/deploy_qemu.py --name test-vm --version 24.04 --dry-run
|
||
```
|
||
|
||
Déploiement non interactif (accepte automatiquement l'installation des
|
||
dépendances) :
|
||
|
||
```bash
|
||
sudo ./script/qemu/deploy_qemu.py --name test-vm --version 24.04 \
|
||
--ssh-key ~/.ssh/id_ed25519.pub -y
|
||
```
|
||
|
||
Catalogue, par architecture (`deploy_qemu.py` fait autorité) :
|
||
|
||
| Distro | Versions | amd64 | arm64 | s390x |
|
||
|---|---|:-:|:-:|:-:|
|
||
| ubuntu | `24.04` (défaut), `25.10`, `26.04` | ✔ | ✔ | ✔ |
|
||
| debian | `11`, `12` (défaut), `13` | ✔ | ✔ | — |
|
||
| fedora | `41`, `42` (défaut), `43`, `44` | ✔ | ✔ | `43` seule |
|
||
| almalinux | `9` (défaut), `10` | ✔ | ✔ | ✔ |
|
||
| rocky | `9`, `10` (défaut) | ✔ | ✔ | ✔ |
|
||
| opensuse | `16.0` (défaut), `tumbleweed` | ✔ | ✔ | ✔ |
|
||
| arch | `latest` | ✔ | — | — |
|
||
| proxmox | `9` | ✔ | ✔ | — |
|
||
|
||
Fedora ne construit s390x que pour la version courante, et sur une
|
||
arborescence à part (`fedora-secondary`) — d'où la version unique.
|
||
|
||
`opensuse` recouvre deux produits distincts, pas deux versions du même. Leap
|
||
`16.0` est numérotée et stable (base SLE) : c'est le défaut. `tumbleweed` est
|
||
la rolling, gardée comme banc d'essai des ruptures à venir — sa dérive
|
||
d'instantanés est réelle, et elle impose un `zypper dup` complet avant toute
|
||
installation.
|
||
|
||
Les deux livrent un qpdf au-dessus du seuil de pikepdf : la compilation de
|
||
qpdf, une demi-heure, ne s'y déclenche jamais — ce qui compte sous émulation
|
||
s390x.
|
||
|
||
`proxmox`, c'est Proxmox VE, et il mérite un mot : il ne publie **aucune image
|
||
cloud** — son ISO est un installateur qui formate le disque. Le déploiement
|
||
fait donc ce que l'amont documente lui-même pour tous les autres cas,
|
||
*Proxmox VE sur Debian* : il télécharge l'image cloud **Debian trixie** (le
|
||
même fichier, si bien qu'un déploiement Debian 13 et un Proxmox se partagent un
|
||
seul téléchargement) et les paquets `pve` en font un hyperviseur — noyau
|
||
Proxmox, interface web sur `:8006`.
|
||
|
||
Le numéro de version est celui de Proxmox, pas de Debian : PVE 9 = trixie.
|
||
arm64 est officiel depuis PVE 9 (le Release `trixie` de l'amont annonce
|
||
`amd64 arm64`, et l'index arm64 sert bien `proxmox-ve`). s390x est absent et le
|
||
restera par cette voie : le dépôt n'a aucun index `binary-s390x` — le catalogue
|
||
le dit avant le déploiement plutôt que d'échouer au premier `apt`.
|
||
|
||
Une VM Proxmox est un hyperviseur DANS une VM : ses propres invités demandent
|
||
la virtualisation imbriquée à tous les étages. L'installation se fait par le
|
||
profil « Hyperviseur Proxmox VE (sans Odoo) » du menu de déploiement, ou à la
|
||
main dans la VM :
|
||
|
||
```bash
|
||
sudo ./script/proxmox/install_proxmox.sh --dry-run # dit ce qu'il ferait
|
||
sudo ./script/proxmox/install_proxmox.sh # puis : sudo reboot
|
||
```
|
||
|
||
Trois pièges de l'image cloud, tous rencontrés sur une VM réelle et traités par
|
||
le script : cloud-init tient encore le verrou d'`apt` au premier démarrage ;
|
||
`grub-pc`, tiré par les paquets `pve`, demande sur quel disque s'installer et
|
||
bloque toute la transaction sans préréponse ; et le chemin de secours UEFI
|
||
(`\EFI\BOOT\`) reçoit les binaires GRUB de Proxmox mais pas le `grub.cfg`
|
||
qui dit où trouver la configuration — sans quoi la VM s'arrête sur l'invite
|
||
`grub>`, sans menu ni noyau.
|
||
|
||
Fournissez un chemin d'image en argument positionnel pour surcharger
|
||
l'emplacement de téléchargement automatique.
|
||
|
||
Les Ubuntu `20.04` et `22.04` sont **abandonnées sur toutes les
|
||
architectures** : pikepdf réclame qpdf 12.2, dont la compilation exige C++20,
|
||
et focal livre GCC 9 — elle ne publie même pas de `g++-10` pour s390x. Python
|
||
3.8, node 10, cargo 0.67 et OpenSSL 1.1.1 avaient chacun leur contournement ;
|
||
leur accumulation, non.
|
||
|
||
## Après le déploiement
|
||
|
||
```bash
|
||
virsh list --all
|
||
virsh console test-vm # Ctrl+] to quit / pour quitter
|
||
virsh domifaddr test-vm --source lease # find the IP / trouver l'IP
|
||
ssh erplibre@<IP>
|
||
```
|
||
|
||
L'utilisateur par défaut est `erplibre` (modifiable avec `--user`).
|
||
|
||
## Via le menu TODO
|
||
|
||
Le script est intégré à l'assistant interactif. Lancez `make todo` (ou
|
||
`./script/todo/todo.py`), puis allez dans **Execute → Deploy → QEMU/KVM -
|
||
Deploy an Ubuntu VM (libvirt)**. De là, vous pouvez déployer une VM,
|
||
prévisualiser un dry-run, télécharger une image, lister les VM et afficher
|
||
l'IP d'une VM — le menu demande les paramètres et construit la commande pour
|
||
vous.
|
||
|
||
Quand une VM est graphique, le menu propose en plus une **liste à cocher
|
||
d'outils de développement** : PyCharm Community (posé depuis l'archive
|
||
officielle JetBrains dans `/opt/pycharm`, son lanceur ouvrant le dépôt
|
||
ERPLibre — la ligne Community, car le build unifié 2025.3 s'arrête sur un
|
||
écran de licence et n'ouvre jamais de projet),
|
||
Android Studio (`/opt/android-studio`, commande `studio` ou
|
||
`android-studio` ; x86_64 seulement — Google ne publie aucune archive Linux
|
||
aarch64) et un jeu
|
||
d'extensions GNOME suggérées.
|
||
|
||
Les extensions empaquetées par la distribution sont installées sans être
|
||
activées — leur UUID n'est pas connu de façon fiable, et le gestionnaire
|
||
d'extensions est là pour choisir. Trois extensions nommées par leur UUID sont,
|
||
elles, installées **et activées**, directement depuis extensions.gnome.org :
|
||
**gTile**, **Freon** et **Tracker**. L'archive est prise pour la version de
|
||
GNOME Shell qui tourne vraiment dans la VM — le même point d'entrée sert gTile
|
||
v59 en GNOME 46 et v62 en GNOME 48, si bien qu'une URL figée poserait une
|
||
version faite pour une autre release. Une archive mal appariée n'est de toute
|
||
façon jamais chargée par GNOME : il compare `metadata.json` à sa propre
|
||
version et affiche l'extension comme obsolète plutôt que de casser la session.
|
||
|
||
Les outils sont posés **avant** le clone et l'installation d'ERPLibre, et
|
||
l'ordre compte : PyCharm écrit le `.idea/` du dépôt à la première ouverture du
|
||
projet, et l'installation qui suit y lance `pycharm_configuration.py`
|
||
(`update_env_version.pycharm_update()`, qui se tait tant qu'il n'y a pas de
|
||
`.idea`). Cette première ouverture est automatisée : PyCharm est lancé une fois sous
|
||
Xvfb — un serveur d'affichage virtuel DANS la VM invitée, si bien que l'hôte
|
||
qui orchestre n'a besoin d'aucune bibliothèque graphique — avec les fenêtres
|
||
de confiance, de confidentialité et de partage de données répondues d'avance.
|
||
Mesuré sur une VM Ubuntu 26.04 à 16 Go : le `.idea/` est écrit en 195 s, et
|
||
l'installation y ajoute ensuite ses exclusions dans le `.iml`. Sans Xvfb, ou
|
||
si l'IDE n'y arrive pas en cinq minutes, le journal le dit et l'installation
|
||
continue.
|
||
|
||
Un quatrième ne demande aucun bureau : **ERPLibre mobile (compilation)**. Il
|
||
ajoute le dépôt mobile au manifeste — additif, donc il cohabite avec une
|
||
installation Odoo 18 —, lance l'`install-android.sh` du dépôt lui-même (JDK 17,
|
||
outils en ligne de commande, licences SDK acceptées, NDK, whisper.cpp et
|
||
sentencepiece), puis compile : `npm ci`, `vite build`, `cap sync`,
|
||
`gradlew assembleDebug`, et enfin `npm test`. **Une compilation en échec fait
|
||
échouer la VM** : le code de sortie remonte au tableau de bord, et le journal
|
||
NOMME la cause probable au lieu de laisser 40 Mo de journal Gradle à relire :
|
||
disque plein, plateforme SDK absente, JDK et Gradle incompatibles, licences non
|
||
acceptées, démon Gradle tué par le noyau (avec la RAM, le swap et le compte de
|
||
l'oom-killer, parce qu'une cause « mémoire » se prouve au lieu de s'affirmer),
|
||
ou trop de fichiers d'assets pour un APK. Le détail va dans
|
||
`~/erplibre-mobile-build.log`, dans la VM, pour que le journal d'installation
|
||
reste lisible.
|
||
|
||
Cette dernière cause est corrigée, et non contournée. L'application embarque les
|
||
dépôts du manifeste pour en parcourir le code hors ligne, et un APK est un ZIP
|
||
borné à 65535 entrées — un fichier par source en réclamait 123 678, et la
|
||
compilation s'arrêtait là. Ces fichiers y entrent désormais en **packs** :
|
||
des tranches de 4 Mo, plus un `index.json` par dépôt qui dit dans quelle tranche
|
||
se trouve un fichier, à quel offset et sur quelle longueur. La lecture demande
|
||
un intervalle d'octets, et retombe sur la tranche entière quand le serveur du
|
||
WebView ignore `Range` — 4 Mo au pire, et c'est pour cela que les tranches sont
|
||
bornées. Les images matricielles restent dehors : des captures d'écran
|
||
d'addons, dans un navigateur qui montre du texte.
|
||
|
||
Les images sont empaquetées aussi, et un fichier empaqueté n'a pas d'URL propre :
|
||
le lecteur fait un blob de ses octets. Les catalogues gettext, en revanche, sont
|
||
écartés — 41 594 fichiers `.po`/`.pot` pour 857 Mo, soit 72 % du poids, d'un
|
||
contenu que Weblate maintient et que personne ne lit sur un téléphone.
|
||
`BUNDLE_KEEP_PO=1` les ramène, `BUNDLE_SKIP_IMG=1` retire les images.
|
||
|
||
Mesuré sur une VM : 139 dépôts, 80 841 fichiers en 233 tranches, un APK de
|
||
354 Mo à **2 844 entrées**, et 20 fichiers relus depuis les packs identiques
|
||
octet pour octet à leur source. L'APK ne suit pas la charge — le texte se
|
||
compresse, le PNG non : le code seul fait 331 Mo d'assets pour environ 130 Mo
|
||
d'APK. L'installation vérifie le transfert avec
|
||
`script/mobile/check_bundle_transfer.py`, qui s'exécute aussi seul, et un
|
||
transfert manqué fait échouer la VM — une application qui ne porte pas le code
|
||
qu'elle est censée montrer n'est pas l'application demandée.
|
||
|
||
Il est borné aux distributions apt, parce que cet installateur amont commence
|
||
par `sudo apt install openjdk-17-jdk`. Il n'exige PAS Android Studio — une
|
||
simple VM serveur produit l'APK — et quand Android Studio est aussi coché, les
|
||
deux partagent un seul SDK via `ANDROID_HOME`. Sans Android, la même
|
||
application tourne dans un navigateur : `npm start`.
|
||
|
||
Un cinquième, **Émulateur Android (Pixel)**, crée un AVD. Conduisez-le depuis
|
||
le menu QEMU, *Émulateur Android (démarrage, tunnel, scrcpy)* : il démarre
|
||
l'émulateur sans fenêtre, puis donne le tunnel adb et la commande scrcpy.
|
||
Préférez cette voie à une fenêtre par X11 — scrcpy reçoit du H.264 encodé PAR
|
||
l'appareil, là où `ssh -X` fait traverser chaque image en pixels bruts, en
|
||
rendu logiciel. Si vous voulez la fenêtre, le chemin doit être ABSOLU, car
|
||
`ssh hôte 'commande'` ne lit ni `~/.profile` ni `~/.bashrc` :
|
||
`ssh -XC erplibre@<ip> '$HOME/android/emulator/emulator -avd erplibre -no-audio'`.
|
||
|
||
Il ne demande aucun bureau dans la VM, mais il exige KVM dans l'invitée, donc
|
||
la virtualisation imbriquée sur l'hôte ; le journal le dit quand `/dev/kvm`
|
||
manque. Le modèle n'est pas figé : on demande au SDK ses profils et le Pixel le
|
||
plus récent au plus petit écran gagne (ni Pro, ni XL, ni pliant, ni tablette).
|
||
Le rendu est « swangle » dans le `config.ini` de l'AVD — « auto » ouvrirait un
|
||
écran noir, et « swiftshader_indirect » n'existe plus, l'émulateur répondant
|
||
`Selected GPU option ... is not valid`.
|
||
|
||
Un sixième, **Forgejo**, installe une forge git auto-hébergée — le logiciel
|
||
derrière Codeberg — depuis le binaire statique officiel du projet, et la laisse
|
||
en service sur le port 3000, avec git par SSH sur 2222. Comme la compilation
|
||
mobile, elle n'a besoin d'aucun bureau ; contrairement à elle, aucune famille de
|
||
paquets n'est exclue : le binaire est statique, donc le même fichier sert apt,
|
||
dnf, pacman et zypper. C'est ce qui la rend portable sur les plateformes
|
||
ERPLibre sans une branche par distribution. Les architectures suivent l'amont,
|
||
qui publie amd64, arm64 et arm-6 — la case se grise sur s390x plutôt que de
|
||
poser un binaire qui ne s'exécutera pas.
|
||
|
||
Le travail vit dans `script/forgejo/install_forgejo.sh`, appelable seul sur une
|
||
machine existante : `./script/forgejo/install_forgejo.sh`. Il vérifie la somme
|
||
de contrôle publiée, écrit lui-même les quatre secrets pour que le service n'ait
|
||
jamais à réécrire sa propre configuration, et garde ses données en SQLite pour
|
||
ne pas disputer PostgreSQL à Odoo sur la même VM. Le rejouer est sans risque et
|
||
bon marché — 1,5 s mesuré, tout étant en place : il saute un binaire déjà à la
|
||
bonne version, ne réécrit jamais un `app.ini` existant et ne recrée pas
|
||
l'administrateur. `FORGEJO_VERSION`, `FORGEJO_HTTP_PORT`, `FORGEJO_ADMIN_USER`
|
||
et quelques autres le règlent ; `--help` les énumère.
|
||
|
||
Un septième, **Outils d'assistance IA**, pré-configure le shell où l'on
|
||
travaille : tig, htop et vim, rtk et son hook global, starship, un agent —
|
||
Claude Code ou opencode —, les réglages git, les hooks du dépôt et les
|
||
commandes Claude. Rien là-dedans ne fait échouer la VM : chaque pose est
|
||
bornée dans le temps et privée d'entrée standard, car un « curl | sh » qui
|
||
pose une question pendrait un déploiement sans terminal pour lui répondre.
|
||
|
||
Un huitième, **nix + nixos-anywhere**, est l'inverse de NixOS choisi dans le
|
||
catalogue : il laisse une VM ordinaire capable d'INSTALLER NixOS sur une
|
||
autre machine joignable en SSH. L'installateur officiel multi-utilisateur,
|
||
les flakes activés, et `nixos-anywhere` dans le profil de l'utilisateur.
|
||
L'option est grisée sur NixOS, où nix est déjà le système, et hors
|
||
amd64/arm64.
|
||
|
||
Chaque outil est filtré VM par VM — architecture, saveur de bureau et famille
|
||
de paquets — et sa place disque s'ajoute au plan avant que rien ne soit créé.
|
||
|
||
## Principales options
|
||
|
||
- `--distro` — `ubuntu` (défaut), `debian`, `fedora`, `almalinux`,
|
||
`rocky`, `opensuse`, `arch`, `nixos` ou `proxmox`. NixOS est la seule
|
||
image qu'aucune distribution ne publie : elle est rebâtie par un tiers,
|
||
donc la version est épinglée, sa somme sha256 vérifiée à chaque
|
||
téléchargement, et l'origine dite avant que rien ne soit créé.
|
||
- `--version` — version de la distro (défaut : celle par défaut de la distro).
|
||
- `--list-images` — affiche toutes les distros/versions et leurs specs.
|
||
- `--image-dir` — répertoire de cache des images (défaut
|
||
`/var/lib/libvirt/images/iso`).
|
||
- `--download-only` — télécharge l'image puis quitte (sans VM).
|
||
- `--name` — nom de la VM (requis pour le déploiement).
|
||
- `--memory`, `--vcpus`, `--disk-size` — dimensionnement de la VM. Omis,
|
||
`--memory` et `--disk-size` prennent le **minimum requis par la version**
|
||
choisie (valeurs libosinfo, voir `--list-images` : Ubuntu 24.04+ →
|
||
3072 Mo/20G, Debian → 1024 Mo/10G, Fedora → 2048 Mo/15G) ; `--vcpus`
|
||
vaut 2 par défaut.
|
||
- `--ssh-key`, `--ask-password`, `--password-hash` — authentification.
|
||
- `-y` / `--assume-yes` — accepte automatiquement l'installation des
|
||
dépendances.
|
||
- `--no-install-deps` — n'installe jamais les dépendances automatiquement.
|
||
- `--dry-run` — affiche les commandes sans rien exécuter.
|
||
- `--force` — écrase le disque de travail qcow2 existant.
|
||
- `--gpu` — accélération 3D par le GPU de l'hôte : `auto` (défaut, activée si
|
||
l'hôte a un nœud de rendu), `on` (forcer), `off` (rendu logiciel).
|
||
- `--gpu-node` — quel nœud de rendu utiliser, sur un hôte à plusieurs cartes.
|
||
- `--lang` — langue du guide affiché à la connexion SSH, `fr` (défaut) ou
|
||
`en`. Le menu TODO passe la sienne.
|
||
- `--erplibre-dir` — où ERPLibre sera installé dans la VM
|
||
(`~/git/erplibre`, ou `/opt/erplibre` en production). Ajoute la section
|
||
ERPLibre au guide de connexion ; omis, cette section est laissée de côté.
|
||
- `--erplibre-make` — la cible make qui a installé la VM
|
||
(ex. `install_odoo_18`), reprise dans le guide pour la mettre à jour.
|
||
- `--no-git-identity` — ne recopie pas les `user.name`, `user.email` et
|
||
`core.editor` de l'hôte dans le `~/.gitconfig` de la VM.
|
||
|
||
Lancez `./script/qemu/deploy_qemu.py --help` pour la liste complète.
|
||
|
||
## Guide de connexion (`/etc/motd`)
|
||
|
||
Chaque VM accueille celui qui s'y connecte en SSH avec les commandes de **sa**
|
||
distribution — `apt`, `dnf`, `zypper` ou `pacman` — et celles d'ERPLibre :
|
||
éditer le serveur, le redémarrer, mettre à jour des modules, mettre à jour
|
||
Odoo, inspecter l'instance, ouvrir le menu TODO. Il est écrit par cloud-init,
|
||
donc présent dès le premier démarrage : avant l'installation d'ERPLibre, et
|
||
encore là si elle échoue — le moment où l'on se connecte justement à la main.
|
||
|
||
`--dry-run` affiche le guide généré avec le reste du user-data. Il ne
|
||
s'affiche PAS pour un `ssh hôte 'commande'` : les journaux d'installation
|
||
restent nets.
|
||
|
||
L'identité git de l'hôte voyage avec lui, dans le `~/.gitconfig` de la VM :
|
||
un commit fait dans la VM porte alors votre nom plutôt que
|
||
`erplibre@<nom-de-vm>`. L'éditeur suit le même chemin — `core.editor`, la
|
||
ligne `config.conf` du guide et le paquet installé dans la VM viennent d'une
|
||
seule table, de sorte que le guide ne nomme jamais une commande absente.
|
||
|
||
## Gestion des VM
|
||
|
||
Lister, arrêter et supprimer les VM (le disque qcow2 sous
|
||
`/var/lib/libvirt/images` est conservé tant que vous ne le supprimez pas) :
|
||
|
||
```bash
|
||
sudo virsh list --all # toutes les VM et leur état / all VMs and state
|
||
sudo virsh shutdown <nom-vm> # arrêt propre ACPI / graceful shutdown
|
||
sudo virsh destroy <nom-vm> # arrêt forcé / force off (pull the plug)
|
||
sudo virsh undefine <nom-vm> # supprime la définition / remove definition
|
||
sudo virsh domifaddr <nom-vm> # adresse IP de la VM / VM IP address
|
||
```
|
||
|
||
`destroy` ne fait qu'éteindre la VM (disque conservé) ; `undefine` supprime sa
|
||
définition. Pour recréer proprement une VM du même nom, faites `destroy` +
|
||
`undefine` d'abord, ou redéployez avec `--force`.
|
||
|
||
## Le sous-réseau des VM
|
||
|
||
Toute VM déployée ici vit dans le réseau libvirt « default », qui sert un /24 —
|
||
`192.168.122.0/24` à l'installation. Les VM y prennent une adresse par DHCP et
|
||
en sortent par le `.1`, porté par le pont. Déplacer ce /24 sous une VM allumée
|
||
lui laisse un bail qui ne mène nulle part ; abattre le réseau détache son tap
|
||
du pont, et libvirt ne l'y remet pas de lui-même.
|
||
|
||
`network_qemu.py` lit cet état, et remet le sous-réseau sous les VM :
|
||
|
||
```bash
|
||
# Ce que sert le réseau, son pont, ses VM, leurs baux — ne modifie rien
|
||
./script/qemu/network_qemu.py --status
|
||
|
||
# Remettre le sous-réseau : arrêter les VM attachées, redéfinir, relancer
|
||
./script/qemu/network_qemu.py --recreate
|
||
./script/qemu/network_qemu.py --recreate --prefix 192.168.140
|
||
./script/qemu/network_qemu.py --recreate --force-off # VM sourdes à l'ACPI
|
||
```
|
||
|
||
Le préfixe par défaut est celui de libvirt, `192.168.122` : c'est celui que
|
||
supposent les entrées de `.ssh/config` et les notes prises avant. Un préfixe
|
||
qui recouvre ce que l'hôte route déjà est refusé — un pont qui prend l'adresse
|
||
de la passerelle de l'hôte, c'est ainsi qu'une machine perd son réseau. Une VM
|
||
qui n'obéit pas au shutdown annule la redéfinition plutôt que d'y perdre son
|
||
pont.
|
||
|
||
Les deux sont aussi au menu **TODO › Execute › Deploy › QEMU/KVM**, section
|
||
**Réseau**.
|
||
|
||
## Accès SSH depuis une autre machine (ProxyJump)
|
||
|
||
Avec le réseau NAT par défaut, la VM n'est joignable que **depuis l'hôte
|
||
KVM**. Pour l'atteindre depuis une autre machine **sans toucher au réseau**,
|
||
utilisez l'hôte comme rebond (il joint déjà la VM). Récupérez l'IP de la VM
|
||
avec `sudo virsh domifaddr <nom-vm>`, puis depuis l'autre machine :
|
||
|
||
```bash
|
||
# Rebond SSH vers la VM / jump through the KVM host
|
||
ssh -J user@<ip-hote> erplibre@<ip-vm>
|
||
|
||
# Tunnel d'un service, ex. Odoo 8069 / tunnel a service, then http://localhost:8069
|
||
ssh -L 8069:<ip-vm>:8069 user@<ip-hote>
|
||
```
|
||
|
||
Pour le rendre permanent, ajoutez ceci à `~/.ssh/config` sur l'autre machine
|
||
(ensuite `ssh myvm` suffit) :
|
||
|
||
```text
|
||
Host myvm
|
||
HostName <ip-vm> # ex. 192.168.122.50 (reseau NAT)
|
||
User erplibre
|
||
ProxyJump user@<ip-hote> # IP LAN de l'hote KVM
|
||
```
|
||
|
||
Ça marche en Wi-Fi et sans arrêter la VM — l'option la plus simple pour un
|
||
accès personnel. Préférez un pont (ci-dessous) si la VM doit être un serveur
|
||
à part entière exposé sur le LAN.
|
||
|
||
## Accélération 3D (GPU de l'hôte)
|
||
|
||
Une VM graphique sans accélération rend tout par le processeur — le bureau
|
||
comme l'émulateur Android qui tourne dedans. Le déploiement prend donc le GPU
|
||
de l'hôte **par défaut** (`--gpu auto`) : si l'hôte expose un nœud de rendu,
|
||
la VM reçoit un virtio-GPU avec `accel3d` et un affichage `egl-headless` qui
|
||
porte le contexte OpenGL **à côté** de la console VNC — il n'ouvre aucun port
|
||
et ne remplace rien. Pas de nœud de rendu, pas de 3D, et le déploiement dit
|
||
pourquoi au lieu de retomber en silence.
|
||
|
||
```bash
|
||
ls /dev/dri/renderD* # le GPU utilisable par QEMU — vide : pas de 3D
|
||
sudo virsh dumpxml <nom-vm> | grep -A2 -E "accel3d|egl-headless"
|
||
```
|
||
|
||
Une VM déjà installée se règle depuis le menu TODO **pendant qu'elle est
|
||
éteinte** : libvirt ne lit ces réglages qu'au démarrage de QEMU. `QEMU/KVM ›
|
||
Liste des VM › [2] Changer l'état`, puis acceptez *Régler le matériel avant de
|
||
démarrer*, ou prenez `[3] Régler le matériel seulement`. En formulaire si
|
||
Textual est présent, en invites sinon, il règle :
|
||
|
||
- **vCPU, RAM, démarrage automatique** — le dimensionnement ordinaire.
|
||
- **Mode CPU** — `host-passthrough` (celui du parc) donne les instructions du
|
||
processeur hôte telles quelles : c'est lui qui rend la virtualisation
|
||
imbriquée possible *dans* la VM. `host-model` décrit un modèle équivalent,
|
||
migrable vers une autre machine.
|
||
- **Écrans** — le `heads` du virtio-gpu, qui devient `max_outputs` sur la
|
||
ligne QEMU. La `vram` n'est délibérément *pas* proposée : sur un virtio-gpu,
|
||
libvirt l'écrit dans le XML et QEMU ne la reçoit jamais (à vérifier avec
|
||
`virsh domxml-to-native` : seul `max_outputs` y apparaît). Seul qxl la
|
||
consomme.
|
||
- **Réseau** — les réseaux libvirt et les ponts de l'hôte, ces derniers pour
|
||
poser la VM sur le LAN (voir la section du pont plus bas). Le basculement
|
||
garde l'adresse MAC et l'emplacement PCI : l'invité retrouve *sa* carte,
|
||
donc son nom d'interface et son bail DHCP.
|
||
|
||
Deux choses à savoir :
|
||
|
||
- Un hôte qui est **lui-même une VM** n'a aucun nœud de rendu, sauf si un GPU
|
||
lui a été transmis. Imbriqué sans passthrough, la 3D est hors d'atteinte :
|
||
l'émulateur Android tourne alors sur SwiftShader, et aucune option n'y
|
||
change rien.
|
||
- Quand la VM a la 3D, l'émulateur peut être essayé en `-gpu host` plutôt
|
||
qu'en `-gpu swangle`, son défaut : `EL_EMULATOR_GPU=host ./todo.sh`. Ça
|
||
reste un essai manuel — un émulateur dont le contexte GL échoue reste pendu
|
||
au lieu de retomber, d'où `swangle` par défaut.
|
||
|
||
## QEMU dans QEMU (imbriqué) & exposer la VM via un pont
|
||
|
||
Si l'hôte KVM est **lui-même une VM** (QEMU dans QEMU), le déploiement ne
|
||
fonctionne que si la **virtualisation imbriquée** est activée sur l'hôte
|
||
physique et que la VM intermédiaire utilise le mode CPU `host-passthrough`.
|
||
Vérifiez depuis l'hôte KVM (la première commande doit être non vide) :
|
||
|
||
```bash
|
||
grep -E -o '(vmx|svm)' /proc/cpuinfo | sort -u # extensions visibles / visible
|
||
# Sur l'hote PHYSIQUE / on the PHYSICAL host:
|
||
cat /sys/module/kvm_intel/parameters/nested # Intel -> Y/1
|
||
cat /sys/module/kvm_amd/parameters/nested # AMD -> Y/1
|
||
```
|
||
|
||
Pour activer l'imbrication sur l'hôte physique (Intel montré ; `kvm_amd` sur
|
||
AMD), puis recréer la VM intermédiaire en `host-passthrough` :
|
||
|
||
```bash
|
||
echo "options kvm_intel nested=1" | sudo tee /etc/modprobe.d/kvm-nested.conf
|
||
sudo modprobe -r kvm_intel && sudo modprobe kvm_intel # ou / or reboot
|
||
```
|
||
|
||
Sur **s390x et arm64**, le paramètre vit sur le module `kvm` lui-même, et non
|
||
sur `kvm_intel` / `kvm_amd` — et `/sys/module/kvm/parameters/nested` n'existe
|
||
même pas sur x86. Lire le mauvais fichier renvoie un `0` rassurant qui ne
|
||
commande rien :
|
||
|
||
```bash
|
||
echo "options kvm nested=1" | sudo tee /etc/modprobe.d/kvm-nested.conf
|
||
sudo modprobe -r kvm && sudo modprobe kvm # ou / or reboot
|
||
```
|
||
|
||
`nested` sur une machine signifie « j'autorise MES invités à faire tourner des
|
||
VM ». Pour accélérer une VM créée sur l'hôte H, le réglage appartient à
|
||
l'hyperviseur **au-dessus** de H, pas à H. La commande qui tranche, sur H :
|
||
|
||
```bash
|
||
ls -l /dev/kvm # absent -> pas d'imbrication, tout sera émulé
|
||
```
|
||
|
||
Mesuré sur un hôte s390x lui-même invité KVM sans imbrication : `/dev/kvm`
|
||
absent, `virsh dumpxml` affichant `<domain type='qemu'>`, et un démarrage de
|
||
7 min 30 au lieu de bien moins d'une minute. `systemd-detect-virt` dans la VM
|
||
ne prouve **pas** l'accélération — sur s390x, QEMU fabrique la réponse STSI et
|
||
annonce `kvm` même en TCG. Seul `<domain type=…>` sur l'hôte fait foi.
|
||
|
||
### Bridge for external access
|
||
|
||
A NAT VM is isolated; a **bridged** VM gets an IP directly on the LAN,
|
||
reachable by any machine. On the KVM host, create a bridge `br0` over the
|
||
physical NIC (**wired only** — Wi-Fi cannot be bridged). netplan (Ubuntu
|
||
server) — replace `enp3s0` with your interface:
|
||
|
||
Si l'imbrication est indisponible, QEMU tourne quand même en émulation
|
||
logicielle (TCG) — ça marche mais c'est lent.
|
||
|
||
### Pont pour l'accès externe
|
||
|
||
Une VM en NAT est isolée ; une VM **pontée** obtient une IP directement sur le
|
||
LAN, joignable par n'importe quelle machine. Sur l'hôte KVM, créez un pont
|
||
`br0` sur la carte physique (**filaire uniquement** — le Wi-Fi ne se ponte
|
||
pas). netplan (Ubuntu serveur) — remplacez `enp3s0` par votre interface :
|
||
|
||
```yaml
|
||
# /etc/netplan/01-br0.yaml
|
||
network:
|
||
version: 2
|
||
renderer: networkd
|
||
ethernets:
|
||
enp3s0: {dhcp4: no, dhcp6: no}
|
||
bridges:
|
||
br0:
|
||
interfaces: [enp3s0]
|
||
dhcp4: yes
|
||
parameters: {stp: false, forward-delay: 0}
|
||
```
|
||
|
||
Appliquez avec filet de sécurité (annulation auto en cas de coupure) et
|
||
vérifiez — ou via NetworkManager (Ubuntu bureau) :
|
||
|
||
```bash
|
||
# netplan
|
||
sudo netplan try && sudo netplan apply
|
||
ip addr show br0 # br0 porte l'IP du LAN / br0 holds the LAN IP
|
||
|
||
# NetworkManager (alternative)
|
||
nmcli con add type bridge ifname br0 con-name br0
|
||
nmcli con add type ethernet ifname enp3s0 master br0 con-name br0-port
|
||
nmcli con modify br0 ipv4.method auto
|
||
nmcli con down "Wired connection 1" ; nmcli con up br0
|
||
```
|
||
|
||
Rattachez ensuite la VM au pont — **soit à la création** :
|
||
|
||
```bash
|
||
sudo ./script/qemu/deploy_qemu.py --name <nom-vm> --version 24.04 \
|
||
--ssh-key ~/.ssh/id_ed25519.pub --network bridge=br0,model=virtio -y --force
|
||
```
|
||
|
||
**soit par édition d'une VM déjà créée** : arrêtez-la, remplacez son bloc
|
||
`<interface>` (`type='network'` / `<source network='default'/>` →
|
||
`type='bridge'` / `<source bridge='br0'/>`), puis redémarrez-la :
|
||
|
||
```bash
|
||
sudo virsh shutdown <nom-vm>
|
||
sudo virsh edit <nom-vm> # mettre l'interface en bridge=br0
|
||
sudo virsh start <nom-vm>
|
||
sudo virsh domifaddr <nom-vm> # nouvelle IP LAN / new LAN IP
|
||
```
|
||
|
||
La VM obtient maintenant une IP LAN de votre routeur, joignable par les autres
|
||
machines. Depuis Internet, il faut en plus une redirection de port sur votre
|
||
routeur (ou un VPN) ; en configuration imbriquée, l'hôte externe doit aussi
|
||
rediriger/exposer la VM intermédiaire. |