Table of Contents
Die Cross-Platform Container Challenge
Docker-Container versprechen überall eine Write-once-run-Anywhere-Portabilität, aber die Realität ist nuancierter, wenn Ihre Bereitstellungsziele Windows- und Linux-Hosts umfassen. Jede Betriebssystemfamilie zeigt grundlegend unterschiedliche Kernel-Schnittstellen: Linux-Container hängen von Cgroups, Namespaces und Ext4-Dateisystemen ab, während Windows-Container den Windows NT-Kernel, die Hyper-V-Isolation und NTFS- oder ReFS-Volume erfordern. Ein auf einer Ubuntu-Basis aufgebautes Image wird sofort auf einem Windows Server-Host fehlschlagen und umgekehrt, es sei denn, Sie entwerfen explizit für Cross-Plattform-Kompatibilität.
Diese Inkompatibilität entsteht, weil ein Container-Image keine vollständig virtualisierte Maschine ist. Es teilt sich den Kernel des Hosts. Ein Linux-Container verwendet den Linux-Kernel des Hosts; ein Windows-Container verwendet den Windows-Kernel des Hosts. Standardmäßig ist keine Emulations- oder Übersetzungsschicht vorgesehen. Für Organisationen, die hybride Infrastruktur verwalten, ist dies ein dringender Bedarf für einen disziplinierten, toolgestützten Ansatz zum Erstellen und Verteilen von Bildern, die in beiden Ökosystemen funktionieren.
Flottenbetreiber und Plattformingenieure müssen daher Strategien anwenden, die entweder separate Bilder pro Plattform erzeugen oder die Multi-Architektur-Manifeste von Docker nutzen, um eine einzelne Bildreferenz zu präsentieren, die für jeden Host die richtige Variante darstellt.
Architektur von Windows vs. Linux Images
Kernel-Kopplung und Basisbildauswahl
Jedes Docker-Image beginnt mit einem Basisbild, das entweder Linux-basiert ist (z. B. , , oder Windows-basiert (z. B. ), Das Basisbild bestimmt die Laufzeitumgebung und den Satz der verfügbaren Systembibliotheken. Linux-Images können so klein wie 5 MB (Alpine) sein, während Windows Server Core-Images etwa 1,5 GB und Nano Server etwa 200 MB beginnen. Diese Größenunterschiede haben operative Auswirkungen auf Bildabrufzeiten, Festplattennutzung und Registrierungsspeicherkosten.
Windows-Container haben auch eine strengere Versionsbindung: Ein Windows-Container-Image, das für einen Build des Host-Betriebssystems (z. B. 20H2) erstellt wurde, läuft möglicherweise nicht auf einem anderen Build (z. B. 21H2). Microsoft mildert dies mit dem Konzept der Prozessisolation vs. Hyper-V-Isolation, aber das Bild selbst muss immer noch mit der Host-Betriebssystemversion übereinstimmen. Linux-Container sind verzeihender, da Linux-Kernel-APIs weitgehend rückwärtskompatibel sind kleinere Versionen.
Filesystem und Berechtigungen
Linux-Images verwenden POSIX-Berechtigungen (Benutzer, Gruppe, andere) und fallsensitive Dateipfade. Windows-Images beruhen auf ACLs (Access Control Lists) und fallinsensitiven Pfaden. Das Ausführen eines Linux-Images mit Code, der eine fallsensitive Dateiverarbeitung auf einem Windows-Host erwartet (auch in einem Container) kann zu subtilen Fehlern führen. Umgekehrt werden Pfade mit Backslashes oder Laufwerksbuchstaben (z. B. ) in einem Linux-Container nicht aufgelöst. Jedes plattformübergreifende Design muss sicherstellen, dass Ihr Anwendungscode und Ihre Konfigurationsdateien plattformunabhängige Pfadkonstrukte verwenden oder dass Sie die richtige Pfadkonvention pro Zielbetriebssystem einfügen.
Multi-Architektur-Bilder mit Docker Buildx
Wie Buildx funktioniert
Docker Buildx ist der empfohlene Weg, um Bilder zu erstellen, die auf mehreren Plattformen von einem einzigen Build-Aufruf ausgeführt werden können. Es verwendet QEMU-basierte Emulation (für Linux-on-Linux-Cross-Compilation) oder native Builder auf separaten Knoten, um das Bild für jede Zielarchitektur zu kompilieren. Für Windows- und Linux-Cross-Plattform-Builds benötigen Sie normalerweise native Windows- und Linux-Build-Knoten, da QEMU den Windows-Kernel nicht emulieren kann.
Buildx erzeugt ein Manifest mit mehreren Architekturen (auch manifest list oder fat manifest), das auf ein oder mehrere Bilder verweist, die jeweils mit ihrer Plattform gekennzeichnet sind. Wenn ein Benutzer auf einem Windows-Computer ausführt, wählt Docker automatisch die Windows-Variante aus dem Manifest aus. Auf einem Linux-Computer wird die Linux-Variante ausgewählt. Es ist kein manuelles Umschalten des Tags erforderlich.
Einrichten eines Buildx Builders für Cross-Platform
Um ein Multi-Architektur-Image zu erstellen, das sowohl Linux- als auch Windows-Varianten enthält, müssen Sie einen Builder registrieren, der sowohl auf einen Linux-Host als auch auf einen Windows-Host zugreifen kann.
Sobald der Builder konfiguriert ist, können Sie das Manifest in einem Schritt erstellen und schieben:
Das Flag FLT:11) schiebt automatisch sowohl die einzelnen Bilder als auch die Manifestliste an die Registry.
Einschränkungen und Gotchas
- Windows-on-Linux Cross-Compilation ist nicht möglich, weil Sie QEMU nicht verwenden können, um den Windows-Kernel zu emulieren.
- Die Registrierungsunterstützung muss Manifestlisten enthalten. Die meisten großen Registrierungsstellen (Docker Hub, AWS ECR, Azure ACR, GitHub Container Registry) unterstützen sie. Einige private Registrierungsstellen können von Ihnen verlangen, dass Sie die Kompatibilität überprüfen.
- Layer-Caching ist pro Plattform. Caching, das auf einem Linux-Knoten aufgebaut ist, gilt nicht für den Windows-Build. Planen Sie separate CI-Schritte oder verwenden Sie einen freigegebenen Cache-Speicherort, der die Plattform respektiert.
- Tag Unveränderlichkeit: Sobald Sie eine Manifestliste schieben, können Sie sie nicht ändern, ohne alle referenzierten Bilder zu verschieben.
Design von Dockerfiles für Cross-Platform
Bedingte Schritte mit Multi-Stage Builds
Anstatt zwei völlig getrennte Dockerfiles zu pflegen, können Sie Build-Argumente und mehrstufige Builds verwenden, um Plattformunterschiede innerhalb einer einzelnen Datei zu behandeln.
ARG BASE_IMAGE
FROM ${BASE_IMAGE} AS base
FROM base AS install-linux
RUN apt-get update && apt-get install -y libfoo
FROM base AS install-windows
RUN powershell -Command Install-Package -Name Foo
FROM install-${TARGETOS} AS final
COPY app /app
CMD ["/app/start"]
In diesem Muster löst sich auf oder auf, und die -Stufe wählt den entsprechenden Installationsschritt.
Umweltvariablen und Config Injection
Verwenden Sie Umgebungsvariablen, um plattformspezifische Werte wie Dateipfade, Zeilenendungen oder Befehlsnamen zu abstrahieren.
ARG TARGETOS
ENV CONFIG_DIR=/etc/myapp
ENV CONFIG_DIR=C:\\ProgramData\\MyApp
Seien Sie jedoch vorsichtig: Das obige Beispiel ist illustrativ, kann aber nicht so funktionieren, wie es ist, weil die Direktive zur Build-Zeit ausgewertet wird, aber die arg zur Build-Zeit pro Plattform verfügbar ist. Sie können einen "Shell-Trick" mit einem plattformbedingten Skript oder einer Build-Zeit-Vorlage verwenden. Ein robusterer Ansatz besteht darin, plattformspezifische Konfigurationsverzeichnisse über einen oder Kubernetes ConfigMap pro Knotentyp einzufügen, anstatt sie in das Bild einzubauen.
Handhabung von Leitungsendungen und ausführbaren Bits
Linux erwartet LF-Zeilenendungen in Shell-Scripts und Konfigurationsdateien; Windows verwendet CRLF. Wenn Sie Dateien in ein Git-Repository einchecken, legen Sie so fest, dass Skripte als LF gespeichert und nur für Windows-Hosts konvertiert werden. In der Dockerdatei markieren Sie Entrypoint-Scripts explizit als ausführbar mit (nur Linux) oder verwenden Sie eine -Direktive, die auf beiden Plattformen identisch funktioniert (erfordert Docker BuildKit).
CI/CD Pipeline Integration
Matrix Builds für jede Plattform
Verwenden Sie in GitHub-Aktionen, GitLab CI oder Azure Pipelines eine Matrixstrategie, um das Bild separat in Linux- und Windows-Runner-Pools zu erstellen und zu testen. Nach jedem Build drücken Sie das plattformspezifische Bild mit einem Tag, der das Plattform-Suffix enthält (z. B. , . Der letzte Schritt ist ein manifeste Erstellung Job, der die beiden Bilder in einer einzigen Manifestliste zusammenführt.
Beispiel GitHub Actions Workflow (vereinfacht)
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
include:
- os: ubuntu-latest
platform: linux/amd64
- os: windows-latest
platform: windows/amd64
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Build and push
uses: docker/build-push-action@v5
with:
platforms: ${{ matrix.platform }}
tags: myapp:${{ matrix.platform }}-${{ github.sha }}
push: true
manifest:
needs: build
runs-on: ubuntu-latest
steps:
- uses: docker/setup-buildx-action@v3
- name: Create manifest list
run: |
docker buildx imagetools create \
-t myregistry.io/myapp:latest \
myregistry.io/myapp:linux-amd64-${{ github.sha }} \
myregistry.io/myapp:windows-amd64-${{ github.sha }}
Testen auf beiden Plattformen
Integrieren Sie plattformspezifische Integrationstests in die gleichen Matrix-Builds. Führen Sie beispielsweise nach dem Erstellen des Windows-Images auf einem Windows-Laufgerät einen Rauchtest durch, der den Anwendungsstart überprüft und auf den erwarteten Port reagiert. Machen Sie dasselbe. Nur wenn beide Testreihen bestehen, sollte die Manifesterstellung fortgesetzt werden. Dadurch wird verhindert, dass ein defektes Windows-Image zu einem "Multi-Bogen"-Tag zusammengeführt wird, von dem Linux-Konsumenten erwarten, dass es funktioniert.
Tipp: Verwenden Sie , um eine bestimmte Plattformvariante während des lokalen Testens zu erzwingen. Dies ist von unschätzbarem Wert, wenn Sie nur eine Linux-Workstation haben, aber die manifeste Struktur vor dem Begehen überprüfen möchten.
Debugging plattformübergreifender Probleme
Häufige Fehlermodi
- Base image mismatch: Die Windows Server Core Version (z.B. ltsc2022 vs. ltsc2019) passt nicht zur Host OS Version. Immer an ein bestimmtes Windows Release Tag anheften und mit Ihrem Infrastrukturteam koordinieren.
- Speichergrenzen und Kernelparameter: Windows-Container erfordern möglicherweise eine Hyper-V-Isolation, um Speichergrenzen durchzusetzen, während Linux-Container CFS (Completely Fair Scheduler) verwenden können.
- Netzwerkunterschiede: Windows-Container verwenden standardmäßig einen NAT-basierten Switch und die Portbindung verhält sich anders. wird unter Windows nicht unterstützt. Stellen Sie sicher, dass Ihre Anwendung nicht auf Host-Netzwerke angewiesen ist, es sei denn, Sie sind auf Linux.
- File-Sperr und Signale: Windows unterstützt POSIX-Signale nicht auf die gleiche Weise wie Linux. Das Senden eines SIGTERM an einen Prozess in einem Windows-Container löst möglicherweise keine anmutige Abschaltung aus. Entwerfen Sie Ihre Anwendung so, dass sie (Windows) als Signal-Handler behandelt.
Logging und Introspektion Tools
Wenn Sie ein plattformübergreifendes Problem debuggen, verwenden Sie , um das Betriebssystem und die Architektur des Bildes zu überprüfen.
Wenn die Plattform fehlt oder nur eine Variante zeigt, ist das Bild kein Multi-Architektur-Manifest.
Wenn Sie nur einen Eintrag sehen, war der Schritt zur Erstellung des Manifests unvollständig oder der Build zielte nicht auf beide OS-Familien ab.
Real-World Muster und Fallstricke
Muster: Verwendung von Docker Desktop für lokale Entwicklung
Docker Desktop unter Windows kann zwischen Linux- und Windows-Containermodi wechseln, aber es kann nicht beide gleichzeitig ausgeführt werden. Für die plattformübergreifende Entwicklung verwenden Sie separate Remote Builder oder virtuelle Maschinen. Docker Desktops Fähigkeit, Linux-Container nativ (über WSL 2) auszuführen, hat die Notwendigkeit von Windows-Containern auf Entwickler-Arbeitsstationen reduziert, aber Sie benötigen immer noch Windows-Container für das Integrationstesten von Windows-only-Bildfunktionen.
Muster: LTS Alignment für Windows Images
Microsoft veröffentlicht eine neue Long-Term Servicing Channel (LTSC) Version von Windows Server etwa alle zwei bis drei Jahre. Jede LTSC Version hat ein entsprechendes Container-Basis-Image. Wenn Ihr Windows Container-Image auf ltsc2022 zielt, müssen Sie es auf einem ltsc2022 Host erstellen und auf ltsc2022 Hosts ausführen. Es gibt keine Rückwärtskompatibilitätsgarantie. Planen Sie Ihre Upgrade-Kadenz so, dass sie mit dem Microsoft Release-Zyklus übereinstimmt. Für Linux ist diese Einschränkung viel lockerer, aber dennoch erwähnenswert: Ein Image, das auf einem sehr alten Kernel aufgebaut ist () kann auf neueren Kerneln laufen, aber ein Image, das mit neueren Kernel-abhängigen Syscalls aufgebaut ist (z. B. ), wird auf älteren Hosts fehlschlagen.
Fallstrick: Ignorieren von ARM64
Während sich der Artikel auf Windows und Linux konzentriert, ist die Zukunft des Computing heterogen: AWS Graviton, Azure Ampere, Apple Silicon und Raspberry Pi Cluster laufen alle auf ARM64 Linux. Wenn Sie ein Multi-Architektur-Image für Windows und Linux erstellen, sollten Sie auch die Einbeziehung von in Ihr Manifest in Betracht ziehen. Viele CI-Linux-Knoten bieten jetzt native ARM64 Linux-Knoten an, und das Docker Buildx-Ökosystem übernimmt die Cross-Compilation nahtlos. Ausklammert ARM64 heute kann einen kostspieligen Re-Engineering-Aufwand in 12 bis 18 Monaten erzwingen.
Pitfall: Überblick auf Dateisystemberechtigungen in COPY
Wenn Sie in einer Dockerdatei verwenden, respektiert Docker die Dateisystemmetadaten des Quell-Hosts. Wenn Sie ein Skript von einem Linux-Host kopieren, behält es seine ausführbaren Bit- und LF-Zeilenendungen bei. Wenn Sie von einem Windows-Host kopieren, landet die Datei ohne das ausführbare Bit und mit CRLF-Endungen. Um ein konsistentes Verhalten zu gewährleisten, verwenden Sie und stellen Sie sicher, dass Ihre Quelldateien mit LF-Zeilenendungen in Git eingecheckt werden.
Schlussfolgerung
Die Verwaltung plattformübergreifender Docker-Images für Windows- und Linux-Kompatibilität ist kein exotischer Anwendungsfall mehr. Es ist eine praktische Anforderung für jede Flotte, die heterogene Infrastrukturen umfasst, von lokalen Windows Server-Clustern bis hin zu Cloud-nativen Linux-Kubernetes. Durch die Einführung von Docker Buildx für Multi-Architektur-Manifeste, die Pflege plattformbedingter Dockerfiles, die Integration einer matrixbasierten CI/CD-Pipeline und das strenge Testen auf beiden Betriebssystemfamilien können Sie ein einziges Image-Tag bereitstellen, das nahtlos über Ihre gesamte Flotte funktioniert.
Die wichtigsten Takeaways sind unkompliziert:
- Verwenden Sie mit nativen Windows- und Linux-Build-Knoten, um Manifestlisten zu erstellen.
- Gestalten Sie Ihre Dockerfiles mit und , um Code-Duplizierung zu vermeiden.
- Automatisieren Sie plattformspezifische Builds und Tests in CI; Merge manifestiert sich erst nach beiden Durchgängen.
- Bleiben Sie auf dem Laufenden mit Microsofts LTSC-Release-Zyklen, um Fehlanpassungen bei der Basisbildversion zu vermeiden.
Wenn diese Praktiken vorhanden sind, können Sie sich auf die Bereitstellung von Anwendungswert konzentrieren, anstatt mit Plattforminkompatibilitäten zu ringen. Für weitere Informationen lesen Sie die Docker-Multiplattform-Builddokumentation, die Windows-Containerübersicht auf Microsoft Learn und das Buildkit-Repository für eine erweiterte Builder-Konfiguration. Diese Ressourcen werden Ihr Verständnis der zugrunde liegenden Mechanismen vertiefen und Sie auf die unvermeidliche Entwicklung der containerisierten Infrastruktur vorbereiten.