Warum Swift und Docker das perfekte Paar für die Serverentwicklung sind
\n\nWenn Sie daran gewöhnt sind zu denken, dass Swift nur für die iOS-Mobilentwicklung geeignet ist, wird es Zeit, überrascht zu sein. Swift bewegt sich selbstbewusst auf die Serverseite: Vapor, Kitura, Perfect – Frameworks, mit denen Sie schnelle und sichere Backends schreiben können. Aber es gibt ein Problem: Wie können Sie garantieren, dass Ihre Anwendung auf einem Entwickler-Mac und auf einem Produktionsserver mit Ubuntu identisch funktioniert?
\n\nDie Antwort ist die Containerisierung von Swift-Anwendungen mit Docker. Docker ermöglicht es Ihnen, alles zu packen: die Binärdatei selbst, Systembibliotheken, Swift Package Manager (SPM)-Abhängigkeiten – und es überall auszuführen. Schluss mit "bei mir funktioniert es"-Problemen.
\n\nIn diesem Artikel behandeln wir, wie man eine Swift-Anwendung in ein Docker-Image baut, seine Größe optimiert, Multi-Stage-Builds einrichtet und sogar Hot Reload für die Entwicklung hinzufügt. Los geht's!
\n\nSchritt 1. Vorbereiten der Swift-Anwendung
\n\nFür das Beispiel nehmen wir eine minimale Webanwendung mit Vapor. Wenn Sie noch kein Projekt haben, erstellen Sie eines:
\n\nmkdir MySwiftApp && cd MySwiftApp\nswift package init --type executable\n\nFügen Sie Vapor zu Package.swift hinzu:
// swift-tools-version:5.7\nimport PackageDescription\n\nlet package = Package(\n name: "MySwiftApp",\n dependencies: [\n .package(url: "https://github.com/vapor/vapor.git", from: "4.89.0")\n ],\n targets: [\n .executableTarget(\n name: "MySwiftApp",\n dependencies: [\n .product(name: "Vapor", package: "vapor")\n ]\n )\n ]\n)\n\nDas einfachste main.swift:
\n\nimport Vapor\n\nvar env = try Environment.detect()\ntry LoggingSystem.bootstrap(from: &env)\nlet app = Application(env)\ndefer { app.shutdown() }\n\napp.get("hello") { req -> String in\n return "Hello from Swift in Docker!"\n}\n\ntry app.run()\n\nÜberprüfen Sie, ob das Projekt lokal kompiliert: swift build. Wenn alles in Ordnung ist, fahren wir mit der Containerisierung fort.
Schritt 2. Schreiben eines Dockerfiles für Swift
\n\nDas Hauptgeheimnis einer guten Swift-Containerisierung sind Multi-Stage-Builds. In der ersten Stufe kompilieren wir die Binärdatei; in der zweiten kopieren wir nur die Binärdatei und die Laufzeitabhängigkeiten. Das endgültige Image ist am Ende 5-10 Mal kleiner.
\n\n# ---- Stage 1: Build ----\nFROM swift:5.9-jammy AS build\n\nWORKDIR /app\n\n# Kopieren von Package.swift und Package.resolved zum Cachen von Abhängigkeiten\nCOPY Package.swift ./\nCOPY Package.resolved ./\n\n# Abhängigkeiten separat herunterladen – dies wird von Docker gecached\nRUN swift package resolve\n\n# Quellcode kopieren\nCOPY Sources ./Sources\n\n# Im Release-Modus bauen\nRUN swift build -c release --static-swift-stdlib\n\n# ---- Stage 2: Runtime ----\nFROM ubuntu:jammy AS runtime\n\n# Minimale Abhängigkeiten für die Swift-Laufzeit installieren\nRUN apt-get update && apt-get install -y \\\n libicu70 \\\n libcurl4 \\\n libxml2 \\\n && rm -rf /var/lib/apt/lists/*\n\nWORKDIR /app\n\n# Nur die Binärdatei aus der Build-Stufe kopieren\nCOPY --from=build /app/.build/release/MySwiftApp .\n\n# Standard-Port, den Vapor abhört\nEXPOSE 8080\n\nCMD ["./MySwiftApp"]\n\nWichtige Punkte:
\n- \n
--static-swift-stdlib– statisches Linken der Standardbibliothek, um libswiftCore.so nicht in die Laufzeit ziehen zu müssen. \n - Getrenntes COPY – zuerst
Package.swift, dann der Code. So nutzt Docker Layer-Caching und baut Abhängigkeiten nicht neu, wenn sich Quelldateien ändern. \n - Das Basis-Image
ubuntu:jammywiegt ~80 MB, das endgültige Image ist etwa 100-150 MB. \n
Schritt 3. Bauen und Ausführen des Containers
\n\nDas Image bauen:
\n\ndocker build -t my-swift-app .\n\nAusführen:
\n\ndocker run -p 8080:8080 my-swift-app