Donnez un runtime à feint et un serveur créé par l'API devient une machine réelle, sur un vrai pont réseau, portant l'adresse que l'API a publiée, et dans laquelle vous ouvrez une session SSH. C'est ce qui sépare un émulateur d'un serveur de maquettes : le bloc demandé est le bloc obtenu, un groupe de sécurité ferme réellement un port, et la machine répond. Cette page compare les quatre modes, donne la procédure Incus et OVN sur Debian 13, et déroule le parcours SSH sur les trois fournisseurs.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Choisir le mode machine selon ce que vous voulez prouver.
- Installer Incus et OVN sur Debian 13, câblage OVN compris.
- Vérifier que la machine porte l'adresse publiée par l'API.
- Ouvrir une session SSH sur Scaleway, Outscale et Exoscale émulés.
Par défaut, feint est un plan de contrôle : il répond, et rien ne tourne. Donnez-lui un runtime et un serveur créé devient un conteneur ou une machine virtuelle réelle, sur un vrai pont réseau, portant l'adresse que l'API a publiée. C'est la différence entre un émulateur et un simple serveur de maquettes.
Les quatre modes ne délivrent pas la même chose, et un seul écart compte vraiment :
--vm | Machines | Adresses | Pare-feu | Isolation inter-VPC | Noyau propre | Nécessite |
|---|---|---|---|---|---|---|
off (défaut) | non | non | non | non | non | rien |
incus | oui | oui | oui | non | non | Incus 6.0.4+ |
incus-vm | oui | oui | oui | non | oui | Incus + KVM |
incus-ovn | oui | oui | oui | oui | non | Incus + OVN |
L'isolation est la seule capacité qui change de verdict selon le mode. Deux réseaux privés de deux VPC différents ne doivent pas se joindre. En mode pont, ils se joignent, et la cause est documentée par le runtime lui-même : le trafic entre ponts gérés sur un même hôte est routé directement. Un réseau OVN est un réseau logique doté de son propre routeur, donc la séparation vient de la topologie et non d'une règle.
Plutôt que de deviner, demandez à l'émulateur ce que son mode sait prouver :
feint start --vm incuscurl -s localhost:4599/_feint/health | jq -c '.capabilities'{"machines":true,"addresses":true,"firewall":true,"isolation":false,"own_kernel":false}Le même appel en --vm incus-ovn renvoie "isolation":true. Cette
déclaration est faite pour être lue par vos tests : une suite qui compare un
nom de mode en dur doit être modifiée à chaque évolution, une suite qui lit
les capacités continue de fonctionner.
Comment installer Incus et OVN sur Debian 13
Section intitulée « Comment installer Incus et OVN sur Debian 13 »Debian 13 est la distribution la plus simple pour ce mode, et la procédure
tient en trois temps : le dépôt, les paquets, le câblage OVN. C'est ce dernier
que personne ne devine, et sans lui incus network create --type=ovn échoue
avec une erreur qui ne nomme ni la cause ni le réglage manquant.
Un mot sur le choix du dépôt, parce qu'il se discute. Debian 13 « trixie »
package Incus 6.0.4, exactement le plancher en dessous duquel les listes
de contrôle d'accès sur une interface sont refusées. Le paquet de la
distribution suffit donc, contrairement à Ubuntu 24.04 qui livre 6.0.0 et ne
bougera plus. Le dépôt Zabbly, publié par le mainteneur d'Incus, apporte la
série 7.2 recommandée, celle sur laquelle les mesures du projet ont été
faites. Sur ma VM Debian 13, apt-cache policy incus affiche bien
6.0.4-2+deb13u8 avant l'ajout du dépôt.
-
Installer les prérequis et récupérer la clé du dépôt dans son propre trousseau. Une clé déposée dans
/etc/apt/keyringsne signe que le dépôt qui la nomme, là où le magasin système vaudrait pour tout.Fenêtre de terminal apt-get updateapt-get install -y --no-install-recommends ca-certificates curl gpg uidmapmkdir -p /etc/apt/keyringscurl -fsSL https://pkgs.zabbly.com/key.asc -o /etc/apt/keyrings/zabbly.ascgpg --show-keys --with-fingerprint /etc/apt/keyrings/zabbly.ascVérifiez l'empreinte affichée avant de continuer : elle doit valoir
4EFC 5906 96CB 15B8 7C73 A3AD 82CC 8797 C838 DCFD, pourZabbly Kernel Builds. C'est le seul moment où vous décidez à qui vous faites confiance. -
Déclarer le dépôt, en dérivant la suite et l'architecture de la machine plutôt qu'en les écrivant en dur.
Fenêtre de terminal cat > /etc/apt/sources.list.d/zabbly-incus.sources <<EOFEnabled: yesTypes: debURIs: https://pkgs.zabbly.com/incus/stableSuites: $(. /etc/os-release && echo "$VERSION_CODENAME")Components: mainArchitectures: $(dpkg --print-architecture)Signed-By: /etc/apt/keyrings/zabbly.ascEOFSur Debian 13,
Suitesvauttrixie. Aucuncurl | shdans cette procédure : chaque fichier est écrit sur disque, et la clé est vérifiée avant que le moindre paquet n'arrive. -
Installer Incus, OVN et Open vSwitch. Incus vient de Zabbly, les paquets OVN de Debian.
Fenêtre de terminal apt-get updateapt-get install -y --no-install-recommends \incus incus-client ovn-central ovn-host openvswitch-switchContrôle immédiat :
incus --versiondoit répondre7.2etovn-nbctl --versionovn-nbctl 25.03.0sur une trixie à jour. -
Démarrer les services, puis attendre. Les unités systemd rendent la main avant que les bases de données OVN n'écoutent, et une commande lancée dans cet intervalle échoue sans raison apparente.
Fenêtre de terminal systemctl enable --now openvswitch-switch ovn-central ovn-hostwhile [ ! -S /run/ovn/ovnnb_db.sock ]; do sleep 1; done -
Indiquer à Open vSwitch où se trouve la base southbound. C'est la moitié du câblage, celle qui relie le commutateur au plan de contrôle OVN.
Fenêtre de terminal ovs-vsctl set open_vswitch . \external_ids:ovn-remote=unix:/run/ovn/ovnsb_db.sock \external_ids:ovn-encap-type=geneve \external_ids:ovn-encap-ip=127.0.0.1 -
Initialiser Incus, puis donner au pont d'uplink la plage que les réseaux OVN viendront y découper.
Fenêtre de terminal incus admin init --minimalincus network set incusbr0 ipv4.address=10.108.0.1/24incus network set incusbr0 \ipv4.dhcp.ranges=10.108.0.10-10.108.0.99 \ipv4.ovn.ranges=10.108.0.100-10.108.0.199L'adresse se fixe dans une commande séparée des plages : celles-ci sont validées contre l'adresse courante du pont, et
incus admin init --minimalen choisit une au hasard. Sansipv4.ovn.ranges, la création d'un réseau OVN est refusée avec un message qui accuse le réseau plutôt que le pont. -
Vérifier par un vrai réseau, pas par la liste des paquets installés.
Fenêtre de terminal incus network create ovntest --type=ovn network=incusbr0incus network delete ovntestNetwork ovntest createdvalide l'ensemble du câblage. C'est le seul contrôle qui prouve quelque chose : des paquets installés mais mal reliés passent tous les autres.
Ne désactivez pas AppArmor. La tentation existe, parce que l'intégration continue du projet OVN coupe AppArmor avant ses tests système. Ce contournement vise des binaires compilés depuis les sources, hors des chemins couverts par les profils packagés. Une installation par paquets embarque les profils dont elle a besoin : sur la VM de test, Incus, OVN et Open vSwitch fonctionnent tous avec AppArmor actif. Couper un contrôle d'accès obligatoire pour faire tourner un outil de test est un mauvais échange.
Le verdict final ne se lit pas dans apt, mais dans la capacité déclarée :
feint doctor --vm autofeint start --vm autocurl -s localhost:4599/_feint/health | jq -c '.machines, .capabilities.isolation'Sur la VM Debian 13 fraîchement installée, doctor annonce machine runtime: incus-ovn avec isolation true, et l'API de santé confirme
"incus-ovn" puis true. Une valeur false signifie qu'OVN est absent,
ou présent mais non câblé.
La machine porte-t-elle vraiment l'adresse publiée ?
Section intitulée « La machine porte-t-elle vraiment l'adresse publiée ? »Oui, et cela se vérifie en trois commandes. Créez un réseau privé sur un bloc choisi, rattachez-y un serveur, démarrez-le, puis demandez au runtime, pas à l'émulateur, ce que la machine porte.
# le bloc demandé est le bloc obtenucurl -s -X POST "http://127.0.0.1:4599/vpc/v2/regions/fr-par/private-networks" \ -H 'Content-Type: application/json' \ -d '{"name":"lab","subnets":["10.51.0.0/24"]}' | jq -c '{id, subnet: .subnets[0].subnet}'
# ce que l'API publiecurl -s "http://127.0.0.1:4599/ipam/v1/regions/fr-par/ips" | jq -r '.ips[0].address'
# ce que la machine porte, vu du runtimeincus exec "feint-scw-<id-du-serveur>" -- ip -4 -o addr showSur mon poste, l'IPAM publie 10.51.0.2/24 et le conteneur porte
10.51.0.2/24 : les deux adresses coïncident. Le même essai en
--vm incus-ovn donne 10.52.0.2/24 des deux côtés. La machine créée
s'appelle feint-scw-<identifiant du serveur>, ce qui permet de la retrouver
sans chercher.
Tout ce que l'émulateur crée est étiqueté, et feint clean retire
exactement ces objets, sans toucher au reste de votre hôte :
feint clean --vm incusremoved 1 machine(s), 1 network(s), 1 rule set(s)Si Incus n'est pas encore installé sur votre poste, la section Incus du site couvre la mise en place.
Comment ouvrir une session SSH sur une machine émulée
Section intitulée « Comment ouvrir une session SSH sur une machine émulée »L'adresse publique que l'API publie est réellement routée, et vous pouvez vous y connecter en SSH sur les trois fournisseurs. La section précédente portait sur le plan privé, l'adresse du sous-réseau ; celle-ci porte sur le plan public, celui par lequel on ouvre une session. C'est le point où l'émulation cesse d'être une affaire de réponses JSON : vous enregistrez une clé par l'API du fournisseur, vous créez un serveur, et vous ouvrez une session dessus avec le client SSH ordinaire, exactement comme sur le cloud réel.

Chaque fournisseur publie ses adresses dans son propre bloc de
documentation, et ce détail a une raison précise : deux clouds émulés sur la
même machine ne doivent jamais router le même /32.
| Fournisseur | Bloc publié | Norme | Utilisateur par défaut |
|---|---|---|---|
| Scaleway | 203.0.113.0/24 | TEST-NET-3 | root |
| Outscale | 198.51.100.0/24 | TEST-NET-2 | outscale |
| Exoscale | 192.0.2.0/24 | TEST-NET-1 | celui que déclare le template |
Ces plages sont réservées à la documentation par la RFC 5737 : elles ne
sont routables nulle part sur Internet, ce qui en fait exactement le bon
choix pour une adresse publique qui n'existe que sur votre poste. Le parcours ci-dessous
demande un runtime de machines actif, donc --vm incus au minimum.
ssh-keygen -t ed25519 -N '' -C feint-lab -f ./ideval "$(feint env scaleway)"
# la clé passe par l'API IAM, comme sur le vrai Scalewayscw iam ssh-key create name=feint-lab public-key="$(cat ./id.pub)"
# une adresse flexible, puis le serveur qui la porteip_id=$(scw instance ip create zone=fr-par-1 -o json | jq -r '(.ip // .).id')srv=$(scw instance server create name=ssh-lab type=DEV1-S zone=fr-par-1 ip="$ip_id" -o json | jq -r .id)scw instance server start "$srv" zone=fr-par-1
# l'adresse que l'API publie est celle où l'on se connecteip=$(scw instance server get "$srv" zone=fr-par-1 -o json | jq -r '.public_ips[0].address')ssh -F /dev/null -i ./id -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "root@$ip"Une fois dans la machine, l'interface porte l'adresse publiée en /32, à côté de
l'adresse privée du réseau :
ssh-labrooteth0 203.0.113.2/32eth0 10.209.84.9/24eval "$(feint env outscale)"
oapi-cli CreateKeypair --KeypairName feint-lab --PublicKey "$(cat ./id.pub)"ip_json=$(oapi-cli CreatePublicIp)ip_id=$(echo "$ip_json" | jq -r '.PublicIp.PublicIpId')public=$(echo "$ip_json" | jq -r '.PublicIp.PublicIp')
vm=$(oapi-cli CreateVms --ImageId ami-00000001 --VmType tinav6.c1r1p2 \ --KeypairName feint-lab | jq -r '.Vms[0].VmId')oapi-cli LinkPublicIp --PublicIpId "$ip_id" --VmId "$vm"
ssh -F /dev/null -i ./id -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "outscale@$public"L'empreinte renvoyée par CreateKeypair est celle que calcule
ssh-keygen, et ReadVms republie la même adresse que CreatePublicIp
a rendue : c'est ce que vérifie la suite de conformité avant même d'essayer de
se connecter.
i-577826f8outscaleL'utilisateur n'est pas une propriété du cloud mais du template, qui le
déclare dans son champ default-user. Le lire plutôt que le supposer
est la seule façon correcte de procéder.
eval "$(feint env exoscale)"
exo compute ssh-key register feint-lab ./id.pubtpl=$(exo -O json compute instance-template list | jq -r '.[0].name')type=$(exo -O json compute instance-type list | jq -r '"\(.[0].family).\(.[0].name)"')
exo compute instance create ssh-lab --zone "$EXOSCALE_ZONE" \ --template "$tpl" --instance-type "$type" --ssh-key feint-lab
# l'adresse sur laquelle on se connecte est une IP élastique attachéeinst=$(exo -O json compute instance list | jq -r '.[] | select(.name=="ssh-lab") | .id')exo -O json compute elastic-ip create --description feint-labeip=$(exo -O json compute elastic-ip list | jq -r '.[0].ip_address')exo compute instance elastic-ip attach "$inst" "$eip"
ssh -F /dev/null -i ./id -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "debian@$eip"Sur le template Debian 12 du catalogue émulé, default-user vaut debian,
et l'IP élastique tombe sur 192.0.2.1.
Ce que l'hôte peut joindre dépend du mode
Section intitulée « Ce que l'hôte peut joindre dépend du mode »Une adresse privée de sous-réseau répond depuis l'hôte en mode pont, mais pas en OVN, et l'émulateur le déclare au lieu de le laisser deviner. La cause tient à la topologie : le routeur logique qui sépare deux VPC traduit aussi les connexions venues de l'hôte au retour.
curl -s localhost:4599/_feint/health | jq -c '.capabilities'{"machines":true,"addresses":true,"firewall":true,"isolation":false,"own_kernel":false,"private_from_host":true}En --vm incus-ovn, la même requête renvoie "isolation":true et
"private_from_host":false. Les deux capacités varient donc en sens
inverse, et c'est le compromis à connaître : le mode qui isole vraiment
deux VPC est celui depuis lequel votre poste ne joint pas une adresse interne. Le plan
public reste joignable dans les deux modes, ce qui est précisément pourquoi les
parcours SSH ci-dessus passent par une adresse publique plutôt que par l'adresse
privée.
À retenir
Section intitulée « À retenir »- Par défaut feint ne fait tourner aucune machine : le mode
offest un plan de contrôle, et c'est ce que la CI exécute. - Seul
--vm incus-ovnisole deux VPC, au prix d'une adresse privée que l'hôte ne joint plus. - La machine porte l'adresse publiée par l'API, vérifiable depuis le runtime plutôt que depuis l'émulateur.
- Une session SSH s'ouvre sur les trois fournisseurs, chacun sur son bloc d'adresses de documentation.
feint cleanne retire que ce que l'émulateur a créé, le reste de l'hôte n'est jamais touché.