docs: build de 17e4795096
|
|
@ -1,86 +0,0 @@
|
||||||
Invariants et pièges
|
|
||||||
====================
|
|
||||||
|
|
||||||
Contraintes découvertes en production ou en corrigeant des bugs. Les enfreindre casse quelque
|
|
||||||
chose qui fonctionne, souvent en silence.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Cette page reprend la section « Invariants et pièges » de ``CLAUDE.md``, qui reste la
|
|
||||||
référence de développement et fait foi en cas d'écart.
|
|
||||||
|
|
||||||
Réseau
|
|
||||||
------
|
|
||||||
|
|
||||||
* **Ne pas retirer la route ``/32`` vers ``169.254.169.254``** de l'option 121, même quand elle
|
|
||||||
paraît redondante avec la route par défaut. La DNAT vers le serveur de metadata est posée dans
|
|
||||||
le netns du VPC en ``PREROUTING`` : le paquet n'y est traité en L3 que si son next-hop est
|
|
||||||
``interface_ip``. Avec un autre next-hop, la trame est commutée en L2 sans traverser
|
|
||||||
``PREROUTING``, et le provisionnement cloud-init échoue.
|
|
||||||
* **RFC 3442** : un client qui lit l'option 121 **ignore l'option 3**. Toute route par défaut
|
|
||||||
doit donc figurer dans la 121 ; l'option 3 ne sert que les clients qui n'implémentent pas la
|
|
||||||
121.
|
|
||||||
* **La route vers le CIDR du VPC garde ``interface_ip`` comme next-hop** dans tous les modes sauf
|
|
||||||
``bridge`` : sur un subnet à IP publique, le trafic interne ne doit pas sortir par la gateway
|
|
||||||
publique.
|
|
||||||
* ``169.254.169.254`` est centralisé dans ``metadata.ServiceIP`` — ne pas le réécrire en dur.
|
|
||||||
|
|
||||||
QEMU et VM
|
|
||||||
----------
|
|
||||||
|
|
||||||
* **Un seul disque ``vdX`` par VM** ; les disques additionnels passent par le SCSI (``sdX``). La
|
|
||||||
carte PCI en dépend : NIC en ``0x03``, contrôleur SCSI en ``0x1e``, virtio-blk en ``0x1f``.
|
|
||||||
* ``bus=pci.0`` est explicite sur les trois ``-device`` : un passage de la machine en **q35**
|
|
||||||
casserait le démarrage (``Bus 'pci.0' not found``).
|
|
||||||
* QEMU est lancé par ``systemd-run --scope``, **jamais** en unit transitoire : le scope est
|
|
||||||
exécuté par le processus appelant et hérite du netns posé par ``netns.Call``. Une unit
|
|
||||||
transitoire, forkée par PID 1, démarrerait dans le netns racine et ne verrait pas le tap.
|
|
||||||
* L'arrêt d'une VM ne touche **jamais** aux fichiers disque. En revanche, un ``quit`` brutal est
|
|
||||||
envoyé à l'expiration de ``dispatcher.timeout_seconds``.
|
|
||||||
|
|
||||||
Arrêt de l'agent
|
|
||||||
----------------
|
|
||||||
|
|
||||||
* Ordre imposé : serveurs HTTP → drainage des workers → fermeture de la base. L'inverser crée une
|
|
||||||
course.
|
|
||||||
* Budget d'arrêt dépassé ⇒ **la base n'est pas fermée** : fermer Badger sous un écrivain
|
|
||||||
concurrent est pire qu'un rejeu du journal au démarrage suivant.
|
|
||||||
* Jamais de ``log.Fatal`` dans une goroutine : ``os.Exit`` n'exécute aucun ``defer``.
|
|
||||||
|
|
||||||
cloud-init
|
|
||||||
----------
|
|
||||||
|
|
||||||
* ``network-config.tmpl`` cible ``eth0`` alors que les guests sont en ``ens3`` : il ne s'applique
|
|
||||||
donc à rien, et le réseau vient du DHCP. **Ne pas le « corriger » ni le supprimer** — le rendre
|
|
||||||
opérant ferait remplacer par cloud-init la configuration réseau de l'image sur toutes les VM.
|
|
||||||
* Un document fourni par l'appelant est servi **verbatim** ; un document absent retombe sur le
|
|
||||||
template ; un document explicitement vide est servi vide. Les trois cas sont distincts.
|
|
||||||
* ``metadata.password`` est un **hash**, pas un mot de passe en clair.
|
|
||||||
* ``instance-id`` vaut le nom de la VM : recréer une VM du même nom sur le même disque fait que
|
|
||||||
cloud-init la reconnaît et **n'applique pas** le user-data.
|
|
||||||
|
|
||||||
Configuration
|
|
||||||
-------------
|
|
||||||
|
|
||||||
* Le chargement se fait par **viper** : tags ``mapstructure``, jamais ``yaml``.
|
|
||||||
* Un chemin configurable se propage par les signatures de fonction, jamais par une variable ou un
|
|
||||||
setter de paquet.
|
|
||||||
|
|
||||||
Sécurité connue et acceptée
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
Ces points sont documentés parce qu'ils sont **assumés en l'état**, pas parce qu'ils sont sans
|
|
||||||
conséquence. Ils doivent être réévalués avant toute exposition élargie de l'API.
|
|
||||||
|
|
||||||
* **L'API n'a aucune authentification** et l'exemple de configuration l'expose sur
|
|
||||||
``0.0.0.0:8080``. Quiconque atteint ce port pilote le host KVM.
|
|
||||||
* ``vm/<name>/password`` est stocké tel quel (c'est un hash) et restitué par ``/db?prefix=vm/``
|
|
||||||
du serveur d'administration — contenu par ``admin.enabled: false`` et l'écoute en boucle
|
|
||||||
locale.
|
|
||||||
* ``/run/two/metadata/<vm>/vendor-data`` est en ``0644`` et contient ce hash : tout compte local
|
|
||||||
du host peut le lire.
|
|
||||||
* ``pkg/systemd.New()`` n'a **pas de timeout** : si le socket D-Bus accepte sans répondre,
|
|
||||||
l'appelant se fige. Concerne le watchdog, la création et la suppression de subnets, et le
|
|
||||||
serveur de metadata.
|
|
||||||
* Il n'y a **pas de rollback** : un échec partiel de création laisse des objets réseau orphelins
|
|
||||||
jusqu'à un ``DELETE`` explicite.
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
Architecture
|
|
||||||
============
|
|
||||||
|
|
||||||
L'organisation interne de l'agent : découpage en paquets, stockage, et contraintes à ne pas
|
|
||||||
enfreindre. Ces pages s'adressent à qui modifie le code ; ``CLAUDE.md``, à la racine du dépôt,
|
|
||||||
reste la référence de développement et fait foi en cas d'écart.
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 1
|
|
||||||
|
|
||||||
vue-densemble
|
|
||||||
stockage
|
|
||||||
contraintes
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
Schéma des clés
|
|
||||||
===============
|
|
||||||
|
|
||||||
Toutes les valeurs stockées dans Badger sont des **chaînes plates** : une clé, une valeur, pas
|
|
||||||
de sérialisation structurée.
|
|
||||||
|
|
||||||
.. code-block:: text
|
|
||||||
|
|
||||||
vpc/<name>/state → creating | running | error | deleting | deleted
|
|
||||||
vpc/<name>/cidr → <cidr>
|
|
||||||
|
|
||||||
subnet/<name>/state → creating | running | error | deleting | deleted
|
|
||||||
subnet/<name>/vpc → <vpc-name>
|
|
||||||
subnet/<name>/mode → vxlan | bridge | public_ip
|
|
||||||
subnet/<name>/vxlan_id → <id> (mode vxlan uniquement)
|
|
||||||
subnet/<name>/cidr → <cidr>
|
|
||||||
subnet/<name>/interface_ip → <ip> (gateway, portée par br-<subnetID>)
|
|
||||||
subnet/<name>/local_iface → <bridge-name>
|
|
||||||
subnet/<name>/default_route → "true" | "false"
|
|
||||||
subnet/<name>/gateway → <ip> (optionnel)
|
|
||||||
subnet/<name>/dhcp/<ip> → <mac>
|
|
||||||
|
|
||||||
vm/<name>/state → creating | running | error | deleting | deleted
|
|
||||||
vm/<name>/subnet → <subnet-name>
|
|
||||||
vm/<name>/tap_id → <int>
|
|
||||||
vm/<name>/ip → <ip>
|
|
||||||
vm/<name>/metadata_port → <port>
|
|
||||||
vm/<name>/disk/<dev> → <path> (une clé par disque : sda, vda, …)
|
|
||||||
vm/<name>/memory → <int> (Mo)
|
|
||||||
vm/<name>/cpus → <int>
|
|
||||||
vm/<name>/uefi → "true" (absent si SeaBIOS)
|
|
||||||
vm/<name>/password → <hash> (optionnel — un hash, pas un mot de passe)
|
|
||||||
vm/<name>/sshkey → <pubkey> (optionnel)
|
|
||||||
vm/<name>/metadata/<document> → <contenu brut> (optionnel : user-data, vendor-data, …)
|
|
||||||
|
|
||||||
Règles
|
|
||||||
------
|
|
||||||
|
|
||||||
**Pas de duplication.** Une ressource ne stocke que ce qui lui est propre. Une VM garde le lien
|
|
||||||
``vm/<name>/subnet`` ; le VPC, le bridge et l'``interface_ip`` sont lus depuis le subnet, leur
|
|
||||||
source canonique.
|
|
||||||
|
|
||||||
**Les états passent par ``state``.** Toujours ``state.Set`` / ``state.Get`` : ``Set`` refuse une
|
|
||||||
valeur hors énumération, ``Get`` refuse de retourner une valeur non reconnue. ``error`` n'est
|
|
||||||
écrit que par ``Dispatcher.Dispatch``, via ``cmd.Key()``.
|
|
||||||
|
|
||||||
**Tout entier lu depuis la base peut être corrompu.** Les erreurs de conversion sont retournées,
|
|
||||||
jamais ignorées : une valeur absente ou illisible est un état d'erreur réel.
|
|
||||||
|
|
||||||
**Un seul ouvreur.** L'agent est le seul processus à ouvrir la base. Le serveur de metadata lit
|
|
||||||
des fichiers écrits par l'agent sous ``metadata.run_dir``, jamais Badger.
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
Vue d'ensemble
|
|
||||||
==============
|
|
||||||
|
|
||||||
Cycle d'une requête
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
.. code-block:: text
|
|
||||||
|
|
||||||
HTTP → internal/api/agent → Dispatcher.Prepare() → Dispatcher.Dispatch() → worker.Queue → Command.Execute()
|
|
||||||
|
|
||||||
**Prepare** (synchrone, dans le handler HTTP)
|
|
||||||
valide l'état, écrit l'état initial (``creating`` / ``deleting``) en base, et retourne 202 ou
|
|
||||||
une erreur.
|
|
||||||
|
|
||||||
**Dispatch** (asynchrone)
|
|
||||||
place la commande sur un canal bufferisé ; une goroutine worker appelle ``Execute``. C'est
|
|
||||||
``Dispatch``, et lui seul, qui marque la ressource en ``error`` si ``Execute`` échoue.
|
|
||||||
|
|
||||||
**Execute**
|
|
||||||
effectue le travail réseau (netns, netif, VXLAN, veth, bridge, DHCP), puis met l'état à
|
|
||||||
``running`` / ``deleted``.
|
|
||||||
|
|
||||||
Paquets
|
|
||||||
-------
|
|
||||||
|
|
||||||
.. list-table::
|
|
||||||
:header-rows: 1
|
|
||||||
:widths: 32 68
|
|
||||||
|
|
||||||
* - Chemin
|
|
||||||
- Rôle
|
|
||||||
* - ``internal/api/agent``
|
|
||||||
- handlers HTTP de ``/vpcs``, ``/subnets`` et ``/vms``
|
|
||||||
* - ``internal/dispatcher/agent``
|
|
||||||
- interface ``Command`` (``Prepare``/``Execute``/``Key``) et commandes concrètes
|
|
||||||
* - ``internal/state``
|
|
||||||
- énumération des états, ``CanDelete``/``IsTransient``, seul point d'écriture des états
|
|
||||||
* - ``internal/migration``
|
|
||||||
- migrations idempotentes jouées au démarrage de l'agent
|
|
||||||
* - ``internal/vpc``, ``internal/subnet``
|
|
||||||
- création et suppression bas niveau (netns + netif)
|
|
||||||
* - ``internal/netns``
|
|
||||||
- network namespaces : create/enter/delete/call
|
|
||||||
* - ``internal/netif``
|
|
||||||
- netlink : bridge, veth, vxlan, tap, routes, adresses
|
|
||||||
* - ``internal/ebtables``, ``internal/iptables``
|
|
||||||
- wrappers dédiés ; ne pas appeler ces binaires ailleurs
|
|
||||||
* - ``internal/qemu``, ``internal/qmp``
|
|
||||||
- lancement de QEMU et client QMP sur socket Unix
|
|
||||||
* - ``internal/vm``
|
|
||||||
- cycle de vie d'une VM : tap, iptables, metadata, qemu
|
|
||||||
* - ``internal/dhcp``
|
|
||||||
- plan d'adressage ip → mac, et configurations dnsmasq du backend historique
|
|
||||||
* - ``internal/metadata``
|
|
||||||
- serveur de metadata cloud-init et ses templates
|
|
||||||
* - ``internal/watchdog``
|
|
||||||
- vérification périodique en lecture seule
|
|
||||||
* - ``internal/config/agent``
|
|
||||||
- chargement par viper — tags ``mapstructure``, jamais ``yaml``
|
|
||||||
* - ``internal/prometheus/agent``
|
|
||||||
- collector des métriques ``syonad_*``
|
|
||||||
* - ``pkg/db/kv``
|
|
||||||
- wrapper Badger ; toutes les valeurs sont des chaînes plates
|
|
||||||
* - ``pkg/worker``
|
|
||||||
- pool de goroutines sur canal
|
|
||||||
* - ``pkg/systemd``
|
|
||||||
- client D-Bus systemd
|
|
||||||
* - ``pkg/logger``, ``pkg/prometheus``
|
|
||||||
- journalisation ``slog`` et serveur de métriques
|
|
||||||
|
|
||||||
Ajouter un type de ressource
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
#. ajouter les helpers KV dans ``pkg/db/kv`` si nécessaire ;
|
|
||||||
#. définir ``Create<X>`` / ``Delete<X>`` dans un nouveau paquet ``internal/<x>/`` ;
|
|
||||||
#. ajouter ``Create<X>Command`` / ``Delete<X>Command`` dans ``internal/dispatcher/agent/``, dont
|
|
||||||
``Key()`` qui retourne ``<x>/<name>`` et le contrôle ``state.CanDelete`` dans
|
|
||||||
``Delete<X>Command.Prepare`` ;
|
|
||||||
#. ajouter les handlers HTTP dans ``internal/api/agent/`` et les routes dans ``server.go``.
|
|
||||||
|
|
||||||
Stubs de plateforme
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
Les fichiers ``_linux.go`` portent l'implémentation netlink/netns réelle ; les ``_other.go``
|
|
||||||
correspondants retournent une erreur « not supported on this platform ». Tous les paquets
|
|
||||||
**compilent** sur macOS, ce qui permet d'y tester la logique qui ne touche ni netlink ni netns.
|
|
||||||
|
|
||||||
Deux exceptions à connaître : les stubs de ``netns`` exécutent ``fn`` **sans changer de
|
|
||||||
namespace** — ``netns.Call`` réussit donc hors Linux — et ``netif`` compile partout parce que
|
|
||||||
netlink fournit une implémentation « unspecified ».
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
Cycle de vie des ressources
|
|
||||||
===========================
|
|
||||||
|
|
||||||
VPC, subnets et VM partagent le même jeu d'états.
|
|
||||||
|
|
||||||
.. mermaid::
|
|
||||||
|
|
||||||
stateDiagram-v2
|
|
||||||
[*] --> creating
|
|
||||||
creating --> running
|
|
||||||
creating --> error
|
|
||||||
running --> deleting
|
|
||||||
running --> error
|
|
||||||
deleting --> deleted
|
|
||||||
deleting --> error
|
|
||||||
error --> deleting
|
|
||||||
deleted --> [*]
|
|
||||||
|
|
||||||
.. list-table::
|
|
||||||
:header-rows: 1
|
|
||||||
:widths: 15 85
|
|
||||||
|
|
||||||
* - État
|
|
||||||
- Signification
|
|
||||||
* - ``creating``
|
|
||||||
- la demande est acceptée et enregistrée ; ``Execute`` n'a pas encore abouti
|
|
||||||
* - ``running``
|
|
||||||
- la ressource existe sur le système
|
|
||||||
* - ``error``
|
|
||||||
- ``Execute`` a échoué ; état **terminal**, il n'y a pas de reprise automatique
|
|
||||||
* - ``deleting``
|
|
||||||
- suppression en cours
|
|
||||||
* - ``deleted``
|
|
||||||
- suppression terminée
|
|
||||||
|
|
||||||
Suppression
|
|
||||||
-----------
|
|
||||||
|
|
||||||
Elle n'est autorisée que depuis ``running`` ou ``error`` — sinon **409**. Depuis ``error``, elle
|
|
||||||
est **best-effort** : les ressources système peuvent n'avoir été créées que partiellement.
|
|
||||||
|
|
||||||
Un VPC ne peut être supprimé qu'une fois tous ses subnets supprimés.
|
|
||||||
|
|
||||||
Pas de rollback
|
|
||||||
---------------
|
|
||||||
|
|
||||||
En cas d'échec partiel pendant une création, les ressources réseau déjà créées **ne sont pas
|
|
||||||
nettoyées**. C'est un choix délibéré : le nettoyage est déclenché explicitement par une
|
|
||||||
suppression, qui est justement autorisée depuis ``error``.
|
|
||||||
|
|
||||||
Conséquence pour l'appelant : après un passage en ``error``, émettre un ``DELETE`` avant toute
|
|
||||||
tentative de recréation, faute de quoi la recréation butera sur des objets système résiduels.
|
|
||||||
|
|
||||||
États transitoires au redémarrage de l'agent
|
|
||||||
--------------------------------------------
|
|
||||||
|
|
||||||
La file d'attente des workers est **en mémoire**. Une ressource restée en ``creating`` ou
|
|
||||||
``deleting`` au moment d'un arrêt de l'agent est donc nécessairement orpheline : plus personne
|
|
||||||
ne la traite.
|
|
||||||
|
|
||||||
Au démarrage, une migration idempotente bascule ces ressources en ``error``, et traduit
|
|
||||||
l'ancien vocabulaire d'états. Une ressource retrouvée en ``error`` après un redémarrage n'a donc
|
|
||||||
pas forcément échoué techniquement — elle peut simplement avoir été interrompue.
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
Concepts
|
|
||||||
========
|
|
||||||
|
|
||||||
Le modèle de données, les modes réseau et le cycle de vie des ressources : comment les éléments
|
|
||||||
fonctionnent entre eux. Le contrat HTTP correspondant est dans
|
|
||||||
:doc:`/exploitation/api-agent/index`.
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 1
|
|
||||||
|
|
||||||
vpc-subnet-vm
|
|
||||||
modes-reseau
|
|
||||||
cycle-de-vie
|
|
||||||
metadata-cloud-init
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
Metadata et cloud-init
|
|
||||||
======================
|
|
||||||
|
|
||||||
Chaque VM dispose d'un serveur de metadata NoCloud, servi sur ``169.254.169.254`` dans le netns
|
|
||||||
de son VPC, sous la forme d'une instance systemd ``metadata@<vm>``.
|
|
||||||
|
|
||||||
Chaîne de production
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
.. mermaid::
|
|
||||||
|
|
||||||
graph LR
|
|
||||||
A["agent<br/>WriteNoCloudFiles"] -->|"/run/two/metadata/<vm>/"| M["binaire metadata"]
|
|
||||||
M -->|HTTP 169.254.169.254| G["guest<br/>cloud-init"]
|
|
||||||
|
|
||||||
L'agent écrit les fichiers cloud-init sur disque **avant** de démarrer le service ; le binaire
|
|
||||||
``metadata`` les lit et les sert. Le processus ``metadata`` n'ouvre **jamais** la base Badger :
|
|
||||||
deux processus ne doivent pas partager une même instance.
|
|
||||||
|
|
||||||
Documents servis
|
|
||||||
----------------
|
|
||||||
|
|
||||||
Chaque document suit la même règle :
|
|
||||||
|
|
||||||
* fourni par l'appelant → servi **verbatim**, l'agent n'interprète rien ;
|
|
||||||
* absent → le template par défaut est rendu ;
|
|
||||||
* fourni **vide** → servi vide.
|
|
||||||
|
|
||||||
Les deux derniers cas sont distincts, et c'est délibéré : fournir une chaîne vide est une façon
|
|
||||||
explicite de neutraliser un document.
|
|
||||||
|
|
||||||
Champs de ``metadata``
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
``sshkey``
|
|
||||||
Clé publique ajoutée au compte ``syonad``. Transmise telle quelle, non encodée.
|
|
||||||
|
|
||||||
``password``
|
|
||||||
Un **hash**, tel qu'attendu par la clé ``passwd`` de cloud-config (``$6$…``) — jamais un mot
|
|
||||||
de passe en clair. Omis, le compte est créé verrouillé ; sans ``password`` ni ``sshkey``,
|
|
||||||
aucun compte n'est créé.
|
|
||||||
|
|
||||||
``user_data``
|
|
||||||
Le user-data cloud-init, **encodé en base64**. L'encodage évite l'échappement JSON des
|
|
||||||
documents multi-lignes et autorise les charges ``gzip+base64``. Un base64 invalide est rejeté
|
|
||||||
en 400 plutôt que servi vide.
|
|
||||||
|
|
||||||
``instance-id``
|
|
||||||
------------------
|
|
||||||
|
|
||||||
``instance-id`` vaut le **nom de la VM**. Recréer une VM du même nom sur le même disque fait que
|
|
||||||
cloud-init la reconnaît comme déjà provisionnée et **n'applique pas** le user-data. Pour rejouer
|
|
||||||
un provisionnement : changer de nom, repartir d'un disque neuf, ou exécuter
|
|
||||||
``cloud-init clean --logs`` dans le guest avant l'extinction.
|
|
||||||
|
|
||||||
Configuration réseau
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
``network-config.tmpl`` cible ``eth0`` alors que les guests utilisent ``ens3`` : il ne
|
|
||||||
s'applique donc à rien, et le réseau des VM vient du DHCP. **Ne pas le « corriger » ni le
|
|
||||||
supprimer.** Le rendre opérant ferait remplacer par cloud-init la configuration réseau de
|
|
||||||
l'image, sur toutes les VM.
|
|
||||||
|
|
||||||
Sécurité
|
|
||||||
--------
|
|
||||||
|
|
||||||
``/run/two/metadata/<vm>/vendor-data`` est en ``0644`` et contient le hash de mot de passe. Tout
|
|
||||||
compte local du host peut le lire. C'est une exposition connue et acceptée en l'état ; elle
|
|
||||||
disqualifie l'usage de hashs faibles ou réutilisés.
|
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
Modes réseau
|
|
||||||
============
|
|
||||||
|
|
||||||
Le champ ``mode`` d'un subnet détermine la façon dont il est raccordé à l'host, et les routes
|
|
||||||
annoncées aux VM.
|
|
||||||
|
|
||||||
.. list-table::
|
|
||||||
:header-rows: 1
|
|
||||||
:widths: 15 45 40
|
|
||||||
|
|
||||||
* - Mode
|
|
||||||
- Raccordement
|
|
||||||
- État
|
|
||||||
* - ``vxlan``
|
|
||||||
- tunnel VXLAN (``vxlan_id``) + bridge dans le netns du VPC
|
|
||||||
- défaut
|
|
||||||
* - ``bridge``
|
|
||||||
- rattachement direct à un bridge existant de l'host, résolu depuis ``iface_type``
|
|
||||||
- disponible
|
|
||||||
* - ``public_ip``
|
|
||||||
- routé comme ``vxlan`` côté DHCP
|
|
||||||
- **mise en place host non implémentée** — la création échoue à l'exécution
|
|
||||||
* - ``vlan``
|
|
||||||
- —
|
|
||||||
- réservé, non implémenté
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
``public_ip`` est accepté par l'API et traité comme ``vxlan`` pour le DHCP, mais sa
|
|
||||||
configuration réseau côté host n'existe pas encore : la création part en ``error`` dans
|
|
||||||
``Execute``. Ne pas s'appuyer dessus en production.
|
|
||||||
|
|
||||||
vxlan
|
|
||||||
-----
|
|
||||||
|
|
||||||
.. mermaid::
|
|
||||||
|
|
||||||
graph LR
|
|
||||||
VM --- TAP[tap] --- BR["br-<subnet><br/>interface_ip"]
|
|
||||||
BR --- VX["vxlan<vni>"] --- HBR["bridge host<br/>(iface_type)"] --- UP[uplink]
|
|
||||||
|
|
||||||
Le subnet vit dans le netns du VPC. La VM n'est donc **pas joignable depuis l'host** sans route
|
|
||||||
explicite — point à connaître avant de câbler un outil externe dessus.
|
|
||||||
|
|
||||||
bridge
|
|
||||||
------
|
|
||||||
|
|
||||||
Le subnet est rattaché directement à un bridge existant de l'host. Pas de tunnel, pas de route
|
|
||||||
VPC : le trafic sort par le bridge, et la VM est joignable depuis l'host.
|
|
||||||
|
|
||||||
Routes annoncées aux VM
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
Les routes sont poussées par DHCP, dans l'**option 121** (routes statiques sans classe,
|
|
||||||
RFC 3442). Trois entrées y figurent :
|
|
||||||
|
|
||||||
#. la route ``/32`` vers ``169.254.169.254``, le serveur de metadata ;
|
|
||||||
#. la route vers le CIDR du VPC ;
|
|
||||||
#. la route par défaut ``0.0.0.0/0``.
|
|
||||||
|
|
||||||
.. important::
|
|
||||||
|
|
||||||
**Un client qui lit l'option 121 ignore l'option 3.** Toute route par défaut doit donc figurer
|
|
||||||
dans l'option 121 ; l'option 3 ne sert que les clients qui n'implémentent pas la 121.
|
|
||||||
|
|
||||||
Route par défaut : ``default_route`` et ``gateway``
|
|
||||||
----------------------------------------------------------
|
|
||||||
|
|
||||||
Une route par défaut est **toujours** annoncée. Le champ ``default_route`` ne choisit que son
|
|
||||||
next-hop :
|
|
||||||
|
|
||||||
``default_route: false`` (défaut)
|
|
||||||
next-hop = ``interface_ip`` du subnet.
|
|
||||||
|
|
||||||
``default_route: true``
|
|
||||||
next-hop = le champ ``gateway`` s'il est fourni, sinon la gateway lue dans la table de routage
|
|
||||||
de l'host.
|
|
||||||
|
|
||||||
``gateway`` n'est **pas validé** par l'agent : sa joignabilité et sa cohérence avec le CIDR du
|
|
||||||
subnet relèvent de l'appelant. Fourni avec ``default_route: false``, il est ignoré.
|
|
||||||
|
|
||||||
Dans tous les modes sauf ``bridge``, la route vers le CIDR du VPC garde ``interface_ip`` comme
|
|
||||||
next-hop : sur un subnet à IP publique, le trafic interne ne doit pas sortir par la gateway
|
|
||||||
publique.
|
|
||||||
|
|
||||||
Pourquoi la route ``/32`` vers le serveur de metadata est indispensable
|
|
||||||
----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
Elle paraît redondante avec la route par défaut. Elle ne l'est pas.
|
|
||||||
|
|
||||||
La DNAT vers le serveur de metadata est posée dans le netns du VPC, en ``PREROUTING``. Le paquet
|
|
||||||
n'y est traité en L3 que si son next-hop est ``interface_ip``, portée par le bridge du netns.
|
|
||||||
Avec un autre next-hop, la trame est commutée en **L2** sans traverser ``PREROUTING`` : le
|
|
||||||
serveur de metadata devient injoignable et tout le provisionnement cloud-init échoue,
|
|
||||||
silencieusement.
|
|
||||||
|
|
||||||
.. danger::
|
|
||||||
|
|
||||||
Ne jamais retirer cette route de l'option 121, quelle que soit l'apparence de redondance.
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
VPC, subnet et VM
|
|
||||||
=================
|
|
||||||
|
|
||||||
Trois types de ressources, une hiérarchie stricte.
|
|
||||||
|
|
||||||
.. mermaid::
|
|
||||||
|
|
||||||
graph TD
|
|
||||||
VPC["VPC<br/><i>network namespace</i><br/>cidr"] --> SN1["Subnet<br/><i>bridge + VXLAN</i><br/>interface_ip, cidr"]
|
|
||||||
VPC --> SN2["Subnet"]
|
|
||||||
SN1 --> VM1["VM<br/><i>QEMU/KVM</i>"]
|
|
||||||
SN1 --> VM2["VM"]
|
|
||||||
SN2 --> VM2
|
|
||||||
|
|
||||||
VPC
|
|
||||||
---
|
|
||||||
|
|
||||||
Un VPC est un **network namespace** portant un espace d'adressage (``cidr``). C'est l'unité
|
|
||||||
d'isolation : deux VPC ne se voient pas, et peuvent réutiliser les mêmes plages d'adresses.
|
|
||||||
|
|
||||||
Un VPC ne peut être supprimé que si tous ses subnets le sont déjà — sinon 409.
|
|
||||||
|
|
||||||
Subnet
|
|
||||||
------
|
|
||||||
|
|
||||||
Un subnet appartient à un VPC et pose, dans son netns, un bridge qui porte ``interface_ip`` — la
|
|
||||||
gateway vue par les VM. Il fournit aussi le DHCP — dnsmasq ou le serveur intégré selon
|
|
||||||
``dhcp.backend`` — et les routes annoncées aux guests.
|
|
||||||
|
|
||||||
``iface_type`` est une clé **logique** (``vms``, ``internet``, ``admin``…), traduite en nom de
|
|
||||||
bridge physique par la configuration de l'agent. Une clé absente ou inconnue retombe sur
|
|
||||||
``default_interface``. Ce niveau d'indirection permet au même appel d'API de fonctionner sur des
|
|
||||||
hosts dont le nommage réseau diffère.
|
|
||||||
|
|
||||||
Le comportement réseau dépend du :doc:`mode </concepts/modes-reseau>`.
|
|
||||||
|
|
||||||
VM
|
|
||||||
--
|
|
||||||
|
|
||||||
Une VM est un processus QEMU/KVM raccordé à un ou plusieurs subnets par des taps.
|
|
||||||
|
|
||||||
**Interfaces.** L'ordre du tableau ``interfaces`` détermine le slot PCI (``0x03 + index``), donc
|
|
||||||
le nom de l'interface dans le guest. Exactement une interface doit être ``primary`` : elle porte
|
|
||||||
la route par défaut et le serveur de metadata. Tous les subnets d'une VM doivent appartenir au
|
|
||||||
**même VPC**.
|
|
||||||
|
|
||||||
**Stockage.** Un seul disque ``vdX`` (virtio-blk) par VM ; les disques supplémentaires passent
|
|
||||||
par le contrôleur SCSI (``sdX``). Cette contrainte vient de la carte PCI figée — voir
|
|
||||||
:doc:`/architecture/contraintes`.
|
|
||||||
|
|
||||||
Ce qui est stocké, et où
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
Une ressource ne porte en base que ce qui lui est propre. Une VM stocke le **lien** vers son
|
|
||||||
subnet (``vm/<name>/subnet``), pas le VPC ni le bridge ni la gateway : ces valeurs sont lues
|
|
||||||
depuis le subnet, leur source canonique. Le schéma complet des clés est dans
|
|
||||||
:doc:`/architecture/stockage`.
|
|
||||||
|
|
||||||
Nommage
|
|
||||||
-------
|
|
||||||
|
|
||||||
L'API est machine-to-machine : elle **ne valide pas** les conventions de nommage, à l'exception
|
|
||||||
du motif documenté pour les VPC (``vp-…``). Les exemples de cette documentation suivent la
|
|
||||||
convention ``vp-`` / ``sn-`` / ``i-``, mais c'est à l'appelant de la faire respecter.
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
Démarrage
|
|
||||||
=========
|
|
||||||
|
|
||||||
Le parcours court : un hyperviseur, un VPC, un subnet, une VM qui démarre. Tout reste sur le
|
|
||||||
même nœud — c'est suffisant pour valider une installation et pour découvrir le modèle, pas pour
|
|
||||||
faire fonctionner un parc.
|
|
||||||
|
|
||||||
Pour un cluster, poursuivre avec :doc:`/deploiement/index`.
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 1
|
|
||||||
|
|
||||||
installation
|
|
||||||
premier-vpc
|
|
||||||
|
|
@ -1,156 +0,0 @@
|
||||||
Installation d'un hyperviseur
|
|
||||||
=============================
|
|
||||||
|
|
||||||
Cette page installe l'agent sur **un** hyperviseur. Le réseau du cluster — routage entre nœuds,
|
|
||||||
plan de contrôle — est traité à part : voir :doc:`/deploiement/index`.
|
|
||||||
|
|
||||||
Prérequis
|
|
||||||
---------
|
|
||||||
|
|
||||||
Un host Linux avec KVM, sur lequel vous avez ``root``. Les opérations réseau (network
|
|
||||||
namespaces, netlink, VXLAN, ebtables, iptables) et QEMU ne fonctionnent que sous Linux.
|
|
||||||
|
|
||||||
Déploiement
|
|
||||||
-----------
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
curl -O https://git.g3e.fr/syonad/two/raw/branch/main/scripts/deploy.sh
|
|
||||||
bash ./deploy.sh -t 0.1.0 -i
|
|
||||||
|
|
||||||
``deploy.sh`` se met à jour lui-même depuis la branche avant toute action — s'il diffère, il se
|
|
||||||
réécrit et demande d'être relancé. Il télécharge ensuite binaires, units systemd et scripts
|
|
||||||
depuis la release, et les vérifie contre le manifeste ``SHA256SUMS``.
|
|
||||||
|
|
||||||
.. list-table::
|
|
||||||
:header-rows: 1
|
|
||||||
:widths: 26 54 20
|
|
||||||
|
|
||||||
* - Option
|
|
||||||
- Effet
|
|
||||||
- Défaut
|
|
||||||
* - ``-t <tag>``
|
|
||||||
- déployer une release donnée
|
|
||||||
- dernière
|
|
||||||
* - ``-b <branche>``
|
|
||||||
- branche utilisée pour l'auto-mise à jour du script
|
|
||||||
- ``main``
|
|
||||||
* - ``-p <profil>``
|
|
||||||
- profil d'host ; seul ``kvm`` installe les units de l'agent
|
|
||||||
- ``kvm``
|
|
||||||
* - ``-i``
|
|
||||||
- préparer l'host : paquets, noyau, réseau
|
|
||||||
- désactivé
|
|
||||||
* - ``-u <iface>``
|
|
||||||
- interface physique d'uplink
|
|
||||||
- ``eno1``
|
|
||||||
* - ``-B <bridge>``
|
|
||||||
- bridge principal, auquel l'uplink est rattaché
|
|
||||||
- ``br-000000``
|
|
||||||
* - ``-P <bridge>``
|
|
||||||
- bridge supplémentaire, créé vide et réservé
|
|
||||||
- ``br-public``
|
|
||||||
* - ``-R <secondes>``
|
|
||||||
- délai avant le redémarrage de secours pendant la migration réseau
|
|
||||||
- ``120``
|
|
||||||
* - ``-d``
|
|
||||||
- dry-run : affiche les commandes sans les exécuter
|
|
||||||
- désactivé
|
|
||||||
|
|
||||||
Les options booléennes actives par défaut se **désactivent** par leur forme longue négative :
|
|
||||||
``--nopackages``, ``--nonetwork``, ``--noverify``, ``--noup_script``.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
``--noverify`` désactive la seule vérification d'intégrité des artefacts téléchargés. Ne
|
|
||||||
l'utiliser que pour diagnostiquer un manifeste cassé, jamais en déploiement courant.
|
|
||||||
|
|
||||||
Ce que fait ``-i``
|
|
||||||
------------------
|
|
||||||
|
|
||||||
**Paquets** — ``qemu-system-x86``, ``ovmf``, ``dnsmasq``, ``ebtables``, ``iptables``,
|
|
||||||
``nfs-common``, ``jq``, ``curl``. Le service ``dnsmasq`` du système est ensuite désactivé et
|
|
||||||
**masqué** : il prendrait le port 53 en concurrence des instances ``dnsmasq@`` que l'agent lance
|
|
||||||
dans les netns.
|
|
||||||
|
|
||||||
``dnsmasq`` reste installé même avec ``dhcp.backend: two`` : le backend intégré ne le remplace que
|
|
||||||
pour les subnets créés après la bascule, et le paquet est nécessaire tant qu'un hyperviseur peut
|
|
||||||
revenir en arrière. Voir :doc:`/exploitation/configuration`.
|
|
||||||
|
|
||||||
**Noyau** — chargement de ``br_netfilter``, puis ``net.ipv4.ip_forward = 1`` et
|
|
||||||
``net.bridge.bridge-nf-call-iptables = 1``. Cette dernière clé est **requise** par la DNAT vers
|
|
||||||
le serveur de metadata : sans elle, iptables ne voit pas le trafic bridgé des VM et cloud-init
|
|
||||||
ne se provisionne pas. Contrepartie assumée : tout le trafic inter-VM traverse les tables NAT.
|
|
||||||
|
|
||||||
**Réseau** — création du bridge réservé, puis rattachement de l'uplink au bridge principal,
|
|
||||||
l'adresse et la route par défaut étant déplacées de l'interface physique vers le bridge.
|
|
||||||
|
|
||||||
.. danger::
|
|
||||||
|
|
||||||
La migration réseau **coupe le réseau de l'host si elle échoue à mi-parcours**, sans console
|
|
||||||
de secours. Deux garde-fous sont en place : un redémarrage de secours armé avant l'opération
|
|
||||||
(``-R``, 120 s par défaut) qui ramène la configuration d'origine puisque rien n'est écrit sur
|
|
||||||
disque, et l'exécution de la séquence sous systemd plutôt que dans la session SSH, pour
|
|
||||||
qu'une coupure de SSH ne l'interrompe pas.
|
|
||||||
|
|
||||||
Le désarmement n'a lieu **qu'après** un ping réussi vers la passerelle. Prévoir un accès
|
|
||||||
physique ou console avant de lancer un ``-i`` à distance sur un host de production.
|
|
||||||
|
|
||||||
Host sans état
|
|
||||||
--------------
|
|
||||||
|
|
||||||
L'hyperviseur est **stateless** : sa racine est en tmpfs, rien de ce que pose ``-i`` ne survit à
|
|
||||||
un redémarrage. ``deploy.sh --bootstrap`` est donc rejoué à chaque démarrage — c'est le
|
|
||||||
mécanisme normal, pas une réparation.
|
|
||||||
|
|
||||||
Binaires installés
|
|
||||||
------------------
|
|
||||||
|
|
||||||
.. list-table::
|
|
||||||
:header-rows: 1
|
|
||||||
:widths: 20 60 20
|
|
||||||
|
|
||||||
* - Binaire
|
|
||||||
- Rôle
|
|
||||||
- Drapeau de config
|
|
||||||
* - ``agent``
|
|
||||||
- processus principal : API, dispatcher, exécution, watchdog
|
|
||||||
- ``-config``
|
|
||||||
* - ``metadata``
|
|
||||||
- serveur de metadata cloud-init, une instance par VM dans le netns du VPC
|
|
||||||
- ``-conf``
|
|
||||||
* - ``db``
|
|
||||||
- inspection de la base clé-valeur en ligne de commande
|
|
||||||
- ``-conf``
|
|
||||||
* - ``dhcp``
|
|
||||||
- serveur DHCP intégré, une instance par subnet dans le netns du VPC ; démarré uniquement
|
|
||||||
avec ``dhcp.backend: two``
|
|
||||||
- ``-conf``
|
|
||||||
|
|
||||||
Les quatre partagent le même fichier, ``/etc/two/agent.yml`` — voir
|
|
||||||
:doc:`/exploitation/configuration`. ``dhcp`` reçoit en plus son bridge et ses deux chemins de
|
|
||||||
fichiers en paramètres, posés par son script d'enrobage.
|
|
||||||
|
|
||||||
Mise à jour
|
|
||||||
-----------
|
|
||||||
|
|
||||||
``deploy.sh`` relève les instances ``dnsmasq@``, ``dhcp@`` et ``metadata@`` actives **avant**
|
|
||||||
d'arrêter les services, et les redémarre ensuite : c'est la seule façon de savoir lesquelles
|
|
||||||
relancer. Arrêter les services à la main avant de lancer le script fait perdre cette liste.
|
|
||||||
|
|
||||||
Vérifier l'installation
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
systemctl status agent
|
|
||||||
curl -s http://127.0.0.1:8080/vpcs
|
|
||||||
|
|
||||||
Une liste JSON — vide au premier démarrage — signifie que l'API répond. Passez à
|
|
||||||
:doc:`/demarrage/premier-vpc`.
|
|
||||||
|
|
||||||
.. important::
|
|
||||||
|
|
||||||
L'API de l'agent **n'a aucune authentification**. Avant d'ouvrir le port au-delà de la boucle
|
|
||||||
locale, lisez l'avertissement de :doc:`/exploitation/configuration` : quiconque atteint ce
|
|
||||||
port pilote la totalité de l'hyperviseur.
|
|
||||||
|
|
@ -1,155 +0,0 @@
|
||||||
Premier VPC, premier subnet, première VM
|
|
||||||
========================================
|
|
||||||
|
|
||||||
Ce tutoriel crée de bout en bout une VM joignable, sur un hyperviseur où l'agent est installé et
|
|
||||||
répond. Il suppose l'API sur ``127.0.0.1:8080`` et une image disque déjà présente sur l'host.
|
|
||||||
|
|
||||||
Tout se passe sur un **seul nœud** : un subnet ne s'étend à d'autres hyperviseurs qu'une fois le
|
|
||||||
plan de contrôle du cluster en place, cf. :doc:`/deploiement/architecture-cluster`.
|
|
||||||
|
|
||||||
Ce que l'on construit
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
.. mermaid::
|
|
||||||
|
|
||||||
graph LR
|
|
||||||
subgraph netns vp-admin
|
|
||||||
BR["br-sn000001<br/>10.1.1.1"]
|
|
||||||
MD["metadata@i-web<br/>169.254.169.254"]
|
|
||||||
end
|
|
||||||
VM["VM i-web<br/>10.1.1.2"] --- BR
|
|
||||||
BR --- MD
|
|
||||||
BR --- VXLAN["VXLAN vni 1<br/>br-000000"]
|
|
||||||
|
|
||||||
Le VPC est un network namespace ; le subnet y pose un bridge porteur de la gateway ; la VM s'y
|
|
||||||
raccroche par un tap, reçoit son adresse en DHCP et son cloud-init depuis le serveur de metadata
|
|
||||||
du netns.
|
|
||||||
|
|
||||||
1. Le VPC
|
|
||||||
---------
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
curl -X POST http://127.0.0.1:8080/vpcs \
|
|
||||||
-H 'Content-Type: application/json' \
|
|
||||||
-d '{"name": "vp-admin", "cidr": "192.168.0.0/16"}'
|
|
||||||
|
|
||||||
Le ``cidr`` est l'espace d'adressage global du VPC : c'est lui qui sera annoncé aux VM comme
|
|
||||||
route interne, quel que soit le mode du subnet.
|
|
||||||
|
|
||||||
La réponse est un **202** : la création est acceptée, pas terminée.
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
curl -s http://127.0.0.1:8080/vpcs/vp-admin
|
|
||||||
|
|
||||||
Attendez ``"state": "running"`` avant l'étape suivante — un subnet dont le VPC parent n'est pas
|
|
||||||
prêt est refusé en **422**. Le modèle d'attente est décrit dans :doc:`/exploitation/api-agent/asynchronisme`.
|
|
||||||
|
|
||||||
2. Le subnet
|
|
||||||
------------
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
curl -X POST http://127.0.0.1:8080/subnets \
|
|
||||||
-H 'Content-Type: application/json' \
|
|
||||||
-d '{"name": "sn-000001",
|
|
||||||
"vpc": "vp-admin",
|
|
||||||
"mode": "vxlan",
|
|
||||||
"vxlan_id": 1,
|
|
||||||
"iface_type": "vms",
|
|
||||||
"interface_ip": "10.1.1.1",
|
|
||||||
"cidr": "10.1.0.0/23"}'
|
|
||||||
|
|
||||||
``iface_type`` est une clé **logique** résolue dans la configuration de l'agent (section
|
|
||||||
``interfaces``) vers un bridge physique de l'host ; une clé inconnue retombe sur
|
|
||||||
``default_interface``. ``interface_ip`` est la gateway du subnet, portée par le bridge créé dans
|
|
||||||
le netns.
|
|
||||||
|
|
||||||
Les modes disponibles et leurs conséquences sur le routage sont détaillés dans
|
|
||||||
:doc:`/concepts/modes-reseau`.
|
|
||||||
|
|
||||||
Là encore, attendez ``running`` :
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
curl -s http://127.0.0.1:8080/subnets/sn-000001
|
|
||||||
|
|
||||||
3. La VM
|
|
||||||
--------
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
curl -X POST http://127.0.0.1:8080/vms \
|
|
||||||
-H 'Content-Type: application/json' \
|
|
||||||
-d '{"name": "i-web",
|
|
||||||
"memory": 2048,
|
|
||||||
"cpus": 2,
|
|
||||||
"uefi": true,
|
|
||||||
"metadata": {"sshkey": "ssh-ed25519 AAAA…",
|
|
||||||
"user_data": "'"$(base64 < user-data.yml | tr -d '\n')"'"},
|
|
||||||
"interfaces": [{"subnet": "sn-000001", "ip": "10.1.1.2", "primary": true}],
|
|
||||||
"storage": [{"path": "/var/lib/two/volumes/i-web.qcow2", "dev": "vda"}]}'
|
|
||||||
|
|
||||||
Quatre points qui coûtent du temps quand on les découvre en production :
|
|
||||||
|
|
||||||
``user_data`` est **encodé en base64**
|
|
||||||
Un base64 invalide est rejeté en 400 plutôt que servi vide. L'agent n'interprète jamais ce
|
|
||||||
contenu.
|
|
||||||
|
|
||||||
``password`` est un **hash**, pas un mot de passe
|
|
||||||
Le champ attend la valeur de la clé ``passwd`` de cloud-config (``$6$…``). Sans ``password``
|
|
||||||
ni ``sshkey``, aucun compte n'est créé.
|
|
||||||
|
|
||||||
Exactement une interface est ``primary``
|
|
||||||
Elle porte la route par défaut et le serveur de metadata. L'ordre du tableau détermine le
|
|
||||||
slot PCI (``0x03 + index``), donc le nom de l'interface dans le guest. Tous les subnets d'une
|
|
||||||
VM doivent appartenir au même VPC.
|
|
||||||
|
|
||||||
Un seul disque ``vdX``
|
|
||||||
Les disques supplémentaires passent par ``sdX``. La carte PCI en dépend — voir
|
|
||||||
:doc:`/architecture/contraintes`.
|
|
||||||
|
|
||||||
4. Vérifier
|
|
||||||
-----------
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
curl -s http://127.0.0.1:8080/vms/i-web
|
|
||||||
|
|
||||||
En ``running``, la VM est démarrée et le serveur de metadata est en place. Le provisionnement
|
|
||||||
cloud-init, lui, se déroule dans le guest ; on l'observe par la console série :
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
socat -,raw,echo=0 UNIX-CONNECT:/run/two/vms/serial/i-web.sock
|
|
||||||
|
|
||||||
Puis, depuis l'host :
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
ssh syonad@10.1.1.2
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
En mode ``vxlan``, la VM vit dans le netns du VPC : elle n'est pas joignable depuis l'host
|
|
||||||
sans route explicite. En mode ``bridge``, elle l'est directement.
|
|
||||||
|
|
||||||
5. Supprimer
|
|
||||||
------------
|
|
||||||
|
|
||||||
Dans l'ordre inverse — un VPC dont il reste des subnets est refusé en **409** :
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
curl -X DELETE http://127.0.0.1:8080/vms/i-web
|
|
||||||
curl -X DELETE http://127.0.0.1:8080/subnets/sn-000001
|
|
||||||
curl -X DELETE http://127.0.0.1:8080/vpcs/vp-admin
|
|
||||||
|
|
||||||
La suppression d'une VM ne touche **jamais** aux fichiers disque.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
``instance-id`` vaut le nom de la VM. Recréer une VM du même nom sur le même disque fait que
|
|
||||||
cloud-init la reconnaît et **n'applique pas** le user-data. Pour rejouer un provisionnement,
|
|
||||||
changez de nom ou repartez d'un disque neuf.
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
Architecture du cluster
|
|
||||||
=======================
|
|
||||||
|
|
||||||
Topologie
|
|
||||||
---------
|
|
||||||
|
|
||||||
.. figure:: /schemas/topologie-cluster.svg
|
|
||||||
:alt: Topologie du cluster : routeurs, route reflector et hyperviseurs
|
|
||||||
:align: center
|
|
||||||
:width: 100%
|
|
||||||
:class: only-light
|
|
||||||
|
|
||||||
Topologie cible. Trait plein : plan de données. Trait pointillé : plan de contrôle.
|
|
||||||
|
|
||||||
.. figure:: /schemas/topologie-cluster-dark.svg
|
|
||||||
:alt: Topologie du cluster : routeurs, route reflector et hyperviseurs
|
|
||||||
:align: center
|
|
||||||
:width: 100%
|
|
||||||
:class: only-dark
|
|
||||||
|
|
||||||
Topologie cible. Trait plein : plan de données. Trait pointillé : plan de contrôle.
|
|
||||||
|
|
||||||
Deux plans distincts, à ne pas confondre au moment du diagnostic :
|
|
||||||
|
|
||||||
Plan de données
|
|
||||||
les tunnels VXLAN entre hyperviseurs, encapsulés sur le réseau qui les relie.
|
|
||||||
|
|
||||||
Plan de contrôle
|
|
||||||
ce qui dit à chaque hyperviseur où se trouvent les adresses MAC des autres. C'est le rôle de
|
|
||||||
FRR et du route reflector.
|
|
||||||
|
|
||||||
Ce que l'agent suppose déjà en place
|
|
||||||
------------------------------------
|
|
||||||
|
|
||||||
L'agent ne configure **que** son propre hyperviseur, et seulement à partir du bridge d'uplink.
|
|
||||||
Tout ce qui est en amont — adressage des hyperviseurs, routage entre eux, plan de contrôle — lui
|
|
||||||
préexiste et n'est jamais créé ni vérifié par lui.
|
|
||||||
|
|
||||||
Concrètement, il attend :
|
|
||||||
|
|
||||||
* le bridge d'uplink de la configuration (``br-000000`` par défaut), avec l'interface physique
|
|
||||||
esclave et l'adresse de l'hyperviseur portée par le bridge — c'est ce que fait
|
|
||||||
``deploy.sh --bootstrap``, voir :doc:`/demarrage/installation` ;
|
|
||||||
* une connectivité IP entre hyperviseurs sur cette adresse, port UDP **4789** ouvert dans les
|
|
||||||
deux sens ;
|
|
||||||
* un plan de contrôle qui peuple la table de transfert VXLAN — voir ci-dessous.
|
|
||||||
|
|
||||||
Pourquoi un plan de contrôle est nécessaire
|
|
||||||
-------------------------------------------
|
|
||||||
|
|
||||||
L'agent crée les interfaces VXLAN sur le port 4789 **sans groupe multicast et avec
|
|
||||||
l'apprentissage désactivé** (``Learning: false``). Il n'y a donc ni inondation multicast, ni
|
|
||||||
apprentissage des adresses MAC depuis le trafic, ni voisin statique configuré.
|
|
||||||
|
|
||||||
.. important::
|
|
||||||
|
|
||||||
Conséquence directe : sur un même VNI, **rien ne traverse d'un hyperviseur à l'autre** tant
|
|
||||||
qu'un composant externe n'a pas peuplé la table de transfert (FDB) du VXLAN. Sur un nœud
|
|
||||||
isolé le trafic reste sur le bridge local et cette absence ne se voit pas ; elle apparaît dès
|
|
||||||
le deuxième nœud.
|
|
||||||
|
|
||||||
C'est exactement le rôle que remplissent FRR sur chaque hyperviseur et le route reflector qui
|
|
||||||
les fait converger.
|
|
||||||
|
|
||||||
MTU
|
|
||||||
---
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
L'agent crée bridges, veth et interfaces VXLAN avec un **MTU figé à 1500**. VXLAN ajoute 50
|
|
||||||
octets d'encapsulation : le réseau qui relie les hyperviseurs doit donc accepter au moins
|
|
||||||
**1550 octets** de MTU, sinon les paquets pleine taille des VM sont perdus.
|
|
||||||
|
|
||||||
Le symptôme est trompeur : le ping passe, les petites requêtes passent, les transferts
|
|
||||||
volumineux et les poignées de main TLS échouent.
|
|
||||||
|
|
||||||
Hyperviseurs sans état
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
L'hyperviseur est **stateless** — sa racine est en tmpfs, rien de ce que pose
|
|
||||||
``deploy.sh --bootstrap`` ne survit à un redémarrage, et le script est rejoué à chaque démarrage.
|
|
||||||
|
|
||||||
Toute configuration ajoutée à un hyperviseur — FRR compris — doit donc être posée par un
|
|
||||||
mécanisme rejouable au démarrage, jamais par une modification manuelle d'un fichier sous
|
|
||||||
``/etc``.
|
|
||||||
|
|
||||||
Adressage
|
|
||||||
---------
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
**À rédiger** — cette page ne décrit pas encore le plan d'adressage du cluster. À documenter :
|
|
||||||
|
|
||||||
* la plage utilisée pour les adresses d'hyperviseurs, et son rapport avec ``br-000000`` ;
|
|
||||||
* l'allocation des VNI VXLAN : qui la tient, et comment on évite les collisions, puisque
|
|
||||||
l'agent ne valide pas ``vxlan_id`` ;
|
|
||||||
* l'usage prévu de ``br-public``, créé vide et réservé par le bootstrap ;
|
|
||||||
* le plan d'adressage des VPC, et ce qui garantit qu'ils ne se recouvrent pas entre clients.
|
|
||||||
|
|
@ -1,337 +0,0 @@
|
||||||
Construction de l'image qcow2
|
|
||||||
=============================
|
|
||||||
|
|
||||||
Toutes les VM du cluster — ``intel``, PostgreSQL, route reflector et les suivantes — partent
|
|
||||||
d'une même image qcow2 « golden », construite une fois puis réutilisée. Cette page décrit la
|
|
||||||
procédure en service.
|
|
||||||
|
|
||||||
.. important::
|
|
||||||
|
|
||||||
Cette image est un **artefact redistribuable** : tout ce qui s'y trouve se retrouve dans
|
|
||||||
chaque VM qui en dérive. Les étapes de nettoyage de la fin ne sont pas une commodité, ce sont
|
|
||||||
des exigences.
|
|
||||||
|
|
||||||
Principe
|
|
||||||
--------
|
|
||||||
|
|
||||||
La construction se fait dans une **VM jetable**, et non par montage de l'image sur l'host : le
|
|
||||||
chroot a besoin d'un noyau et d'un espace utilisateur cohérents avec la distribution cible, ce
|
|
||||||
que l'host ne fournit pas nécessairement.
|
|
||||||
|
|
||||||
Cette VM de construction démarre sur un overlay de l'image du fournisseur et voit deux disques
|
|
||||||
supplémentaires : le futur disque « golden », et un espace de travail.
|
|
||||||
|
|
||||||
.. mermaid::
|
|
||||||
|
|
||||||
graph LR
|
|
||||||
ISO["seed.iso<br/>cloud-init NoCloud"] --> BVM
|
|
||||||
OVL["<os>-tmp.qcow2<br/><i>overlay, jetable</i>"] --> BVM["VM de construction"]
|
|
||||||
BVM --> ROOT["<os>-root.qcow2<br/><b>image golden</b>"]
|
|
||||||
BVM --> WORK["tmp.qcow2<br/><i>espace de travail</i>"]
|
|
||||||
BASE["image du fournisseur<br/>(qcow2)"] -.backing file.-> OVL
|
|
||||||
|
|
||||||
.. list-table::
|
|
||||||
:header-rows: 1
|
|
||||||
:widths: 26 20 54
|
|
||||||
|
|
||||||
* - Disque
|
|
||||||
- Vu dans la VM
|
|
||||||
- Rôle
|
|
||||||
* - ``<os>-tmp.qcow2``
|
|
||||||
- ``vda`` (virtio-blk)
|
|
||||||
- système de la VM de construction ; overlay de l'image du fournisseur, jeté à la fin
|
|
||||||
* - ``<os>-root.qcow2``
|
|
||||||
- ``sda`` (SCSI)
|
|
||||||
- **le résultat** : l'image golden, écrite en brut depuis la VM
|
|
||||||
* - ``tmp.qcow2``
|
|
||||||
- ``sdb`` (SCSI)
|
|
||||||
- espace de travail : téléchargement et conversion
|
|
||||||
|
|
||||||
Variables
|
|
||||||
---------
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
export os=<nom_os>
|
|
||||||
export os_link=<url_du_qcow2_fournisseur>
|
|
||||||
export os_file=<nom_du_fichier_qcow2>
|
|
||||||
export os_dir=<repertoire_de_telechargement>
|
|
||||||
export disk_dir=<repertoire_des_disques>
|
|
||||||
|
|
||||||
Étape 1 — Le seed cloud-init de la VM de construction
|
|
||||||
------------------------------------------------------
|
|
||||||
|
|
||||||
Ce seed ne concerne **que la VM de construction**. Il n'a aucun rapport avec la configuration
|
|
||||||
cloud-init de l'image produite, qui est posée plus loin en chroot. Son seul rôle est de donner
|
|
||||||
un accès à la VM le temps du build.
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
mkdir -p "${os_dir}" && cd "${os_dir}"
|
|
||||||
mkdir -p /opt/seed/${os}
|
|
||||||
|
|
||||||
cat << 'ENDFILE' > /opt/seed/${os}/meta-data
|
|
||||||
instance-id: iid-local01
|
|
||||||
local-hostname: my-vm-01
|
|
||||||
ENDFILE
|
|
||||||
|
|
||||||
cat << 'ENDFILE' > /opt/seed/${os}/network-config
|
|
||||||
version: 2
|
|
||||||
renderer: networkd
|
|
||||||
ethernets:
|
|
||||||
eth0:
|
|
||||||
dhcp4: true
|
|
||||||
ENDFILE
|
|
||||||
|
|
||||||
cat << 'ENDFILE' > /opt/seed/${os}/user-data
|
|
||||||
#cloud-config
|
|
||||||
users:
|
|
||||||
- name: <utilisateur>
|
|
||||||
lock_passwd: false
|
|
||||||
passwd: "<hash du mot de passe>"
|
|
||||||
sudo: ALL=(ALL) NOPASSWD:ALL
|
|
||||||
ssh_authorized_keys:
|
|
||||||
- <clé publique ssh>
|
|
||||||
ENDFILE
|
|
||||||
|
|
||||||
mkisofs -o /opt/seed/${os}_seed.iso -V cidata -J -r /opt/seed/${os}/
|
|
||||||
|
|
||||||
Le label de volume ``cidata`` n'est pas décoratif : c'est ce qui fait reconnaître l'ISO comme une
|
|
||||||
source NoCloud par cloud-init.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
``passwd`` attend un **hash**, et ``ssh_authorized_keys`` une clé publique personnelle : ces
|
|
||||||
deux valeurs sont des données à ne pas recopier hors de l'host de construction. Elles ne
|
|
||||||
figurent volontairement pas dans cette documentation.
|
|
||||||
|
|
||||||
``openssl passwd -5`` pour generer un hash
|
|
||||||
|
|
||||||
Étape 2 — Les disques
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
curl "${os_link}" -O
|
|
||||||
|
|
||||||
qemu-img create -f qcow2 "${disk_dir}/${os}-root.qcow2" 10G
|
|
||||||
qemu-img create -f qcow2 "${disk_dir}/tmp.qcow2" 50G
|
|
||||||
qemu-img create -f qcow2 -b "${os_dir}/${os_file}" -F qcow2 "${disk_dir}/${os}-tmp.qcow2" 10G
|
|
||||||
|
|
||||||
.. important::
|
|
||||||
|
|
||||||
``-F qcow2`` est **obligatoire** sur qemu récent : sans lui, le format du backing file n'est
|
|
||||||
pas figé dans l'en-tête de l'overlay.
|
|
||||||
|
|
||||||
La taille de ``<os>-root.qcow2`` (10 Gio ici) borne l'image produite : elle doit être au moins
|
|
||||||
égale à la taille **virtuelle** de l'image du fournisseur, pas à la taille de son fichier.
|
|
||||||
|
|
||||||
Étape 3 — Lancer la VM de construction
|
|
||||||
--------------------------------------
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
qemu-system-x86_64 \
|
|
||||||
-enable-kvm \
|
|
||||||
-cpu host \
|
|
||||||
-m 2048 \
|
|
||||||
-smp 2 \
|
|
||||||
-nographic \
|
|
||||||
-serial mon:stdio \
|
|
||||||
-monitor unix:/tmp/vm-build.mon-sock,server,nowait \
|
|
||||||
-drive file=/opt/seed/${os}_seed.iso,media=cdrom,if=ide \
|
|
||||||
\
|
|
||||||
-drive file=${disk_dir}/${os}-tmp.qcow2,format=qcow2,if=none,id=vda \
|
|
||||||
-device virtio-blk-pci,drive=vda,bootindex=0 \
|
|
||||||
\
|
|
||||||
-device virtio-scsi-pci,id=scsi0 \
|
|
||||||
\
|
|
||||||
-drive file=${disk_dir}/${os}-root.qcow2,if=none,id=hd0 \
|
|
||||||
-device scsi-hd,drive=hd0,bus=scsi0.0 \
|
|
||||||
\
|
|
||||||
-drive file=${disk_dir}/tmp.qcow2,if=none,id=hd1 \
|
|
||||||
-device scsi-hd,drive=hd1,bus=scsi0.0 \
|
|
||||||
\
|
|
||||||
-netdev tap,id=net0,ifname=tap0,script=no,downscript=no \
|
|
||||||
-device virtio-net-pci,netdev=net0,mac=00:22:33:00:00:01
|
|
||||||
|
|
||||||
La répartition virtio-blk pour le système / SCSI pour les disques supplémentaires est la même que
|
|
||||||
celle qu'impose l'agent — voir :doc:`/architecture/contraintes`. Le tap ``tap0`` doit exister et
|
|
||||||
être raccordé à un réseau qui donne un accès sortant : la suite télécharge l'image du
|
|
||||||
fournisseur depuis la VM.
|
|
||||||
|
|
||||||
Étape 4 — Écrire l'image du fournisseur sur le disque cible
|
|
||||||
------------------------------------------------------------
|
|
||||||
|
|
||||||
Les commandes suivantes s'exécutent **dans la VM de construction**. Identifier d'abord les
|
|
||||||
disques : le disque de travail et le disque cible ne doivent pas être confondus.
|
|
||||||
|
|
||||||
.. danger::
|
|
||||||
|
|
||||||
``qemu-img convert`` écrase intégralement le disque cible. Vérifier les noms avant, avec
|
|
||||||
``lsblk``, plutôt que de supposer l'ordre d'énumération.
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
work_disk=/dev/sdb
|
|
||||||
os_disk=/dev/sda
|
|
||||||
|
|
||||||
mkdir /work
|
|
||||||
mkfs.xfs ${work_disk}
|
|
||||||
mount ${work_disk} /work
|
|
||||||
cd /work
|
|
||||||
|
|
||||||
curl "${os_link}" -O
|
|
||||||
qemu-img convert ./*.qcow2 -O raw "${os_disk}"
|
|
||||||
|
|
||||||
L'image du fournisseur est écrite **en brut** directement sur le disque cible : le qcow2 obtenu
|
|
||||||
côté host contient donc une image disque complète et amorçable, sans backing file.
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
partprobe
|
|
||||||
echo 1 > /sys/block/sda/device/rescan
|
|
||||||
sleep 2
|
|
||||||
|
|
||||||
# La partition racine est la plus grande du disque
|
|
||||||
root_partition=$(fdisk -lo device,size "${os_disk}" | grep -E '^/dev/' | tr -s ' ' \
|
|
||||||
| sort -rhk2 | head -n1 | cut -d ' ' -f1)
|
|
||||||
|
|
||||||
mount -o nouuid $root_partition /mnt
|
|
||||||
mount -o bind /dev /mnt/dev
|
|
||||||
mount -o bind /proc /mnt/proc
|
|
||||||
mount -o bind /sys /mnt/sys
|
|
||||||
|
|
||||||
cp /etc/resolv.conf /mnt/etc/resolv.conf
|
|
||||||
|
|
||||||
``-o nouuid`` est nécessaire parce que le système de fichiers qui vient d'être écrit porte le
|
|
||||||
même UUID que celui déjà monté par la VM de construction. Le ``resolv.conf`` est copié pour que
|
|
||||||
les commandes en chroot aient la résolution DNS ; il est supprimé au nettoyage.
|
|
||||||
|
|
||||||
Étape 5 — Personnaliser l'image
|
|
||||||
-------------------------------
|
|
||||||
|
|
||||||
**Accès SSH**
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
yum install -y augeas
|
|
||||||
|
|
||||||
echo "The default user for Syonad VMs is 'syonad'." > /mnt/etc/banner
|
|
||||||
|
|
||||||
augtool -r /mnt -s <<'EOF'
|
|
||||||
set /files/etc/ssh/sshd_config/X11Forwarding no
|
|
||||||
set /files/etc/ssh/sshd_config/PermitTunnel no
|
|
||||||
set /files/etc/ssh/sshd_config/PermitRootLogin no
|
|
||||||
set /files/etc/ssh/sshd_config/RSAAuthentication yes
|
|
||||||
set /files/etc/ssh/sshd_config/PubkeyAuthentication yes
|
|
||||||
set /files/etc/ssh/sshd_config/PasswordAuthentication no
|
|
||||||
set /files/etc/ssh/sshd_config/UseDNS no
|
|
||||||
set /files/etc/ssh/sshd_config/ChallengeResponseAuthentication no
|
|
||||||
set /files/etc/ssh/sshd_config/GSSAPIAuthentication no
|
|
||||||
set /files/etc/ssh/sshd_config/Match[1]/Condition/User "root,centos,ubuntu,debian,ec2-user"
|
|
||||||
set /files/etc/ssh/sshd_config/Match[1]/Settings/Banner "/etc/banner"
|
|
||||||
EOF
|
|
||||||
|
|
||||||
``PasswordAuthentication no`` vaut pour toutes les VM dérivées : l'accès se fait par clé, et le
|
|
||||||
champ ``password`` de l'API de l'agent ne sert donc **pas** à ouvrir une session SSH.
|
|
||||||
|
|
||||||
**Utilisateur par défaut et source de metadata**
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
cat << 'ENDFILE' > /mnt/etc/cloud/cloud.cfg.d/20_user.cfg
|
|
||||||
system_info:
|
|
||||||
default_user:
|
|
||||||
name: syonad
|
|
||||||
ENDFILE
|
|
||||||
|
|
||||||
cat << 'ENDFILE' > /mnt/etc/cloud/cloud.cfg.d/99_metadata.cfg
|
|
||||||
datasource_list: [ NoCloud ]
|
|
||||||
datasource:
|
|
||||||
NoCloud:
|
|
||||||
seedfrom: 'http://169.254.169.254:80'
|
|
||||||
timeout: 5
|
|
||||||
max_wait: 10
|
|
||||||
ENDFILE
|
|
||||||
|
|
||||||
C'est ce second fichier qui raccorde l'image au serveur de metadata de l'agent : ``NoCloud`` est
|
|
||||||
la seule source retenue, et elle pointe sur ``169.254.169.254``. La route ``/32`` vers cette
|
|
||||||
adresse est indispensable côté agent — voir :doc:`/concepts/modes-reseau`.
|
|
||||||
|
|
||||||
**Services et durcissement**
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
chroot /mnt/ systemctl enable fstrim.timer
|
|
||||||
|
|
||||||
chroot /mnt/ systemctl disable rpcbind.service
|
|
||||||
chroot /mnt/ systemctl disable rpcbind.socket
|
|
||||||
|
|
||||||
augtool -r /mnt -s set /files/etc/selinux/config/SELINUX disabled
|
|
||||||
|
|
||||||
chroot /mnt/ dnf remove -y 'cockpit*'
|
|
||||||
chroot /mnt/ rm -rf /run/cockpit
|
|
||||||
|
|
||||||
Étape 6 — Nettoyer, puis éteindre
|
|
||||||
---------------------------------
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
rm -f /mnt/etc/resolv.conf
|
|
||||||
rm -rf /mnt/var/cache/yum
|
|
||||||
rm -rf /mnt/root/.ssh
|
|
||||||
rm -rf /mnt/root/.bash_history
|
|
||||||
rm -rf /mnt/tmp/*
|
|
||||||
rm -rf /mnt/var/lib/dhcp/*
|
|
||||||
rm -rf /mnt/var/tmp/*
|
|
||||||
find /mnt/var/log ! -type d -exec rm '{}' \;
|
|
||||||
rm -rf /mnt/var/lib/cloud/*
|
|
||||||
|
|
||||||
poweroff
|
|
||||||
|
|
||||||
``/mnt/var/lib/cloud/*`` est le nettoyage le plus important : c'est lui qui fait que cloud-init
|
|
||||||
considère chaque VM dérivée comme une instance neuve. Sans lui, l'image embarque l'identité de
|
|
||||||
l'instance de construction et le user-data n'est pas appliqué — même mécanisme que la
|
|
||||||
recréation d'une VM sous un nom déjà utilisé, cf. :doc:`/concepts/metadata-cloud-init`.
|
|
||||||
|
|
||||||
Une fois la VM éteinte, ``${disk_dir}/${os}-root.qcow2`` est l'image golden.
|
|
||||||
``${os}-tmp.qcow2`` et ``tmp.qcow2`` sont jetables.
|
|
||||||
|
|
||||||
Points de vigilance
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
**SELinux est désactivé** dans l'image. C'est une couche de protection en moins sur toutes les
|
|
||||||
VM qui en dérivent, y compris celles qui portent des fonctions sensibles comme le route
|
|
||||||
reflector ou la base de données. Décision à assumer explicitement, et à réévaluer : le mode
|
|
||||||
``permissive`` permettrait au minimum de savoir ce qui serait bloqué.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
``fstrim.timer`` est activé dans l'image, mais l'agent lance QEMU **sans** ``discard=unmap``
|
|
||||||
ni ``detect-zeroes=unmap`` sur les disques. Le ``fstrim`` du guest ne rend donc aujourd'hui
|
|
||||||
aucun espace à l'host : les qcow2 ne se rétractent pas. L'activation reste utile pour le jour
|
|
||||||
où l'option sera ajoutée côté agent, mais ne pas compter dessus pour la place disque.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
**À vérifier** — ``seedfrom`` sans barre oblique finale. cloud-init construit l'URL des
|
|
||||||
documents en concaténant ``seedfrom`` avec ``meta-data`` et ``user-data``. Confirmer sur une
|
|
||||||
VM réelle que les deux documents sont bien récupérés, et corriger en
|
|
||||||
``http://169.254.169.254:80/`` si ce n'est pas le cas.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
**Provenance de l'image du fournisseur** — tout ce qui est construit en hérite. Vérifier la
|
|
||||||
somme de contrôle, et la signature quand elle existe, avant de construire dessus. La
|
|
||||||
procédure actuelle télécharge l'image deux fois, une fois sur l'host et une fois dans la VM,
|
|
||||||
sans vérification.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
**À rédiger** — la procédure ne dit pas encore comment l'image produite est nommée, versionnée
|
|
||||||
et distribuée aux hyperviseurs, ni quelles variantes existent par rôle (``intel``, PostgreSQL,
|
|
||||||
route reflector) : image unique personnalisée au démarrage par cloud-init, ou images
|
|
||||||
dérivées ?
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
Déploiement d'un cluster
|
|
||||||
========================
|
|
||||||
|
|
||||||
Le :doc:`/demarrage/index` couvre un hyperviseur isolé : un VPC, un subnet, une VM, tout sur le
|
|
||||||
même nœud. Cette section couvre la mise en place d'un **cluster** complet, dans l'ordre où les
|
|
||||||
étapes se font.
|
|
||||||
|
|
||||||
Cet ordre n'est pas indifférent : chaque étape a besoin de la précédente. L'image doit exister
|
|
||||||
avant qu'on puisse démarrer quoi que ce soit ; le réseau doit être en place avant le premier
|
|
||||||
hyperviseur ; le route reflector est lui-même une VM, il lui faut donc un hyperviseur qui
|
|
||||||
fonctionne.
|
|
||||||
|
|
||||||
Étapes
|
|
||||||
------
|
|
||||||
|
|
||||||
#. :doc:`architecture-cluster` — la topologie cible, à lire avant tout le reste
|
|
||||||
#. :doc:`image-qcow2` — l'image golden dont dérivent toutes les VM du cluster
|
|
||||||
#. :doc:`routeurs` — le matériel : routeurs de cluster, de datacentre et de bordure
|
|
||||||
#. :doc:`premier-hyperviseur` — le premier nœud, agent et plan de contrôle
|
|
||||||
#. :doc:`route-reflector` — les VM route reflector
|
|
||||||
|
|
||||||
D'autres étapes viendront à mesure que les composants d'orchestration seront livrés.
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:hidden:
|
|
||||||
:maxdepth: 1
|
|
||||||
|
|
||||||
architecture-cluster
|
|
||||||
image-qcow2
|
|
||||||
routeurs
|
|
||||||
premier-hyperviseur
|
|
||||||
route-reflector
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
Premier hyperviseur
|
|
||||||
===================
|
|
||||||
|
|
||||||
Le premier nœud du cluster se déploie comme les suivants, mais il est le seul à devoir
|
|
||||||
fonctionner **avant** que le plan de contrôle existe : c'est lui qui hébergera la première VM
|
|
||||||
route reflector.
|
|
||||||
|
|
||||||
Installation
|
|
||||||
------------
|
|
||||||
|
|
||||||
L'installation de l'agent est identique à celle d'un nœud isolé et n'est pas reprise ici :
|
|
||||||
voir :doc:`/demarrage/installation` pour ``deploy.sh``, ses options, la préparation de l'host et
|
|
||||||
la migration réseau vers le bridge d'uplink.
|
|
||||||
|
|
||||||
Deux points à relire avant de lancer un ``-i`` sur un nœud de production : la migration réseau
|
|
||||||
coupe le réseau de l'host si elle échoue à mi-parcours, et l'hyperviseur est **sans état** —
|
|
||||||
tout ce qui est posé doit l'être par un mécanisme rejoué à chaque démarrage.
|
|
||||||
|
|
||||||
Plan de contrôle — FRR
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
FRR tourne sur chaque hyperviseur et peuple la table de transfert (FDB) des interfaces VXLAN
|
|
||||||
créées par l'agent. C'est ce qui rend un subnet utilisable au-delà d'un seul nœud, puisque
|
|
||||||
l'agent désactive l'apprentissage et ne configure aucun voisin — cf.
|
|
||||||
:doc:`architecture-cluster`.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
**À rédiger.** À documenter :
|
|
||||||
|
|
||||||
* la version de FRR de référence et son mode d'installation, sachant que l'hyperviseur est
|
|
||||||
sans état : le paquet et la configuration doivent être posés à chaque démarrage, par le
|
|
||||||
bootstrap ou par un mécanisme équivalent ;
|
|
||||||
* les démons activés dans ``/etc/frr/daemons`` ;
|
|
||||||
* la configuration de référence : numéro d'AS, session vers le route reflector, famille
|
|
||||||
d'adresses utilisée pour annoncer les MAC et les VNI ;
|
|
||||||
* l'articulation avec les interfaces créées par l'agent : comment FRR découvre une interface
|
|
||||||
VXLAN qui apparaît à la création d'un subnet, et si une action est nécessaire ensuite ;
|
|
||||||
* ce qui se passe au démarrage à froid, quand FRR démarre avant ou après l'agent ;
|
|
||||||
* le cas particulier du **premier** hyperviseur, dont la session ne peut pas s'établir tant
|
|
||||||
que le route reflector n'existe pas.
|
|
||||||
|
|
||||||
Vérifier le plan de données
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
Ces deux vérifications restent valables quelle que soit la configuration retenue, et méritent
|
|
||||||
d'être dans toute procédure de diagnostic :
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
# La FDB du VXLAN doit contenir des entrées vers les autres hyperviseurs.
|
|
||||||
# Vide, c'est le plan de contrôle qui ne fonctionne pas, pas l'agent.
|
|
||||||
ip netns exec <vpc> bridge fdb show dev <interface-vxlan>
|
|
||||||
|
|
||||||
# L'interface VXLAN telle que l'agent l'a créée : port 4789, learning off,
|
|
||||||
# aucun groupe multicast, aucun remote.
|
|
||||||
ip netns exec <vpc> ip -d link show <interface-vxlan>
|
|
||||||
|
|
||||||
.. important::
|
|
||||||
|
|
||||||
Une FDB vide alors que le subnet est en ``running`` n'est **pas** un défaut de l'agent : il
|
|
||||||
crée délibérément l'interface sans apprentissage ni voisin, et laisse le peuplement au plan
|
|
||||||
de contrôle.
|
|
||||||
|
|
||||||
Valider le nœud
|
|
||||||
---------------
|
|
||||||
|
|
||||||
Avant de passer à la suite, le nœud doit savoir créer une VM de bout en bout à partir de l'image
|
|
||||||
golden — c'est exactement le parcours de :doc:`/demarrage/premier-vpc`, avec
|
|
||||||
``storage[0].path`` pointant sur une copie de l'image produite par :doc:`image-qcow2`.
|
|
||||||
|
|
||||||
Une VM qui démarre, obtient son adresse en DHCP et applique son user-data valide d'un coup
|
|
||||||
l'agent, le DHCP, la route vers le serveur de metadata et l'image. C'est le prérequis de
|
|
||||||
:doc:`route-reflector`.
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
VM route reflector
|
|
||||||
==================
|
|
||||||
|
|
||||||
Le route reflector est le point de rendez-vous du plan de contrôle : plutôt que de maintenir une
|
|
||||||
session entre chaque paire d'hyperviseurs, chaque hyperviseur ouvre une session vers le route
|
|
||||||
reflector, qui redistribue.
|
|
||||||
|
|
||||||
Il tourne lui-même en machine virtuelle, ce qui crée une dépendance circulaire à traiter
|
|
||||||
explicitement : la VM qui porte le plan de contrôle du cluster est hébergée par le cluster.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
**À rédiger.** À documenter :
|
|
||||||
|
|
||||||
* la création de la VM : ressources, subnet et mode utilisés, et s'il s'agit d'une VM créée
|
|
||||||
par l'agent comme les autres ou d'un cas particulier — l'image, elle, est l'image golden de
|
|
||||||
:doc:`image-qcow2` ;
|
|
||||||
* son adressage, et comment les hyperviseurs le connaissent ;
|
|
||||||
* la configuration du démon de routage qu'elle héberge ;
|
|
||||||
* la redondance : une seule VM route reflector, ou deux, et sur quels hyperviseurs ;
|
|
||||||
* la procédure de reconstruction, et l'état du cluster pendant que le route reflector est
|
|
||||||
absent — les tunnels déjà établis continuent-ils de fonctionner, et pendant combien de
|
|
||||||
temps ;
|
|
||||||
* la procédure d'amorçage : ce qui fonctionne, et dans quel ordre, quand on démarre un cluster
|
|
||||||
entier depuis zéro — le premier hyperviseur n'a pas de session FRR établie tant que cette VM
|
|
||||||
n'existe pas, cf. :doc:`premier-hyperviseur`.
|
|
||||||
|
|
||||||
Points de vigilance
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
L'agent **ne réattache pas** les VM existantes à son démarrage, et les processus QEMU ne
|
|
||||||
survivent pas à un redémarrage de l'hyperviseur. Le redémarrage de l'hyperviseur qui héberge
|
|
||||||
le route reflector est donc un événement à part entière : la procédure de remise en service
|
|
||||||
doit être écrite, et testée.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
``instance-id`` valant le nom de la VM, recréer la VM route reflector sous le même nom sur le
|
|
||||||
même disque fait que cloud-init **ne rejoue pas** le user-data. Cf.
|
|
||||||
:doc:`/concepts/metadata-cloud-init`.
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
Routeurs
|
|
||||||
========
|
|
||||||
|
|
||||||
Trois niveaux de routage entourent le cluster, du plus proche des hyperviseurs au plus proche de
|
|
||||||
l'extérieur. Tous préexistent à l'agent : celui-ci ne les configure pas et n'en a aucune
|
|
||||||
connaissance.
|
|
||||||
|
|
||||||
Routeurs de cluster
|
|
||||||
raccordent les hyperviseurs entre eux. C'est le niveau dont dépend directement le plan de
|
|
||||||
données VXLAN.
|
|
||||||
|
|
||||||
Routeurs de datacentre
|
|
||||||
agrègent les clusters d'un même site.
|
|
||||||
|
|
||||||
Routeurs de bordure
|
|
||||||
terminent le routage vers l'extérieur.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
**À rédiger.** Cette page attend les éléments de terrain. Pour chacun des trois niveaux :
|
|
||||||
|
|
||||||
* le matériel ou le logiciel employé, et la version de référence ;
|
|
||||||
* la configuration de référence : interfaces, adressage, protocole de routage et numéros
|
|
||||||
d'AS ;
|
|
||||||
* la redondance : combien d'équipements, quel mécanisme de bascule, quel comportement attendu
|
|
||||||
pendant une bascule ;
|
|
||||||
* ce qui est annoncé et ce qui est filtré à chaque niveau ;
|
|
||||||
* l'ordre de mise en service, et ce qui doit être opérationnel avant de préparer le premier
|
|
||||||
hyperviseur.
|
|
||||||
|
|
||||||
Contraintes imposées par le reste du cluster
|
|
||||||
---------------------------------------------
|
|
||||||
|
|
||||||
Indépendamment des choix d'équipement, deux contraintes viennent de ce que fait l'agent.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
**MTU** — l'agent crée bridges, veth et interfaces VXLAN avec un MTU figé à 1500, et VXLAN
|
|
||||||
ajoute 50 octets d'encapsulation. Les liens entre hyperviseurs doivent donc accepter au moins
|
|
||||||
**1550 octets**. Cf. :doc:`architecture-cluster`.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
**UDP 4789** doit passer entre hyperviseurs, dans les deux sens : c'est le port des tunnels
|
|
||||||
VXLAN.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
**L'API de l'agent n'a aucune authentification.** Le filtrage réalisé ici est aujourd'hui
|
|
||||||
l'une des rares barrières entre cette API et le reste du réseau : son port ne doit être
|
|
||||||
joignable que depuis le réseau d'administration. Cf. :doc:`/exploitation/configuration`.
|
|
||||||
|
|
@ -1,91 +0,0 @@
|
||||||
Modèle asynchrone et codes de retour
|
|
||||||
====================================
|
|
||||||
|
|
||||||
Toute création et toute suppression sont **asynchrones**. C'est le point qui surprend le plus
|
|
||||||
souvent à l'intégration : un ``202`` ne dit pas que la ressource existe, il dit que la demande
|
|
||||||
a été acceptée et enregistrée.
|
|
||||||
|
|
||||||
Les deux temps d'une requête
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
.. mermaid::
|
|
||||||
|
|
||||||
sequenceDiagram
|
|
||||||
participant C as Appelant
|
|
||||||
participant A as API
|
|
||||||
participant W as Worker
|
|
||||||
C->>A: POST /subnets
|
|
||||||
A->>A: Prepare — valide, écrit "creating"
|
|
||||||
A-->>C: 202 + ressource en creating
|
|
||||||
A->>W: Dispatch (file d'attente)
|
|
||||||
W->>W: Execute — netns, netif, dhcp
|
|
||||||
W->>W: état → running (ou error)
|
|
||||||
C->>A: GET /subnets/<name>
|
|
||||||
A-->>C: 200 + state
|
|
||||||
|
|
||||||
**Prepare** est synchrone, dans le handler HTTP : il valide l'état, écrit l'état initial en base
|
|
||||||
et répond. **Execute** est asynchrone : il fait le travail réseau réel, puis positionne l'état
|
|
||||||
final.
|
|
||||||
|
|
||||||
Conséquence directe : un échec d'``Execute`` ne peut pas être remonté dans la réponse HTTP. Il
|
|
||||||
se lit dans l'état de la ressource, qui passe à ``error``.
|
|
||||||
|
|
||||||
Attendre correctement
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
Il n'y a pas de webhook ni de long-polling : l'appelant interroge ``GET /<type>/<name>`` jusqu'à
|
|
||||||
un état stable.
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
until [ "$(curl -sf http://127.0.0.1:8080/subnets/sn-000001 | jq -r .state)" = running ]; do
|
|
||||||
sleep 2
|
|
||||||
done
|
|
||||||
|
|
||||||
Trois règles pour un appelant robuste :
|
|
||||||
|
|
||||||
* **Toujours borner l'attente.** Côté agent, ``dispatcher.timeout_seconds`` (300 s par défaut)
|
|
||||||
borne les opérations qui attendent une transition ; l'appelant doit avoir sa propre borne.
|
|
||||||
* **Traiter ``error`` comme terminal, pas comme un échec transitoire.** Un ``Execute`` en échec
|
|
||||||
ne se rejoue pas tout seul.
|
|
||||||
* **Ne pas enchaîner sans vérifier.** Créer un subnet dont le VPC est encore en ``creating``
|
|
||||||
échoue en 422 ; démarrer une VM sur un subnet en ``creating`` ou ``running`` est en revanche
|
|
||||||
accepté.
|
|
||||||
|
|
||||||
Codes de retour
|
|
||||||
---------------
|
|
||||||
|
|
||||||
.. list-table::
|
|
||||||
:header-rows: 1
|
|
||||||
:widths: 12 88
|
|
||||||
|
|
||||||
* - Code
|
|
||||||
- Signification
|
|
||||||
* - ``202``
|
|
||||||
- demande acceptée et enregistrée ; l'état passera à ``running`` ou ``error``
|
|
||||||
* - ``200``
|
|
||||||
- lecture réussie (``GET``)
|
|
||||||
* - ``400``
|
|
||||||
- champ obligatoire manquant, corps invalide, ``iface_type`` inconnu, base64 invalide
|
|
||||||
* - ``404``
|
|
||||||
- ressource inexistante
|
|
||||||
* - ``409``
|
|
||||||
- conflit d'existence ou d'état : ressource déjà créée, ou suppression depuis un état qui
|
|
||||||
ne l'autorise pas ; pour un VPC, subnets encore présents
|
|
||||||
* - ``422``
|
|
||||||
- dépendance absente ou pas prête : VPC parent d'un subnet, subnet d'une VM
|
|
||||||
* - ``500``
|
|
||||||
- erreur interne
|
|
||||||
|
|
||||||
Le corps d'erreur est uniforme : ``{"error": "…"}``.
|
|
||||||
|
|
||||||
Idempotence
|
|
||||||
-----------
|
|
||||||
|
|
||||||
Les créations ne sont **pas** idempotentes : recréer une ressource existante donne 409, pas 202.
|
|
||||||
Un appelant qui rejoue une requête après un timeout réseau doit donc traiter 409 comme
|
|
||||||
« déjà fait », après avoir vérifié l'état par un ``GET``.
|
|
||||||
|
|
||||||
Les suppressions depuis l'état ``error`` sont acceptées mais **best-effort** : les ressources
|
|
||||||
système peuvent n'avoir été créées que partiellement, et il n'y a pas de rollback — voir
|
|
||||||
:doc:`/concepts/cycle-de-vie`.
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
API de l'agent
|
|
||||||
==============
|
|
||||||
|
|
||||||
L'agent expose une API HTTP par hyperviseur. C'est aujourd'hui la seule API de ``two`` ; les
|
|
||||||
API de niveau supérieur viendront avec les composants d'orchestration, et seront documentées
|
|
||||||
à part.
|
|
||||||
|
|
||||||
Elle est **machine-to-machine** : elle est consommée par un autre logiciel, pas par un humain.
|
|
||||||
La validation de cohérence des entrées (format des CIDR, plage des VXLAN ID, convention de
|
|
||||||
nommage) est à la charge de l'appelant — l'agent ne la refait pas.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
Cette API **n'a aucune authentification**. Voir :doc:`/exploitation/configuration` avant de
|
|
||||||
l'exposer au-delà de la boucle locale.
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 1
|
|
||||||
|
|
||||||
asynchronisme
|
|
||||||
reference
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
Référence
|
|
||||||
=========
|
|
||||||
|
|
||||||
Cette page est générée depuis ``api/agent.yaml``, à la racine du dépôt. C'est la source unique
|
|
||||||
du contrat : en cas d'écart avec le reste de la documentation, c'est elle qui fait foi.
|
|
||||||
|
|
||||||
.. openapi:: ../../../api/agent.yaml
|
|
||||||
:examples:
|
|
||||||
|
|
@ -1,210 +0,0 @@
|
||||||
Configuration
|
|
||||||
=============
|
|
||||||
|
|
||||||
Un seul fichier, ``/etc/two/agent.yml``, partagé par les quatre binaires : ``agent -config``,
|
|
||||||
``metadata -conf``, ``db -conf`` et ``dhcp -conf``. Le fichier de référence commenté est
|
|
||||||
``conf/agent/config.exemple.yml`` dans le dépôt.
|
|
||||||
|
|
||||||
Le chargement se fait par **viper** : les clés sont celles ci-dessous, en YAML.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
Un fichier **absent** est toléré : toutes les valeurs par défaut s'appliquent. Un fichier
|
|
||||||
**présent mais invalide** fait en revanche échouer le démarrage, volontairement — jusqu'à
|
|
||||||
la version 0.1.0 il était ignoré en silence, et l'agent tournait alors entièrement sur les
|
|
||||||
défauts sans le dire. Une tabulation d'indentation ou un ``--`` égaré suffisent.
|
|
||||||
|
|
||||||
.. danger::
|
|
||||||
|
|
||||||
**L'API de l'agent n'a aucune authentification.** L'exemple livré écoute sur
|
|
||||||
``0.0.0.0:8080`` : quiconque atteint ce port peut créer et détruire des VM et des réseaux sur
|
|
||||||
l'host KVM, c'est-à-dire en prendre le contrôle.
|
|
||||||
|
|
||||||
Sur tout déploiement réel : restreindre ``api.address`` à une adresse d'administration, ou
|
|
||||||
filtrer le port en amont (pare-feu, réseau dédié). Traiter l'ouverture de ce port comme une
|
|
||||||
décision d'architecture, pas comme un réglage.
|
|
||||||
|
|
||||||
Base de données
|
|
||||||
---------------
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
database:
|
|
||||||
path: "/var/lib/two/data/"
|
|
||||||
|
|
||||||
Répertoire de la base clé-valeur Badger. **Un seul processus l'ouvre** : l'agent. Ni le serveur
|
|
||||||
de metadata ni aucun autre outil ne doit être configuré pour ouvrir le même répertoire pendant
|
|
||||||
que l'agent tourne.
|
|
||||||
|
|
||||||
Serveurs
|
|
||||||
--------
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
api:
|
|
||||||
address: "0.0.0.0"
|
|
||||||
port: 8080
|
|
||||||
|
|
||||||
prometheus:
|
|
||||||
address: "0.0.0.0"
|
|
||||||
port: 9090
|
|
||||||
|
|
||||||
admin:
|
|
||||||
enabled: false
|
|
||||||
address: "127.0.0.1"
|
|
||||||
port: 9091
|
|
||||||
|
|
||||||
``admin`` expose une inspection en lecture seule de la base (``/db?prefix=…``). Elle est
|
|
||||||
désactivée par défaut et prévue pour la boucle locale uniquement.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
Le contenu de la base inclut ``vm/<name>/password``, qui est un hash de mot de passe.
|
|
||||||
L'activation de l'API d'administration rend ces valeurs lisibles par tout ce qui atteint le
|
|
||||||
port. Ne pas l'exposer hors de la boucle locale.
|
|
||||||
|
|
||||||
Exécution des commandes
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
worker:
|
|
||||||
count: 4
|
|
||||||
buffer_size: 100
|
|
||||||
|
|
||||||
dispatcher:
|
|
||||||
timeout_seconds: 300
|
|
||||||
poll_seconds: 2
|
|
||||||
|
|
||||||
``worker.count`` est le nombre de goroutines qui exécutent les commandes ; ``buffer_size`` le
|
|
||||||
nombre de commandes en attente au-delà duquel ``Dispatch`` bloque.
|
|
||||||
|
|
||||||
``dispatcher.timeout_seconds`` borne les opérations qui attendent une transition d'état, dont
|
|
||||||
l'extinction d'une VM.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
À l'expiration de ce délai, une VM qui ne s'est pas éteinte reçoit un ``quit`` QMP — un arrêt
|
|
||||||
**brutal**. Pour des charges dont l'extinction est lente (bases de données, construction
|
|
||||||
d'images), une valeur confortable évite un système de fichiers invité incohérent.
|
|
||||||
|
|
||||||
Correspondance des interfaces
|
|
||||||
-----------------------------
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
default_interface: br-000000
|
|
||||||
|
|
||||||
interfaces:
|
|
||||||
vms: br-000000
|
|
||||||
internet: br-000000
|
|
||||||
admin: br-000000
|
|
||||||
|
|
||||||
Traduit les clés logiques ``iface_type`` de l'API vers les bridges physiques de l'host. Une clé
|
|
||||||
inconnue ou omise retombe silencieusement sur ``default_interface`` — ce n'est pas une erreur,
|
|
||||||
mais c'est une source de subnets branchés au mauvais endroit sans le dire.
|
|
||||||
|
|
||||||
Metadata et QEMU
|
|
||||||
----------------
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
metadata:
|
|
||||||
run_dir: "/run/two/metadata"
|
|
||||||
|
|
||||||
qemu:
|
|
||||||
ovmf_code_path: "/usr/share/OVMF/OVMF_CODE.fd"
|
|
||||||
ovmf_vars_template: "/usr/share/OVMF/OVMF_VARS.fd"
|
|
||||||
uefi_vars_dir: "/run/two/vms/efi"
|
|
||||||
serial_dir: "/run/two/vms/serial"
|
|
||||||
monitor_dir: "/run/two/vms/monitor"
|
|
||||||
qmp_dir: "/run/two/vms/qmp"
|
|
||||||
|
|
||||||
Les chemins OVMF sont nécessaires aux VM démarrées avec ``uefi: true`` (paquet ``ovmf`` sur
|
|
||||||
Debian et Ubuntu). ``uefi_vars_dir`` reçoit une copie inscriptible des variables UEFI par VM,
|
|
||||||
créée au démarrage et supprimée à l'arrêt.
|
|
||||||
|
|
||||||
Backend DHCP
|
|
||||||
------------
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
dhcp:
|
|
||||||
backend: dnsmasq # ou two
|
|
||||||
|
|
||||||
Choisit qui sert le DHCP des subnets **créés par cet agent** :
|
|
||||||
|
|
||||||
.. list-table::
|
|
||||||
:header-rows: 1
|
|
||||||
:widths: 14 44 42
|
|
||||||
|
|
||||||
* - Valeur
|
|
||||||
- Serveur
|
|
||||||
- Unit
|
|
||||||
* - ``dnsmasq``
|
|
||||||
- dnsmasq, configuré par fichiers dans ``/etc/dnsmasq.d``
|
|
||||||
- ``dnsmasq@<netns>_<bridge>``
|
|
||||||
* - ``two``
|
|
||||||
- le binaire ``dhcp``, piloté par socket Unix
|
|
||||||
- ``dhcp@<netns>_<bridge>``
|
|
||||||
|
|
||||||
Le défaut est ``dnsmasq`` : un fichier de configuration de la 0.1.0, non modifié, se comporte
|
|
||||||
exactement comme avant. Toute autre valeur que ``dnsmasq`` ou ``two`` fait échouer le démarrage.
|
|
||||||
|
|
||||||
Le répertoire d'exécution du backend ``two`` — ``/run/two/dhcp`` — **n'est pas configurable** :
|
|
||||||
le script d'enrobage le code en dur, une clé que lui ignorerait serait un mensonge.
|
|
||||||
|
|
||||||
Ce que le backend ``two`` apporte : la configuration DHCP devient modifiable par VM et non plus
|
|
||||||
seulement par subnet, ce qui permet de n'annoncer la route par défaut que sur **une** interface
|
|
||||||
d'une VM multi-réseaux. Le watchdog peut en outre interroger le serveur et comparer ce qu'il sert
|
|
||||||
à ce que la base dit — voir :doc:`diagnostic`.
|
|
||||||
|
|
||||||
Bascule d'un backend à l'autre
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
L'option ne décide que du backend des **nouveaux** subnets. Elle ne migre rien : un subnet
|
|
||||||
déjà créé continue d'être servi par le serveur qui l'a été. Changer la valeur sans vider
|
|
||||||
l'hyperviseur laisse l'agent parler à un serveur qui ne tourne pas — les VM existantes
|
|
||||||
continuent, les nouvelles n'obtiennent pas d'adresse.
|
|
||||||
|
|
||||||
La bascule est **manuelle** et suppose un hyperviseur vide :
|
|
||||||
|
|
||||||
1. Supprimer toutes les VM, puis tous les subnets, puis les VPC.
|
|
||||||
2. Vérifier qu'il ne reste aucune unit DHCP active et aucun résidu :
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
systemctl list-units 'dnsmasq@*' 'dhcp@*'
|
|
||||||
ls /etc/dnsmasq.d/ /run/two/dhcp/
|
|
||||||
|
|
||||||
3. Modifier ``dhcp.backend`` dans ``/etc/two/agent.yml``.
|
|
||||||
4. ``systemctl restart agent`` — la valeur est lue au démarrage, pas à chaque commande.
|
|
||||||
5. Recréer VPC, subnets et VM.
|
|
||||||
6. Sur la première VM, vérifier l'adresse **et les trois routes** : la route par défaut, la
|
|
||||||
route vers le CIDR du VPC, et la route ``/32`` vers ``169.254.169.254``. C'est cette
|
|
||||||
dernière qui conditionne le provisionnement cloud-init.
|
|
||||||
|
|
||||||
Le retour arrière suit la même procédure. Il n'y a pas de bascule à chaud, dans un sens ni dans
|
|
||||||
l'autre.
|
|
||||||
|
|
||||||
Watchdog
|
|
||||||
--------
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
watchdog:
|
|
||||||
enabled: true
|
|
||||||
interval_seconds: 60
|
|
||||||
|
|
||||||
Vérification périodique **en lecture seule** — voir :doc:`/exploitation/observabilite`.
|
|
||||||
|
|
||||||
Journalisation
|
|
||||||
--------------
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
logger:
|
|
||||||
level: info # debug, info, warn, error
|
|
||||||
debug: false # force le niveau debug quel que soit level
|
|
||||||
|
|
@ -1,148 +0,0 @@
|
||||||
Diagnostic
|
|
||||||
==========
|
|
||||||
|
|
||||||
Symptôme, cause probable, vérification. Les causes listées sont celles réellement rencontrées.
|
|
||||||
|
|
||||||
La ressource part en ``error`` juste après le 202
|
|
||||||
-------------------------------------------------
|
|
||||||
|
|
||||||
``Execute`` a échoué : la cause est dans le journal de l'agent, pas dans la réponse HTTP.
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
journalctl -u agent -n 100
|
|
||||||
|
|
||||||
Cas fréquents :
|
|
||||||
|
|
||||||
* subnet en mode ``public_ip`` — la mise en place host n'est pas implémentée, l'échec est attendu ;
|
|
||||||
* ``vxlan_id`` déjà utilisé sur l'host ;
|
|
||||||
* bridge cible absent : ``iface_type`` inconnu retombé sur ``default_interface``, lui-même
|
|
||||||
inexistant.
|
|
||||||
|
|
||||||
Avant toute recréation, émettre un ``DELETE`` : il n'y a pas de rollback, les objets système
|
|
||||||
partiellement créés subsistent.
|
|
||||||
|
|
||||||
La VM démarre mais n'a pas d'adresse
|
|
||||||
------------------------------------
|
|
||||||
|
|
||||||
Le DHCP est servi par une instance dédiée au subnet. Quelle unit selon ``dhcp.backend`` :
|
|
||||||
|
|
||||||
**Backend ``dnsmasq``**
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
systemctl status 'dnsmasq@<netns>_<bridge>'
|
|
||||||
tail -50 /var/log/dnsmasq-<netns>_<bridge>.log
|
|
||||||
cat /run/dnsmasq-<netns>_<bridge>.leases
|
|
||||||
cat /etc/dnsmasq.d/<netns>_<bridge>.conf
|
|
||||||
|
|
||||||
**Backend ``two``**
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
systemctl status 'dhcp@<netns>_<bridge>'
|
|
||||||
journalctl -u 'dhcp@<netns>_<bridge>' -n 50
|
|
||||||
|
|
||||||
# Ce que le serveur a réellement en mémoire
|
|
||||||
echo '{"verb":"get-state"}' \
|
|
||||||
| socat - UNIX-CONNECT:/run/two/dhcp/<netns>_<bridge>.sock | jq .
|
|
||||||
|
|
||||||
# Ce qu'il enverrait à une MAC donnée, sans effet de bord
|
|
||||||
echo '{"verb":"probe","mac":"00:22:33:00:00:0A"}' \
|
|
||||||
| socat - UNIX-CONNECT:/run/two/dhcp/<netns>_<bridge>.sock | jq .lease
|
|
||||||
|
|
||||||
``probe`` est le point de départ le plus rapide : il montre l'adresse, le masque, le routeur, les
|
|
||||||
DNS et les routes classless tels qu'ils partiraient. Une réponse ``"served": false`` signifie que
|
|
||||||
la MAC n'est pas réservée — l'ordre ``set-host`` n'a jamais atteint le serveur, ou la VM n'a pas
|
|
||||||
été créée par cet agent.
|
|
||||||
|
|
||||||
Le watchdog signale ces écarts de lui-même, à chaque tick, en comparant l'état servi à la base :
|
|
||||||
``dhcp reservation missing on the server``, ``stale dhcp reservation``, ``dhcp reservation
|
|
||||||
diverges``. Regarder ses notifications avant de sonder à la main.
|
|
||||||
|
|
||||||
Si le serveur ne voit passer aucune requête, le problème est en amont : tap absent, bridge non
|
|
||||||
raccordé, VM dans le mauvais netns.
|
|
||||||
|
|
||||||
La VM a une adresse mais cloud-init n'applique rien
|
|
||||||
---------------------------------------------------
|
|
||||||
|
|
||||||
Deux causes distinctes, à écarter dans cet ordre.
|
|
||||||
|
|
||||||
**1. La VM porte déjà cet ``instance-id``.** ``instance-id`` vaut le nom de la VM : sur un disque
|
|
||||||
déjà provisionné sous le même nom, cloud-init considère l'instance connue et ne rejoue pas le
|
|
||||||
user-data. Vérification dans le guest :
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
cloud-init query instance-id
|
|
||||||
ls /var/lib/cloud/instances/
|
|
||||||
|
|
||||||
**2. Le serveur de metadata est injoignable.** Depuis le guest :
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
ip route
|
|
||||||
curl -s http://169.254.169.254/latest/meta-data/
|
|
||||||
|
|
||||||
La route ``169.254.169.254/32`` doit être présente, avec l'``interface_ip`` du subnet comme
|
|
||||||
next-hop. Si elle est absente ou pointe ailleurs, la DNAT posée en ``PREROUTING`` dans le netns
|
|
||||||
n'est jamais traversée : la trame est commutée en L2 et le serveur reste injoignable. Voir
|
|
||||||
:doc:`/concepts/modes-reseau`.
|
|
||||||
|
|
||||||
Depuis l'host, l'instance correspondante :
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
systemctl status 'metadata@<vm>'
|
|
||||||
ls -l /run/two/metadata/<vm>/
|
|
||||||
|
|
||||||
Le user-data est servi vide
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
Un document fourni explicitement vide est servi vide — ce n'est pas la même chose qu'un document
|
|
||||||
absent, qui retombe sur le template. Vérifier le contenu réellement écrit :
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
cat /run/two/metadata/<vm>/user-data
|
|
||||||
|
|
||||||
Un base64 invalide, lui, aurait été rejeté en 400 à la création.
|
|
||||||
|
|
||||||
La VM ne démarre pas (UEFI)
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
``uefi: true`` exige les fichiers OVMF déclarés dans la configuration :
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
ls -l /usr/share/OVMF/OVMF_CODE.fd /usr/share/OVMF/OVMF_VARS.fd
|
|
||||||
ls -l /run/two/vms/efi/
|
|
||||||
|
|
||||||
Sur Debian et Ubuntu, le paquet est ``ovmf``.
|
|
||||||
|
|
||||||
Le ``DELETE`` renvoie 409
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
La suppression n'est autorisée que depuis ``running`` ou ``error``. Depuis ``creating`` ou
|
|
||||||
``deleting``, attendre l'état stable. Pour un VPC, tous les subnets doivent être supprimés
|
|
||||||
d'abord.
|
|
||||||
|
|
||||||
La base et le système ont divergé
|
|
||||||
---------------------------------
|
|
||||||
|
|
||||||
Le watchdog signale une ressource ``running`` absente du système. Il ne répare rien : la
|
|
||||||
correction est un ``DELETE`` explicite suivi d'une recréation. Après un redémarrage de l'agent,
|
|
||||||
les ressources restées transitoires sont basculées en ``error`` par la migration de démarrage —
|
|
||||||
elles n'ont pas forcément échoué, elles ont été interrompues.
|
|
||||||
|
|
||||||
L'agent ne redémarre pas après un arrêt brutal
|
|
||||||
----------------------------------------------
|
|
||||||
|
|
||||||
Badger rejoue son journal au démarrage : c'est normal et attendu, notamment si le budget d'arrêt
|
|
||||||
précédent a été dépassé et que la base n'a pas été fermée. Si le démarrage échoue vraiment, le
|
|
||||||
message se trouve dans ``journalctl -u agent``.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Les VM ne sont pas réattachées au redémarrage de l'agent : un processus QEMU survivant à
|
|
||||||
l'agent n'est plus piloté par lui.
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
Exploitation
|
|
||||||
============
|
|
||||||
|
|
||||||
Faire tourner un hyperviseur en service : configuration, services systemd, API de l'agent,
|
|
||||||
observabilité et diagnostic.
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 1
|
|
||||||
|
|
||||||
configuration
|
|
||||||
services
|
|
||||||
api-agent/index
|
|
||||||
observabilite
|
|
||||||
diagnostic
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
Observabilité
|
|
||||||
=============
|
|
||||||
|
|
||||||
Métriques Prometheus
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
Exposées sur le port ``prometheus.port`` (9090 par défaut), alimentées par l'état lu en base :
|
|
||||||
|
|
||||||
.. list-table::
|
|
||||||
:header-rows: 1
|
|
||||||
:widths: 40 60
|
|
||||||
|
|
||||||
* - Métrique
|
|
||||||
- Description
|
|
||||||
* - ``syonad_vpcs_total``
|
|
||||||
- nombre de VPC, par état
|
|
||||||
* - ``syonad_subnets_total``
|
|
||||||
- nombre de subnets, par état
|
|
||||||
* - ``syonad_vms_total``
|
|
||||||
- nombre de VM, par état
|
|
||||||
|
|
||||||
Les états sont ceux du :doc:`cycle de vie </concepts/cycle-de-vie>`. Une valeur non nulle et
|
|
||||||
durable sur ``error`` est l'alerte la plus utile à poser ; une valeur durable sur ``creating``
|
|
||||||
ou ``deleting`` signale une opération qui n'aboutit pas.
|
|
||||||
|
|
||||||
Watchdog
|
|
||||||
--------
|
|
||||||
|
|
||||||
Une goroutine périodique vérifie que les ressources marquées ``running`` en base existent
|
|
||||||
toujours sur le système, et **notifie les écarts sans jamais réparer**.
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
watchdog:
|
|
||||||
enabled: true
|
|
||||||
interval_seconds: 60
|
|
||||||
|
|
||||||
Il contrôle notamment l'existence des network namespaces, des liens réseau des subnets, des taps
|
|
||||||
de VM et la réponse des units systemd associées.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Un écart persistant est signalé **à chaque tick**, sans déduplication. Le volume de
|
|
||||||
notifications est donc proportionnel à la durée de l'anomalie : c'est voulu, mais cela veut
|
|
||||||
dire qu'une alerte doit agréger, pas compter.
|
|
||||||
|
|
||||||
Le watchdog étant strictement en lecture seule, une divergence entre la base et le système
|
|
||||||
subsiste jusqu'à une action explicite (``DELETE`` puis recréation).
|
|
||||||
|
|
||||||
Journaux
|
|
||||||
--------
|
|
||||||
|
|
||||||
``slog`` structuré, niveau réglé par ``logger.level`` (``debug``, ``info``, ``warn``, ``error``),
|
|
||||||
``logger.debug: true`` forçant ``debug``.
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
journalctl -u agent -f
|
|
||||||
journalctl -u 'metadata@i-web' -n 50
|
|
||||||
tail -f /var/log/dnsmasq-vp-admin_br-sn000001.log # backend dnsmasq
|
|
||||||
journalctl -fu 'dhcp@vp-admin_br-sn000001' # backend two
|
|
||||||
|
|
||||||
Inspection de la base
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
En ligne de commande, sur l'host :
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
/opt/two/bin/db -conf /etc/two/agent.yml
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
``db`` ouvre directement la base Badger. **Ne pas l'utiliser pendant que l'agent tourne** :
|
|
||||||
deux processus ne doivent pas ouvrir la même instance.
|
|
||||||
|
|
||||||
L'API d'administration donne la même lecture sans ce risque, quand elle est activée :
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
curl -s 'http://127.0.0.1:9091/db?prefix=vm/'
|
|
||||||
|
|
||||||
Elle expose l'intégralité des valeurs, **y compris les hashs de mot de passe** — cf.
|
|
||||||
:doc:`/exploitation/configuration`.
|
|
||||||
|
|
@ -1,132 +0,0 @@
|
||||||
Services systemd
|
|
||||||
================
|
|
||||||
|
|
||||||
Quatre units, installées sous ``/opt/two/bin`` par ``deploy.sh``. Les deux units DHCP
|
|
||||||
s'excluent : celle qui tourne dépend de ``dhcp.backend`` (voir :doc:`configuration`).
|
|
||||||
|
|
||||||
.. list-table::
|
|
||||||
:header-rows: 1
|
|
||||||
:widths: 28 32 40
|
|
||||||
|
|
||||||
* - Unit
|
|
||||||
- Instance ``%i``
|
|
||||||
- Rôle
|
|
||||||
* - ``agent.service``
|
|
||||||
- —
|
|
||||||
- processus principal : API, dispatcher, exécution, watchdog
|
|
||||||
* - ``dnsmasq@.service``
|
|
||||||
- ``<netns>_<bridge>``
|
|
||||||
- dnsmasq lancé dans le netns du VPC, un par subnet — backend ``dnsmasq``
|
|
||||||
* - ``dhcp@.service``
|
|
||||||
- ``<netns>_<bridge>``
|
|
||||||
- serveur DHCP intégré, un par subnet — backend ``two``
|
|
||||||
* - ``metadata@.service``
|
|
||||||
- ``<nom de la VM>``
|
|
||||||
- serveur de metadata cloud-init, un par VM
|
|
||||||
|
|
||||||
Les instances sont créées et pilotées par l'agent au fil des créations de subnets et de VM : il
|
|
||||||
n'y a pas à les démarrer à la main en fonctionnement normal.
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
systemctl status agent
|
|
||||||
systemctl status 'dnsmasq@vp-admin_br-sn000001' # backend dnsmasq
|
|
||||||
systemctl status 'dhcp@vp-admin_br-sn000001' # backend two
|
|
||||||
systemctl status 'metadata@i-web'
|
|
||||||
|
|
||||||
dnsmasq
|
|
||||||
-------
|
|
||||||
|
|
||||||
Backend historique. Le script ``run-dnsmasq-in-netns.sh`` entre dans le netns puis exécute dnsmasq
|
|
||||||
avec un fichier de configuration par subnet, généré par l'agent :
|
|
||||||
|
|
||||||
.. list-table::
|
|
||||||
:widths: 40 60
|
|
||||||
|
|
||||||
* - Configuration
|
|
||||||
- ``/etc/dnsmasq.d/<netns>_<bridge>.conf``
|
|
||||||
* - Baux
|
|
||||||
- ``/run/dnsmasq-<netns>_<bridge>.leases``
|
|
||||||
* - Journal
|
|
||||||
- ``/var/log/dnsmasq-<netns>_<bridge>.log``
|
|
||||||
* - PID
|
|
||||||
- ``/run/dnsmasq-<netns>_<bridge>.pid``
|
|
||||||
|
|
||||||
Le fichier de baux et le journal sont les deux premiers endroits à regarder quand une VM n'obtient
|
|
||||||
pas d'adresse.
|
|
||||||
|
|
||||||
Serveur DHCP intégré
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
Backend ``two``. Le script ``run-dhcp-in-netns.sh`` entre dans le netns puis exécute le binaire
|
|
||||||
``dhcp``, à qui il passe le bridge à servir et ses deux chemins de fichiers — il ne déduit rien et
|
|
||||||
ignore le netns dans lequel il tourne :
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
/opt/two/bin/dhcp -conf /etc/two/agent.yml \
|
|
||||||
-interface br-sn000001 \
|
|
||||||
-state /run/two/dhcp/vp-admin_br-sn000001.state \
|
|
||||||
-socket /run/two/dhcp/vp-admin_br-sn000001.sock
|
|
||||||
|
|
||||||
.. list-table::
|
|
||||||
:widths: 40 60
|
|
||||||
|
|
||||||
* - Socket de contrôle
|
|
||||||
- ``/run/two/dhcp/<netns>_<bridge>.sock``
|
|
||||||
* - État
|
|
||||||
- ``/run/two/dhcp/<netns>_<bridge>.state``
|
|
||||||
* - Journal
|
|
||||||
- ``journalctl -u 'dhcp@<netns>_<bridge>'``
|
|
||||||
|
|
||||||
Il n'y a **ni fichier de configuration ni fichier de baux**. L'agent pousse l'état désiré sur la
|
|
||||||
socket de contrôle : la configuration du subnet à sa création, une réservation par interface à
|
|
||||||
chaque création ou suppression de VM. Les réservations sont statiques — une MAC inconnue n'obtient
|
|
||||||
rien, et le serveur reste silencieux plutôt que de répondre par un refus.
|
|
||||||
|
|
||||||
Le fichier d'état **appartient au processus**, qui l'écrit et le relit à son démarrage. L'agent ne
|
|
||||||
l'écrit jamais ; il le supprime seulement, à la création du subnet pour écarter un résidu et à sa
|
|
||||||
suppression après avoir arrêté l'unit. Il vit dans ``/run`` parce qu'il n'a aucun sens sans le
|
|
||||||
netns, qui ne survit pas au redémarrage de l'host.
|
|
||||||
|
|
||||||
Diagnostic : voir :doc:`diagnostic`, qui montre comment interroger la socket.
|
|
||||||
|
|
||||||
QEMU n'est pas une unit
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
Les processus QEMU sont lancés par ``systemd-run --scope``, **jamais** en unit transitoire. Un
|
|
||||||
scope est exécuté par le processus appelant et hérite donc du network namespace posé par
|
|
||||||
l'agent ; une unit transitoire, forkée par PID 1, démarrerait dans le netns racine et ne verrait
|
|
||||||
pas le tap de la VM.
|
|
||||||
|
|
||||||
Conséquence pratique : les VM n'apparaissent pas dans ``systemctl list-units`` mais dans
|
|
||||||
``systemd-cgls``, et elles ne survivent pas à un ``systemctl stop agent`` suivi d'un
|
|
||||||
redémarrage — l'agent ne réattache pas les VM existantes.
|
|
||||||
|
|
||||||
Sockets par VM
|
|
||||||
--------------
|
|
||||||
|
|
||||||
.. code-block:: text
|
|
||||||
|
|
||||||
/run/two/vms/serial/<vm>.sock console série
|
|
||||||
/run/two/vms/monitor/<vm>.sock monitor QEMU
|
|
||||||
/run/two/vms/qmp/<vm>.sock QMP (utilisé par l'agent)
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
socat -,raw,echo=0 UNIX-CONNECT:/run/two/vms/serial/i-web.sock
|
|
||||||
|
|
||||||
Arrêt de l'agent
|
|
||||||
----------------
|
|
||||||
|
|
||||||
L'ordre d'arrêt est imposé : serveurs HTTP, puis drainage des workers, puis fermeture de la base.
|
|
||||||
Si le budget d'arrêt est dépassé, la base **n'est pas fermée** — fermer Badger sous un écrivain
|
|
||||||
concurrent est pire qu'un rejeu du journal au démarrage suivant. Un message à ce sujet dans le
|
|
||||||
journal au moment d'un ``stop`` n'est donc pas une anomalie.
|
|
||||||
|
|
||||||
Mise à jour
|
|
||||||
-----------
|
|
||||||
|
|
||||||
``deploy.sh`` relève les instances ``dnsmasq@``, ``dhcp@`` et ``metadata@`` actives **avant** d'arrêter les
|
|
||||||
services, et les redémarre ensuite : c'est la seule façon de savoir lesquelles relancer. Arrêter
|
|
||||||
les services à la main avant de lancer le script fait perdre cette liste.
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
two
|
|
||||||
===
|
|
||||||
|
|
||||||
**two** est un orchestrateur de virtualisation et de réseau : il pilote un parc d'hyperviseurs,
|
|
||||||
le réseau qui les relie, et les machines virtuelles qui y tournent.
|
|
||||||
|
|
||||||
Il se compose de plusieurs éléments, déployés et versionnés séparément.
|
|
||||||
|
|
||||||
.. list-table::
|
|
||||||
:header-rows: 1
|
|
||||||
:widths: 22 58 20
|
|
||||||
|
|
||||||
* - Composant
|
|
||||||
- Rôle
|
|
||||||
- État
|
|
||||||
* - **agent**
|
|
||||||
- un par hyperviseur : expose une API HTTP qui crée des VPC — isolés par network
|
|
||||||
namespace —, des subnets — VXLAN ou bridge — et des VM QEMU/KVM raccordées à ces
|
|
||||||
subnets, avec DHCP, routage et metadata cloud-init
|
|
||||||
- livré (0.1.0)
|
|
||||||
* - *à venir*
|
|
||||||
- les composants de niveau supérieur — ordonnancement sur le parc, API d'orchestration,
|
|
||||||
interface d'administration — sont à documenter au fur et à mesure de leur livraison
|
|
||||||
- à venir
|
|
||||||
|
|
||||||
À ce stade, la totalité de cette documentation porte donc sur l'**agent** et sur le réseau du
|
|
||||||
cluster qui l'entoure.
|
|
||||||
|
|
||||||
Par où commencer
|
|
||||||
----------------
|
|
||||||
|
|
||||||
:doc:`/demarrage/index`
|
|
||||||
Installer l'agent sur un hyperviseur et créer un premier VPC, un subnet et une VM. C'est le
|
|
||||||
parcours court, sur un nœud isolé.
|
|
||||||
|
|
||||||
:doc:`/deploiement/index`
|
|
||||||
L'architecture complète : réseau du cluster, routage, et ce qu'il faut mettre en place avant
|
|
||||||
qu'un parc d'hyperviseurs fonctionne ensemble.
|
|
||||||
|
|
||||||
:doc:`/exploitation/index`
|
|
||||||
Configuration, services, API de l'agent, métriques et diagnostic sur un nœud en service.
|
|
||||||
|
|
||||||
:doc:`/concepts/index`
|
|
||||||
Comment les éléments fonctionnent entre eux : modèle de données, modes réseau, cycle de vie,
|
|
||||||
metadata. À lire avant de diagnostiquer un comportement inattendu.
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:hidden:
|
|
||||||
:caption: Mise en œuvre
|
|
||||||
|
|
||||||
demarrage/index
|
|
||||||
deploiement/index
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:hidden:
|
|
||||||
:caption: Exploitation
|
|
||||||
|
|
||||||
exploitation/index
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:hidden:
|
|
||||||
:caption: Interne
|
|
||||||
|
|
||||||
concepts/index
|
|
||||||
architecture/index
|
|
||||||
versions/index
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
```{include} ../../release_notes/0.1.0.md
|
|
||||||
```
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
```{include} ../../release_notes/0.2.0.md
|
|
||||||
```
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
Versions
|
|
||||||
========
|
|
||||||
|
|
||||||
Chaque version porte un nom de code dérivé du rang de sa publication : anges et démons alternés.
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 1
|
|
||||||
|
|
||||||
0.2.0
|
|
||||||
0.1.0
|
|
||||||
|
|
||||||
.. include:: ../../release_notes/codenames.md
|
|
||||||
:parser: myst_parser.sphinx_
|
|
||||||
517
index.html
|
|
@ -1,513 +1,10 @@
|
||||||
|
<!doctype html>
|
||||||
<!DOCTYPE html>
|
<html lang="fr">
|
||||||
|
|
||||||
|
|
||||||
<html lang="fr" data-content_root="./" >
|
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="viewport" content="width=device-width, initial-scale=1" />
|
<title>two — documentation</title>
|
||||||
|
<meta http-equiv="refresh" content="0; url=./main/">
|
||||||
<title>two — Documentation two 0.1.0</title>
|
<link rel="canonical" href="https://syonad.g3e.fr/two/main/">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script data-cfasync="false">
|
|
||||||
document.documentElement.dataset.mode = localStorage.getItem("mode") || "";
|
|
||||||
document.documentElement.dataset.theme = localStorage.getItem("theme") || "";
|
|
||||||
</script>
|
|
||||||
<!--
|
|
||||||
this give us a css class that will be invisible only if js is disabled
|
|
||||||
-->
|
|
||||||
<noscript>
|
|
||||||
<style>
|
|
||||||
.pst-js-only { display: none !important; }
|
|
||||||
|
|
||||||
</style>
|
|
||||||
</noscript>
|
|
||||||
|
|
||||||
<!-- Loaded before other Sphinx assets -->
|
|
||||||
<link href="_static/styles/theme.css?digest=8878045cc6db502f8baf" rel="stylesheet" />
|
|
||||||
<link href="_static/styles/pydata-sphinx-theme.css?digest=8878045cc6db502f8baf" rel="stylesheet" />
|
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=8f2a1f02" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/sphinx-book-theme.css?v=3c74b3bc" />
|
|
||||||
|
|
||||||
<!-- So that users can add custom icons -->
|
|
||||||
<script src="_static/scripts/fontawesome.js?digest=8878045cc6db502f8baf"></script>
|
|
||||||
<!-- Pre-loaded scripts that we'll load fully later -->
|
|
||||||
<link rel="preload" as="script" href="_static/scripts/bootstrap.js?digest=8878045cc6db502f8baf" />
|
|
||||||
<link rel="preload" as="script" href="_static/scripts/pydata-sphinx-theme.js?digest=8878045cc6db502f8baf" />
|
|
||||||
|
|
||||||
<script src="_static/documentation_options.js?v=4d0cb239"></script>
|
|
||||||
<script src="_static/doctools.js?v=fd6eb6e6"></script>
|
|
||||||
<script src="_static/sphinx_highlight.js?v=6ffebe34"></script>
|
|
||||||
<script src="_static/scripts/sphinx-book-theme.js?v=fab101a9"></script>
|
|
||||||
<script src="_static/translations.js?v=e6b791cb"></script>
|
|
||||||
<script>DOCUMENTATION_OPTIONS.pagename = 'index';</script>
|
|
||||||
<link rel="index" title="Index" href="genindex.html" />
|
|
||||||
<link rel="search" title="Recherche" href="search.html" />
|
|
||||||
<link rel="next" title="Démarrage" href="demarrage/index.html" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
||||||
<meta name="docsearch:language" content="fr"/>
|
|
||||||
<meta name="docsearch:version" content="0.1" />
|
|
||||||
</head>
|
</head>
|
||||||
|
<body><p><a href="./main/">Documentation de two</a></p></body>
|
||||||
|
|
||||||
<body data-bs-spy="scroll" data-bs-target=".bd-toc-nav" data-offset="180" data-bs-root-margin="0px 0px -60%" data-default-mode="">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div id="pst-skip-link" class="skip-link d-print-none"><a href="#main-content">Passer au contenu principal</a></div>
|
|
||||||
|
|
||||||
<div id="pst-scroll-pixel-helper"></div>
|
|
||||||
|
|
||||||
<button type="button" class="btn rounded-pill" id="pst-back-to-top">
|
|
||||||
<i class="fa-solid fa-arrow-up"></i>Haut de page</button>
|
|
||||||
|
|
||||||
|
|
||||||
<dialog id="pst-search-dialog">
|
|
||||||
|
|
||||||
<form class="bd-search d-flex align-items-center"
|
|
||||||
action="search.html"
|
|
||||||
method="get">
|
|
||||||
<i class="fa-solid fa-magnifying-glass"></i>
|
|
||||||
<input type="search"
|
|
||||||
class="form-control"
|
|
||||||
name="q"
|
|
||||||
placeholder="Search..."
|
|
||||||
aria-label="Search..."
|
|
||||||
autocomplete="off"
|
|
||||||
autocorrect="off"
|
|
||||||
autocapitalize="off"
|
|
||||||
spellcheck="false"/>
|
|
||||||
<span class="search-button__kbd-shortcut"><kbd class="kbd-shortcut__modifier">Ctrl</kbd>+<kbd>K</kbd></span>
|
|
||||||
</form>
|
|
||||||
</dialog>
|
|
||||||
|
|
||||||
<div class="pst-async-banner-revealer d-none">
|
|
||||||
<aside id="bd-header-version-warning" class="d-none d-print-none" aria-label="Alerte de version"></aside>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<header class="bd-header navbar navbar-expand-lg bd-navbar d-print-none">
|
|
||||||
</header>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="bd-container">
|
|
||||||
<div class="bd-container__inner bd-page-width">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<dialog id="pst-primary-sidebar-modal"></dialog>
|
|
||||||
<div id="pst-primary-sidebar" class="bd-sidebar-primary bd-sidebar">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="sidebar-header-items sidebar-primary__section">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="sidebar-primary-items__start sidebar-primary__section">
|
|
||||||
<div class="sidebar-primary-item">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a class="navbar-brand logo" href="#">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p class="title logo__title">Documentation two 0.1.0</p>
|
|
||||||
|
|
||||||
</a></div>
|
|
||||||
<div class="sidebar-primary-item">
|
|
||||||
|
|
||||||
<button class="btn search-button-field search-button__button pst-js-only" title="Recherche" aria-label="Recherche" data-bs-placement="bottom" data-bs-toggle="tooltip">
|
|
||||||
<i class="fa-solid fa-magnifying-glass"></i>
|
|
||||||
<span class="search-button__default-text">Recherche</span>
|
|
||||||
<span class="search-button__kbd-shortcut"><kbd class="kbd-shortcut__modifier">Ctrl</kbd>+<kbd class="kbd-shortcut__modifier">K</kbd></span>
|
|
||||||
</button></div>
|
|
||||||
<div class="sidebar-primary-item"><nav class="bd-links bd-docs-nav" aria-label="Main">
|
|
||||||
<div class="bd-toc-item navbar-nav active">
|
|
||||||
|
|
||||||
<ul class="nav bd-sidenav bd-sidenav__home-link">
|
|
||||||
<li class="toctree-l1 current active">
|
|
||||||
<a class="reference internal" href="#">
|
|
||||||
two
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<p aria-level="2" class="caption" role="heading"><span class="caption-text">Mise en œuvre</span></p>
|
|
||||||
<ul class="nav bd-sidenav">
|
|
||||||
<li class="toctree-l1 has-children"><a class="reference internal" href="demarrage/index.html">Démarrage</a><details><summary><span class="toctree-toggle" role="presentation"><i class="fa-solid fa-chevron-down"></i></span></summary><ul>
|
|
||||||
<li class="toctree-l2"><a class="reference internal" href="demarrage/installation.html">Installation d’un hyperviseur</a></li>
|
|
||||||
<li class="toctree-l2"><a class="reference internal" href="demarrage/premier-vpc.html">Premier VPC, premier subnet, première VM</a></li>
|
|
||||||
</ul>
|
|
||||||
</details></li>
|
|
||||||
<li class="toctree-l1 has-children"><a class="reference internal" href="deploiement/index.html">Déploiement d’un cluster</a><details><summary><span class="toctree-toggle" role="presentation"><i class="fa-solid fa-chevron-down"></i></span></summary><ul>
|
|
||||||
<li class="toctree-l2"><a class="reference internal" href="deploiement/architecture-cluster.html">Architecture du cluster</a></li>
|
|
||||||
<li class="toctree-l2"><a class="reference internal" href="deploiement/image-qcow2.html">Construction de l’image qcow2</a></li>
|
|
||||||
<li class="toctree-l2"><a class="reference internal" href="deploiement/routeurs.html">Routeurs</a></li>
|
|
||||||
<li class="toctree-l2"><a class="reference internal" href="deploiement/premier-hyperviseur.html">Premier hyperviseur</a></li>
|
|
||||||
<li class="toctree-l2"><a class="reference internal" href="deploiement/route-reflector.html">VM route reflector</a></li>
|
|
||||||
</ul>
|
|
||||||
</details></li>
|
|
||||||
</ul>
|
|
||||||
<p aria-level="2" class="caption" role="heading"><span class="caption-text">Exploitation</span></p>
|
|
||||||
<ul class="nav bd-sidenav">
|
|
||||||
<li class="toctree-l1 has-children"><a class="reference internal" href="exploitation/index.html">Exploitation</a><details><summary><span class="toctree-toggle" role="presentation"><i class="fa-solid fa-chevron-down"></i></span></summary><ul>
|
|
||||||
<li class="toctree-l2"><a class="reference internal" href="exploitation/configuration.html">Configuration</a></li>
|
|
||||||
<li class="toctree-l2"><a class="reference internal" href="exploitation/services.html">Services systemd</a></li>
|
|
||||||
<li class="toctree-l2 has-children"><a class="reference internal" href="exploitation/api-agent/index.html">API de l’agent</a><details><summary><span class="toctree-toggle" role="presentation"><i class="fa-solid fa-chevron-down"></i></span></summary><ul>
|
|
||||||
<li class="toctree-l3"><a class="reference internal" href="exploitation/api-agent/asynchronisme.html">Modèle asynchrone et codes de retour</a></li>
|
|
||||||
<li class="toctree-l3"><a class="reference internal" href="exploitation/api-agent/reference.html">Référence</a></li>
|
|
||||||
</ul>
|
|
||||||
</details></li>
|
|
||||||
<li class="toctree-l2"><a class="reference internal" href="exploitation/observabilite.html">Observabilité</a></li>
|
|
||||||
<li class="toctree-l2"><a class="reference internal" href="exploitation/diagnostic.html">Diagnostic</a></li>
|
|
||||||
</ul>
|
|
||||||
</details></li>
|
|
||||||
</ul>
|
|
||||||
<p aria-level="2" class="caption" role="heading"><span class="caption-text">Interne</span></p>
|
|
||||||
<ul class="nav bd-sidenav">
|
|
||||||
<li class="toctree-l1 has-children"><a class="reference internal" href="concepts/index.html">Concepts</a><details><summary><span class="toctree-toggle" role="presentation"><i class="fa-solid fa-chevron-down"></i></span></summary><ul>
|
|
||||||
<li class="toctree-l2"><a class="reference internal" href="concepts/vpc-subnet-vm.html">VPC, subnet et VM</a></li>
|
|
||||||
<li class="toctree-l2"><a class="reference internal" href="concepts/modes-reseau.html">Modes réseau</a></li>
|
|
||||||
<li class="toctree-l2"><a class="reference internal" href="concepts/cycle-de-vie.html">Cycle de vie des ressources</a></li>
|
|
||||||
<li class="toctree-l2"><a class="reference internal" href="concepts/metadata-cloud-init.html">Metadata et cloud-init</a></li>
|
|
||||||
</ul>
|
|
||||||
</details></li>
|
|
||||||
<li class="toctree-l1 has-children"><a class="reference internal" href="architecture/index.html">Architecture</a><details><summary><span class="toctree-toggle" role="presentation"><i class="fa-solid fa-chevron-down"></i></span></summary><ul>
|
|
||||||
<li class="toctree-l2"><a class="reference internal" href="architecture/vue-densemble.html">Vue d’ensemble</a></li>
|
|
||||||
<li class="toctree-l2"><a class="reference internal" href="architecture/stockage.html">Schéma des clés</a></li>
|
|
||||||
<li class="toctree-l2"><a class="reference internal" href="architecture/contraintes.html">Invariants et pièges</a></li>
|
|
||||||
</ul>
|
|
||||||
</details></li>
|
|
||||||
<li class="toctree-l1 has-children"><a class="reference internal" href="versions/index.html">Versions</a><details><summary><span class="toctree-toggle" role="presentation"><i class="fa-solid fa-chevron-down"></i></span></summary><ul>
|
|
||||||
<li class="toctree-l2"><a class="reference internal" href="versions/0.2.0.html">Bael</a></li>
|
|
||||||
<li class="toctree-l2"><a class="reference internal" href="versions/0.1.0.html">Michael</a></li>
|
|
||||||
</ul>
|
|
||||||
</details></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</nav></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="sidebar-primary-items__end sidebar-primary__section">
|
|
||||||
<div class="sidebar-primary-item">
|
|
||||||
<div id="ethical-ad-placement"
|
|
||||||
class="flat"
|
|
||||||
data-ea-publisher="readthedocs"
|
|
||||||
data-ea-type="readthedocs-sidebar"
|
|
||||||
data-ea-manual="true">
|
|
||||||
</div></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<main id="main-content" class="bd-main" role="main">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="sbt-scroll-pixel-helper"></div>
|
|
||||||
|
|
||||||
<div class="bd-content">
|
|
||||||
<div class="bd-article-container">
|
|
||||||
|
|
||||||
<div class="bd-header-article d-print-none">
|
|
||||||
<div class="header-article-items header-article__inner">
|
|
||||||
|
|
||||||
<div class="header-article-items__start">
|
|
||||||
|
|
||||||
<div class="header-article-item"><button class="sidebar-toggle primary-toggle btn btn-sm" title="Toggle primary sidebar" data-bs-placement="bottom" data-bs-toggle="tooltip">
|
|
||||||
<span class="fa-solid fa-bars"></span>
|
|
||||||
</button></div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="header-article-items__end">
|
|
||||||
|
|
||||||
<div class="header-article-item">
|
|
||||||
|
|
||||||
<div class="article-header-buttons">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="dropdown dropdown-download-buttons">
|
|
||||||
<button class="btn dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false" aria-label="Téléchargez cette page">
|
|
||||||
<i class="fas fa-download"></i>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<li><a href="_sources/index.rst" target="_blank"
|
|
||||||
class="btn btn-sm btn-download-source-button dropdown-item"
|
|
||||||
title="Télécharger le fichier source"
|
|
||||||
data-bs-placement="left" data-bs-toggle="tooltip"
|
|
||||||
>
|
|
||||||
|
|
||||||
|
|
||||||
<span class="btn__icon-container">
|
|
||||||
<i class="fas fa-file"></i>
|
|
||||||
</span>
|
|
||||||
<span class="btn__text-container">.rst</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<button onclick="window.print()"
|
|
||||||
class="btn btn-sm btn-download-pdf-button dropdown-item"
|
|
||||||
title="Imprimer au format PDF"
|
|
||||||
data-bs-placement="left" data-bs-toggle="tooltip"
|
|
||||||
>
|
|
||||||
|
|
||||||
|
|
||||||
<span class="btn__icon-container">
|
|
||||||
<i class="fas fa-file-pdf"></i>
|
|
||||||
</span>
|
|
||||||
<span class="btn__text-container">.pdf</span>
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<button onclick="toggleFullScreen()"
|
|
||||||
class="btn btn-sm btn-fullscreen-button pst-navbar-icon"
|
|
||||||
title="Mode plein écran"
|
|
||||||
data-bs-placement="bottom" data-bs-toggle="tooltip"
|
|
||||||
>
|
|
||||||
|
|
||||||
|
|
||||||
<span class="btn__icon-container">
|
|
||||||
<i class="fas fa-expand"></i>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
</button>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<button class="btn btn-sm nav-link pst-navbar-icon theme-switch-button pst-js-only" aria-label="Thème" data-bs-title="Thème" data-bs-placement="bottom" data-bs-toggle="tooltip">
|
|
||||||
<i class="theme-switch fa-solid fa-sun fa-lg" data-mode="light" title="Clair"></i>
|
|
||||||
<i class="theme-switch fa-solid fa-moon fa-lg" data-mode="dark" title="Sombre"></i>
|
|
||||||
<i class="theme-switch fa-solid fa-circle-half-stroke fa-lg" data-mode="auto" title="Paramètres système"></i>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
|
|
||||||
<button class="btn btn-sm pst-navbar-icon search-button search-button__button pst-js-only" title="Recherche" aria-label="Recherche" data-bs-placement="bottom" data-bs-toggle="tooltip">
|
|
||||||
<i class="fa-solid fa-magnifying-glass fa-lg"></i>
|
|
||||||
</button>
|
|
||||||
<button class="sidebar-toggle secondary-toggle btn btn-sm pst-navbar-icon" title="Toggle secondary sidebar" data-bs-placement="bottom" data-bs-toggle="tooltip">
|
|
||||||
<span class="fa-solid fa-list"></span>
|
|
||||||
</button>
|
|
||||||
</div></div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div id="jb-print-docs-body" class="onlyprint">
|
|
||||||
<h1>two</h1>
|
|
||||||
<!-- Table of contents -->
|
|
||||||
<div id="print-main-content">
|
|
||||||
<div id="jb-print-toc">
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h2> Contenu </h2>
|
|
||||||
</div>
|
|
||||||
<nav aria-label="Page">
|
|
||||||
<ul class="visible nav section-nav flex-column">
|
|
||||||
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#par-ou-commencer">Par où commencer</a><ul class="nav section-nav flex-column">
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div id="searchbox"></div>
|
|
||||||
<article class="bd-article">
|
|
||||||
|
|
||||||
<section id="two">
|
|
||||||
<h1>two<a class="headerlink" href="#two" title="Lien vers cette rubrique">#</a></h1>
|
|
||||||
<p><strong>two</strong> est un orchestrateur de virtualisation et de réseau : il pilote un parc d’hyperviseurs,
|
|
||||||
le réseau qui les relie, et les machines virtuelles qui y tournent.</p>
|
|
||||||
<p>Il se compose de plusieurs éléments, déployés et versionnés séparément.</p>
|
|
||||||
<div class="pst-scrollable-table-container"><table class="table">
|
|
||||||
<colgroup>
|
|
||||||
<col style="width: 22.0%" />
|
|
||||||
<col style="width: 58.0%" />
|
|
||||||
<col style="width: 20.0%" />
|
|
||||||
</colgroup>
|
|
||||||
<thead>
|
|
||||||
<tr class="row-odd"><th class="head"><p>Composant</p></th>
|
|
||||||
<th class="head"><p>Rôle</p></th>
|
|
||||||
<th class="head"><p>État</p></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr class="row-even"><td><p><strong>agent</strong></p></td>
|
|
||||||
<td><p>un par hyperviseur : expose une API HTTP qui crée des VPC — isolés par network
|
|
||||||
namespace —, des subnets — VXLAN ou bridge — et des VM QEMU/KVM raccordées à ces
|
|
||||||
subnets, avec DHCP, routage et metadata cloud-init</p></td>
|
|
||||||
<td><p>livré (0.1.0)</p></td>
|
|
||||||
</tr>
|
|
||||||
<tr class="row-odd"><td><p><em>à venir</em></p></td>
|
|
||||||
<td><p>les composants de niveau supérieur — ordonnancement sur le parc, API d’orchestration,
|
|
||||||
interface d’administration — sont à documenter au fur et à mesure de leur livraison</p></td>
|
|
||||||
<td><p>à venir</p></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<p>À ce stade, la totalité de cette documentation porte donc sur l”<strong>agent</strong> et sur le réseau du
|
|
||||||
cluster qui l’entoure.</p>
|
|
||||||
<section id="par-ou-commencer">
|
|
||||||
<h2>Par où commencer<a class="headerlink" href="#par-ou-commencer" title="Lien vers cette rubrique">#</a></h2>
|
|
||||||
<dl class="simple">
|
|
||||||
<dt><a class="reference internal" href="demarrage/index.html"><span class="doc">Démarrage</span></a></dt><dd><p>Installer l’agent sur un hyperviseur et créer un premier VPC, un subnet et une VM. C’est le
|
|
||||||
parcours court, sur un nœud isolé.</p>
|
|
||||||
</dd>
|
|
||||||
<dt><a class="reference internal" href="deploiement/index.html"><span class="doc">Déploiement d’un cluster</span></a></dt><dd><p>L’architecture complète : réseau du cluster, routage, et ce qu’il faut mettre en place avant
|
|
||||||
qu’un parc d’hyperviseurs fonctionne ensemble.</p>
|
|
||||||
</dd>
|
|
||||||
<dt><a class="reference internal" href="exploitation/index.html"><span class="doc">Exploitation</span></a></dt><dd><p>Configuration, services, API de l’agent, métriques et diagnostic sur un nœud en service.</p>
|
|
||||||
</dd>
|
|
||||||
<dt><a class="reference internal" href="concepts/index.html"><span class="doc">Concepts</span></a></dt><dd><p>Comment les éléments fonctionnent entre eux : modèle de données, modes réseau, cycle de vie,
|
|
||||||
metadata. À lire avant de diagnostiquer un comportement inattendu.</p>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
<div class="toctree-wrapper compound">
|
|
||||||
</div>
|
|
||||||
<div class="toctree-wrapper compound">
|
|
||||||
</div>
|
|
||||||
<div class="toctree-wrapper compound">
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
|
|
||||||
</article>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<footer class="prev-next-footer d-print-none">
|
|
||||||
|
|
||||||
<div class="prev-next-area">
|
|
||||||
<a class="right-next"
|
|
||||||
href="demarrage/index.html"
|
|
||||||
title="page suivante">
|
|
||||||
<div class="prev-next-info">
|
|
||||||
<p class="prev-next-subtitle">suivant</p>
|
|
||||||
<p class="prev-next-title">Démarrage</p>
|
|
||||||
</div>
|
|
||||||
<i class="fa-solid fa-angle-right"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<dialog id="pst-secondary-sidebar-modal"></dialog>
|
|
||||||
<div id="pst-secondary-sidebar" class="bd-sidebar-secondary bd-toc"><div class="sidebar-secondary-items sidebar-secondary__inner">
|
|
||||||
|
|
||||||
|
|
||||||
<div class="sidebar-secondary-item"><div
|
|
||||||
id="pst-page-navigation-heading-2"
|
|
||||||
class="page-toc tocsection onthispage">
|
|
||||||
<i class="fa-solid fa-list"></i> Contenu
|
|
||||||
</div>
|
|
||||||
<nav id="pst-page-toc-nav" class="page-toc" aria-labelledby="pst-page-navigation-heading-2">
|
|
||||||
<ul class="visible nav section-nav flex-column">
|
|
||||||
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#par-ou-commencer">Par où commencer</a><ul class="nav section-nav flex-column">
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav></div>
|
|
||||||
|
|
||||||
</div></div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<footer class="bd-footer-content">
|
|
||||||
|
|
||||||
<div class="bd-footer-content__inner container">
|
|
||||||
|
|
||||||
<div class="footer-item">
|
|
||||||
|
|
||||||
<p class="component-author">
|
|
||||||
Par Nicolas Boufidjeline
|
|
||||||
</p>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="footer-item">
|
|
||||||
|
|
||||||
|
|
||||||
<p class="copyright">
|
|
||||||
|
|
||||||
© Copyright 2026, Nicolas Boufidjeline.
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
</p>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="footer-item">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="footer-item">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Scripts loaded after <body> so the DOM is not blocked -->
|
|
||||||
<script defer src="_static/scripts/bootstrap.js?digest=8878045cc6db502f8baf"></script>
|
|
||||||
<script defer src="_static/scripts/pydata-sphinx-theme.js?digest=8878045cc6db502f8baf"></script>
|
|
||||||
|
|
||||||
<footer class="bd-footer">
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
||||||
|
Before Width: | Height: | Size: 3 KiB After Width: | Height: | Size: 3 KiB |
|
Before Width: | Height: | Size: 3 KiB After Width: | Height: | Size: 3 KiB |
|
|
@ -5,7 +5,7 @@ const DOCUMENTATION_OPTIONS = {
|
||||||
BUILDER: 'html',
|
BUILDER: 'html',
|
||||||
FILE_SUFFIX: '.html',
|
FILE_SUFFIX: '.html',
|
||||||
LINK_SUFFIX: '.html',
|
LINK_SUFFIX: '.html',
|
||||||
HAS_SOURCE: true,
|
HAS_SOURCE: false,
|
||||||
SOURCELINK_SUFFIX: '',
|
SOURCELINK_SUFFIX: '',
|
||||||
NAVIGATION_WITH_KEYS: false,
|
NAVIGATION_WITH_KEYS: false,
|
||||||
SHOW_SEARCH_SUMMARY: true,
|
SHOW_SEARCH_SUMMARY: true,
|
||||||
|
Before Width: | Height: | Size: 286 B After Width: | Height: | Size: 286 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 681 B After Width: | Height: | Size: 681 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |