<?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/de/categories/infrastructure/</link><description>Recent content in Infrastructure on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>de</language><copyright>kenjinote</copyright><lastBuildDate>Sun, 13 Sep 2026 07:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/de/categories/infrastructure/index.xml" rel="self" type="application/rss+xml"/><item><title>Hyper-V vs WSL2: Vergleich von Virtualisierungstechnologien auf Windows</title><link>http://kenji.blog/de/p/hyper-v-vs-wsl2-windows-virtualization/</link><pubDate>Sun, 13 Sep 2026 07:00:00 +0900</pubDate><guid>http://kenji.blog/de/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: Vergleich von Virtualisierungstechnologien auf Windows" />&lt;h2 id="1-einführung-die-evolution-der-virtualisierung-unter-windows">1. Einführung: Die Evolution der Virtualisierung unter Windows
&lt;/h2>&lt;p>Die Virtualisierungstechnologie auf der Windows-Plattform hat sich in den letzten Jahrzehnten dramatisch entwickelt. Früher waren Type-2-Hypervisoren von Drittanbietern (wie VMware Workstation und VirtualBox) der Mainstream, aber seit Microsoft &amp;ldquo;Hyper-V&amp;rdquo; in Windows Server 2008 eingeführt hat, sind Type-1-Hypervisoren auch in Desktop-Betriebssysteme wie Windows 10/11 integriert.&lt;/p>
&lt;p>In den letzten Jahren hat &amp;ldquo;WSL2 (Windows Subsystem for Linux 2)&amp;rdquo; bei Entwicklern die meiste Aufmerksamkeit erregt. Während WSL1 auf die Übersetzung von Systemaufrufen (Translation) angewiesen war, nutzt WSL2 eine &amp;ldquo;Lightweight Utility VM&amp;rdquo;, die Hyper-V-Technologie anwendet, um vollständige Linux-Kompatibilität und dramatische Leistungsverbesserungen zu erzielen.&lt;/p>
&lt;p>In diesem Artikel werden wir die Architektur, Leistung (CPU, Speicher, Festplatten-I/O), Netzwerkkonfiguration und optimale Anwendungsfälle dieser beiden leistungsstarken Virtualisierungstechnologien – der voll ausgestatteten &amp;ldquo;Hyper-V&amp;rdquo; und der auf Entwicklererfahrung fokussierten &amp;ldquo;WSL2&amp;rdquo; – mit tiefgreifenden technischen Details ausführlich vergleichen und erklären.&lt;/p>
&lt;hr>
&lt;h2 id="2-grundlegende-theorie-von-hypervisoren-und-architekturvergleich">2. Grundlegende Theorie von Hypervisoren und Architekturvergleich
&lt;/h2>&lt;p>Um die Virtualisierungstechnologie zu verstehen, ist die Typklassifizierung von Hypervisoren (Virtual Machine Monitors: VMM) unerlässlich.&lt;/p>
&lt;h3 id="21-unterschiede-zwischen-type-1--und-type-2-hypervisoren">2.1. Unterschiede zwischen Type-1- und Type-2-Hypervisoren
&lt;/h3>&lt;p>Ein Hypervisor ist eine Softwareschicht, die den Hardwarezugriff abstrahiert und es mehreren Betriebssystemen (Gastbetriebssystemen) ermöglicht, gleichzeitig auf einer einzigen physischen Maschine zu laufen.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Type 1 (Bare-Metal)&lt;/strong>: Läuft direkt auf der Hardware. Das Konzept eines Host-Betriebssystems existiert nicht (streng genommen kann es ein Management-Betriebssystem mit Privilegien geben), was einen extrem geringen Overhead aufweist und hohe Leistung sowie Sicherheit bietet. Beispiele: Hyper-V, VMware ESXi, Xen.&lt;/li>
&lt;li>&lt;strong>Type 2 (Hosted)&lt;/strong>: Läuft als Anwendung auf einem Host-Betriebssystem (wie Windows oder macOS). Alle Hardwarezugriffe laufen über das Host-Betriebssystem, was zu einem größeren Overhead führt. Beispiele: VMware Workstation, Oracle VirtualBox.&lt;/li>
&lt;/ul>
&lt;p>Windows Hyper-V ist ein reiner &lt;strong>Type-1-Hypervisor&lt;/strong>. Wenn Hyper-V aktiviert ist, läuft das Windows-Betriebssystem, mit dem der Benutzer normalerweise interagiert, tatsächlich innerhalb einer speziellen virtuellen Maschine, die als &amp;ldquo;Root-Partition (Root Partition)&amp;rdquo; bezeichnet wird.&lt;/p>
&lt;h3 id="22-architekturdetails-von-hyper-v">2.2. Architekturdetails von Hyper-V
&lt;/h3>&lt;p>Die Hyper-V-Architektur verwendet ein Microkernel-Design und basiert auf logischen Isolationseinheiten, die als Partitionen (Partitions) bezeichnet werden.&lt;/p>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;Hardware (CPU, RAM, Festplatte, NIC)&amp;#34;] --&amp;gt; B[&amp;#34;Windows Hypervisor (Ring -1)&amp;#34;]
B --&amp;gt; C[&amp;#34;Root-Partition (Windows OS)&amp;#34;]
B --&amp;gt; D[&amp;#34;Kind-Partition 1 (Windows VM)&amp;#34;]
B --&amp;gt; E[&amp;#34;Kind-Partition 2 (Linux VM)&amp;#34;]
C --&amp;gt; F[&amp;#34;VMBus (Virtual Machine Bus)&amp;#34;]
D --&amp;gt; F
E --&amp;gt; F
C --&amp;gt; G[&amp;#34;VID (Virtualization Infrastructure Driver)&amp;#34;]
C --&amp;gt; H[&amp;#34;VMWP.exe (Worker-Prozess)&amp;#34;]
&lt;/pre>
&lt;ul>
&lt;li>&lt;strong>Windows Hypervisor&lt;/strong>: Läuft im am höchsten privilegierten CPU-Status (Ring -1 oder VMX Root Mode) und ist nur für die Speicherzuweisung und CPU-Planung zuständig. Er enthält keine Gerätetreiber.&lt;/li>
&lt;li>&lt;strong>Root-Partition&lt;/strong>: Die Partition, in der das Host-Windows-Betriebssystem läuft. Sie verfügt über alle Gerätetreiber und steuert die Hardware direkt. Sie bietet auch Verwaltungsfunktionen für die Kind-Partitionen (wie WMI-Provider und VMWP.exe).&lt;/li>
&lt;li>&lt;strong>Kind-Partition (Child Partition)&lt;/strong>: Die Partition, in der das Gastbetriebssystem läuft. Ein direkter Zugriff auf die Hardware ist nicht gestattet; stattdessen sendet sie über einen logischen Memory-Sharing-Bus namens &amp;ldquo;VMBus&amp;rdquo; I/O-Anfragen (Synthetic I/O) an die Root-Partition.&lt;/li>
&lt;/ul>
&lt;h3 id="23-wie-wsl2-und-die-lightweight-utility-vm-funktionieren">2.3. Wie WSL2 und die Lightweight Utility VM funktionieren
&lt;/h3>&lt;p>WSL2 nutzt die gleiche zugrundeliegende Type-1-Hypervisor-Technologie wie Hyper-V, verwendet jedoch eine Unterfunktion namens &amp;ldquo;Virtual Machine Platform (VMP)&amp;rdquo;, die sich von einer voll funktionsfähigen Hyper-V-Maschine unterscheidet.&lt;/p>
&lt;p>Die in WSL2 verwendete &amp;ldquo;Lightweight Utility VM&amp;rdquo; eliminiert vollständig die Legacy-Hardware-Emulation (wie virtuelles BIOS oder virtuelles Motherboard), über die herkömmliche VMs verfügen.&lt;/p>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;Windows Host OS (User Space)&amp;#34;]
B[&amp;#34;NTFS-Dateisystem&amp;#34;]
C[&amp;#34;9P-Protokoll-Server (Plan 9)&amp;#34;]
D[&amp;#34;Lightweight Utility VM (Linux Kernel)&amp;#34;]
E[&amp;#34;ext4.vhdx (Virtuelle Festplatte)&amp;#34;]
F[&amp;#34;Linux User Space (WSL2-Distributionen)&amp;#34;]
A --&amp;gt; C
C --&amp;gt;| &amp;#34;Betriebssystemübergreifende Dateifreigabe&amp;#34; | D
D --&amp;gt; E
D --&amp;gt; F
&lt;/pre>
&lt;p>Die größten Merkmale von WSL2 sind seine &lt;strong>Startgeschwindigkeit&lt;/strong> und &lt;strong>nahtlose Integration mit dem Host-Betriebssystem&lt;/strong>. Der Linux-Kernel bootet in weniger als ein paar Sekunden und greift über das &lt;code>9P&lt;/code>-Netzwerkdateisystemprotokoll von Plan 9 auf das Dateisystem der Windows-Seite (NTFS) zu.&lt;/p>
&lt;hr>
&lt;h2 id="3-gründliche-leistungsanalyse-rechenressourcen-und-io">3. Gründliche Leistungsanalyse: Rechenressourcen und I/O
&lt;/h2>&lt;p>Die Leistung einer virtuellen Maschine drückt sich als die Summe des Overheads in CPU-, Speicher- und Festplatten-I/O-Komponenten aus.&lt;/p>
&lt;h3 id="31-cpu--und-kontextwechsel-overhead">3.1. CPU- und Kontextwechsel-Overhead
&lt;/h3>&lt;p>Sowohl Hyper-V als auch WSL2 verwenden hardwareunterstützte Virtualisierung (Intel VT-x / AMD-V). CPU-Befehle werden grundsätzlich mit nativer Geschwindigkeit ausgeführt, aber bei der Ausführung privilegierter Befehle oder der Verarbeitung von I/O tritt ein Interrupt namens &amp;ldquo;VM Exit&amp;rdquo; auf, und es erfolgt ein Kontextwechsel zum Hypervisor.&lt;/p>
&lt;p>Der CPU-Overhead $T_{overhead}$ zu dieser Zeit kann durch das folgende mathematische Modell ausgedrückt werden:&lt;/p>
$$ T_{overhead} = \sum_{i=1}^{N} (t_{vm\_exit} + t_{hypercall\_process} + t_{vm\_entry}) $$&lt;p>Wobei:&lt;/p>
&lt;ul>
&lt;li>$N$: Anzahl der VM Exits pro Zeiteinheit&lt;/li>
&lt;li>$t_{vm\_exit}$: Übergangszeit vom Gast zum Hypervisor&lt;/li>
&lt;li>$t_{hypercall\_process}$: Verarbeitungszeit für I/O oder Interrupts über den VMBus&lt;/li>
&lt;li>$t_{vm\_entry}$: Rückkehrzeit vom Hypervisor zum Gast&lt;/li>
&lt;/ul>
&lt;p>Da WSL2 keine Legacy-Emulation aufweist, ist $t_{hypercall\_process}$ extrem gering und optimiert. Daher bleibt bei reinen CPU-Berechnungen (wie z.B. Kernel-Kompilierung oder Inferenz von Machine-Learning-Modellen) die Leistungseinbuße im Vergleich zu einer Bare-Metal-Umgebung innerhalb weniger Prozent.&lt;/p>
&lt;h3 id="32-mechanismen-der-speicherzuweisung">3.2. Mechanismen der Speicherzuweisung
&lt;/h3>&lt;p>Es gibt einen klaren Unterschied in der Designphilosophie zwischen den beiden in Bezug auf Speicherverwaltungsmethoden.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Hyper-V (Dynamic Memory)&lt;/strong>: Die Root-Partition weist je nach Speicherbedarf der Gast-VM dynamisch Speicher zu und gibt ihn wieder frei. Allerdings wird Speicher, der als Page-Cache im Gast-Betriebssystem reserviert wurde, tendenziell nur dann freigegeben, wenn das System unter Druck steht.&lt;/li>
&lt;li>&lt;strong>WSL2 (Dynamische Speicherfreigabe)&lt;/strong>: WSL2 verfügt über einen eigenen Mechanismus zur regelmäßigen Rückgabe (Reclaim) von Speicher (einschließlich Cache), der innerhalb der Linux-VM nicht mehr benötigt wird, an den Windows-Host. In frühen Versionen von WSL2 gab es ein Problem, bei dem der Linux-Page-Cache den Windows-Speicher aufzehrte (Aufblähung des Vmmem-Prozesses), was jedoch mittlerweile durch Kernel-Patches behoben wurde.&lt;/li>
&lt;/ul>
&lt;h3 id="33-eigenschaften-von-festplatten-io-vhdx-vs-ext4vhdx">3.3. Eigenschaften von Festplatten-I/O (VHDX vs ext4.vhdx)
&lt;/h3>&lt;p>Festplatten-I/O ist der am häufigsten auftretende Flaschenhals in der Leistung virtueller Maschinen.&lt;/p>
&lt;p>Die I/O-Latenz $L_{total}$ wird wie folgt berechnet:&lt;/p>
$$ L_{total} = L_{guest\_fs} + L_{vmbus} + L_{host\_fs} + L_{physical\_disk} $$&lt;p>&lt;strong>Im Falle von Hyper-V&lt;/strong>:
Typische Hyper-V-Gäste verwenden virtuelle Festplatten im &lt;code>VHDX&lt;/code>-Format. I/O-Anfragen, die vom Dateisystem (ext4 oder NTFS) innerhalb des Gastbetriebssystems ausgegeben werden, durchlaufen den VMBus-Block-Device-Storage-Treiber (storvsc) und werden als Zugriff auf die VHDX-Datei auf dem NTFS-Dateisystem der Windows-Seite verarbeitet.&lt;/p>
&lt;p>&lt;strong>Im Falle von WSL2&lt;/strong>:
WSL2-Linux-Distributionen laufen auf einem nativen ext4-Dateisystem, das in einer dedizierten &lt;code>ext4.vhdx&lt;/code>-Datei erstellt wurde. Dateioperationen innerhalb von Linux (wie im &lt;code>~&lt;/code> Verzeichnis) bieten eine native Leistung, die der von Hyper-V oben entspricht.
Wenn Sie jedoch &lt;strong>von WSL2-Linux auf Dateien auf der Windows-Seite zugreifen (wie &lt;code>/mnt/c/&lt;/code>)&lt;/strong>, oder umgekehrt, ist der Prozess ganz anders. Dieses Cross-OS-Zugriffsverfahren nutzt das &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>Der Zugriff über dieses 9P-Protokoll weist einen hohen Overhead für Serialisierungsverarbeitung auf, und bei Anwendungen, die eine große Anzahl kleiner Dateien lesen und schreiben (z.B. &lt;code>npm install&lt;/code> oder Git-Operationen in einem Node.js-Projekt in einem Windows-Verzeichnis), verschlechtert sich die Leistung erheblich (manchmal mehr als die 10-fache Verzögerung).
Aus diesem Grund ist es &lt;strong>eine eiserne Regel bei der Verwendung von WSL2, Projektdateien immer im nativen Linux-Dateisystem (unter &lt;code>~/&lt;/code>) abzulegen&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h2 id="4-netzwerkstruktur-nat-default-switch-bridged">4. Netzwerkstruktur: NAT, Default Switch, Bridged
&lt;/h2>&lt;p>Die Flexibilität der Netzwerkfunktionen ist einer der Hauptunterschiede zwischen Hyper-V und WSL2.&lt;/p>
&lt;h3 id="41-netzwerk-von-wsl2-nat-basiert">4.1. Netzwerk von WSL2 (NAT-basiert)
&lt;/h3>&lt;p>Standardmäßig nutzt das WSL2-Netzwerk eine &amp;ldquo;NAT (Network Address Translation)&amp;quot;-Konfiguration unter Verwendung der Virtual Switch-Technologie von Hyper-V.
Der Linux-VM wird automatisch eine private IP-Adresse zugewiesen (z.B. &lt;code>172.20.x.x&lt;/code>), die sich vom Windows-Host unterscheidet. Ein Mechanismus ist integriert, der Dienste (Ports), die innerhalb von WSL2 gestartet wurden, vom Windows-Host als &lt;code>localhost&lt;/code> weiterleitet, sodass Entwickler Webserver usw. testen können, ohne sich der Netzwerkkonfiguration bewusst sein zu müssen.&lt;/p>
&lt;p>In den letzten Jahren wurde in einer Vorschauversion ein neuer Netzwerkmodus namens &amp;ldquo;Mirrored Mode&amp;rdquo; für WSL2 eingeführt. Dies verbessert die IPv6-Unterstützung und die VPN-Verbindungskompatibilität (kann in &lt;code>.wslconfig&lt;/code> konfiguriert werden).&lt;/p>
&lt;h3 id="42-hyper-v-virtual-switch">4.2. Hyper-V Virtual Switch
&lt;/h3>&lt;p>Hyper-V ermöglicht die Erstellung von fortgeschrittenen Netzwerken auf Enterprise-Niveau. Über den &amp;ldquo;Virtual Switch Manager&amp;rdquo; bietet es hauptsächlich drei Modi:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Extern (External)&lt;/strong>: Bindet die physische NIC der Hostmaschine an den Virtual Switch, sodass die Gast-VM direkt an das physische Netzwerk angeschlossen wird (Bridged Connection). Die VM bezieht eine IP aus dem gleichen Subnetz wie das physische Netzwerk vom DHCP-Server.&lt;/li>
&lt;li>&lt;strong>Intern (Internal)&lt;/strong>: Erlaubt nur die Kommunikation zwischen dem Host OS und der VM sowie zwischen VMs. Sie können nicht direkt nach außen kommunizieren.&lt;/li>
&lt;li>&lt;strong>Privat (Private)&lt;/strong>: Erlaubt nur die Kommunikation zwischen VMs und blockiert auch die Kommunikation mit dem Host OS. Wird verwendet, um isolierte Testumgebungen zu erstellen.&lt;/li>
&lt;/ol>
&lt;h3 id="43-fortgeschrittene-hyper-v-netzwerkkonfiguration-mit-powershell">4.3. Fortgeschrittene Hyper-V Netzwerkkonfiguration mit PowerShell
&lt;/h3>&lt;p>In Entwicklungs- oder Testumgebungen ermöglicht PowerShell eine detaillierte Kontrolle, wenn Sie ein benutzerdefiniertes NAT-Netzwerk für VMs erstellen möchten. Nachfolgend ein Skriptbeispiel, um einen internen Virtual Switch zu erstellen, NAT zu konfigurieren und VMs Internetzugang zu ermöglichen.&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. Erstellung eines internen Virtual Switch&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. IP-Adresse für die virtuelle Host-NIC festlegen (Gateway-IP)&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. Konfiguration des NAT-Netzwerks&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"># Befehl zur Überprüfung&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>Mit dieser Konfiguration können Sie ein eigenes NAT-Segment aufbauen, das über den Host mit der Außenwelt kommunizieren kann, indem Sie der angegebenen Hyper-V-Gastmaschine manuell eine &lt;code>192.168.100.x&lt;/code> IP und das Gateway &lt;code>192.168.100.1&lt;/code> zuweisen.&lt;/p>
&lt;hr>
&lt;h2 id="5-anwendungsfälle-und-praktischer-leitfaden-zur-auswahl">5. Anwendungsfälle und praktischer Leitfaden zur Auswahl
&lt;/h2>&lt;p>Basierend auf den bisherigen Architektur- und Leistungsunterschieden definieren wir, in welchen Situationen welche Technologie angewendet werden sollte.&lt;/p>
&lt;h3 id="51-szenarien-in-denen-wsl2-gewählt-werden-sollte">5.1. Szenarien, in denen WSL2 gewählt werden sollte
&lt;/h3>&lt;p>WSL2 ist speziell zur &amp;ldquo;Steigerung der Produktivität von Entwicklern&amp;rdquo; konzipiert. Es ist ideal für folgende Zwecke:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Webentwicklung und Cloud-native Entwicklung&lt;/strong>: Containerentwicklung mit Docker Desktop (WSL2-Backend) oder Podman.&lt;/li>
&lt;li>&lt;strong>Verwendung von dedizierten Linux-Tools&lt;/strong>: Bei der täglichen Verwendung von bash, grep, awk, sed oder GCC/Clang-Compilern für Linux.&lt;/li>
&lt;li>&lt;strong>GUI-Anwendungen (WSLg)&lt;/strong>: Wenn Sie Linux-X11/Wayland-Anwendungen nahtlos auf dem Windows-Desktop ausführen möchten.&lt;/li>
&lt;li>&lt;strong>Machine Learning und KI-Entwicklung&lt;/strong>: Schnelles Training mit TensorFlow oder PyTorch unter Verwendung von GPU-Passthrough-Funktionen (NVIDIA CUDA on WSL).&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Hinweis&lt;/strong>: Einschränkungen können auftreten, wenn Sie den Kernel stark anpassen möchten oder komplexe Dienste aufbauen, die stark von systemd abhängen (obwohl systemd derzeit unterstützt wird, ist es standardmäßig deaktiviert oder eingeschränkt).&lt;/p>
&lt;h3 id="52-szenarien-in-denen-hyper-v-gewählt-werden-sollte">5.2. Szenarien, in denen Hyper-V gewählt werden sollte
&lt;/h3>&lt;p>Hyper-V ist auf &amp;ldquo;Infrastrukturvirtualisierung und vollständige Isolation&amp;rdquo; ausgerichtet. Es ist unverzichtbar für folgende Zwecke:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Ausführung von Windows VMs&lt;/strong>: Zum Ausführen verschiedener Windows-Versionen (z.B. Windows Server oder ein älteres Windows 10) als Testumgebungen.&lt;/li>
&lt;li>&lt;strong>Nested Virtualization (verschachtelte Virtualisierung)&lt;/strong>: Wenn Sie eine virtuelle Maschine (Hyper-V oder KVM) innerhalb einer anderen virtuellen Maschine ausführen möchten. Unerlässlich für die Testumgebungen von Infrastruktur-Ingenieuren.&lt;/li>
&lt;li>&lt;strong>Fortgeschrittene Netzwerkanforderungen&lt;/strong>: Wenn Sie die Netzwerkkonfiguration strikt kontrollieren müssen, wie z.B. bei externen Bridged-Verbindungen (Teilnahme am selben LAN), VLAN-Tagging, Zuweisung mehrerer NICs, usw.&lt;/li>
&lt;li>&lt;strong>Snapshots (Checkpoints)&lt;/strong>: Eine Funktion, mit der Sie den Zustand einer VM zu einem bestimmten Zeitpunkt speichern und jederzeit sofort zurücksetzen können. Äußerst nützlich für destruktive Softwaretests oder Malware-Analysen.&lt;/li>
&lt;li>&lt;strong>Feste Ressourcenzuweisung&lt;/strong>: Wenn Sie die Anzahl der CPU-Kerne und die Speichermenge strikt fixieren möchten, um die Auswirkungen auf das Host OS zu minimieren.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="6-überlegungen-zum-io-durchsatz-anhand-mathematischer-modelle-anhang">6. Überlegungen zum I/O-Durchsatz anhand mathematischer Modelle (Anhang)
&lt;/h2>&lt;p>Als Systemingenieur ist es wichtig, die Beziehung zwischen Durchsatz $S$ und Blockgröße $B$ theoretisch zu verstehen, wenn man die Grenzen der I/O-Leistung beider Technologien bewertet.&lt;/p>
&lt;p>Der Datentransfer-Durchsatz $S$ ist die pro Zeiteinheit übertragene Datenmenge und wird wie folgt modelliert:&lt;/p>
$$ S(B) = \frac{B}{L_{setup} + \frac{B}{R_{max}}} $$&lt;ul>
&lt;li>$B$: Blockgröße (Bytes)&lt;/li>
&lt;li>$L_{setup}$: Feste Latenz im Zusammenhang mit der Einrichtung von I/O-Anfragen und dem Kontextwechsel&lt;/li>
&lt;li>$R_{max}$: Maximale Hardware-Bandbreite bei Kopien oder Geräteübertragungen&lt;/li>
&lt;/ul>
&lt;p>Beim Dateizugriff über das 9P-Protokoll von WSL2 wird dieser $L_{setup}$ sehr groß (aufgrund von Socket-Kommunikation und der Serialisierung/Deserialisierung von Protokollen). Wenn die Blockgröße $B$ gering ist (Lesen und Schreiben vieler kleiner Dateien von wenigen KB), wird daher der Einfluss von $L_{setup}$ im Nenner dominant, und der Durchsatz $S$ sinkt drastisch.
Im Gegensatz dazu wird beim VHDX-Zugriff über den VMBus von Hyper-V der $L_{setup}$ auf ein Niveau nahe einem Hardware-Interrupt optimiert, sodass hohe IOPS auch bei kleinen Blöcken aufrechterhalten werden können.&lt;/p>
&lt;p>Diese mathematische Realität ist die logische Grundlage für die Best Practice: &amp;ldquo;Projektdateien sollten unter WSL2 nicht auf der Windows-Seite abgelegt werden&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h2 id="7-zusammenfassung-zwei-koexistierende-virtualisierungstechnologien">7. Zusammenfassung: Zwei koexistierende Virtualisierungstechnologien
&lt;/h2>&lt;p>Hyper-V und WSL2 sind nicht so, dass das eine besser ist als das andere, sondern &lt;strong>&amp;ldquo;zwei Lösungen mit unterschiedlichen Zielen&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>WSL2&lt;/strong> ist das &amp;ldquo;beste Integrationstool&amp;rdquo;, um die Hülle des Windows-Betriebssystems zu durchbrechen und das Linux-Ökosystem Windows-Benutzern nahtlos und mit hoher Geschwindigkeit zur Verfügung zu stellen. Es ist keine Übertreibung zu sagen, dass dies die ultimative CLI-Umgebung für Entwickler ist.&lt;/li>
&lt;li>&lt;strong>Hyper-V&lt;/strong> ist ein &amp;ldquo;vollwertiger Hypervisor&amp;rdquo;, der die robuste Isolation und Verwaltungsfunktionen, die in Enterprise-Rechenzentren entwickelt wurden, auf den Desktop bringt. Beim Aufbau von Netzwerken, Testen von Windows-Betriebssystemen und Simulieren von Infrastrukturumgebungen ist er unübertroffen.&lt;/li>
&lt;/ul>
&lt;p>In der modernen Windows-Umgebung konkurrieren diese beiden Technologien nicht, sondern koexistieren harmonisch auf derselben VM-Plattform. Durch den richtigen Einsatz beider Technologien je nach Verwendungszweck wird Windows zur leistungsstärksten und flexibelsten Engineering-Workstation der Welt.&lt;/p></description></item><item><title>Aufbau einer reproduzierbaren lokalen Entwicklungsumgebung mit Docker</title><link>http://kenji.blog/de/p/docker-reproducible-local-dev-environment/</link><pubDate>Sun, 13 Sep 2026 01:00:00 +0900</pubDate><guid>http://kenji.blog/de/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 Aufbau einer reproduzierbaren lokalen Entwicklungsumgebung mit Docker" />&lt;h2 id="1-einführung-weg-von-auf-meiner-maschine-funktioniert-es">1. Einführung: Weg von &amp;ldquo;Auf meiner Maschine funktioniert es&amp;rdquo;
&lt;/h2>&lt;p>In der Softwareentwicklung ist das Problem &amp;ldquo;Auf meiner Maschine funktioniert es&amp;rdquo; (It works on my machine), das auf unterschiedliche Umgebungen zwischen Entwicklern zurückzuführen ist, seit langem ein Faktor, der in vielen Projekten Zeit verschwendet. Aufgrund von Unterschieden im Betriebssystem, Versionen installierter Sprachen, Bibliotheksabhängigkeiten und Konflikten zwischen global installierten Tools ist die lokale Umgebung ständig einer &amp;ldquo;Zustandsunsicherheit&amp;rdquo; ausgesetzt.&lt;/p>
&lt;p>Was diese Probleme von Grund auf löst, sind Container-Technologien wie &lt;strong>Docker&lt;/strong> und das Paradigma von &lt;strong>Infrastructure as Code (IaC)&lt;/strong>. Durch die Containerisierung der lokalen Entwicklungsumgebung wird eine Isolierung auf Betriebssystemebene erreicht, was es ermöglicht, die Umgebung selbst zusammen mit der Codebasis zu versionieren.&lt;/p>
&lt;p>In diesem Artikel erklären wir ausführlich die Schritte zum Aufbau einer &lt;strong>&amp;ldquo;reproduzierbaren lokalen Entwicklungsumgebung, die immer in exakt demselben Zustand startet, unabhängig davon, wer sie wann und auf welcher Maschine startet&amp;rdquo;&lt;/strong>, unter Verwendung von Docker, Docker Compose und VSCode DevContainers. Wir beleuchten auch die tiefgreifenden technischen Mechanismen dahinter, einschließlich einer mathematischen Perspektive.&lt;/p>
&lt;hr>
&lt;h2 id="2-die-affinität-zwischen-infrastructure-as-code-iac-und-container-technologie">2. Die Affinität zwischen Infrastructure as Code (IaC) und Container-Technologie
&lt;/h2>&lt;h3 id="prinzipien-von-iac-und-deren-anwendung-auf-lokale-umgebungen">Prinzipien von IaC und deren Anwendung auf lokale Umgebungen
&lt;/h3>&lt;p>Infrastructure as Code (IaC) ist ein Ansatz zur Verwaltung der Konfiguration und Bereitstellung von Infrastruktur durch maschinenlesbare Definitionsdateien anstelle von manuellen Prozessen. Zu den Kernprinzipien von IaC gehören folgende Elemente:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Deklarativer Ansatz (Declarative Approach)&lt;/strong>: Definiert &amp;ldquo;wie der Endzustand sein soll&amp;rdquo;, anstatt &amp;ldquo;wie der Zustand geändert werden soll&amp;rdquo;.&lt;/li>
&lt;li>&lt;strong>Idempotenz (Idempotency)&lt;/strong>: Egal wie oft das Skript ausgeführt wird, es wird immer dasselbe Ergebnis (Zustand) garantiert.&lt;/li>
&lt;li>&lt;strong>Versionskontrolle (Version Control)&lt;/strong>: Der Zustand der Infrastruktur wird als Code in einem VCS wie Git gespeichert, was die Verfolgung des Änderungsverlaufs und Peer-Reviews ermöglicht.&lt;/li>
&lt;/ol>
&lt;p>Die Umsetzung von IaC in einer lokalen Entwicklungsumgebung bedeutet, den &amp;ldquo;idealen Zustand&amp;rdquo; der Entwicklungsumgebung mit &lt;code>Dockerfile&lt;/code>, &lt;code>docker-compose.yml&lt;/code> und &lt;code>devcontainer.json&lt;/code> in Code zu fassen. Dadurch wird ein Onboarding-Erlebnis geschaffen, bei dem neue Teammitglieder einfach das Repository klonen und einen einzigen Befehl ausführen können, um sofort mit der Entwicklung zu beginnen.&lt;/p>
&lt;h3 id="kernel-funktionen-die-die-container-technologie-unterstützen">Kernel-Funktionen, die die Container-Technologie unterstützen
&lt;/h3>&lt;p>Im Gegensatz zur Hypervisor-basierten Virtualisierung wie bei virtuellen Maschinen (VMs) ist die Container-Technologie eine leichtgewichtige Virtualisierungstechnik, die Prozesse isoliert, während sie den Kernel des Host-Betriebssystems teilt. Um dies zu erreichen, werden hauptsächlich die folgenden Funktionen des Linux-Kernels verwendet:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Namespaces&lt;/strong>: Bieten eine isolierte Sicht auf Systemressourcen (PID, Netzwerk, Mount-Punkte, Benutzer usw.) für jeden Prozess.&lt;/li>
&lt;li>&lt;strong>Cgroups (Control Groups)&lt;/strong>: Begrenzen und weisen physische Ressourcen (CPU, Speicher, Festplatten-I/O usw.) zu, die von Prozessen verwendet werden können.&lt;/li>
&lt;li>&lt;strong>UnionFS (Union File System)&lt;/strong>: Eine Technologie, die mehrere Verzeichnisbäume (Ebenen) transparent überlagert, sodass sie als ein einziges Dateisystem erscheinen. Die Image-Ebenen von Docker basieren auf dieser Technologie.&lt;/li>
&lt;/ul>
&lt;p>Betrachten wir ein mathematisches Modell der Ressourcenbegrenzung. Sei $M_{\text{total}}$ die Gesamtspeicherkapazität der Host-Maschine und $m_i$ die Speicherbegrenzung von $n$ Containern, die auf dem Host laufen. Unter Berücksichtigung des Basisspeichers $M_{\text{os}}$, der vom Host-Betriebssystem und anderen Prozessen verbraucht wird, kann die notwendige Bedingung für den stabilen Betrieb des Systems durch folgende Ungleichung ausgedrückt werden:&lt;/p>
$$ \sum_{i=1}^{n} m_i \le M_{\text{total}} - M_{\text{os}} $$&lt;p>Indem $m_i$ für jeden Container mithilfe von Cgroups strikt definiert wird, kann verhindert werden, dass der OOM (Out Of Memory) Killer andere Container oder das gesamte Host-System zum Absturz bringt, selbst wenn ein bestimmter Container ein Speicherleck verursacht.&lt;/p>
&lt;hr>
&lt;h2 id="3-effizientes-dockerfile-design-multi-stage-builds-meistern">3. Effizientes Dockerfile-Design: Multi-Stage-Builds meistern
&lt;/h2>&lt;p>Der erste Schritt zu einer reproduzierbaren Umgebung ist das Design des &lt;code>Dockerfile&lt;/code>, das die Ausführungsumgebung der Anwendung definiert. Hier erklären wir am Beispiel von Python (FastAPI) die Best Practices für ein sicheres und leichtgewichtiges Dockerfile unter Verwendung von &lt;strong>Multi-Stage-Builds&lt;/strong>.&lt;/p>
&lt;p>Ein Multi-Stage-Build ist eine Technik, die mehrere &lt;code>FROM&lt;/code>-Anweisungen in einem einzigen &lt;code>Dockerfile&lt;/code> verwendet, um die Build-Umgebung (eine schwere Umgebung, die Compiler und Entwicklungstools enthält) von der Laufzeitumgebung (einer leichtgewichtigen Umgebung, die nur die notwendigen Artefakte enthält) zu trennen.&lt;/p>
&lt;h3 id="praktisches-dockerfile-für-python-fastapi">Praktisches Dockerfile für Python FastAPI
&lt;/h3>&lt;p>Der folgende Code ist ein Beispiel für ein fortgeschrittenes &lt;code>Dockerfile&lt;/code>, das Abhängigkeitsmanagement mit Poetry und Multi-Stage-Builds kombiniert.&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 (Build-Umgebung)&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"># Festlegen erforderlicher Umgebungsvariablen&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 von Abhängigkeiten&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"># Kopieren und Installieren der Abhängigkeitsdateien&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 (Laufzeitumgebung)&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"># Erstellen eines minimalen, nicht privilegierten Benutzers&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"># Kopieren nur der virtuellen Umgebung (Abhängigkeiten) vom 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 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"># Kopieren des Anwendungscodes&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"># Wechseln zum nicht privilegierten Benutzer&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"># Standardbefehl beim Starten des Containers&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="mathematische-bewertung-der-image-größe-durch-multi-stage-builds">Mathematische Bewertung der Image-Größe durch Multi-Stage-Builds
&lt;/h3>&lt;p>Sei $S_{\text{single}}$ die Image-Größe bei einem Single-Stage-Build und $S_{\text{multi}}$ die Image-Größe bei Anwendung eines Multi-Stage-Builds. Die Reduktionsrate der Größe $R$ wird wie folgt berechnet:&lt;/p>
$$ R = \left( 1 - \frac{S_{\text{multi}}}{S_{\text{single}}} \right) \times 100 \ (\%) $$&lt;p>Angenommen, $S_{\text{single}}$ enthält das Basis-Image des Betriebssystems (ca. 110 MB), Entwicklungspakete (z. B. gcc, ca. 150 MB), Poetry selbst (ca. 40 MB), die Abhängigkeitsbibliotheken des Projekts (ca. 80 MB) und den Quellcode (ca. 5 MB), was insgesamt 385 MB ergibt.
Andererseits werden in $S_{\text{multi}}$ nur die Abhängigkeitsbibliotheken (80 MB) und der Quellcode (5 MB) in das Basis-Image (110 MB) kopiert, was insgesamt 195 MB ergibt.&lt;/p>
$$ R = \left( 1 - \frac{195}{385} \right) \times 100 \approx 49.35\% $$&lt;p>Wie gezeigt, kann durch die Einführung von Multi-Stage-Builds die Image-Größe um etwa die Hälfte reduziert werden. Die Reduzierung der Image-Größe führt direkt zu kürzeren Pull-Zeiten aus der Registry, Einsparungen beim Festplattenspeicher und einer Erhöhung der Sicherheit durch Verkleinerung der Angriffsfläche (Attack Surface).&lt;/p>
&lt;hr>
&lt;h2 id="4-orchestrierung-mehrerer-container-mit-docker-compose">4. Orchestrierung mehrerer Container mit Docker Compose
&lt;/h2>&lt;p>In der modernen Entwicklung von Webanwendungen ist eine Microservices-Architektur üblich, in der mehrere Komponenten wie Webserver, Datenbanken und Cache-Server zusammenarbeiten. Um diese in einer lokalen Umgebung zentral zu verwalten, wird &lt;code>docker-compose.yml&lt;/code> verwendet.&lt;/p>
&lt;p>In diesem Fall werden wir ein 3-Schicht-System bestehend aus &amp;ldquo;Web (FastAPI)&amp;rdquo;, &amp;ldquo;Database (PostgreSQL)&amp;rdquo; und &amp;ldquo;Cache (Redis)&amp;rdquo; lokal aufbauen.&lt;/p>
&lt;h3 id="architekturdiagramm-mermaid">Architekturdiagramm (Mermaid)
&lt;/h3>&lt;p>Das folgende Diagramm ist ein Blockdiagramm, das die Beziehungen zwischen den einzelnen Containern, dem Netzwerk und den Volumes auf dem lokalen Rechner darstellt.&lt;/p>
&lt;pre class="mermaid">
graph TD
User[&amp;#34;Host-Maschine (Browser/curl)&amp;#34;] --&amp;gt;|Localhost:8000| Web[&amp;#34;FastAPI Web-Container&amp;#34;]
subgraph &amp;#34;Docker Bridge Netzwerk (app-network)&amp;#34;
Web --&amp;gt;|Port 5432| DB[&amp;#34;PostgreSQL-Container&amp;#34;]
Web --&amp;gt;|Port 6379| Redis[&amp;#34;Redis-Container&amp;#34;]
end
DB --&amp;gt; Volume1[&amp;#34;Benanntes Volume (postgres_data)&amp;#34;]
Redis --&amp;gt; Volume2[&amp;#34;Benanntes Volume (redis_data)&amp;#34;]
HostDir[&amp;#34;Host-Quellcode (./src)&amp;#34;] -.-&amp;gt;|Bind Mount| Web
&lt;/pre>
&lt;h3 id="implementierung-und-detaillierte-erklärung-der-docker-composeyml">Implementierung und detaillierte Erklärung der docker-compose.yml
&lt;/h3>&lt;p>Im Folgenden zeigen wir ein Beispiel für eine robuste &lt;code>docker-compose.yml&lt;/code>, die für den Aufbau einer praktischen Umgebung geeignet ist.&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"># Host-Code im schreibgeschützten Modus einhängen (für Hot Reloading)&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-und-datenpersistenz">Volumes und Datenpersistenz
&lt;/h3>&lt;p>Container sind grundsätzlich &amp;ldquo;zustandslos (stateless)&amp;rdquo; und &amp;ldquo;kurzlebig (ephemeral)&amp;rdquo;. Wenn ein Container zerstört wird, gehen auch die internen Daten verloren. Um Datenbankdaten oder Caches beizubehalten, muss ein Bereich des Dateisystems der Host-Maschine im Container gemountet werden.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Bind Mount&lt;/strong>: Das &lt;code>./src:/app/src:ro&lt;/code> im obigen &lt;code>web&lt;/code>-Service ist ein Beispiel dafür. Ein bestimmtes Verzeichnis auf dem Host wird direkt in den Container gemappt. Dies wird verwendet, um lokale Code-Änderungen sofort im Container widerzuspiegeln (Hot Reloading). Aus Sicherheitsgründen ist es eine Best Practice, die Option &lt;code>:ro&lt;/code> (Read-Only) hinzuzufügen, um zu verhindern, dass der Host-Quellcode vom Container aus geändert wird.&lt;/li>
&lt;li>&lt;strong>Benanntes Volume (Named Volume)&lt;/strong>: Hierunter fallen &lt;code>postgres_data&lt;/code> und &lt;code>redis_data&lt;/code>. Es handelt sich um Bereiche, die Docker intern verwaltet (z.B. &lt;code>/var/lib/docker/volumes/&lt;/code>). Sie bieten eine bessere I/O-Leistung als Bind Mounts und verbergen die Unterschiede zwischen den Dateisystemen verschiedener Betriebssysteme. Für die Persistenz von Datenbanken sollte immer diese Methode verwendet werden.&lt;/li>
&lt;/ul>
&lt;h3 id="netzwerk-networking-und-service-discovery">Netzwerk (Networking) und Service Discovery
&lt;/h3>&lt;p>Docker Compose erstellt standardmäßig für jedes Projekt ein eigenes Bridge-Netzwerk, wie das obige &lt;code>app-network&lt;/code>.
Container, die zum selben Netzwerk gehören, können einander über den &amp;ldquo;Service-Namen (z. B. &lt;code>db&lt;/code>, &lt;code>redis&lt;/code>)&amp;rdquo; als Hostnamen auflösen (DNS-Auflösung) anstatt über IP-Adressen.
Zum Beispiel kann der Web-Container über die URL &lt;code>postgresql://postgres:password@db:5432/mydb&lt;/code> auf die Datenbank zugreifen. Dies ermöglicht es, das Verbindungsziel transparent über Umgebungsvariablen umzuschalten, sowohl in lokalen Umgebungen als auch in Produktionsumgebungen.&lt;/p>
&lt;h3 id="health-checks-und-steuerung-der-startreihenfolge">Health Checks und Steuerung der Startreihenfolge
&lt;/h3>&lt;p>Die Direktive &lt;code>depends_on&lt;/code> steuert die Startreihenfolge von Containern, aber wenn Sie nur &lt;code>depends_on&lt;/code> angeben, wird der Web-Container gestartet, sobald &amp;ldquo;der DB-Container gestartet ist&amp;rdquo;. In der Realität dauert es jedoch einige Sekunden, bis der Initialisierungsprozess der Datenbank (Start des PostgreSQL-Prozesses und Vorbereitung der Tabellen) abgeschlossen ist, was dazu führen kann, dass die DB-Verbindung vom Web-Container aus fehlschlägt.
Um dies zu verhindern, kann man einen &lt;code>healthcheck&lt;/code> definieren und &lt;code>condition: service_healthy&lt;/code> festlegen. Dadurch wird sichergestellt, dass der Web-Container erst gestartet wird, nachdem verifiziert wurde, dass &amp;ldquo;die Datenbank bereit ist, Verbindungsanfragen anzunehmen&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h2 id="5-verwaltung-von-umgebungsvariablen-und-sicherheit-env">5. Verwaltung von Umgebungsvariablen und Sicherheit (.env)
&lt;/h2>&lt;p>Das Festcodieren sensibler Informationen wie Datenbankpasswörter oder API-Schlüssel in der &lt;code>docker-compose.yml&lt;/code> ist ein Anti-Pattern, das unbedingt vermieden werden sollte. Stattdessen verwenden wir eine Umgebungsvariablendatei &lt;code>.env&lt;/code>, um diese Werte zu injizieren.&lt;/p>
&lt;p>Erstellen Sie eine &lt;code>.env&lt;/code>-Datei im Projektstammverzeichnis.&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"># .env Datei (Zur .gitignore hinzufügen, um sie von der Git-Verwaltung auszuschließen)&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 liest standardmäßig die &lt;code>.env&lt;/code>-Datei im Ausführungsverzeichnis und erweitert die Platzhalter &lt;code>${VAR_NAME}&lt;/code> in der YAML-Datei. Diese Methode ermöglicht es, unterschiedliche Konfigurationswerte für Umgebungen wie lokal, Staging und Produktion sicher zu verwalten, ohne den Infrastrukturcode zu ändern.&lt;/p>
&lt;hr>
&lt;h2 id="6-das-ultimative-entwicklungserlebnis-mit-vscode-devcontainers">6. Das ultimative Entwicklungserlebnis mit VSCode DevContainers
&lt;/h2>&lt;p>Bis hierhin haben wir eine robuste Backend-Umgebung mit Docker aufgebaut. Wir können jedoch noch einen Schritt weiter gehen. Durch die Nutzung der Funktion &lt;strong>VSCode DevContainers (Remote - Containers)&lt;/strong> ist es möglich, das Backend des Editors (VSCode) selbst innerhalb des Containers auszuführen.&lt;/p>
&lt;p>Dadurch müssen weder Python noch Node.js auf der lokalen Maschine installiert werden. Alles, von Lintern (flake8/eslint) und Formattern (black/prettier) bis hin zu IDE-Erweiterungen, kann innerhalb der Codebasis definiert und mit dem gesamten Team geteilt werden.&lt;/p>
&lt;h3 id="konfiguration-von-devcontainerjson">Konfiguration von devcontainer.json
&lt;/h3>&lt;p>Erstellen Sie im Projektstammverzeichnis ein &lt;code>.devcontainer&lt;/code>-Verzeichnis und platzieren Sie die Konfigurationsdatei darin.&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>Indem Sie diese Datei in das Repository aufnehmen, wird sofort nach dem Öffnen des Projekts in VSCode die Aufforderung &amp;ldquo;Reopen in Container&amp;rdquo; angezeigt. Mit nur einem Klick werden alle notwendigen Container gestartet, Erweiterungen installiert und Sie sind sofort bereit, mit dem Coden zu beginnen. Es ist wahrlich ein magisches Erlebnis.&lt;/p>
&lt;hr>
&lt;h2 id="7-sequenz-der-anforderungsverarbeitung-und-leistungsmodellierung">7. Sequenz der Anforderungsverarbeitung und Leistungsmodellierung
&lt;/h2>&lt;p>Wir betrachten den Lebenszyklus der Anforderungsverarbeitung der Webanwendung in der aufgebauten lokalen Entwicklungsumgebung anhand eines Sequenzdiagramms und diskutieren ein mathematisches Modell für deren Leistung.&lt;/p>
&lt;h3 id="sequenzdiagramm-anforderungsfluss">Sequenzdiagramm (Anforderungsfluss)
&lt;/h3>&lt;pre class="mermaid">
sequenceDiagram
participant Client as &amp;#34;Browser / VSCode&amp;#34;
participant Web as &amp;#34;FastAPI (Web)&amp;#34;
participant Redis as &amp;#34;Redis Cache&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;Check Cache for user:123&amp;#34;
activate Redis
alt &amp;#34;Cache Hit (Daten vorhanden)&amp;#34;
Redis--&amp;gt;&amp;gt;Web: &amp;#34;Return Cached User Data&amp;#34;
Web--&amp;gt;&amp;gt;Client: &amp;#34;200 OK (Fast Response)&amp;#34;
else &amp;#34;Cache Miss (Keine Daten)&amp;#34;
Redis--&amp;gt;&amp;gt;Web: &amp;#34;Null (Not Found)&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;Return Database Row&amp;#34;
deactivate DB
Web-&amp;gt;&amp;gt;Redis: &amp;#34;SET user:123 Data (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 (Standard Response)&amp;#34;
end
deactivate Web
&lt;/pre>
&lt;h3 id="mathematisches-modell-der-verarbeitungsverzögerung-latenz">Mathematisches Modell der Verarbeitungsverzögerung (Latenz)
&lt;/h3>&lt;p>Wir modellieren mathematisch die durchschnittliche Anforderungsverarbeitungszeit $T_{\text{total}}$ im obigen System.
Wir definieren die Latenz jeder Verarbeitung wie folgt:&lt;/p>
&lt;ul>
&lt;li>$T_{\text{net}}$: Netzwerklatenz zwischen Client und Web-Container&lt;/li>
&lt;li>$T_{\text{app}}$: Reine Verarbeitungszeit auf Anwendungsseite (Serialisierung usw.)&lt;/li>
&lt;li>$T_{\text{cache}}$: Lese-/Schreibzeit von/nach Redis&lt;/li>
&lt;li>$T_{\text{db}}$: Ausführungszeit für Abfragen an PostgreSQL&lt;/li>
&lt;li>$p_{\text{miss}}$: Cache-Miss-Rate ($0 \le p_{\text{miss}} \le 1$)&lt;/li>
&lt;/ul>
&lt;p>Dann wird die durchschnittliche Antwortzeit durch die folgende Erwartungswertformel dargestellt:&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>In einer lokalen Entwicklungsumgebung (innerhalb von Docker) liegt $T_{\text{net}}$ nahe bei 0. Beachtenswert ist jedoch die &lt;strong>I/O-Leistung bei Bind Mounts&lt;/strong>. Insbesondere bei der Verwendung von Docker Desktop auf Windows/macOS tendiert $T_{\text{app}}$ (wie z. B. die Zeit zum Laden von Code) dazu, aufgrund des Overhead bei der Dateifreigabe zwischen dem Host-Betriebssystem und der VM (Container) anzusteigen. Um diesen Engpass zu beheben, wird dringend eine Architektur empfohlen, bei der entweder die gesamten Quellcodes in einem benannten Volume mit den zuvor erwähnten DevContainers abgelegt werden oder die Docker-Engine nativ in einer WSL2-Umgebung (Windows Subsystem for Linux 2) ausgeführt wird.&lt;/p>
&lt;hr>
&lt;h2 id="8-leistungsoptimierung-für-docker-builds-layer-cache-strategie">8. Leistungsoptimierung für Docker-Builds: Layer-Cache-Strategie
&lt;/h2>&lt;p>Beim Schreiben eines Dockerfile ändert sich die Build-Zeit drastisch, je nachdem, ob Sie den Mechanismus des &amp;ldquo;Layer Caches&amp;rdquo; verstehen oder nicht.
Docker erstellt Dateisystemdifferenzen (Ebenen) für jede Anweisung im Dockerfile (wie &lt;code>FROM&lt;/code>, &lt;code>RUN&lt;/code>, &lt;code>COPY&lt;/code>) und hält sie als Cache bereit. Bei einem erneuten Build wird der Cache der unveränderten Ebenen wiederverwendet.&lt;/p>
&lt;p>Ein wichtiges Prinzip lautet: &lt;strong>&amp;ldquo;Schreiben Sie in der Reihenfolge von der geringsten zur höchsten Änderungshäufigkeit.&amp;rdquo;&lt;/strong>&lt;/p>
&lt;p>Betrachten wir die Modellierung der Auswirkungen von Quellcodeänderungen auf die Build-Zeit. Sei $T_{\text{build}}$ die Gesamt-Build-Zeit, $T_{\text{layer}_i}$ die Ausführungszeit jedes Schritts und das Vorhandensein eines Cache-Treffers der boolesche Wert $c_i \in \{0, 1\}$ (1 bei Cache-Treffer).&lt;/p>
$$ T_{\text{build}} = T_{\text{init}} + \sum_{i=1}^{n} (1 - c_i) \times T_{\text{layer}_i} $$&lt;p>Sobald ein Cache-Miss ($c_k = 0$) in Ebene $k$ auftritt, wird der Cache für alle nachfolgenden Ebenen $j > k$ ungültig ($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"># Schlechtes Beispiel (Quellcode wird zuerst kopiert)&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>In obigem Fall führt das Ändern nur einer Codezeile zu einem Cache-Miss beim ersten &lt;code>COPY&lt;/code>, und das zeitaufwändige &lt;code>RUN poetry install&lt;/code> wird jedes Mal ausgeführt.&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"># Gutes Beispiel (Abhängigkeiten werden zuerst aufgelöst)&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>Durch diese Schreibweise bleibt der Layer-Cache von &lt;code>poetry install&lt;/code> ($c_i = 1$) auch bei Änderung des Quellcodes wirksam, wodurch die Build-Zeit drastisch von einigen Minuten auf wenige Sekunden reduziert wird.&lt;/p>
&lt;hr>
&lt;h2 id="9-fehlerbehebung-troubleshooting-und-tipps">9. Fehlerbehebung (Troubleshooting) und Tipps
&lt;/h2>&lt;p>Hier sind einige häufige Probleme und Lösungen beim Betrieb einer lokalen Umgebung:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Fehler durch Portkonflikt&lt;/strong>
Wenn ein Fehler wie &lt;code>Bind for 0.0.0.0:8000 failed: port is already allocated&lt;/code> auftritt, verwendet ein anderer Prozess auf dem lokalen Rechner diesen Port. Sie können dies vermeiden, indem Sie die hostseitige Portnummer ändern, z. B. auf &lt;code>ports: - &amp;quot;8080:8000&amp;quot;&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Erschöpfung des Festplattenspeichers&lt;/strong>
Wenn Sie Docker über einen längeren Zeitraum verwenden, können sich ungenutzte Images und Volumes (Dangling Images / Volumes) ansammeln und zig Gigabyte Festplattenspeicherplatz belegen. Es wird empfohlen, das System regelmäßig mit dem folgenden Befehl zu bereinigen:&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>Probleme mit Dateiberechtigungen&lt;/strong>
Wenn Sie Bind Mounts in einer Linux-Umgebung verwenden, kann der Eigentümer der innerhalb des Containers erstellten Dateien zu &lt;code>root&lt;/code> werden, was die Bearbeitung auf der Host-Seite unmöglich macht. Dieses Problem kann gelöst werden, indem ein nicht privilegierter Benutzer im Dockerfile erstellt wird, dessen UID/GID mit der Ihres eigenen Host-Betriebssystems (z. B. 1000:1000) übereinstimmt.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="10-fazit-steigerung-der-entwicklungsgeschwindigkeit-durch-reproduzierbarkeit">10. Fazit: Steigerung der Entwicklungsgeschwindigkeit durch Reproduzierbarkeit
&lt;/h2>&lt;p>Durch die Kombination von Docker, Docker Compose und VSCode DevContainers wird eine robuste lokale Entwicklungsumgebung realisiert, bei der &amp;ldquo;unabhängig davon, wer die Umgebung startet, immer exakt derselbe Zustand erreicht wird&amp;rdquo;.&lt;/p>
&lt;p>Das Einbringen des IaC-Paradigmas in die lokale Umgebung verkürzt nicht nur die anfängliche Einrichtungszeit. Es steigert die Geschwindigkeit und Qualität des gesamten Entwicklungszyklus dramatisch, indem es die Angst vor Änderungen an der Infrastrukturkonfiguration nimmt, das Experimentieren mit neuen Technologie-Stacks erleichtert und einen reibungslosen Übergang zu CI/CD-Pipelines ermöglicht.&lt;/p>
&lt;p>Bitte nutzen Sie die in diesem Artikel erläuterten Best Practices, wie z. B. die Optimierung der Image-Größe durch Multi-Stage-Builds, die Steuerung von Abhängigkeiten mithilfe von Health Checks und das Schreiben eines Dockerfiles unter Berücksichtigung des Layer-Caches, und führen Sie unbedingt das beste Entwicklungserlebnis (DX: Developer Experience) in Ihren eigenen Projekten ein.&lt;/p></description></item></channel></rss>