two/main/deploiement/architecture-cluster.html
2026-09-09 22:07:27 +00:00

554 lines
No EOL
25 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="fr" data-content_root="../" >
<head>
<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>Architecture du cluster &#8212; Documentation two 0.1.0</title>
<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=57395fb6"></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 = 'deploiement/architecture-cluster';</script>
<script>
DOCUMENTATION_OPTIONS.theme_version = '0.16.1';
DOCUMENTATION_OPTIONS.theme_switcher_json_url = '../switcher.json';
DOCUMENTATION_OPTIONS.theme_switcher_version_match = 'main';
DOCUMENTATION_OPTIONS.show_version_warning_banner =
false;
</script>
<link rel="index" title="Index" href="../genindex.html" />
<link rel="search" title="Recherche" href="../search.html" />
<link rel="next" title="Construction de l’image qcow2" href="image-qcow2.html" />
<link rel="prev" title="Déploiement d’un cluster" href="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>
<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="../index.html">
<p class="title logo__title">Documentation two 0.1.0</p>
</a></div>
<div class="sidebar-primary-item"><ul class="navbar-icon-links"
aria-label="Icon Links">
<li class="nav-item">
<a href="https://git.g3e.fr/syonad/two" title="Dépôt" class="nav-link pst-navbar-icon" rel="noopener" target="_blank" data-bs-toggle="tooltip" data-bs-placement="bottom"><i class="fa-solid fa-code-branch fa-lg" aria-hidden="true"></i>
<span class="sr-only">Dépôt</span></a>
</li>
</ul></div>
<div class="sidebar-primary-item">
<div class="version-switcher__container dropdown pst-js-only">
<button id="pst-version-switcher-button-2"
type="button"
class="version-switcher__button btn btn-sm dropdown-toggle"
data-bs-toggle="dropdown"
aria-haspopup="listbox"
aria-controls="pst-version-switcher-list-2"
aria-label="Version switcher list"
>
Choose version <!-- this text may get changed later by javascript -->
<span class="caret"></span>
</button>
<div id="pst-version-switcher-list-2"
class="version-switcher__menu dropdown-menu list-group-flush py-0"
role="listbox" aria-labelledby="pst-version-switcher-button-2">
<!-- dropdown will be populated by javascript on page load -->
</div>
</div></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">
<a class="reference internal" href="../index.html">
two
</a>
</li>
</ul>
<p aria-level="2" class="caption" role="heading"><span class="caption-text">Mise en œuvre</span></p>
<ul class="current 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 current active has-children"><a class="reference internal" href="index.html">Déploiement d’un cluster</a><details open="open"><summary><span class="toctree-toggle" role="presentation"><i class="fa-solid fa-chevron-down"></i></span></summary><ul class="current">
<li class="toctree-l2 current active"><a class="current reference internal" href="#">Architecture du cluster</a></li>
<li class="toctree-l2"><a class="reference internal" href="image-qcow2.html">Construction de l’image qcow2</a></li>
<li class="toctree-l2"><a class="reference internal" href="routeurs.html">Routeurs</a></li>
<li class="toctree-l2"><a class="reference internal" href="premier-hyperviseur.html">Premier hyperviseur</a></li>
<li class="toctree-l2"><a class="reference internal" href="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">
<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>Architecture du cluster</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="#topologie">Topologie</a></li>
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#ce-que-l-agent-suppose-deja-en-place">Ce que l’agent suppose déjà en place</a></li>
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#pourquoi-un-plan-de-controle-est-necessaire">Pourquoi un plan de contrôle est nécessaire</a></li>
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#mtu">MTU</a></li>
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#hyperviseurs-sans-etat">Hyperviseurs sans état</a></li>
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#adressage">Adressage</a></li>
</ul>
</nav>
</div>
</div>
</div>
<div id="searchbox"></div>
<article class="bd-article">
<section id="architecture-du-cluster">
<h1>Architecture du cluster<a class="headerlink" href="#architecture-du-cluster" title="Lien vers cette rubrique">#</a></h1>
<section id="topologie">
<h2>Topologie<a class="headerlink" href="#topologie" title="Lien vers cette rubrique">#</a></h2>
<figure class="align-center" id="id1">
<a class="only-light reference internal image-reference" href="../_images/topologie-cluster.svg"><img alt="Topologie du cluster : routeurs, route reflector et hyperviseurs" class="only-light" src="../_images/topologie-cluster.svg" style="width: 100%;" />
</a>
<figcaption>
<p><span class="caption-text">Topologie cible. Trait plein : plan de données. Trait pointillé : plan de contrôle.</span><a class="headerlink" href="#id1" title="Lien vers cette image">#</a></p>
</figcaption>
</figure>
<figure class="align-center" id="id2">
<a class="only-dark reference internal image-reference" href="../_images/topologie-cluster-dark.svg"><img alt="Topologie du cluster : routeurs, route reflector et hyperviseurs" class="only-dark" src="../_images/topologie-cluster-dark.svg" style="width: 100%;" />
</a>
<figcaption>
<p><span class="caption-text">Topologie cible. Trait plein : plan de données. Trait pointillé : plan de contrôle.</span><a class="headerlink" href="#id2" title="Lien vers cette image">#</a></p>
</figcaption>
</figure>
<p>Deux plans distincts, à ne pas confondre au moment du diagnostic :</p>
<dl class="simple">
<dt>Plan de données</dt><dd><p>les tunnels VXLAN entre hyperviseurs, encapsulés sur le réseau qui les relie.</p>
</dd>
<dt>Plan de contrôle</dt><dd><p>ce qui dit à chaque hyperviseur où se trouvent les adresses MAC des autres. C’est le rôle de
FRR et du route reflector.</p>
</dd>
</dl>
</section>
<section id="ce-que-l-agent-suppose-deja-en-place">
<h2>Ce que l’agent suppose déjà en place<a class="headerlink" href="#ce-que-l-agent-suppose-deja-en-place" title="Lien vers cette rubrique">#</a></h2>
<p>L’agent ne configure <strong>que</strong> 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.</p>
<p>Concrètement, il attend :</p>
<ul class="simple">
<li><p>le bridge d’uplink de la configuration (<code class="docutils literal notranslate"><span class="pre">br-000000</span></code> par défaut), avec l’interface physique
esclave et l’adresse de l’hyperviseur portée par le bridge — c’est ce que fait
<code class="docutils literal notranslate"><span class="pre">deploy.sh</span> <span class="pre">--bootstrap</span></code>, voir <a class="reference internal" href="../demarrage/installation.html"><span class="doc">Installation d’un hyperviseur</span></a> ;</p></li>
<li><p>une connectivité IP entre hyperviseurs sur cette adresse, port UDP <strong>4789</strong> ouvert dans les
deux sens ;</p></li>
<li><p>un plan de contrôle qui peuple la table de transfert VXLAN — voir ci-dessous.</p></li>
</ul>
</section>
<section id="pourquoi-un-plan-de-controle-est-necessaire">
<h2>Pourquoi un plan de contrôle est nécessaire<a class="headerlink" href="#pourquoi-un-plan-de-controle-est-necessaire" title="Lien vers cette rubrique">#</a></h2>
<p>L’agent crée les interfaces VXLAN sur le port 4789 <strong>sans groupe multicast et avec
l’apprentissage désactivé</strong> (<code class="docutils literal notranslate"><span class="pre">Learning:</span> <span class="pre">false</span></code>). Il n’y a donc ni inondation multicast, ni
apprentissage des adresses MAC depuis le trafic, ni voisin statique configuré.</p>
<div class="admonition important">
<p class="admonition-title">Important</p>
<p>Conséquence directe : sur un même VNI, <strong>rien ne traverse d’un hyperviseur à l’autre</strong> 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.</p>
</div>
<p>C’est exactement le rôle que remplissent FRR sur chaque hyperviseur et le route reflector qui
les fait converger.</p>
</section>
<section id="mtu">
<h2>MTU<a class="headerlink" href="#mtu" title="Lien vers cette rubrique">#</a></h2>
<div class="admonition warning">
<p class="admonition-title">Avertissement</p>
<p>L’agent crée bridges, veth et interfaces VXLAN avec un <strong>MTU figé à 1500</strong>. VXLAN ajoute 50
octets d’encapsulation : le réseau qui relie les hyperviseurs doit donc accepter au moins
<strong>1550 octets</strong> de MTU, sinon les paquets pleine taille des VM sont perdus.</p>
<p>Le symptôme est trompeur : le ping passe, les petites requêtes passent, les transferts
volumineux et les poignées de main TLS échouent.</p>
</div>
</section>
<section id="hyperviseurs-sans-etat">
<h2>Hyperviseurs sans état<a class="headerlink" href="#hyperviseurs-sans-etat" title="Lien vers cette rubrique">#</a></h2>
<p>L’hyperviseur est <strong>stateless</strong> — sa racine est en tmpfs, rien de ce que pose
<code class="docutils literal notranslate"><span class="pre">deploy.sh</span> <span class="pre">--bootstrap</span></code> ne survit à un redémarrage, et le script est rejoué à chaque démarrage.</p>
<p>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
<code class="docutils literal notranslate"><span class="pre">/etc</span></code>.</p>
</section>
<section id="adressage">
<h2>Adressage<a class="headerlink" href="#adressage" title="Lien vers cette rubrique">#</a></h2>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p><strong>À rédiger</strong> — cette page ne décrit pas encore le plan d’adressage du cluster. À documenter :</p>
<ul class="simple">
<li><p>la plage utilisée pour les adresses d’hyperviseurs, et son rapport avec <code class="docutils literal notranslate"><span class="pre">br-000000</span></code> ;</p></li>
<li><p>l’allocation des VNI VXLAN : qui la tient, et comment on évite les collisions, puisque
l’agent ne valide pas <code class="docutils literal notranslate"><span class="pre">vxlan_id</span></code> ;</p></li>
<li><p>l’usage prévu de <code class="docutils literal notranslate"><span class="pre">br-public</span></code>, créé vide et réservé par le bootstrap ;</p></li>
<li><p>le plan d’adressage des VPC, et ce qui garantit qu’ils ne se recouvrent pas entre clients.</p></li>
</ul>
</div>
</section>
</section>
</article>
<footer class="prev-next-footer d-print-none">
<div class="prev-next-area">
<a class="left-prev"
href="index.html"
title="page précédente">
<i class="fa-solid fa-angle-left"></i>
<div class="prev-next-info">
<p class="prev-next-subtitle">précédent</p>
<p class="prev-next-title">Déploiement d’un cluster</p>
</div>
</a>
<a class="right-next"
href="image-qcow2.html"
title="page suivante">
<div class="prev-next-info">
<p class="prev-next-subtitle">suivant</p>
<p class="prev-next-title">Construction de l’image qcow2</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="#topologie">Topologie</a></li>
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#ce-que-l-agent-suppose-deja-en-place">Ce que l’agent suppose déjà en place</a></li>
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#pourquoi-un-plan-de-controle-est-necessaire">Pourquoi un plan de contrôle est nécessaire</a></li>
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#mtu">MTU</a></li>
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#hyperviseurs-sans-etat">Hyperviseurs sans état</a></li>
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#adressage">Adressage</a></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>