Pourquoi Swift et Docker sont le duo idéal pour le développement serveur
\n\nSi vous avez l'habitude de penser que Swift est réservé au développement mobile iOS, il est temps de vous surprendre. Swift s'impose côté serveur : Vapor, Kitura, Perfect — des frameworks qui permettent d'écrire des backends rapides et sécurisés. Mais un problème persiste : comment garantir que votre application fonctionnera de la même manière sur le Mac du développeur et sur le serveur de production sous Ubuntu ?
\n\nLa réponse — la conteneurisation d'applications Swift avec Docker. Docker permet d'empaqueter tout : le binaire lui-même, les bibliothèques système, les dépendances Swift Package Manager (SPM) — et de l'exécuter n'importe où. Fini les « ça marche chez moi » et « pas chez moi ».
\n\nDans cet article, nous allons voir comment construire une application Swift dans une image Docker, optimiser sa taille, configurer une construction multi-étapes et même ajouter un rechargement à chaud pour le développement. C'est parti !
\n\nÉtape 1. Préparation de l'application Swift
\n\nPour l'exemple, prenons une application web minimale avec Vapor. Si vous n'avez pas encore de projet, créez-le :
\n\nmkdir MySwiftApp && cd MySwiftApp\nswift package init --type executable\n\nAjoutons Vapor dans Package.swift :
// 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\nLe main.swift le plus simple :
\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\nVérifiez que le projet compile localement : swift build. Si tout est ok, passons à la conteneurisation.
Étape 2. Écrire le Dockerfile pour Swift
\n\nLe secret d'une bonne conteneurisation Swift est la construction multi-étapes. Dans la première étape, nous compilons le binaire ; dans la seconde, nous copions uniquement celui-ci et les dépendances d'exécution. L'image finale est 5 à 10 fois plus petite.
\n\n# ---- Étape 1 : Construction ----\nFROM swift:5.9-jammy AS build\n\nWORKDIR /app\n\n# Copie de Package.swift et Package.resolved pour la mise en cache des dépendances\nCOPY Package.swift ./\nCOPY Package.resolved ./\n\n# Téléchargement des dépendances séparément — mis en cache par Docker\nRUN swift package resolve\n\n# Copie du code source\nCOPY Sources ./Sources\n\n# Construction en mode release\nRUN swift build -c release --static-swift-stdlib\n\n# ---- Étape 2 : Runtime ----\nFROM ubuntu:jammy AS runtime\n\n# Installation des dépendances minimales pour le runtime Swift\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# Copie uniquement du binaire depuis l'étape de construction\nCOPY --from=build /app/.build/release/MySwiftApp .\n\n# Port écouté par Vapor par défaut\nEXPOSE 8080\n\nCMD ["./MySwiftApp"]\n\nPoints clés :
\n- \n
--static-swift-stdlib— liaison statique de la bibliothèque standard, pour éviter d'emporter libswiftCore.so dans le runtime. \n - Séparation des COPY — d'abord
Package.swift, puis le code. Ainsi Docker utilise le cache des couches et ne reconstruit pas les dépendances lors des modifications du code source. \n - L'image de base
ubuntu:jammypèse ~80 Mo, l'image finale environ 100-150 Mo. \n
Étape 3. Construction et lancement du conteneur
\n\nConstruisons l'image :
\n\ndocker build -t my-swift-app .\n\nLançons-la :
\n\ndocker run -p 8080:8080 my-swift-app\n\nO