Por que Swift e Docker são a combinação perfeita para desenvolvimento server-side
\n\nSe você está acostumado a pensar que Swift é apenas para desenvolvimento mobile iOS, é hora de se surpreender. Swift está avançando com confiança para o lado do servidor: Vapor, Kitura, Perfect — frameworks que permitem escrever backends rápidos e seguros. Mas há um problema: como garantir que sua aplicação funcione da mesma forma no Mac do desenvolvedor e no servidor de produção rodando Ubuntu?
\n\nA resposta é a containerização de aplicações Swift com Docker. O Docker permite empacotar tudo: o binário em si, bibliotecas do sistema, dependências do Swift Package Manager (SPM) — e executar em qualquer lugar. Sem aqueles "funciona na minha máquina" e "na sua não".
\n\nNeste artigo, vamos abordar como construir uma aplicação Swift em uma imagem Docker, otimizar seu tamanho, configurar uma build multi-estágio e até adicionar recarregamento a quente para desenvolvimento. Vamos lá!
\n\nPasso 1. Preparando a aplicação Swift
\n\nPara o exemplo, vamos usar uma aplicação web mínima com Vapor. Se você ainda não tem um projeto, crie um:
\n\nmkdir MySwiftApp && cd MySwiftApp\nswift package init --type executable\n\nAdicione o Vapor no 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\nO main.swift mais simples:
\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\nVerifique se o projeto compila localmente: swift build. Se estiver tudo certo, vamos para a containerização.
Passo 2. Escrevendo o Dockerfile para Swift
\n\nO segredo principal para uma boa containerização Swift é a build multi-estágio. No primeiro estágio, compilamos o binário; no segundo, copiamos apenas ele e as dependências de runtime. A imagem final fica de 5 a 10 vezes menor.
\n\n# ---- Estágio 1: Build ----\nFROM swift:5.9-jammy AS build\n\nWORKDIR /app\n\n# Copia Package.swift e Package.resolved para cachear dependências\nCOPY Package.swift ./\nCOPY Package.resolved ./\n\n# Baixa as dependências separadamente — isso é cacheado pelo Docker\nRUN swift package resolve\n\n# Copia o código-fonte\nCOPY Sources ./Sources\n\n# Build em modo release\nRUN swift build -c release --static-swift-stdlib\n\n# ---- Estágio 2: Runtime ----\nFROM ubuntu:jammy AS runtime\n\n# Instala dependências mínimas para o Swift runtime\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# Copia apenas o binário do estágio de build\nCOPY --from=build /app/.build/release/MySwiftApp .\n\n# Porta que o Vapor escuta por padrão\nEXPOSE 8080\n\nCMD ["./MySwiftApp"]\n\nPontos-chave:
\n- \n
--static-swift-stdlib— linkagem estática da biblioteca padrão, para não carregar libswiftCore.so no runtime. \n - Separação do COPY — primeiro
Package.swift, depois o código. Assim o Docker usa o cache de camadas e não recompila as dependências ao alterar o código-fonte. \n - A imagem base
ubuntu:jammypesa ~80 MB, a imagem final fica em torno de 100-150 MB. \n
Passo 3. Build e execução do container
\n\nConstruindo a imagem:
\n\ndocker build -t my-swift-app .\n\nExecutando:
\n\ndocker run -p 8080:8080 my-swift-app\n\nO