Introdução: Por que a conteinerização Swift é um desafio e uma vantagem
\nA conteinerização de aplicações Swift para iOS e macOS foi por muito tempo considerada coisa de entusiastas. Ao contrário de Go ou Node.js, Swift requer compilação na plataforma alvo, e as imagens oficiais do Docker permaneceram exóticas por muito tempo. No entanto, com o lançamento do Swift 5.x e o surgimento de imagens oficiais swift:5.10 (e mais recentes), a situação mudou drasticamente. Hoje, o Docker permite não apenas executar backends de servidor no Vapor, mas também automatizar a compilação de utilitários CLI, testar código em ambiente isolado e até preparar artefatos binários para implantação no macOS.
Neste artigo, abordaremos técnicas avançadas: compilação em múltiplos estágios (multi-stage build), cache agressivo de dependências, otimização do tamanho da imagem e integração com o Swift Package Manager. Os exemplos são voltados para projetos reais — desde um simples servidor HTTP até um microsserviço complexo.
\n\n1. Compilação em múltiplos estágios: separando compilação e runtime
\nO erro clássico de iniciantes é usar uma única imagem para compilar e executar. O resultado é uma imagem com mais de 2 GB devido ao SDK, ferramentas e código-fonte. A abordagem correta são dois estágios: builder e runner.
\nExemplo de Dockerfile para aplicação Vapor
\n# Estágio 1: compilação\nFROM swift:5.10 AS builder\nWORKDIR /app\nCOPY Package.swift Package.resolved ./\nRUN swift package resolve --cache-path .build/artifacts\nCOPY Sources Sources\nCOPY Tests Tests\nRUN swift build -c release --static-swift-stdlib\n\n# Estágio 2: runtime mínimo\nFROM ubuntu:22.04\nRUN apt-get update && apt-get install -y libicu70 libxml2 ca-certificates && rm -rf /var/lib/apt/lists/*\nWORKDIR /app\nCOPY --from=builder /app/.build/release/Run .\nEXPOSE 8080\nCMD ["./Run"]\nPontos-chave:
\n- \n
- Cache de dependências — copiamos
Package.swiftePackage.resolvedantes de copiar o código-fonte. O Docker armazena em cache a camada e, ao alterar apenas o código, apenas os binários são recompilados. \n - Flag
--static-swift-stdlib— vinculação estática da biblioteca padrão do Swift. Isso reduz os requisitos de runtime, mas aumenta o tamanho do binário em ~30%. \n - Imagem mínima — usamos Ubuntu com as bibliotecas de sistema necessárias (
libicu,libxml2). A imagem final tem menos de 150 MB contra 2 GB na compilação de estágio único. \n
2. Cache agressivo com BuildKit e agentes SSH
\nAo trabalhar em CI/CD (GitHub Actions, GitLab CI), cada execução baixa as dependências novamente. Use o Docker BuildKit para montar o cache do Swift PM.
\nDockerfile com cache BuildKit
\n# syntax=docker/dockerfile:1\nFROM swift:5.10 AS builder\nWORKDIR /app\nRUN --mount=type=cache,target=/app/.build/artifacts \\\n --mount=type=bind,source=Package.swift,target=Package.swift \\\n --mount=type=bind,source=Package.resolved,target=Package.resolved \\\n swift package resolve\nCOPY Sources Sources\nRUN --mount=type=cache,target=/app/.build/artifacts \\\n swift build -c release --static-swift-stdlib\nVantagens:
\n- \n
- Cache persistente — os artefatos de dependência são preservados entre compilações no mesmo host. \n
- Agentes SSH — se seu
Package.swiftreferencia repositórios privados, use--mount=type=sshpara autenticação sem armazenar chaves na imagem. \n
Para hosts macOS (ex.: MacStadium), adicione obrigatoriamente --platform linux/amd64, pois Swift no ARM (M1/M2) requer emulação.
3. Trabalho avançado com Swift Package Manager
\nSwift PM não é apenas um gerenciador de dependências, mas também uma ferramenta poderosa para testes e linting no contêiner.
\nExecução de testes no contêiner com suporte a Code Coverage
\nFROM swift:5.10 AS te