Warum Swift und Docker das ideale Paar für die Serverentwicklung sind
\n\nWenn Sie gewohnt sind zu denken, dass Swift nur für die mobile Entwicklung unter iOS gedacht ist, wird es Zeit, sich zu wundern. Swift macht sich selbstbewusst auf die Serverseite: Vapor, Kitura, Perfect – Frameworks, die es ermöglichen, schnelle und sichere Backends zu schreiben. Aber es gibt ein Problem: Wie kann man garantieren, dass Ihre Anwendung sowohl auf dem Mac des Entwicklers als auch auf dem Produktionsserver unter Ubuntu gleich funktioniert?
\n\nDie Antwort ist Containerisierung von Swift-Anwendungen mit Docker. Docker ermöglicht es, alles zu packen: die Binärdatei selbst, Systembibliotheken, Abhängigkeiten des Swift Package Manager (SPM) – und dies überall auszuführen. Kein „bei mir funktioniert es“ und „bei mir nicht“.
\n\nIn diesem Artikel werden wir durchgehen, wie man eine Swift-Anwendung in ein Docker-Image baut, seine Größe optimiert, einen mehrstufigen Build einrichtet und sogar Hot Reload für die Entwicklung hinzufügt. Los geht's!
\n\nSchritt 1. Vorbereitung der Swift-Anwendung
\n\nAls Beispiel nehmen wir eine minimale Webanwendung mit Vapor. Wenn Sie noch kein Projekt haben, erstellen Sie es:
\n\nmkdir MySwiftApp && cd MySwiftApp\nswift package init --type executable\n\nFügen wir Vapor in 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\nDer 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, gehen wir zur Containerisierung über.
Schritt 2. Dockerfile für Swift schreiben
\n\nDas Hauptgeheimnis einer guten Containerisierung von Swift ist der mehrstufige Build. In der ersten Phase kompilieren wir die Binärdatei, in der zweiten kopieren wir nur diese und die Laufzeitabhängigkeiten. Das resultierende Image ist 5-10 Mal kleiner.
\n\n# ---- Phase 1: Build ----\nFROM swift:5.9-jammy AS build\n\nWORKDIR /app\n\n# Kopieren von Package.swift und Package.resolved zum Caching der Abhängigkeiten\nCOPY Package.swift ./\nCOPY Package.resolved ./\n\n# Abhängigkeiten separat herunterladen – dies wird von Docker gecacht\nRUN swift package resolve\n\n# Quellcode kopieren\nCOPY Sources ./Sources\n\n# Build im Release-Modus\nRUN swift build -c release --static-swift-stdlib\n\n# ---- Phase 2: Runtime ----\nFROM ubuntu:jammy AS runtime\n\n# Minimale Abhängigkeiten für Swift-Runtime 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-Phase kopieren\nCOPY --from=build /app/.build/release/MySwiftApp .\n\n# Port, den Vapor standardmäßig 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 Runtime mitzunehmen. \n - Aufteilung von COPY – zuerst
Package.swift, dann der Code. So nutzt Docker den Layer-Cache und baut Abhängigkeiten bei Änderungen am Quellcode nicht neu. \n - Das Basis-Image
ubuntu:jammywiegt ~80 MB, das resultierende Image etwa 100-150 MB. \n
Schritt 3. Build und Start des Containers
\n\nImage bauen:
\n\ndocker build -t my-swift-app .\n\nStarten:
\n\ndocker run -p 8080:8080 my-swift-app\n\nO