<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Infrastructure on kenji.blog</title><link>http://kenji.blog/fr/categories/infrastructure/</link><description>Recent content in Infrastructure on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>fr</language><copyright>kenjinote</copyright><lastBuildDate>Sun, 13 Sep 2026 07:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/fr/categories/infrastructure/index.xml" rel="self" type="application/rss+xml"/><item><title>Hyper-V vs WSL2 : Comparaison des technologies de virtualisation sur Windows</title><link>http://kenji.blog/fr/p/hyper-v-vs-wsl2-windows-virtualization/</link><pubDate>Sun, 13 Sep 2026 07:00:00 +0900</pubDate><guid>http://kenji.blog/fr/p/hyper-v-vs-wsl2-windows-virtualization/</guid><description>&lt;img src="http://kenji.blog/p/hyper-v-vs-wsl2-windows-virtualization/img/eyecatch.jpg" alt="Featured image of post Hyper-V vs WSL2 : Comparaison des technologies de virtualisation sur Windows" />&lt;h2 id="1-introduction--lévolution-de-la-virtualisation-sur-windows">1. Introduction : L&amp;rsquo;évolution de la virtualisation sur Windows
&lt;/h2>&lt;p>La technologie de virtualisation sur la plateforme Windows a connu une évolution spectaculaire au cours des dernières décennies. Autrefois, les hyperviseurs de type 2 tiers (tels que VMware Workstation ou VirtualBox) étaient la norme, mais depuis que Microsoft a introduit &amp;ldquo;Hyper-V&amp;rdquo; avec Windows Server 2008, un hyperviseur de type 1 a également été intégré aux systèmes d&amp;rsquo;exploitation de bureau tels que Windows 10/11.&lt;/p>
&lt;p>Et ces dernières années, ce qui attire le plus l&amp;rsquo;attention parmi les développeurs est &amp;ldquo;WSL2 (Windows Subsystem for Linux 2)&amp;rdquo;. Alors que WSL1 s&amp;rsquo;appuyait sur la traduction des appels système, WSL2 utilise une &amp;ldquo;Lightweight Utility VM&amp;rdquo; (machine virtuelle utilitaire légère) qui applique la technologie d&amp;rsquo;Hyper-V, réalisant ainsi une compatibilité Linux complète et une amélioration spectaculaire des performances.&lt;/p>
&lt;p>Dans cet article, nous comparerons et expliquerons en détail ces deux puissantes technologies de virtualisation — le &amp;ldquo;Hyper-V&amp;rdquo; complet et le &amp;ldquo;WSL2&amp;rdquo; axé sur l&amp;rsquo;expérience du développeur — en termes d&amp;rsquo;architecture, de performances (CPU, mémoire, E/S disque), de configuration réseau, et des meilleurs cas d&amp;rsquo;utilisation, avec une profondeur technique abyssale.&lt;/p>
&lt;hr>
&lt;h2 id="2-théorie-fondamentale-des-hyperviseurs-et-comparaison-architecturale">2. Théorie fondamentale des hyperviseurs et comparaison architecturale
&lt;/h2>&lt;p>Pour comprendre la technologie de virtualisation, il est essentiel de classer les hyperviseurs (Virtual Machine Monitors : VMM) par type.&lt;/p>
&lt;h3 id="21-différences-entre-les-hyperviseurs-de-type-1-et-de-type-2">2.1. Différences entre les hyperviseurs de type 1 et de type 2
&lt;/h3>&lt;p>Un hyperviseur est une couche logicielle qui abstrait l&amp;rsquo;accès au matériel et permet à plusieurs systèmes d&amp;rsquo;exploitation (OS invités) de s&amp;rsquo;exécuter simultanément sur une seule machine physique.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Type 1 (Bare Metal)&lt;/strong> : S&amp;rsquo;exécute directement sur le matériel. Il n&amp;rsquo;y a pas de concept d&amp;rsquo;OS hôte (bien qu&amp;rsquo;il puisse y avoir un OS de gestion avec des privilèges stricts), l&amp;rsquo;overhead est extrêmement faible, offrant des performances et une sécurité élevées. Exemples : Hyper-V, VMware ESXi, Xen.&lt;/li>
&lt;li>&lt;strong>Type 2 (Hébergé)&lt;/strong> : S&amp;rsquo;exécute comme une application sur l&amp;rsquo;OS hôte (Windows, macOS, etc.). Étant donné que tous les accès au matériel passent par l&amp;rsquo;OS hôte, l&amp;rsquo;overhead est important. Exemples : VMware Workstation, Oracle VirtualBox.&lt;/li>
&lt;/ul>
&lt;p>Le Hyper-V de Windows est un pur &lt;strong>hyperviseur de Type 1&lt;/strong>. Lorsque Hyper-V est activé, l&amp;rsquo;OS Windows lui-même avec lequel l&amp;rsquo;utilisateur interagit habituellement s&amp;rsquo;exécute dans une machine virtuelle spéciale appelée &amp;ldquo;Partition racine&amp;rdquo; (Root Partition).&lt;/p>
&lt;h3 id="22-détails-architecturaux-dhyper-v">2.2. Détails architecturaux d&amp;rsquo;Hyper-V
&lt;/h3>&lt;p>L&amp;rsquo;architecture d&amp;rsquo;Hyper-V adopte une conception de micro-noyau et est basée sur des unités logiques d&amp;rsquo;isolation appelées partitions (Partition).&lt;/p>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;Matériel (CPU, RAM, Disque, NIC)&amp;#34;] --&amp;gt; B[&amp;#34;Hyperviseur Windows (Ring -1)&amp;#34;]
B --&amp;gt; C[&amp;#34;Partition racine (OS Windows)&amp;#34;]
B --&amp;gt; D[&amp;#34;Partition enfant 1 (VM Windows)&amp;#34;]
B --&amp;gt; E[&amp;#34;Partition enfant 2 (VM Linux)&amp;#34;]
C --&amp;gt; F[&amp;#34;VMBus (Bus de machine virtuelle)&amp;#34;]
D --&amp;gt; F
E --&amp;gt; F
C --&amp;gt; G[&amp;#34;VID (Pilote d&amp;#39;infrastructure de virtualisation)&amp;#34;]
C --&amp;gt; H[&amp;#34;VMWP.exe (Processus de travail)&amp;#34;]
&lt;/pre>
&lt;ul>
&lt;li>&lt;strong>Hyperviseur Windows&lt;/strong> : S&amp;rsquo;exécute avec le niveau de privilège le plus élevé du CPU (Ring -1 ou mode VMX Root) et gère uniquement l&amp;rsquo;allocation de la mémoire et la planification du CPU. Il ne contient pas de pilotes de périphériques.&lt;/li>
&lt;li>&lt;strong>Partition racine&lt;/strong> : La partition où s&amp;rsquo;exécute l&amp;rsquo;OS Windows hôte. Elle possède tous les pilotes de périphériques et contrôle directement le matériel. Elle fournit également les fonctions de gestion (fournisseur WMI, VMWP.exe, etc.) des partitions enfants.&lt;/li>
&lt;li>&lt;strong>Partition enfant&lt;/strong> : La partition où s&amp;rsquo;exécute l&amp;rsquo;OS invité. L&amp;rsquo;accès direct au matériel n&amp;rsquo;est pas autorisé ; elle envoie des requêtes d&amp;rsquo;E/S (Synthetic I/O) à la partition racine via un bus de mémoire partagée logique appelé &amp;ldquo;VMBus&amp;rdquo;.&lt;/li>
&lt;/ul>
&lt;h3 id="23-fonctionnement-de-wsl2-et-lightweight-utility-vm">2.3. Fonctionnement de WSL2 et Lightweight Utility VM
&lt;/h3>&lt;p>WSL2 utilise la même technologie de base d&amp;rsquo;hyperviseur de type 1 que Hyper-V, mais utilise un sous-ensemble de fonctionnalités appelé &amp;ldquo;Virtual Machine Platform (VMP)&amp;rdquo;, différent d&amp;rsquo;une machine virtuelle Hyper-V complète.&lt;/p>
&lt;p>La &amp;ldquo;Lightweight Utility VM&amp;rdquo; adoptée par WSL2 élimine complètement l&amp;rsquo;émulation de matériel hérité (comme les BIOS ou cartes mères virtuels) que possèdent les machines virtuelles traditionnelles.&lt;/p>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;OS Hôte Windows (Espace utilisateur)&amp;#34;]
B[&amp;#34;Système de fichiers NTFS&amp;#34;]
C[&amp;#34;Serveur de protocole 9P (Plan 9)&amp;#34;]
D[&amp;#34;Lightweight Utility VM (Noyau Linux)&amp;#34;]
E[&amp;#34;ext4.vhdx (Disque virtuel)&amp;#34;]
F[&amp;#34;Espace utilisateur Linux (Distributions WSL2)&amp;#34;]
A --&amp;gt; C
C --&amp;gt;| Partage de fichiers inter-OS | D
D --&amp;gt; E
D --&amp;gt; F
&lt;/pre>
&lt;p>La plus grande caractéristique de WSL2 est sa &lt;strong>rapidité de démarrage&lt;/strong> et son &lt;strong>intégration transparente avec l&amp;rsquo;OS hôte&lt;/strong>. Le noyau Linux démarre en moins d&amp;rsquo;une seconde, et il accède au système de fichiers (NTFS) du côté Windows via le protocole de système de fichiers réseau &lt;code>9P&lt;/code> de Plan 9.&lt;/p>
&lt;hr>
&lt;h2 id="3-analyse-approfondie-des-performances--ressources-de-calcul-et-es">3. Analyse approfondie des performances : Ressources de calcul et E/S
&lt;/h2>&lt;p>Les performances des machines virtuelles sont représentées par la somme des overheads pour chaque composant : CPU, mémoire et E/S disque.&lt;/p>
&lt;h3 id="31-cpu-et-overhead-de-changement-de-contexte">3.1. CPU et overhead de changement de contexte
&lt;/h3>&lt;p>Hyper-V et WSL2 utilisent tous deux la virtualisation assistée par le matériel (Intel VT-x / AMD-V). Les instructions CPU s&amp;rsquo;exécutent généralement à une vitesse native, mais lors de l&amp;rsquo;exécution d&amp;rsquo;instructions privilégiées ou d&amp;rsquo;E/S, une interruption appelée &amp;ldquo;VM Exit&amp;rdquo; se produit, entraînant un changement de contexte vers l&amp;rsquo;hyperviseur.&lt;/p>
&lt;p>Cet overhead CPU $T_{overhead}$ peut être exprimé par le modèle mathématique suivant :&lt;/p>
$$ T_{overhead} = \sum_{i=1}^{N} (t_{vm\_exit} + t_{hypercall\_process} + t_{vm\_entry}) $$&lt;p>Où :&lt;/p>
&lt;ul>
&lt;li>$N$ : Nombre d&amp;rsquo;occurrences de VM Exit par unité de temps&lt;/li>
&lt;li>$t_{vm\_exit}$ : Temps de transition de l&amp;rsquo;invité à l&amp;rsquo;hyperviseur&lt;/li>
&lt;li>$t_{hypercall\_process}$ : Temps de traitement des E/S ou des interruptions via VMBus&lt;/li>
&lt;li>$t_{vm\_entry}$ : Temps de retour de l&amp;rsquo;hyperviseur à l&amp;rsquo;invité&lt;/li>
&lt;/ul>
&lt;p>Comme WSL2 n&amp;rsquo;a pas d&amp;rsquo;émulation héritée, $t_{hypercall\_process}$ est optimisé et extrêmement petit. Par conséquent, pour les calculs CPU purs (comme la compilation du noyau ou l&amp;rsquo;inférence de modèles d&amp;rsquo;apprentissage automatique), la dégradation des performances est de l&amp;rsquo;ordre de quelques pourcents par rapport à un environnement bare metal.&lt;/p>
&lt;h3 id="32-mécanisme-dallocation-de-la-mémoire">3.2. Mécanisme d&amp;rsquo;allocation de la mémoire
&lt;/h3>&lt;p>En termes de méthodes de gestion de la mémoire, il existe une nette différence de philosophie de conception entre les deux.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Hyper-V (Dynamic Memory)&lt;/strong> : La partition racine alloue et récupère dynamiquement la mémoire en fonction des besoins en mémoire de la VM invitée. Cependant, la mémoire allouée en tant que cache de page dans l&amp;rsquo;OS invité a tendance à ne pas être libérée à moins que le système ne soit sous pression.&lt;/li>
&lt;li>&lt;strong>WSL2 (Récupération dynamique de mémoire)&lt;/strong> : WSL2 dispose d&amp;rsquo;un mécanisme propriétaire qui renvoie (Reclaim) régulièrement la mémoire devenue inutile (y compris le cache) dans la VM Linux à l&amp;rsquo;hôte Windows. Les premières versions de WSL2 avaient un problème où le cache de page Linux dévorait la mémoire Windows (processus Vmmem gonflé), mais cela a maintenant été amélioré par des patchs du noyau.&lt;/li>
&lt;/ul>
&lt;h3 id="33-caractéristiques-des-es-disque-vhdx-vs-ext4vhdx">3.3. Caractéristiques des E/S disque (VHDX vs ext4.vhdx)
&lt;/h3>&lt;p>Le goulot d&amp;rsquo;étranglement le plus fréquent des performances des machines virtuelles est l&amp;rsquo;E/S disque.&lt;/p>
&lt;p>La latence d&amp;rsquo;E/S $L_{total}$ est calculée comme suit :&lt;/p>
$$ L_{total} = L_{guest\_fs} + L_{vmbus} + L_{host\_fs} + L_{physical\_disk} $$&lt;p>&lt;strong>Pour Hyper-V&lt;/strong> :
Les invités Hyper-V typiques utilisent des disques virtuels au format &lt;code>VHDX&lt;/code>. Les requêtes d&amp;rsquo;E/S émises par le système de fichiers (ext4 ou NTFS) dans l&amp;rsquo;OS invité traversent le pilote de stockage de périphérique bloc de VMBus (storvsc) et sont traitées comme un accès au fichier VHDX sur le NTFS côté Windows.&lt;/p>
&lt;p>&lt;strong>Pour WSL2&lt;/strong> :
Les distributions Linux sur WSL2 s&amp;rsquo;exécutent sur un système de fichiers ext4 natif construit à l&amp;rsquo;intérieur d&amp;rsquo;un fichier dédié &lt;code>ext4.vhdx&lt;/code>. Les opérations de fichiers à l&amp;rsquo;intérieur de Linux (comme dans le répertoire &lt;code>~&lt;/code>) atteignent des performances natives équivalentes à celles de l&amp;rsquo;Hyper-V ci-dessus.
Cependant, &lt;strong>lors de l&amp;rsquo;accès aux fichiers du côté Windows depuis le Linux de WSL2 (comme &lt;code>/mnt/c/&lt;/code>)&lt;/strong>, ou vice versa, le processus est très différent. Cet accès inter-OS utilise le &lt;code>9P (Plan 9 File System Protocol)&lt;/code>.&lt;/p>
$$ L_{cross\_os} = L_{9p\_client} + L_{socket\_transfer} + L_{9p\_server} + L_{ntfs} $$&lt;p>L&amp;rsquo;accès via ce protocole 9P a un important overhead de traitement de sérialisation, et les performances chutent drastiquement (parfois avec un délai plus de 10 fois supérieur) pour les applications lisant et écrivant un grand nombre de petits fichiers (par exemple, &lt;code>npm install&lt;/code> ou les opérations Git dans un projet Node.js situé dans un répertoire côté Windows).
C&amp;rsquo;est pourquoi la &lt;strong>règle d&amp;rsquo;or absolue lors de l&amp;rsquo;utilisation de WSL2 est de toujours placer les fichiers de projet dans le système de fichiers natif de Linux (sous &lt;code>~/&lt;/code>)&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h2 id="4-structure-du-réseau--nat-default-switch-bridged">4. Structure du réseau : NAT, Default Switch, Bridged
&lt;/h2>&lt;p>La flexibilité des capacités réseau est l&amp;rsquo;une des principales différences entre Hyper-V et WSL2.&lt;/p>
&lt;h3 id="41-le-réseau-de-wsl2-basé-sur-le-nat">4.1. Le réseau de WSL2 (Basé sur le NAT)
&lt;/h3>&lt;p>Par défaut, le réseau de WSL2 est configuré en &amp;ldquo;NAT (Network Address Translation)&amp;rdquo; en utilisant la technologie de commutateur virtuel d&amp;rsquo;Hyper-V.
Une adresse IP privée (par exemple : &lt;code>172.20.x.x&lt;/code>) différente de celle de l&amp;rsquo;hôte Windows est automatiquement attribuée à la VM Linux. Un mécanisme est intégré pour que l&amp;rsquo;accès à &lt;code>localhost&lt;/code> depuis l&amp;rsquo;hôte Windows soit redirigé vers les services (ports) démarrés dans WSL2, permettant aux développeurs de tester des serveurs web sans se soucier du réseau.&lt;/p>
&lt;p>Récemment, un nouveau mode réseau appelé &amp;ldquo;Mirrored mode&amp;rdquo; (mode miroir) a été introduit dans les versions preview de WSL2. Cela vise à améliorer le support d&amp;rsquo;IPv6 et la compatibilité des connexions VPN (configurable via &lt;code>.wslconfig&lt;/code>).&lt;/p>
&lt;h3 id="42-commutateur-virtuel-dhyper-v-virtual-switch">4.2. Commutateur virtuel d&amp;rsquo;Hyper-V (Virtual Switch)
&lt;/h3>&lt;p>Hyper-V permet la construction de réseaux avancés au niveau de l&amp;rsquo;entreprise. Via le &amp;ldquo;Gestionnaire de commutateur virtuel&amp;rdquo;, il offre principalement trois modes :&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Externe (External)&lt;/strong> : Lie la carte réseau physique de la machine hôte au commutateur virtuel, permettant aux VM invitées de participer directement au réseau physique (connexion par pont). La VM obtient une IP du même sous-réseau que le réseau physique depuis un serveur DHCP.&lt;/li>
&lt;li>&lt;strong>Interne (Internal)&lt;/strong> : N&amp;rsquo;autorise que les communications entre l&amp;rsquo;OS hôte et les VM, et entre les VM elles-mêmes. Elles ne peuvent pas accéder directement au réseau externe.&lt;/li>
&lt;li>&lt;strong>Privé (Private)&lt;/strong> : N&amp;rsquo;autorise que les communications entre les VM, bloquant même la communication avec l&amp;rsquo;OS hôte. Utilisé pour créer des environnements de test isolés.&lt;/li>
&lt;/ol>
&lt;h3 id="43-construction-de-réseaux-hyper-v-avancés-avec-powershell">4.3. Construction de réseaux Hyper-V avancés avec PowerShell
&lt;/h3>&lt;p>Dans les environnements de développement et de test, si vous souhaitez construire un réseau NAT personnalisé pour les VM, PowerShell permet un contrôle détaillé. Voici un exemple de script pour créer un commutateur virtuel interne, y configurer le NAT, et fournir un accès à internet aux VM.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># 1. Création d&amp;#39;un commutateur virtuel interne&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$SwitchName&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;HyperV-NatSwitch&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">New-VMSwitch&lt;/span> &lt;span class="n">-SwitchName&lt;/span> &lt;span class="nv">$SwitchName&lt;/span> &lt;span class="n">-SwitchType&lt;/span> &lt;span class="n">Internal&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># 2. Configuration d&amp;#39;une adresse IP pour la carte réseau virtuelle côté hôte (IP servant de passerelle)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$GatewayIP&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;192.168.100.1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$NetPrefix&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="mf">24&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$InterfaceAlias&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;vEthernet (&lt;/span>&lt;span class="nv">$SwitchName&lt;/span>&lt;span class="s2">)&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">New-NetIPAddress&lt;/span> &lt;span class="n">-IPAddress&lt;/span> &lt;span class="nv">$GatewayIP&lt;/span> &lt;span class="n">-PrefixLength&lt;/span> &lt;span class="nv">$NetPrefix&lt;/span> &lt;span class="n">-InterfaceAlias&lt;/span> &lt;span class="nv">$InterfaceAlias&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># 3. Configuration du réseau NAT&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$NatName&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;HyperV-NatNetwork&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$NatSubnet&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;192.168.100.0/24&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">New-NetNat&lt;/span> &lt;span class="n">-Name&lt;/span> &lt;span class="nv">$NatName&lt;/span> &lt;span class="n">-InternalIPInterfaceAddressPrefix&lt;/span> &lt;span class="nv">$NatSubnet&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Commande de vérification&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">Get-NetNat&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Avec cette configuration, en attribuant manuellement une IP &lt;code>192.168.100.x&lt;/code> et la passerelle &lt;code>192.168.100.1&lt;/code> à l&amp;rsquo;invité Hyper-V spécifié, vous pouvez construire votre propre segment NAT capable de communiquer avec l&amp;rsquo;extérieur via l&amp;rsquo;hôte.&lt;/p>
&lt;hr>
&lt;h2 id="5-cas-dutilisation-et-guide-de-sélection-pratique">5. Cas d&amp;rsquo;utilisation et guide de sélection pratique
&lt;/h2>&lt;p>Sur la base des différences d&amp;rsquo;architecture et de performances discutées jusqu&amp;rsquo;à présent, nous définissons dans quelles situations chacune de ces technologies devrait être adoptée.&lt;/p>
&lt;h3 id="51-scénarios-où-wsl2-doit-être-sélectionné">5.1. Scénarios où WSL2 doit être sélectionné
&lt;/h3>&lt;p>WSL2 a été conçu spécifiquement pour &amp;ldquo;l&amp;rsquo;amélioration de la productivité des développeurs&amp;rdquo;. Il est idéal pour les utilisations suivantes :&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Développement web et cloud-native&lt;/strong> : Développement de conteneurs utilisant Docker Desktop (backend WSL2) ou Podman.&lt;/li>
&lt;li>&lt;strong>Utilisation d&amp;rsquo;outils exclusifs à Linux&lt;/strong> : Utilisation quotidienne de bash, grep, awk, sed ou des compilateurs GCC et Clang pour Linux.&lt;/li>
&lt;li>&lt;strong>Applications GUI (WSLg)&lt;/strong> : Lorsque vous souhaitez exécuter de manière transparente des applications X11/Wayland de Linux sur le bureau Windows.&lt;/li>
&lt;li>&lt;strong>Apprentissage automatique et développement d&amp;rsquo;IA&lt;/strong> : Apprentissage rapide de TensorFlow ou PyTorch utilisant la fonction de passthrough GPU (NVIDIA CUDA sur WSL).&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Attention&lt;/strong> : Vous pouvez rencontrer des restrictions si vous souhaitez personnaliser finement le noyau ou construire des services complexes qui dépendent fortement de systemd (actuellement, systemd est pris en charge mais désactivé par défaut ou restreint).&lt;/p>
&lt;h3 id="52-scénarios-où-hyper-v-doit-être-sélectionné">5.2. Scénarios où Hyper-V doit être sélectionné
&lt;/h3>&lt;p>Hyper-V a pour but &amp;ldquo;la virtualisation de l&amp;rsquo;infrastructure et une isolation complète&amp;rdquo;. Il est essentiel pour les utilisations suivantes :&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Exécution de VM Windows&lt;/strong> : Lors de l&amp;rsquo;exécution de différentes versions de Windows (Windows Server, anciens Windows 10, etc.) comme environnements de test.&lt;/li>
&lt;li>&lt;strong>Virtualisation imbriquée (Nested Virtualization)&lt;/strong> : Lorsque vous souhaitez exécuter une machine virtuelle (Hyper-V ou KVM) à l&amp;rsquo;intérieur d&amp;rsquo;une autre machine virtuelle. Indispensable pour les environnements de test des ingénieurs d&amp;rsquo;infrastructure.&lt;/li>
&lt;li>&lt;strong>Besoins réseau avancés&lt;/strong> : Lorsqu&amp;rsquo;il est nécessaire de contrôler strictement la configuration du réseau, comme pour les connexions par pont externes (participation au même LAN), le marquage VLAN ou l&amp;rsquo;attribution de plusieurs NIC.&lt;/li>
&lt;li>&lt;strong>Instantanés (Checkpoints)&lt;/strong> : Fonctionnalité permettant de sauvegarder l&amp;rsquo;état d&amp;rsquo;une VM à un moment donné et de revenir en arrière instantanément à tout moment. Extrêmement utile pour les tests destructifs de logiciels ou l&amp;rsquo;analyse de logiciels malveillants.&lt;/li>
&lt;li>&lt;strong>Allocation fixe des ressources&lt;/strong> : Lorsque vous souhaitez fixer strictement le nombre de cœurs CPU et la quantité de mémoire pour minimiser l&amp;rsquo;impact sur l&amp;rsquo;OS hôte.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="6-considérations-sur-le-débit-des-avec-un-modèle-mathématique-annexe">6. Considérations sur le débit d&amp;rsquo;E/S avec un modèle mathématique (Annexe)
&lt;/h2>&lt;p>En tant qu&amp;rsquo;ingénieur système, lorsqu&amp;rsquo;il s&amp;rsquo;agit d&amp;rsquo;identifier les limites des performances d&amp;rsquo;E/S des deux, il est important de comprendre théoriquement la relation entre le débit $S$ et la taille de bloc $B$.&lt;/p>
&lt;p>Le débit de transfert de données $S$ est la quantité de données transférées par unité de temps, modélisée comme suit :&lt;/p>
$$ S(B) = \frac{B}{L_{setup} + \frac{B}{R_{max}}} $$&lt;ul>
&lt;li>$B$ : Taille de bloc (Octets)&lt;/li>
&lt;li>$L_{setup}$ : Latence fixe due à la configuration de la requête d&amp;rsquo;E/S et au changement de contexte&lt;/li>
&lt;li>$R_{max}$ : Bande passante maximale du matériel lors de la copie ou du transfert d&amp;rsquo;appareils&lt;/li>
&lt;/ul>
&lt;p>Pour l&amp;rsquo;accès aux fichiers de WSL2 via le protocole 9P, ce $L_{setup}$ devient très important (en raison des communications via socket et de la sérialisation/désérialisation du protocole). Par conséquent, lorsque la taille de bloc $B$ est petite (lecture et écriture massives de petits fichiers de quelques Ko), l&amp;rsquo;impact de $L_{setup}$ dans le dénominateur devient dominant, et le débit $S$ chute considérablement.
À l&amp;rsquo;inverse, pour l&amp;rsquo;accès aux VHDX via le VMBus d&amp;rsquo;Hyper-V, $L_{setup}$ est optimisé à un niveau proche de l&amp;rsquo;interruption matérielle, de sorte qu&amp;rsquo;il peut maintenir des IOPS élevés même pour des petits blocs.&lt;/p>
&lt;p>Cette réalité mathématique est le fondement logique de la bonne pratique selon laquelle &amp;ldquo;il ne faut pas placer les fichiers de projet du côté Windows avec WSL2&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h2 id="7-conclusion--deux-technologies-de-virtualisation-qui-coexistent">7. Conclusion : Deux technologies de virtualisation qui coexistent
&lt;/h2>&lt;p>Ce n&amp;rsquo;est pas que l&amp;rsquo;un entre Hyper-V et WSL2 soit meilleur que l&amp;rsquo;autre, ce sont &lt;strong>&amp;ldquo;deux solutions avec des objectifs différents&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>WSL2&lt;/strong> est le &amp;ldquo;meilleur outil d&amp;rsquo;intégration&amp;rdquo; pour briser la coquille de l&amp;rsquo;OS Windows et apporter l&amp;rsquo;écosystème Linux de manière transparente et rapide aux utilisateurs de Windows. Il n&amp;rsquo;est pas exagéré de dire que c&amp;rsquo;est l&amp;rsquo;environnement CLI ultime pour les développeurs.&lt;/li>
&lt;li>&lt;strong>Hyper-V&lt;/strong> est un &amp;ldquo;hyperviseur authentique&amp;rdquo; qui apporte au bureau la forte isolation et les capacités de gestion cultivées dans les centres de données d&amp;rsquo;entreprise. Il n&amp;rsquo;y a pas mieux pour la construction de réseaux, les tests du système d&amp;rsquo;exploitation Windows et la simulation d&amp;rsquo;environnements d&amp;rsquo;infrastructure.&lt;/li>
&lt;/ul>
&lt;p>Dans les environnements Windows modernes, ces deux technologies ne sont pas en concurrence directe, mais coexistent magnifiquement sur la même plateforme de VM. En les utilisant de manière appropriée en fonction de l&amp;rsquo;usage, Windows deviendra la station de travail d&amp;rsquo;ingénierie la plus puissante et la plus flexible au monde.&lt;/p></description></item><item><title>Étapes pour construire un environnement de développement local reproductible avec Docker</title><link>http://kenji.blog/fr/p/docker-reproducible-local-dev-environment/</link><pubDate>Sun, 13 Sep 2026 01:00:00 +0900</pubDate><guid>http://kenji.blog/fr/p/docker-reproducible-local-dev-environment/</guid><description>&lt;img src="http://kenji.blog/p/docker-reproducible-local-dev-environment/img/eyecatch.jpg" alt="Featured image of post Étapes pour construire un environnement de développement local reproductible avec Docker" />&lt;h2 id="1-introduction--sortir-du--ça-marche-sur-ma-machine-">1. Introduction : Sortir du « Ça marche sur ma machine »
&lt;/h2>&lt;p>Dans le développement logiciel, le problème du « Ça marche sur ma machine » (It works on my machine), dû aux différences d&amp;rsquo;environnements entre les développeurs, est depuis longtemps un facteur de perte de temps dans de nombreux projets. En raison des différences de systèmes d&amp;rsquo;exploitation, de versions de langages installées, des dépendances de bibliothèques, ou des conflits d&amp;rsquo;outils installés globalement, l&amp;rsquo;environnement local est toujours exposé à une « incertitude d&amp;rsquo;état ».&lt;/p>
&lt;p>Ce qui résout fondamentalement ces défis, c&amp;rsquo;est la technologie des conteneurs, notamment &lt;strong>Docker&lt;/strong>, et le paradigme d&amp;rsquo;&lt;strong>Infrastructure as Code (IaC)&lt;/strong>. Conteneuriser l&amp;rsquo;environnement de développement local permet une isolation au niveau du système d&amp;rsquo;exploitation et de versionner l&amp;rsquo;environnement lui-même avec la base de code.&lt;/p>
&lt;p>Cet article explique en détail, avec des perspectives mathématiques, la méthode pour construire &lt;strong>« un environnement de développement local reproductible qui reste exactement le même peu importe qui, quand, et sur quelle machine il est lancé »&lt;/strong>, en tirant parti de Docker, Docker Compose, et VSCode DevContainers.&lt;/p>
&lt;hr>
&lt;h2 id="2-affinité-entre-linfrastructure-as-code-iac-et-la-technologie-des-conteneurs">2. Affinité entre l&amp;rsquo;Infrastructure as Code (IaC) et la technologie des conteneurs
&lt;/h2>&lt;h3 id="principes-de-liac-et-son-application-à-lenvironnement-local">Principes de l&amp;rsquo;IaC et son application à l&amp;rsquo;environnement local
&lt;/h3>&lt;p>L&amp;rsquo;Infrastructure as Code (IaC) est une approche qui gère la configuration et le provisionnement de l&amp;rsquo;infrastructure via des fichiers de définition lisibles par machine, plutôt que par un processus manuel. Les principes fondamentaux de l&amp;rsquo;IaC incluent :&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Approche déclarative (Declarative Approach)&lt;/strong> : On définit « quel devrait être l&amp;rsquo;état final » plutôt que « comment changer l&amp;rsquo;état ».&lt;/li>
&lt;li>&lt;strong>Idempotence (Idempotency)&lt;/strong> : Quel que soit le nombre d&amp;rsquo;exécutions du script, le même résultat (état) est toujours garanti.&lt;/li>
&lt;li>&lt;strong>Contrôle de version (Version Control)&lt;/strong> : L&amp;rsquo;état de l&amp;rsquo;infrastructure est stocké sous forme de code dans des systèmes comme Git, ce qui permet le suivi de l&amp;rsquo;historique et la revue par les pairs.&lt;/li>
&lt;/ol>
&lt;p>Pratiquer l&amp;rsquo;IaC dans un environnement de développement local signifie coder « l&amp;rsquo;état idéal » de l&amp;rsquo;environnement de développement à l&amp;rsquo;aide de &lt;code>Dockerfile&lt;/code>, &lt;code>docker-compose.yml&lt;/code>, et &lt;code>devcontainer.json&lt;/code>. Cela permet aux nouveaux membres de l&amp;rsquo;équipe de commencer immédiatement le développement en clonant simplement le dépôt et en exécutant une seule commande, offrant ainsi une excellente expérience d&amp;rsquo;intégration (onboarding).&lt;/p>
&lt;h3 id="fonctionnalités-du-noyau-derrière-la-technologie-des-conteneurs">Fonctionnalités du noyau derrière la technologie des conteneurs
&lt;/h3>&lt;p>Contrairement à la virtualisation par hyperviseur comme les machines virtuelles (VM), la technologie des conteneurs est une technologie de virtualisation légère qui isole les processus tout en partageant le noyau du système d&amp;rsquo;exploitation hôte. Pour y parvenir, les fonctionnalités suivantes du noyau Linux sont principalement utilisées :&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Namespaces&lt;/strong> : Fournissent une vue isolée des ressources système (PID, réseau, points de montage, utilisateurs, etc.) pour chaque processus.&lt;/li>
&lt;li>&lt;strong>Cgroups (Control Groups)&lt;/strong> : Limitent et allouent les ressources physiques (CPU, mémoire, E/S disque, etc.) qu&amp;rsquo;un processus peut utiliser.&lt;/li>
&lt;li>&lt;strong>UnionFS (Union File System)&lt;/strong> : Technologie permettant de superposer de manière transparente plusieurs arborescences de répertoires (couches) et de les présenter comme un système de fichiers unique. Les couches d&amp;rsquo;images Docker reposent sur cette technologie.&lt;/li>
&lt;/ul>
&lt;p>Considérons un modèle mathématique de limitation de ressources. Soit $M_{\text{total}}$ la capacité de mémoire totale de la machine hôte et $m_i$ la limite de mémoire pour $n$ conteneurs s&amp;rsquo;exécutant sur l&amp;rsquo;hôte. En tenant compte de la mémoire de base $M_{\text{os}}$ consommée par l&amp;rsquo;OS hôte et d&amp;rsquo;autres processus, la condition nécessaire au fonctionnement stable du système peut être exprimée par l&amp;rsquo;inégalité suivante :&lt;/p>
$$ \sum_{i=1}^{n} m_i \le M_{\text{total}} - M_{\text{os}} $$&lt;p>En définissant strictement $m_i$ pour chaque conteneur à l&amp;rsquo;aide de Cgroups, même si un conteneur spécifique présente une fuite de mémoire, l&amp;rsquo;OOM (Out Of Memory) Killer empêchera les autres conteneurs et l&amp;rsquo;ensemble du système hôte de tomber en panne.&lt;/p>
&lt;hr>
&lt;h2 id="3-conception-efficace-de-dockerfile--maîtriser-les-builds-multi-étapes">3. Conception efficace de Dockerfile : Maîtriser les builds multi-étapes
&lt;/h2>&lt;p>La première étape vers un environnement reproductible est la conception du &lt;code>Dockerfile&lt;/code>, qui définit l&amp;rsquo;environnement d&amp;rsquo;exécution de l&amp;rsquo;application. Ici, en utilisant Python (FastAPI) comme exemple, nous expliquons les meilleures pratiques pour des Dockerfiles sécurisés et légers utilisant des &lt;strong>builds multi-étapes&lt;/strong> (multi-stage builds).&lt;/p>
&lt;p>Le build multi-étapes est une technique qui utilise plusieurs instructions &lt;code>FROM&lt;/code> dans un seul &lt;code>Dockerfile&lt;/code> pour séparer l&amp;rsquo;environnement de construction (un environnement lourd avec compilateurs et outils de développement) de l&amp;rsquo;environnement d&amp;rsquo;exécution (un environnement léger avec seulement les artefacts nécessaires).&lt;/p>
&lt;h3 id="dockerfile-pratique-pour-python-fastapi">Dockerfile pratique pour Python FastAPI
&lt;/h3>&lt;p>Le code suivant est un exemple de &lt;code>Dockerfile&lt;/code> avancé combinant la gestion des dépendances avec Poetry et les builds multi-étapes.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-dockerfile" data-lang="dockerfile">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># ---------------------------------------------------------&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Stage 1: Builder (Environnement de construction)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># ---------------------------------------------------------&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="s"> python:3.11-slim AS builder&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Configuration des variables d&amp;#39;environnement nécessaires&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">ENV&lt;/span> &lt;span class="nv">PYTHONUNBUFFERED&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="nv">PYTHONDONTWRITEBYTECODE&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="nv">POETRY_VERSION&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span>.6.1 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="nv">POETRY_HOME&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;/opt/poetry&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="nv">POETRY_VIRTUALENVS_IN_PROJECT&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nb">true&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="nv">POETRY_NO_INTERACTION&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Installation des paquets de dépendances&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> apt-get update &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> apt-get install -y --no-install-recommends &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> curl build-essential &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> curl -sSL https://install.python-poetry.org &lt;span class="p">|&lt;/span> python3 - &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> apt-get clean &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> rm -rf /var/lib/apt/lists/*&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">ENV&lt;/span> &lt;span class="nv">PATH&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$POETRY_HOME&lt;/span>&lt;span class="s2">/bin:&lt;/span>&lt;span class="nv">$PATH&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">WORKDIR&lt;/span>&lt;span class="s"> /app&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Copie des fichiers de dépendances et installation&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> pyproject.toml poetry.lock ./&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> poetry install --no-root --only main&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># ---------------------------------------------------------&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Stage 2: Runtime (Environnement d&amp;#39;exécution)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># ---------------------------------------------------------&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="s"> python:3.11-slim AS runtime&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">ENV&lt;/span> &lt;span class="nv">PYTHONUNBUFFERED&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="nv">PYTHONDONTWRITEBYTECODE&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="nv">PATH&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;/app/.venv/bin:&lt;/span>&lt;span class="nv">$PATH&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Création d&amp;#39;un utilisateur non privilégié minimal&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> groupadd -r appuser &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> useradd -r -g appuser appuser&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">WORKDIR&lt;/span>&lt;span class="s"> /app&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Copier uniquement l&amp;#39;environnement virtuel (dépendances) depuis le constructeur&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> --from&lt;span class="o">=&lt;/span>builder --chown&lt;span class="o">=&lt;/span>appuser:appuser /app/.venv /app/.venv&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Copie du code de l&amp;#39;application&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> --chown&lt;span class="o">=&lt;/span>appuser:appuser ./src /app/src&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Passer à l&amp;#39;utilisateur non privilégié&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">USER&lt;/span>&lt;span class="s"> appuser&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Commande par défaut au lancement du conteneur&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">ENTRYPOINT&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;uvicorn&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;src.main:app&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;--host&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;0.0.0.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;--port&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;8000&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="évaluation-mathématique-de-la-taille-de-limage-grâce-au-build-multi-étapes">Évaluation mathématique de la taille de l&amp;rsquo;image grâce au build multi-étapes
&lt;/h3>&lt;p>Soit $S_{\text{single}}$ la taille de l&amp;rsquo;image avec un build à une étape, et $S_{\text{multi}}$ la taille avec un build multi-étapes. Le taux de réduction de taille $R$ est calculé comme suit :&lt;/p>
$$ R = \left( 1 - \frac{S_{\text{multi}}}{S_{\text{single}}} \right) \times 100 \ (\%) $$&lt;p>Par exemple, supposons que $S_{\text{single}}$ comprenne l&amp;rsquo;image de base de l&amp;rsquo;OS (environ 110 Mo), les paquets de développement (gcc etc., environ 150 Mo), Poetry lui-même (environ 40 Mo), les bibliothèques de dépendances du projet (environ 80 Mo) et le code source (environ 5 Mo), pour un total de 385 Mo.
D&amp;rsquo;autre part, $S_{\text{multi}}$ ne copie que les bibliothèques (80 Mo) et le code source (5 Mo) dans l&amp;rsquo;image de base (110 Mo), ce qui donne un total de 195 Mo.&lt;/p>
$$ R = \left( 1 - \frac{195}{385} \right) \times 100 \approx 49.35\% $$&lt;p>Ainsi, en introduisant le build multi-étapes, la taille de l&amp;rsquo;image peut être réduite d&amp;rsquo;environ la moitié. La réduction de la taille de l&amp;rsquo;image est directement liée à des temps de téléchargement (pull) plus courts depuis le registre, des économies d&amp;rsquo;espace disque, et une sécurité améliorée grâce à la réduction de la surface d&amp;rsquo;attaque (Attack Surface).&lt;/p>
&lt;hr>
&lt;h2 id="4-orchestration-de-plusieurs-conteneurs-avec-docker-compose">4. Orchestration de plusieurs conteneurs avec Docker Compose
&lt;/h2>&lt;p>Dans le développement d&amp;rsquo;applications Web modernes, il est courant d&amp;rsquo;avoir une architecture de microservices où plusieurs composants tels qu&amp;rsquo;un serveur Web, une base de données, et un serveur de cache interagissent. Pour gérer cela de manière centralisée dans un environnement local, nous utilisons &lt;code>docker-compose.yml&lt;/code>.&lt;/p>
&lt;p>Cette fois, nous allons construire localement un système à 3 couches : « Web (FastAPI) », « Database (PostgreSQL) », et « Cache (Redis) ».&lt;/p>
&lt;h3 id="diagramme-darchitecture-mermaid">Diagramme d&amp;rsquo;architecture (Mermaid)
&lt;/h3>&lt;p>Le diagramme suivant illustre la relation entre les conteneurs, le réseau, et les volumes sur la machine locale.&lt;/p>
&lt;pre class="mermaid">
graph TD
User[&amp;#34;Machine Hôte (Navigateur/curl)&amp;#34;] --&amp;gt;|Localhost:8000| Web[&amp;#34;Conteneur Web FastAPI&amp;#34;]
subgraph &amp;#34;Réseau Bridge Docker (app-network)&amp;#34;
Web --&amp;gt;|Port 5432| DB[&amp;#34;Conteneur PostgreSQL&amp;#34;]
Web --&amp;gt;|Port 6379| Redis[&amp;#34;Conteneur Redis&amp;#34;]
end
DB --&amp;gt; Volume1[&amp;#34;Volume Nommé (postgres_data)&amp;#34;]
Redis --&amp;gt; Volume2[&amp;#34;Volume Nommé (redis_data)&amp;#34;]
HostDir[&amp;#34;Code Source Hôte (./src)&amp;#34;] -.-&amp;gt;|Bind Mount| Web
&lt;/pre>
&lt;h3 id="implémentation-de-docker-composeyml-et-explications-détaillées">Implémentation de docker-compose.yml et explications détaillées
&lt;/h3>&lt;p>Voici un exemple de &lt;code>docker-compose.yml&lt;/code> robuste adapté à un environnement pratique.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;3.8&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">services&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">web&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">build&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">context&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">target&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">runtime&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">container_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">dev_web&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;8000:8000&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">./src:/app/src:ro &lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># Monter le code de l&amp;#39;hôte en lecture seule (pour le rechargement à chaud)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">REDIS_URL=redis://redis:6379/0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">env_file&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">.env&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">depends_on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">db&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">condition&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">service_healthy&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">redis&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">condition&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">service_started&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">networks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">app-network&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">command&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;uvicorn&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;src.main:app&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;--host&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;0.0.0.0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;--port&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;8000&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;--reload&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">db&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">postgres:15-alpine&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">container_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">dev_db&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;5432:5432&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">POSTGRES_USER&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">postgres&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">POSTGRES_PASSWORD&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${POSTGRES_PASSWORD}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">POSTGRES_DB&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${POSTGRES_DB}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">postgres_data:/var/lib/postgresql/data&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">networks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">app-network&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">healthcheck&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">test&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;CMD-SHELL&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;pg_isready -U postgres -d ${POSTGRES_DB}&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">interval&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">5s&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">timeout&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">5s&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">retries&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">5&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">redis&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">redis:7-alpine&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">container_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">dev_redis&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;6379:6379&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">redis_data:/data&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">networks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">app-network&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">command&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;redis-server&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;--appendonly&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;yes&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">postgres_data&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">redis_data&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">networks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">app-network&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">driver&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">bridge&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="volumes-et-persistance-des-données">Volumes et persistance des données
&lt;/h3>&lt;p>En principe, les conteneurs sont « stateless » (sans état) et « éphémères » (à courte durée de vie). Lorsqu&amp;rsquo;un conteneur est détruit, ses données internes sont également perdues. Pour conserver les données de base de données ou le cache, un espace système de fichiers de la machine hôte doit être monté dans le conteneur.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Bind Mount (Montage lié)&lt;/strong> : Dans le service &lt;code>web&lt;/code> ci-dessus, &lt;code>./src:/app/src:ro&lt;/code> y correspond. Il mappe directement un répertoire spécifique de l&amp;rsquo;hôte à l&amp;rsquo;intérieur du conteneur. Il est utilisé pour refléter immédiatement les modifications de code locales dans le conteneur (rechargement à chaud). Pour des raisons de sécurité, la meilleure pratique est d&amp;rsquo;ajouter l&amp;rsquo;option &lt;code>:ro&lt;/code> (Read-Only) afin que le conteneur ne puisse pas modifier le code source de l&amp;rsquo;hôte.&lt;/li>
&lt;li>&lt;strong>Named Volume (Volume nommé)&lt;/strong> : C&amp;rsquo;est le cas de &lt;code>postgres_data&lt;/code> ou &lt;code>redis_data&lt;/code>. C&amp;rsquo;est une zone gérée en interne par Docker (comme &lt;code>/var/lib/docker/volumes/&lt;/code>), qui a de meilleures performances d&amp;rsquo;E/S qu&amp;rsquo;un bind mount et qui absorbe les différences de systèmes de fichiers entre les OS. Il faut toujours l&amp;rsquo;utiliser pour la persistance de base de données.&lt;/li>
&lt;/ul>
&lt;h3 id="réseau-networking-et-découverte-de-services">Réseau (Networking) et découverte de services
&lt;/h3>&lt;p>Docker Compose crée par défaut un réseau pont (bridge) distinct pour chaque projet. Il s&amp;rsquo;agit du &lt;code>app-network&lt;/code> ci-dessus.
Les conteneurs appartenant au même réseau peuvent se résoudre (résolution DNS) via leur « nom de service » (par exemple : &lt;code>db&lt;/code>, &lt;code>redis&lt;/code>) plutôt que par leur adresse IP.
Par exemple, le conteneur Web peut accéder à la base de données via l&amp;rsquo;URL &lt;code>postgresql://postgres:password@db:5432/mydb&lt;/code>. Cela permet de changer de manière transparente de cible de connexion via des variables d&amp;rsquo;environnement entre les environnements locaux, de pré-production, et de production sans modifier le code de l&amp;rsquo;infrastructure.&lt;/p>
&lt;h3 id="bilan-de-santé-healthcheck-et-ordre-de-démarrage">Bilan de santé (Healthcheck) et ordre de démarrage
&lt;/h3>&lt;p>La directive &lt;code>depends_on&lt;/code> contrôle l&amp;rsquo;ordre de démarrage des conteneurs, mais si l&amp;rsquo;on spécifie simplement &lt;code>depends_on&lt;/code>, le conteneur Web démarrera dès que le conteneur DB sera « lancé ». En réalité, le processus d&amp;rsquo;initialisation de la DB (démarrage du processus PostgreSQL, préparation des tables) prend quelques secondes, et une connexion depuis le conteneur Web pendant ce temps résultera en une erreur.
Pour éviter cela, nous définissons un &lt;code>healthcheck&lt;/code> et spécifions &lt;code>condition: service_healthy&lt;/code>, ce qui permet de s&amp;rsquo;assurer que la « DB est prête à accepter les requêtes de connexion » avant de démarrer le conteneur Web.&lt;/p>
&lt;hr>
&lt;h2 id="5-gestion-des-variables-denvironnement-et-sécurité-env">5. Gestion des variables d&amp;rsquo;environnement et sécurité (.env)
&lt;/h2>&lt;p>C&amp;rsquo;est un anti-pattern à éviter absolument que de coder en dur des informations sensibles telles que les mots de passe de bases de données ou les clés d&amp;rsquo;API dans &lt;code>docker-compose.yml&lt;/code>. À la place, utilisez un fichier de variables d&amp;rsquo;environnement &lt;code>.env&lt;/code> pour injecter ces valeurs.&lt;/p>
&lt;p>Créez un fichier &lt;code>.env&lt;/code> à la racine du projet.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Fichier .env (Assurez-vous de l&amp;#39;ajouter à .gitignore pour l&amp;#39;exclure de Git)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">POSTGRES_PASSWORD&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">supersecretpassword&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">POSTGRES_DB&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">devdb&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">API_SECRET_KEY&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">dev_secret_key_12345&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Docker Compose charge par défaut le fichier &lt;code>.env&lt;/code> dans le répertoire d&amp;rsquo;exécution et développe les espaces réservés &lt;code>${VAR_NAME}&lt;/code> dans les fichiers YAML. Cette méthode permet de gérer en toute sécurité différentes valeurs de configuration pour des environnements tels que le local, la pré-production, et la production, sans modifier le code de l&amp;rsquo;infrastructure.&lt;/p>
&lt;hr>
&lt;h2 id="6-lexpérience-de-développement-ultime-avec-vscode-devcontainers">6. L&amp;rsquo;expérience de développement ultime avec VSCode DevContainers
&lt;/h2>&lt;p>À ce stade, nous avons construit un environnement backend robuste à l&amp;rsquo;aide de Docker. Cependant, nous pouvons aller encore plus loin. En utilisant la fonctionnalité &lt;strong>VSCode DevContainers (Remote - Containers)&lt;/strong>, il est possible d&amp;rsquo;exécuter le backend de l&amp;rsquo;éditeur (VSCode) lui-même à l&amp;rsquo;intérieur du conteneur.&lt;/p>
&lt;p>Cela élimine le besoin d&amp;rsquo;installer Python ou Node.js sur la machine locale. Les linters (flake8/eslint), les formateurs (black/prettier) et même les extensions de l&amp;rsquo;IDE, tout peut être défini dans la base de code et partagé avec tous les membres de l&amp;rsquo;équipe.&lt;/p>
&lt;h3 id="configuration-de-devcontainerjson">Configuration de devcontainer.json
&lt;/h3>&lt;p>Créez un répertoire &lt;code>.devcontainer&lt;/code> à la racine du projet, et placez-y le fichier de configuration.&lt;/p>
&lt;p>&lt;code>.devcontainer/devcontainer.json&lt;/code> :&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Python FastAPI Dev Environment&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;dockerComposeFile&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;../docker-compose.yml&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;service&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;web&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;workspaceFolder&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;/app&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;customizations&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;vscode&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;settings&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;python.defaultInterpreterPath&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;/app/.venv/bin/python&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;python.formatting.provider&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;black&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;editor.formatOnSave&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;extensions&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;ms-python.python&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;ms-python.vscode-pylance&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;ms-python.black-formatter&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;tamasfe.even-better-toml&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;forwardPorts&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">8000&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">5432&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">6379&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;remoteUser&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;appuser&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;postCreateCommand&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;poetry install&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>En incluant ce fichier dans le dépôt, dès l&amp;rsquo;ouverture du projet dans VSCode, une invite « Reopen in Container » (Rouvrir dans le conteneur) apparaît. D&amp;rsquo;un simple clic, tous les conteneurs nécessaires démarrent, les extensions sont installées, et vous êtes immédiatement prêt à coder. C&amp;rsquo;est une expérience véritablement magique.&lt;/p>
&lt;hr>
&lt;h2 id="7-séquence-de-traitement-des-requêtes-et-modélisation-des-performances">7. Séquence de traitement des requêtes et modélisation des performances
&lt;/h2>&lt;p>Examinons le cycle de vie du traitement d&amp;rsquo;une requête d&amp;rsquo;application Web dans l&amp;rsquo;environnement de développement local à l&amp;rsquo;aide d&amp;rsquo;un diagramme de séquence, puis analysons le modèle mathématique de ses performances.&lt;/p>
&lt;h3 id="diagramme-de-séquence-flux-de-requête">Diagramme de séquence (Flux de requête)
&lt;/h3>&lt;pre class="mermaid">
sequenceDiagram
participant Client as &amp;#34;Navigateur / VSCode&amp;#34;
participant Web as &amp;#34;FastAPI (Web)&amp;#34;
participant Redis as &amp;#34;Cache Redis&amp;#34;
participant DB as &amp;#34;PostgreSQL&amp;#34;
Client-&amp;gt;&amp;gt;Web: &amp;#34;GET /api/users/123&amp;#34;
activate Web
Web-&amp;gt;&amp;gt;Redis: &amp;#34;Vérifier le cache pour user:123&amp;#34;
activate Redis
alt &amp;#34;Succès du cache (Données présentes)&amp;#34;
Redis--&amp;gt;&amp;gt;Web: &amp;#34;Retourner les données utilisateur en cache&amp;#34;
Web--&amp;gt;&amp;gt;Client: &amp;#34;200 OK (Réponse rapide)&amp;#34;
else &amp;#34;Échec du cache (Aucune donnée)&amp;#34;
Redis--&amp;gt;&amp;gt;Web: &amp;#34;Nul (Non trouvé)&amp;#34;
deactivate Redis
Web-&amp;gt;&amp;gt;DB: &amp;#34;SELECT * FROM users WHERE id = 123&amp;#34;
activate DB
DB--&amp;gt;&amp;gt;Web: &amp;#34;Retourner la ligne de la base de données&amp;#34;
deactivate DB
Web-&amp;gt;&amp;gt;Redis: &amp;#34;Définir les données user:123 (TTL: 60s)&amp;#34;
activate Redis
Redis--&amp;gt;&amp;gt;Web: &amp;#34;OK&amp;#34;
deactivate Redis
Web--&amp;gt;&amp;gt;Client: &amp;#34;200 OK (Réponse standard)&amp;#34;
end
deactivate Web
&lt;/pre>
&lt;h3 id="modèle-mathématique-de-la-latence-de-traitement">Modèle mathématique de la latence de traitement
&lt;/h3>&lt;p>Nous modélisons mathématiquement le temps de traitement moyen des requêtes $T_{\text{total}}$ dans le système ci-dessus.
La latence de chaque processus est définie comme suit :&lt;/p>
&lt;ul>
&lt;li>$T_{\text{net}}$ : Latence réseau entre le client et le conteneur Web&lt;/li>
&lt;li>$T_{\text{app}}$ : Temps de traitement pur côté application (sérialisation, etc.)&lt;/li>
&lt;li>$T_{\text{cache}}$ : Temps requis pour la lecture/écriture depuis Redis&lt;/li>
&lt;li>$T_{\text{db}}$ : Temps requis pour l&amp;rsquo;exécution des requêtes vers PostgreSQL&lt;/li>
&lt;li>$p_{\text{miss}}$ : Taux d&amp;rsquo;échec du cache ($0 \le p_{\text{miss}} \le 1$)&lt;/li>
&lt;/ul>
&lt;p>Le temps de réponse moyen est exprimé par l&amp;rsquo;équation d&amp;rsquo;espérance mathématique suivante :&lt;/p>
$$ T_{\text{total}} = T_{\text{net}} + T_{\text{app}} + T_{\text{cache}} + p_{\text{miss}} \times (T_{\text{db}} + T_{\text{cache\_write}}) $$&lt;p>Dans l&amp;rsquo;environnement de développement local (dans Docker), $T_{\text{net}}$ est proche de 0, mais ce qui requiert notre attention, ce sont &lt;strong>les performances d&amp;rsquo;E/S du bind mount&lt;/strong>. En particulier, lors de l&amp;rsquo;utilisation de Docker Desktop sous Windows/macOS, la surcharge de partage de fichiers entre l&amp;rsquo;OS hôte et la VM (conteneur) a tendance à gonfler $T_{\text{app}}$ (comme les temps de lecture du code). Pour résoudre ce goulot d&amp;rsquo;étranglement, il est fortement recommandé d&amp;rsquo;utiliser DevContainers pour placer l&amp;rsquo;intégralité du code source dans un volume nommé, ou d&amp;rsquo;adopter une architecture exécutant le moteur Docker de manière native dans l&amp;rsquo;environnement WSL2 (Windows Subsystem for Linux 2).&lt;/p>
&lt;hr>
&lt;h2 id="8-optimisation-des-performances-de-build-docker--stratégie-de-mise-en-cache-des-couches">8. Optimisation des performances de build Docker : Stratégie de mise en cache des couches
&lt;/h2>&lt;p>Lorsque vous rédigez un Dockerfile, comprendre le mécanisme de « cache de couche » (layer cache) peut considérablement modifier le temps de build.
Docker crée un delta du système de fichiers (une couche) pour chaque instruction du Dockerfile (&lt;code>FROM&lt;/code>, &lt;code>RUN&lt;/code>, &lt;code>COPY&lt;/code>, etc.) et la conserve comme cache. Lors d&amp;rsquo;une nouvelle construction, le cache des couches inchangées est réutilisé.&lt;/p>
&lt;p>Le principe clé est de &lt;strong>« décrire dans l&amp;rsquo;ordre de la fréquence de modification la plus faible »&lt;/strong>.&lt;/p>
&lt;p>Considérons la modélisation de l&amp;rsquo;impact des modifications du code source sur le temps de build. Soit $T_{\text{build}}$ le temps de build total, $T_{\text{layer}_i}$ le temps d&amp;rsquo;exécution de chaque étape, et $c_i \in \{0, 1\}$ la valeur booléenne indiquant si le cache a été utilisé (1 en cas de succès du cache).&lt;/p>
$$ T_{\text{build}} = T_{\text{init}} + \sum_{i=1}^{n} (1 - c_i) \times T_{\text{layer}_i} $$&lt;p>Une fois qu&amp;rsquo;un échec de cache survient à la couche $k$ ($c_k = 0$), le cache est invalidé pour toutes les couches suivantes $j > k$ ($c_j = 0$).&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-dockerfile" data-lang="dockerfile">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Mauvais exemple (Copie du code source en premier)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> ./src /app/src&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> pyproject.toml poetry.lock ./&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> poetry install&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Dans ce cas, modifier une seule ligne de code invalidera le cache dès le premier &lt;code>COPY&lt;/code>, ce qui entraînera l&amp;rsquo;exécution longue de &lt;code>RUN poetry install&lt;/code> à chaque fois.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-dockerfile" data-lang="dockerfile">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Bon exemple (Résolution des dépendances en premier)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> pyproject.toml poetry.lock ./&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> poetry install&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> ./src /app/src&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>En écrivant ainsi, même si le code source change, le cache de la couche &lt;code>poetry install&lt;/code> ($c_i = 1$) est effectif, réduisant considérablement le temps de build, passant de plusieurs minutes à quelques secondes.&lt;/p>
&lt;hr>
&lt;h2 id="9-dépannage-et-conseils">9. Dépannage et conseils
&lt;/h2>&lt;p>Voici des problèmes fréquents rencontrés lors de l&amp;rsquo;utilisation d&amp;rsquo;un environnement local et leurs solutions.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Erreur de conflit de port&lt;/strong>
Si vous obtenez une erreur telle que &lt;code>Bind for 0.0.0.0:8000 failed: port is already allocated&lt;/code>, c&amp;rsquo;est qu&amp;rsquo;un autre processus sur la machine locale utilise ce port. Vous pouvez éviter cela en changeant le numéro de port côté hôte, comme &lt;code>ports: - &amp;quot;8080:8000&amp;quot;&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Épuisement de l&amp;rsquo;espace disque&lt;/strong>
L&amp;rsquo;utilisation de Docker sur une longue période peut accumuler des images et volumes inutilisés (Dangling Images / Volumes), occupant parfois des dizaines de Go d&amp;rsquo;espace disque. Il est recommandé de nettoyer périodiquement le système avec la commande suivante :&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker system prune -a --volumes
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>Problème de permissions de fichiers&lt;/strong>
Lors de l&amp;rsquo;utilisation de bind mounts sous Linux, le propriétaire des fichiers créés dans le conteneur peut devenir &lt;code>root&lt;/code>, les rendant non modifiables côté hôte. Vous pouvez résoudre cela en créant un utilisateur non privilégié dans le Dockerfile dont l&amp;rsquo;UID/GID (par ex. 1000:1000) correspond à celui de votre système d&amp;rsquo;exploitation hôte.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="10-conclusion--lamélioration-de-la-vitesse-de-développement-grâce-à-la-reproductibilité">10. Conclusion : L&amp;rsquo;amélioration de la vitesse de développement grâce à la reproductibilité
&lt;/h2>&lt;p>La combinaison de Docker, Docker Compose et VSCode DevContainers permet de créer un environnement de développement local robuste, de sorte que « peu importe qui lance l&amp;rsquo;environnement, l&amp;rsquo;état sera exactement le même ».&lt;/p>
&lt;p>Apporter le paradigme de l&amp;rsquo;IaC à l&amp;rsquo;environnement local ne se limite pas à raccourcir le temps de configuration initial. Il élimine les craintes liées aux modifications de la configuration de l&amp;rsquo;infrastructure, facilite l&amp;rsquo;expérimentation de nouvelles piles technologiques et permet une transition en douceur vers les pipelines CI/CD, améliorant considérablement la vitesse et la qualité globales du cycle de développement.&lt;/p>
&lt;p>N&amp;rsquo;hésitez pas à introduire les meilleures pratiques abordées dans cet article, telles que l&amp;rsquo;optimisation de la taille de l&amp;rsquo;image via le build multi-étapes, le contrôle des dépendances avec les bilans de santé, et l&amp;rsquo;écriture de Dockerfile tenant compte du cache des couches, afin d&amp;rsquo;offrir la meilleure expérience de développement (DX: Developer Experience) à vos propres projets.&lt;/p></description></item></channel></rss>