Introducción: Por qué la contenerización de Swift es un desafío y una ventaja
\nLa contenerización de aplicaciones Swift para iOS y macOS durante mucho tiempo se consideró cosa de entusiastas. A diferencia de Go o Node.js, Swift requiere compilación en la plataforma de destino, y las imágenes oficiales de Docker durante mucho tiempo fueron una rareza. Sin embargo, con el lanzamiento de Swift 5.x y la aparición de imágenes oficiales swift:5.10 (y más recientes), la situación cambió drásticamente. Hoy en día, Docker permite no solo ejecutar backends de servidor en Vapor, sino también automatizar la compilación de utilidades CLI, probar código en un entorno aislado e incluso preparar artefactos binarios para el despliegue en macOS.
En este artículo, analizaremos técnicas avanzadas: compilación multi-etapa (multi-stage build), almacenamiento en caché agresivo de dependencias, optimización del tamaño de la imagen e integración con Swift Package Manager. Los ejemplos están orientados a proyectos reales, desde un servidor HTTP simple hasta un microservicio complejo.
\n\n1. Compilación multi-etapa: separando compilación y tiempo de ejecución
\nEl error clásico de los principiantes es usar una sola imagen para compilar y ejecutar. Como resultado, la imagen pesa >2 GB debido al SDK, las herramientas y los archivos fuente. El enfoque correcto son dos etapas: builder y runner.
\nEjemplo de Dockerfile para una aplicación Vapor
\n# Etapa 1: compilación\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# Etapa 2: tiempo de ejecución 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"]\nPuntos clave:
\n- \n
- Almacenamiento en caché de dependencias — copiamos
Package.swiftyPackage.resolvedantes de copiar los archivos fuente. Docker almacena en caché la capa, y al cambiar solo el código, solo se recompilan los binarios. \n - Bandera
--static-swift-stdlib— enlace estático de la biblioteca estándar de Swift. Esto reduce los requisitos de tiempo de ejecución, pero aumenta el tamaño del binario en ~30%. \n - Imagen mínima — usamos Ubuntu con las bibliotecas del sistema necesarias (
libicu,libxml2). La imagen final tiene menos de 150 MB frente a los 2 GB de la compilación de una sola etapa. \n
2. Almacenamiento en caché agresivo mediante BuildKit y agentes SSH
\nAl trabajar en CI/CD (GitHub Actions, GitLab CI), cada ejecución descarga las dependencias de nuevo. Use Docker BuildKit para montar el caché de Swift PM.
\nDockerfile con almacenamiento en caché de 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\nVentajas:
\n- \n
- Caché persistente — los artefactos de dependencias se conservan entre compilaciones en el mismo host. \n
- Agentes SSH — si su
Package.swifthace referencia a repositorios privados, use--mount=type=sshpara autenticación sin almacenar claves en la imagen. \n
Para hosts macOS (por ejemplo, MacStadium), asegúrese de agregar --platform linux/amd64, ya que Swift en ARM (M1/M2) requiere emulación.
3. Trabajo avanzado con Swift Package Manager
\nSwift PM no es solo un gestor de dependencias, sino también una herramienta poderosa para pruebas y linting en el contenedor.
\nEjecución de pruebas en el contenedor con soporte de Code Coverage
\nFROM swift:5.10 AS te