documente la capacité, la restauration et le redémarrage (étape 13)
Capacité mesurée sur 20 VM (docs/capacite.md), restauration complète du portail réussie, retour automatique de tous les services après redémarrage. Retire l'unité systemd orpheline de ProxySQL, rend fidèle au navigateur la mesure du temps d'ouverture d'un lab (2,8 s), et regroupe la dette révélée. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
42884597a9
commit
0a5df89201
8 changed files with 273 additions and 8 deletions
|
|
@ -2,3 +2,7 @@
|
||||||
kolla_serie: "2025.2"
|
kolla_serie: "2025.2"
|
||||||
kolla_venv: /opt/kolla-venv
|
kolla_venv: /opt/kolla-venv
|
||||||
kolla_config_dir: /etc/kolla
|
kolla_config_dir: /etc/kolla
|
||||||
|
# Services désactivés dans globals.yml après un premier déploiement : leur
|
||||||
|
# unité systemd est retirée (voir tasks/main.yml).
|
||||||
|
kolla_services_retires:
|
||||||
|
- proxysql
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,41 @@
|
||||||
sur le serveur (voir docs/runbooks/openstack.md).
|
sur le serveur (voir docs/runbooks/openstack.md).
|
||||||
when: kolla_surcharge_nova.changed
|
when: kolla_surcharge_nova.changed
|
||||||
|
|
||||||
|
# Kolla ne retire pas l'unité systemd d'un service qu'on désactive ensuite
|
||||||
|
# dans globals.yml : proxysql (désactivé à l'étape 5) échouait à chaque
|
||||||
|
# démarrage et polluait `systemctl --failed`. Unité déplacée, pas supprimée.
|
||||||
|
- name: vérifier la présence d'unités Kolla de services désactivés
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "/etc/systemd/system/kolla-{{ item }}-container.service"
|
||||||
|
loop: "{{ kolla_services_retires }}"
|
||||||
|
register: kolla_unites_retirees
|
||||||
|
|
||||||
|
- name: arrêter et désactiver ces unités
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: "kolla-{{ item.item }}-container.service"
|
||||||
|
state: stopped
|
||||||
|
enabled: false
|
||||||
|
loop: "{{ kolla_unites_retirees.results }}"
|
||||||
|
when: item.stat.exists
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: créer le dossier des unités retirées
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /root/unites-retirees
|
||||||
|
state: directory
|
||||||
|
mode: "0700"
|
||||||
|
|
||||||
|
- name: déplacer ces unités hors de systemd
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "mv /etc/systemd/system/kolla-{{ item.item }}-container.service /root/unites-retirees/"
|
||||||
|
loop: "{{ kolla_unites_retirees.results }}"
|
||||||
|
when: item.stat.exists
|
||||||
|
register: kolla_unites_deplacees
|
||||||
|
|
||||||
|
- name: recharger systemd et oublier leurs échecs
|
||||||
|
ansible.builtin.shell: systemctl daemon-reload && systemctl reset-failed
|
||||||
|
when: kolla_unites_deplacees.changed
|
||||||
|
|
||||||
- name: installer les dépendances Ansible de Kolla (collections Galaxy)
|
- name: installer les dépendances Ansible de Kolla (collections Galaxy)
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
cmd: "{{ kolla_venv }}/bin/kolla-ansible install-deps"
|
cmd: "{{ kolla_venv }}/bin/kolla-ansible install-deps"
|
||||||
|
|
|
||||||
96
docs/capacite.md
Normal file
96
docs/capacite.md
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
# Capacité du serveur Syllabus
|
||||||
|
|
||||||
|
Mesuré le 2026-10-09 (étape 13) sur le serveur de production : Scaleway
|
||||||
|
Dedibox Start-2-L, Xeon D-1531 (6 cœurs, 12 fils), 32 Go de RAM, OpenStack
|
||||||
|
Kolla 2025.2 tout-en-un. Script : `openstack/tests/capacite.py` ; données
|
||||||
|
brutes sur le serveur : `/root/capacite-20261009/` (`hote.csv` toutes les
|
||||||
|
2 s, `demarrage.csv`, `resume.json`).
|
||||||
|
|
||||||
|
## Capacité retenue : 22 VM `eleve.petit` en même temps
|
||||||
|
|
||||||
|
Sessions d'élèves et stock chaud compris. Choisie par Adam d'après les
|
||||||
|
mesures ci-dessous. Elle est appliquée à deux endroits, à garder alignés :
|
||||||
|
|
||||||
|
| Où | Réglage |
|
||||||
|
|---|---|
|
||||||
|
| Nova (refus au-delà, propre : « No valid host » → session en échec visible du prof) | `reserved_host_memory_mb = 15000` dans `kolla/config/nova/nova-compute.conf` → Placement : (31993 − 15000) Mo / 768 Mo = **22 VM** |
|
||||||
|
| Portail (plafond du stock chaud) | `LABS_CAPACITE_VM`, défaut 22 (`portail/config/labs.php`) |
|
||||||
|
| Quota du projet (filet de sécurité, au-dessus) | 40 instances, 40 vCPU, 30 Go (rôle `openstack-projet`) |
|
||||||
|
|
||||||
|
Ce que ça permet : une classe de 22 sur un lab à 1 VM ; 7 élèves sur le
|
||||||
|
lab 08 (3 VM chacun). Moins le stock en attente (1 stack par lab hors séance).
|
||||||
|
|
||||||
|
## Mesures
|
||||||
|
|
||||||
|
### Mémoire
|
||||||
|
|
||||||
|
| Situation | Mémoire disponible sur l'hôte |
|
||||||
|
|---|---|
|
||||||
|
| Sans VM (Kolla, portail, supervision) | ≈ 16,5 Go (Kolla seul occupe ≈ 15,5 Go) |
|
||||||
|
| 3 VM du stock au repos | 15,1 Go |
|
||||||
|
| + 20 VM démarrées, au repos | 10,1 Go → **≈ 250 Mo par VM** (RSS moyen 269 Mo) |
|
||||||
|
| + 350 Mo occupés dans chacune des 20 VM | 3,4 Go → **+332 Mo par VM**, soit **≈ 600 Mo par VM active** ; swap utilisé : 264 Mo |
|
||||||
|
|
||||||
|
Coût d'une VM `eleve.petit` (768 Mo) sur l'hôte :
|
||||||
|
|
||||||
|
| Usage | Coût réel | VM possibles (16,3 Go − 2 Go de marge) |
|
||||||
|
|---|---|---|
|
||||||
|
| Au repos | ≈ 250 Mo | ≈ 57 |
|
||||||
|
| Élève actif (mesuré ci-dessus) | ≈ 600 Mo | **≈ 23** |
|
||||||
|
| Pire cas (VM pleine + QEMU) | ≈ 870 Mo | ≈ 16 |
|
||||||
|
|
||||||
|
À 22 VM, si toutes remplissaient entièrement leur mémoire en même temps
|
||||||
|
(cas improbable dans un lab réseau ou système), l'hôte dépasserait sa
|
||||||
|
mémoire d'environ 2 Go et puiserait dans le swap (4 Go) : ralentissement
|
||||||
|
plutôt que panne. Le test n'a pas cherché à le provoquer.
|
||||||
|
|
||||||
|
### Démarrage de 20 VM lancées en même temps
|
||||||
|
|
||||||
|
20 sur 20, aucune erreur.
|
||||||
|
|
||||||
|
| | min | médiane | p95 | max |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| Passage à ACTIVE | 23,7 s | 48,8 s | 67,6 s | 71,1 s |
|
||||||
|
| Système démarré (agent invité qui répond) | 27,8 s | 53,2 s | 74,7 s | 78,2 s |
|
||||||
|
|
||||||
|
Pendant la rafale : CPU de l'hôte jusqu'à 86 %. D'où l'intérêt du stock
|
||||||
|
chaud et du pré-remplissage 10 minutes avant une séance : une classe
|
||||||
|
entière qui démarre à froid attendrait plus d'une minute.
|
||||||
|
|
||||||
|
### CPU
|
||||||
|
|
||||||
|
20 VM avec un cœur à 100 % chacune pendant 60 s (20 vCPU pour 12 fils) :
|
||||||
|
CPU de l'hôte à 89 % en moyenne, charge 14,5 au maximum. Une commande de
|
||||||
|
correction par l'agent invité répond tout de même en **0,6 s**. Le CPU n'est
|
||||||
|
pas la limite : la mémoire l'est. (Ratio par défaut de Nova : 4 vCPU par fil,
|
||||||
|
soit 48 vCPU.)
|
||||||
|
|
||||||
|
### Ouverture d'un lab pris dans le stock (critère < 5 s)
|
||||||
|
|
||||||
|
Mesurée de bout en bout par `https://syllabuslab.fr` comme un navigateur
|
||||||
|
(`portail/tests/Reels/parcours-lab.py`) : **2,80 s, 2,85 s, 2,85 s**.
|
||||||
|
|
||||||
|
Décomposition (`portail/tests/Reels/decomposer-ouverture.py`) :
|
||||||
|
|
||||||
|
| Morceau | Durée |
|
||||||
|
|---|---|
|
||||||
|
| La page élève voit l'état « actif » (interrogation chaque seconde) | 0 à 1 s |
|
||||||
|
| Rechargement de la page | 0,03 s |
|
||||||
|
| Jetons des consoles (appel Nova `remote-consoles`, ≈ 1,1 s chacun, demandés en parallèle) | 1,1 s |
|
||||||
|
| WebSocket jusqu'à l'invite `login:` | 1,0 s |
|
||||||
|
|
||||||
|
Le poste le plus coûteux est l'appel Nova pour obtenir l'adresse de la
|
||||||
|
console : piste d'optimisation si besoin (préparer ces jetons à
|
||||||
|
l'attribution plutôt qu'à l'affichage).
|
||||||
|
|
||||||
|
## Quand refaire ces mesures
|
||||||
|
|
||||||
|
Après une montée de version de Kolla, un changement d'image ou de gabarit,
|
||||||
|
l'ajout d'un service lourd, ou un changement de serveur. Commande :
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo python3 /root/capacite.py --vm 20 --sortie /root/capacite-AAAAMMJJ
|
||||||
|
```
|
||||||
|
|
||||||
|
Le script s'arrête de lui-même si la mémoire disponible passe sous 3 Go. Il
|
||||||
|
crée un réseau `capacite-test` et des VM `capacite-N` à supprimer ensuite.
|
||||||
|
|
@ -6,9 +6,10 @@ retirer la ligne (et le noter dans le journal) quand c'est fait.
|
||||||
| Sujet | Pourquoi c'est une dette | Quand | Origine |
|
| Sujet | Pourquoi c'est une dette | Quand | Origine |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| **Image Uptime Kuma non figée** (`louislam/uptime-kuma:1`) | Une version différente peut arriver à n'importe quel redéploiement, sans test. Figer un tag précis (voire le digest) dans une variable du rôle `supervision`, et documenter la mise à jour par Ansible (changer la variable, relancer le rôle, vérifier) — jamais par le bouton de l'interface. Kuma lui-même qualifie ce tag d'« historique » (il injecte `docker-latest-warning.js` dans les images `latest`/`1`, constaté le 2026-10-09) : prévoir la migration vers un tag versionné, en testant la page de statut et les sondes. | Avant le pilote | Demande d'Adam, 2026-10-09 |
|
| **Image Uptime Kuma non figée** (`louislam/uptime-kuma:1`) | Une version différente peut arriver à n'importe quel redéploiement, sans test. Figer un tag précis (voire le digest) dans une variable du rôle `supervision`, et documenter la mise à jour par Ansible (changer la variable, relancer le rôle, vérifier) — jamais par le bouton de l'interface. Kuma lui-même qualifie ce tag d'« historique » (il injecte `docker-latest-warning.js` dans les images `latest`/`1`, constaté le 2026-10-09) : prévoir la migration vers un tag versionné, en testant la page de statut et les sondes. | Avant le pilote | Demande d'Adam, 2026-10-09 |
|
||||||
|
| **Base de données d'OpenStack non sauvegardée** (MariaDB de Kolla : Keystone, Nova, Neutron, Heat, Placement) | Seule la base du portail l'est. Une perte de ce serveur oblige à recréer projet, compte de service, gabarits, et perd les stacks en cours. Kolla fournit `kolla-ansible mariadb_backup` : l'intégrer à la sauvegarde quotidienne et au test de restauration. | Avant le pilote | Étape 13 |
|
||||||
|
| Projet `paillasse`, compte `paillasse-service` et son rôle créés à la main (étape 6) | Pas rejouables par Ansible (gabarit et quota le sont depuis l'étape 13, rôle `openstack-projet`). | Bloc B | Étape 6 |
|
||||||
|
| Clé age des sauvegardes : copie hors du serveur non confirmée | Sans elle, aucune sauvegarde n'est déchiffrable si le serveur est perdu (voir `docs/runbooks/sauvegardes.md`). Adam doit confirmer l'avoir mise en lieu sûr. | Dès que possible | Étape 3 |
|
||||||
| Larastan (PHPStan niveau 6) absent | Exigé par le CLAUDE.md depuis l'étape 8, jamais installé. | Bloc B | Étape 8 |
|
| Larastan (PHPStan niveau 6) absent | Exigé par le CLAUDE.md depuis l'étape 8, jamais installé. | Bloc B | Étape 8 |
|
||||||
| Temps d'ouverture d'un lab : p95 non mesuré | Deux mesures seulement (4,06 et 4,35 s, critère < 5 s). Il faut une série. | Étape 13 | Étape 11 |
|
|
||||||
| Capacité : 15 VM au pire cas | Une classe de 30 sur le lab 08 demande 90 VM. Mesurer en charge, décider du surengagement mémoire (KSM). | Étape 13 | Étape 11 |
|
|
||||||
| Vitrine : Tailwind par CDN d'exécution (`cdn.tailwindcss.com`, 19 pages) | Non destiné à la production selon Tailwind, lent, et oblige la vitrine à tolérer les scripts en ligne dans sa CSP. Compiler ces styles avec Vite. | Avant ouverture publique | Étape 12 (design) |
|
| Vitrine : Tailwind par CDN d'exécution (`cdn.tailwindcss.com`, 19 pages) | Non destiné à la production selon Tailwind, lent, et oblige la vitrine à tolérer les scripts en ligne dans sa CSP. Compiler ces styles avec Vite. | Avant ouverture publique | Étape 12 (design) |
|
||||||
| Vitrine : 52 scripts en ligne, Google Fonts, jsDelivr (démos xterm, CodeMirror) | CSP vitrine avec `'unsafe-inline'` ; CDN parfois filtrés par les réseaux scolaires. Auto-héberger polices et bibliothèques, passer les scripts en fichiers. | Avant ouverture publique | Étape 12 |
|
| Vitrine : 52 scripts en ligne, Google Fonts, jsDelivr (démos xterm, CodeMirror) | CSP vitrine avec `'unsafe-inline'` ; CDN parfois filtrés par les réseaux scolaires. Auto-héberger polices et bibliothèques, passer les scripts en fichiers. | Avant ouverture publique | Étape 12 |
|
||||||
| xterm.js 5.5.0 (6.0.0 disponible) | Version majeure à évaluer séparément. | Bloc B | Étape 12 |
|
| xterm.js 5.5.0 (6.0.0 disponible) | Version majeure à évaluer séparément. | Bloc B | Étape 12 |
|
||||||
|
|
@ -16,6 +17,7 @@ retirer la ligne (et le noter dans le journal) quand c'est fait.
|
||||||
| Aucune vérification dans un vrai navigateur | Extension Chrome non connectée (étapes 8 et 12). La CSP n'est contrôlée que par ses rapports. | Dès que possible | Étapes 8 et 12 |
|
| Aucune vérification dans un vrai navigateur | Extension Chrome non connectée (étapes 8 et 12). La CSP n'est contrôlée que par ses rapports. | Dès que possible | Étapes 8 et 12 |
|
||||||
| Pas de sonde hors du serveur | Si le serveur tombe, personne n'est prévenu. | À décider avec Adam | Étape 7 |
|
| Pas de sonde hors du serveur | Si le serveur tombe, personne n'est prévenu. | À décider avec Adam | Étape 7 |
|
||||||
| Kolla installé depuis une branche, pas un tag | Reproductibilité. | Montée de version | ADR 0001 |
|
| Kolla installé depuis une branche, pas un tag | Reproductibilité. | Montée de version | ADR 0001 |
|
||||||
| Correction séquentielle (~5 s pour le lab 08) | Acceptable ; à paralléliser si la charge l'exige. | Étape 13 | Étape 11 |
|
| Correction séquentielle (~5 s pour le lab 08) | Acceptable ; à paralléliser si la charge l'exige. | Bloc B | Étape 11 |
|
||||||
|
| Jeton de console : ≈ 1,1 s par appel Nova `remote-consoles` | Plus gros poste de l'ouverture d'un lab (2,8 s au total, critère < 5 s tenu). Préparer les jetons à l'attribution si besoin. | Bloc B | Étape 13 |
|
||||||
| Tâches Ansible toujours « modifiées » (composer, npm, build, cache) | Brouille la lecture de l'idempotence. | Bloc B | Étape 8 |
|
| Tâches Ansible toujours « modifiées » (composer, npm, build, cache) | Brouille la lecture de l'idempotence. | Bloc B | Étape 8 |
|
||||||
| `Permissions-Policy` ferme caméra et micro | À ouvrir pour la visio. | Étape 15 | Étape 12 |
|
| `Permissions-Policy` ferme caméra et micro | À ouvrir pour la visio. | Étape 15 | Étape 12 |
|
||||||
|
|
|
||||||
|
|
@ -525,3 +525,44 @@ HTTPS ne se résume pas au cadenas : le certificat prouve l'identité du site, H
|
||||||
- Vérifié depuis le Mac : `/` → 302 vers `/status/syllabus` ; `/dashboard`, `/manage-status-page`, `/settings/general`, `/metrics`, `/socket.io/…`, `/api/push/…` → 404 ; `POST /api/status-page/syllabus` → 403 ; HTTP → 301 vers HTTPS. `/status/syllabus` → 404 renvoyé par Kuma lui-même (page pas encore créée), pas par le filtre.
|
- Vérifié depuis le Mac : `/` → 302 vers `/status/syllabus` ; `/dashboard`, `/manage-status-page`, `/settings/general`, `/metrics`, `/socket.io/…`, `/api/push/…` → 404 ; `POST /api/status-page/syllabus` → 403 ; HTTP → 301 vers HTTPS. `/status/syllabus` → 404 renvoyé par Kuma lui-même (page pas encore créée), pas par le filtre.
|
||||||
- Reste (Adam) : créer la page de statut `syllabus` dans Kuma, supprimer la sonde en double « syllabus ».
|
- Reste (Adam) : créer la page de statut `syllabus` dans Kuma, supprimer la sonde en double « syllabus ».
|
||||||
- Page de statut créée par Adam (slug `syllabus`, publiée). Vérifié depuis le Mac par `https://status.syllabuslab.fr` : page, API, pulsations, scripts, CSS et icône en 200. Seul `/docker-latest-warning.js` est bloqué (404) : script que Kuma injecte dans ses images « historiques » `latest`/`1`, actif seulement sur `/setup`, inutile au public — et argument de plus pour figer l'image (dette).
|
- Page de statut créée par Adam (slug `syllabus`, publiée). Vérifié depuis le Mac par `https://status.syllabuslab.fr` : page, API, pulsations, scripts, CSS et icône en 200. Seul `/docker-latest-warning.js` est bloqué (404) : script que Kuma injecte dans ses images « historiques » `latest`/`1`, actif seulement sur `/setup`, inutile au public — et argument de plus pour figer l'image (dette).
|
||||||
|
|
||||||
|
## 2026-10-09 — Étape 13 : tests de capacité et de résistance
|
||||||
|
|
||||||
|
**Fait**
|
||||||
|
|
||||||
|
- **Constats avant de tester** (valeurs par défaut de Nova lues dans son code installé, pas de mémoire) : `reserved_host_memory_mb = 512` (Nova se croyait libre de distribuer 31,5 Go alors que Kolla en occupe 15,5 : il aurait accepté ~41 VM et l'hôte aurait manqué de mémoire avant tout refus), `resume_guests_state_on_host_boot = false` (après un redémarrage, toutes les VM — dont le stock chaud — seraient restées éteintes), quota du projet à 10 instances (défaut OpenStack : une classe aurait été bloquée bien avant la capacité réelle).
|
||||||
|
- **Surcharge Nova** versionnée (`kolla/config/nova/nova-compute.conf`, déposée par le rôle `kolla`) : reprise des VM au démarrage, réserve mémoire. **Rôle `openstack-projet`** : gabarit `eleve.petit` et quota (40/40/30 Go) rejouables.
|
||||||
|
- **Test de capacité** (`openstack/tests/capacite.py`, en service systemd) : 20 VM lancées en même temps, puis repos, CPU à 100 %, mémoire chargée. Résultats et décisions : `docs/capacite.md`. **Capacité retenue par Adam : 22 VM** (Nova et portail alignés ; Placement confirme 16 993 Mo distribuables = 22 VM).
|
||||||
|
- **Sauvegarde complétée** : la base du portail (`mariadb-dump --single-transaction`), son `.env` (APP_KEY) et `storage/app` n'étaient **pas sauvegardés** (la sauvegarde date de l'étape 3, le portail de l'étape 8). Le mot de passe MariaDB est lu dans le conteneur, jamais sur la ligne de commande de l'hôte.
|
||||||
|
- **Test de restauration** (`/usr/local/sbin/syllabus-test-restauration`, chaque lundi 04:30, alerte `RestaurationNonTestee` au-delà de 8 jours) : télécharge, déchiffre, restaure la base dans un conteneur jetable, fait tourner le code du portail contre la copie, compare avec la production.
|
||||||
|
- **Redémarrage du serveur** (accord d'Adam) avec relevé d'état avant/après (`/root/etat-serveur.sh`).
|
||||||
|
- Unité systemd `kolla-proxysql-container` en échec depuis le 7 octobre (ProxySQL désactivé à l'étape 5, unité restée activée) : retirée par le rôle `kolla` (accord d'Adam), rangée dans `/root/unites-retirees/`. `systemctl --failed` : 0.
|
||||||
|
|
||||||
|
**Problèmes rencontrés et solution**
|
||||||
|
|
||||||
|
1. Le playbook `kolla` a perdu sa connexion SSH pendant une tâche longue (« Shared connection closed ») : ni bannissement fail2ban ni panne ; relancé, il est allé au bout. Les `kolla-ansible reconfigure` sont désormais lancés en service systemd (`systemd-run`), insensibles à une coupure SSH.
|
||||||
|
2. **Mesure du temps d'ouverture trompeuse** : après le redémarrage, le parcours a donné 5,51 s (critère < 5 s). Décomposition : le script demandait les 3 jetons de console l'un après l'autre (≈ 1,1 s chacun) alors que le navigateur les demande en parallèle. Script corrigé ; 3 mesures réelles fidèles au navigateur : 2,80 s, 2,85 s, 2,85 s. Les 4,06 et 4,35 s des étapes 11 et 12 étaient donc pessimistes aussi.
|
||||||
|
3. Ma boucle d'attente du retour des services ne sortait jamais (sous zsh, `set -- $s` ne découpe pas la chaîne) : les valeurs affichées montraient pourtant le retour complet ; boucle arrêtée, relevé fait à la main. Mon premier `diff` avant/après a échoué sur des droits (`sudo` ne s'applique pas aux substitutions de processus) : comparaison faite ligne à ligne.
|
||||||
|
4. Premier « retour SSH » après la demande de redémarrage : faux positif (connexion établie juste avant l'arrêt effectif, uptime de 2 jours). Attente refaite sur l'uptime (< 10 min).
|
||||||
|
5. Le projet `paillasse`, le compte de service et le gabarit avaient été créés à la main à l'étape 6 sans être codifiés (règle « tout est du code ») : gabarit et quota codifiés ; projet et compte notés en dette. La base d'OpenStack elle-même n'est pas sauvegardée : dette prioritaire.
|
||||||
|
|
||||||
|
**Critère vérifié**
|
||||||
|
|
||||||
|
- **Chiffres de capacité écrits** : `docs/capacite.md`. 20/20 VM démarrées sans erreur (prêtes en 53 s médiane, 75 s p95) ; ≈ 250 Mo par VM au repos, ≈ 600 Mo active ; 20 vCPU à 100 % → hôte à 89 %, commande de correction en 0,6 s.
|
||||||
|
- **Restauration réussie** :
|
||||||
|
```
|
||||||
|
quotidienne-20261009.tar.zst.age (108M)
|
||||||
|
APP_KEY restaurée identique à la production (valeur non affichée)
|
||||||
|
migrations dans la copie : 14 appliquées, 0 en attente
|
||||||
|
17 tables : copie = production pour chacune
|
||||||
|
== RESTAURATION RÉUSSIE
|
||||||
|
```
|
||||||
|
- **Tout est revenu seul après le redémarrage** (19:50 → SSH 19:53 → services Kolla et VM avant 20:01) : services, 46 unités Kolla, 49 conteneurs (0 en mauvaise santé), pare-feu, écoutes, réseau OpenStack, Nova/Neutron, 3 VM du stock relancées et dont l'agent invité répond, correcteur, Horizon, HTTPS, page de statut — identiques à l'état d'avant. Seule différence attendue : `f2b-table` (créée au premier bannissement). Parcours complet rejoué ensuite : « Pas encore réussi » 6/10 puis « Réussi » 10/10.
|
||||||
|
|
||||||
|
**Comprendre**
|
||||||
|
|
||||||
|
Un serveur qui « marche » n'est pas un serveur qui *revient* : trois valeurs par défaut raisonnables pour un cloud générique (VM éteintes après redémarrage, réserve mémoire minime, quota de 10 instances) auraient fait échouer une vraie classe, et aucune ne se voyait en usage normal. On ne les trouve qu'en provoquant la situation : charger le serveur, le redémarrer, restaurer une sauvegarde. C'est aussi vrai des sauvegardes : celle-ci « marchait » depuis l'étape 3, mais ne contenait pas la base du portail — seule une vraie restauration l'a montré. Enfin, une mesure dépend de la manière de mesurer : le même chemin a donné 5,5 s ou 2,8 s selon que le script imitait ou non le navigateur.
|
||||||
|
|
||||||
|
**Suite**
|
||||||
|
|
||||||
|
Étape 14 : pages légales et RGPD.
|
||||||
|
|
|
||||||
|
|
@ -86,3 +86,26 @@ openstack network agent list
|
||||||
- `prechecks` : `No module named 'docker'` — Kolla utilise le Python du venv, il faut `docker` et `dbus-python` **dans le venv** (fait par le rôle `kolla`).
|
- `prechecks` : `No module named 'docker'` — Kolla utilise le Python du venv, il faut `docker` et `dbus-python` **dans le venv** (fait par le rôle `kolla`).
|
||||||
- Docker 29 crée des tables `ip6 nat` / `ip6 filter` malgré `iptables: false` : ajouter `ip6tables: false` (`docker_custom_config` dans `globals.yml`).
|
- Docker 29 crée des tables `ip6 nat` / `ip6 filter` malgré `iptables: false` : ajouter `ip6tables: false` (`docker_custom_config` dans `globals.yml`).
|
||||||
- `kolla-genpwd` réécrit `passwords.yml` en `640` : le rôle remet `600`.
|
- `kolla-genpwd` réécrit `passwords.yml` en `640` : le rôle remet `600`.
|
||||||
|
|
||||||
|
## Surcharges de configuration et projet des labs (étape 13)
|
||||||
|
|
||||||
|
- `kolla/config/nova/nova-compute.conf` (déposé par le rôle `kolla` dans `/etc/kolla/config/nova/`) :
|
||||||
|
- `resume_guests_state_on_host_boot = true` : les VM redémarrent avec l'hôte (défaut Nova : elles restaient éteintes) ;
|
||||||
|
- `reserved_host_memory_mb = 15000` : Nova refuse au-delà de 22 VM `eleve.petit` (voir `docs/capacite.md`).
|
||||||
|
Après modification : `kolla-ansible reconfigure -i /etc/kolla/all-in-one --tags nova` (les VM en cours ne sont pas arrêtées). Le lancer comme service pour survivre à une coupure SSH :
|
||||||
|
```
|
||||||
|
sudo systemd-run --unit=kolla-reconfigure-nova --setenv=PATH=/opt/kolla-venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
|
||||||
|
--setenv=HOME=/root --working-directory=/etc/kolla \
|
||||||
|
bash -c 'kolla-ansible reconfigure -i /etc/kolla/all-in-one --tags nova > /var/log/kolla-reconfigure-nova.log 2>&1'
|
||||||
|
```
|
||||||
|
- Gabarit `eleve.petit` et quota du projet `paillasse` (40 instances, 40 vCPU, 30 Go) : rôle `openstack-projet` (`ansible-playbook openstack-projet.yml`).
|
||||||
|
- Vérifier ce que Placement accepte réellement : inventaire `MEMORY_MB` du fournisseur de ressources (`total`, `reserved`, `allocation_ratio`), par l'API Placement (le client `osc-placement` n'est pas installé).
|
||||||
|
- Services désactivés après un premier déploiement : leur unité systemd `kolla-<service>-container` est retirée par le rôle `kolla` (liste `kolla_services_retires`, aujourd'hui `proxysql`) et rangée dans `/root/unites-retirees/`.
|
||||||
|
|
||||||
|
## Redémarrage de l'hôte (testé le 2026-10-09)
|
||||||
|
|
||||||
|
Tout revient seul : les 46 conteneurs Kolla sont relancés par leurs unités systemd (et non par Docker, politique `no`), les VM par Nova, le reste par systemd. SSH en ≈ 3 min, services Kolla et VM ACTIVE en moins de 10 min.
|
||||||
|
|
||||||
|
Avant : sauvegarde récente et restauration testée, accord d'Adam, aucune modification réseau ou pare-feu en attente. Relevé comparable avant/après : `sudo /root/etat-serveur.sh` (services, unités Kolla, conteneurs, pare-feu, écoutes, réseau OpenStack, VM, socket du correcteur, Horizon, HTTPS, statut).
|
||||||
|
|
||||||
|
Juste après le démarrage, la table nftables `f2b-table` est absente : fail2ban ne la crée qu'à son premier bannissement (normal).
|
||||||
|
|
|
||||||
60
portail/tests/Reels/decomposer-ouverture.py
Normal file
60
portail/tests/Reels/decomposer-ouverture.py
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Décompose le temps d'ouverture d'un lab déjà admis (étape 13) : où passent
|
||||||
|
les secondes entre « la page voit l'état actif » et « l'invite login: » ?
|
||||||
|
|
||||||
|
Usage (sur le serveur, séance préparée par preparer-parcours.php et élève
|
||||||
|
déjà admis par parcours-lab.py) :
|
||||||
|
sudo -u www-data python3 tests/Reels/decomposer-ouverture.py
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import time
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import websocket
|
||||||
|
|
||||||
|
BASE = os.environ.get("PARCOURS_BASE", "https://syllabuslab.fr")
|
||||||
|
BASE_WS = re.sub(r"^http", "ws", BASE)
|
||||||
|
ids = json.load(open("/tmp/parcours-identifiants.json"))
|
||||||
|
|
||||||
|
s = requests.Session()
|
||||||
|
tok = re.search(r'name="_token" value="([^"]+)"', s.get(BASE + "/connexion").text).group(1)
|
||||||
|
s.post(BASE + "/connexion/eleve", data={"_token": tok, "identifiant": ids["eleve_identifiant"], "code": ids["eleve_code"]})
|
||||||
|
|
||||||
|
|
||||||
|
def chrono(f):
|
||||||
|
t = time.time()
|
||||||
|
r = f()
|
||||||
|
return r, time.time() - t
|
||||||
|
|
||||||
|
|
||||||
|
def jeton(url, csrf):
|
||||||
|
return s.post(url, headers={"X-CSRF-TOKEN": csrf}).json()["jeton"]
|
||||||
|
|
||||||
|
|
||||||
|
def invite(jeton_console):
|
||||||
|
ws = websocket.create_connection(f"{BASE_WS}/console/{jeton_console}", timeout=10)
|
||||||
|
ws.send("\r")
|
||||||
|
sortie, fin = b"", time.time() + 10
|
||||||
|
# « login: » sur une VM neuve, « $ » si quelqu'un y est déjà connecté.
|
||||||
|
while not re.search(rb"login:|\$ ", sortie) and time.time() < fin:
|
||||||
|
m = ws.recv()
|
||||||
|
sortie += m if isinstance(m, bytes) else m.encode()
|
||||||
|
ws.close()
|
||||||
|
|
||||||
|
|
||||||
|
for essai in range(1, 4):
|
||||||
|
(page, t_page) = chrono(lambda: s.get(f"{BASE}/seances/{ids['seance_id']}/attente-eleve").text)
|
||||||
|
csrf = re.search(r"'X-CSRF-TOKEN': '([^']+)'", page).group(1)
|
||||||
|
urls = [re.sub(r"^https?://[^/]+", BASE, u) for u in re.findall(r"fetch\('(http[^']+/console)'", page)]
|
||||||
|
|
||||||
|
(jetons_seq, t_seq) = chrono(lambda: [jeton(u, csrf) for u in urls])
|
||||||
|
with ThreadPoolExecutor(len(urls)) as pool:
|
||||||
|
(jetons_par, t_par) = chrono(lambda: list(pool.map(lambda u: jeton(u, csrf), urls)))
|
||||||
|
(_, t_ws) = chrono(lambda: invite(jetons_par[0]))
|
||||||
|
|
||||||
|
navigateur = 1.0 + t_page + t_par + t_ws
|
||||||
|
print(f"essai {essai} : page {t_page:.2f} s | 3 jetons console : séquentiel {t_seq:.2f} s, parallèle {t_par:.2f} s "
|
||||||
|
f"| WebSocket → login: {t_ws:.2f} s | chemin navigateur (pire attente d'interrogation 1 s) ≈ {navigateur:.2f} s")
|
||||||
|
|
@ -16,6 +16,7 @@ import os
|
||||||
import re
|
import re
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import websocket
|
import websocket
|
||||||
|
|
@ -70,11 +71,14 @@ etat_change.wait(60)
|
||||||
|
|
||||||
page = eleve.get(f"{BASE}/seances/{seance}/attente-eleve").text
|
page = eleve.get(f"{BASE}/seances/{seance}/attente-eleve").text
|
||||||
csrf = re.search(r"'X-CSRF-TOKEN': '([^']+)'", page).group(1)
|
csrf = re.search(r"'X-CSRF-TOKEN': '([^']+)'", page).group(1)
|
||||||
consoles = {}
|
# Comme le navigateur : les jetons des consoles sont demandés en parallèle
|
||||||
for url, nom in zip(re.findall(r"fetch\('(http[^']+/console)'", page), re.findall(r'data-onglet="\d+"[^>]*>([^<]+)<', page)):
|
# (une série de fetch sans attente). Les demander l'un après l'autre
|
||||||
url = re.sub(r"^https?://[^/]+", BASE, url)
|
# ajoutait ~2 s à la mesure (décomposition : decomposer-ouverture.py).
|
||||||
jeton = eleve.post(url, headers={"X-CSRF-TOKEN": csrf}).json()["jeton"]
|
noms = re.findall(r'data-onglet="\d+"[^>]*>([^<]+)<', page)
|
||||||
consoles[nom] = websocket.create_connection(f"{BASE_WS}/console/{jeton}", timeout=10)
|
urls = [re.sub(r"^https?://[^/]+", BASE, u) for u in re.findall(r"fetch\('(http[^']+/console)'", page)]
|
||||||
|
with ThreadPoolExecutor(len(urls)) as pool:
|
||||||
|
jetons = list(pool.map(lambda u: eleve.post(u, headers={"X-CSRF-TOKEN": csrf}).json()["jeton"], urls))
|
||||||
|
consoles = {nom: websocket.create_connection(f"{BASE_WS}/console/{j}", timeout=10) for nom, j in zip(noms, jetons)}
|
||||||
|
|
||||||
|
|
||||||
def lire_jusqua(ws, motif, delai=15):
|
def lire_jusqua(ws, motif, delai=15):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue