Docker y Swift: contenerización de aplicaciones para iOS/macOS (nivel avanzado)
La contenerización de aplicaciones Swift va más allá de simplemente ejecutar "Hello, World!" en Docker. A nivel avanzado, los desarrolladores se enfrentan a tareas como la optimización del tamaño de las imágenes, el multiprocesamiento, el trabajo con frameworks de red (SwiftNIO) y servidores web (Vapor). En este artículo, analizaremos cómo crear una imagen Docker eficiente para un proyecto Swift, utilizando compilación multi-etapa, y configurar el contenedor para un entorno de producción.
1. Compilación multi-etapa (Multi-stage builds) para Swift
El principal problema de las imágenes Docker para Swift es su tamaño. Una imagen completa con herramientas de compilación puede pesar más de 2 GB. La solución es la compilación multi-etapa. La primera etapa (build) compila el binario, la segunda (runtime) contiene solo el archivo ejecutable y las dependencias mínimas.
Ejemplo de Dockerfile para una aplicación Swift de servidor (por ejemplo, Vapor):
# Etapa 1: compilaciónFROM swift:5.9-focal AS build
WORKDIR /app
COPY Package.swift Package.resolved ./RUN swift package resolve
COPY Sources ./SourcesRUN swift build -c release --static-swift-stdlib
# Etapa 2: imagen mínimaFROM ubuntu:22.04 AS runtime
RUN apt-get update && apt-get install -y libssl-dev ca-certificates && rm -rf /var/lib/apt/lists/*
WORKDIR /appCOPY --from=build /app/.build/release/Run .
EXPOSE 8080CMD ["./Run"]Puntos clave:
--static-swift-stdlib— enlace estático de la biblioteca estándar de Swift, lo que reduce las dependencias de bibliotecas dinámicas.- Copiar solo el binario de release — la imagen final se reduce a ~100-150 MB.
- Usar Ubuntu 22.04 en lugar de la imagen completa de Swift — ahorro de espacio.
2. Optimización de capas y almacenamiento en caché de dependencias
Docker almacena en caché las capas, por lo que el orden de las instrucciones es crítico. Para proyectos Swift, las dependencias (Package.swift, Package.resolved) cambian con menos frecuencia que el código fuente. Separe la copia de los manifiestos y el código fuente:
COPY Package.swift Package.resolved ./RUN swift package resolve
COPY Sources ./SourcesRUN swift build -c releaseEsto permite almacenar en caché la resolución de dependencias y no recompilarlas en cada cambio de código. Para acelerar la compilación, use --build-arg para seleccionar la configuración:
docker build --build-arg CONFIGURATION=release -t my-swift-app .En el Dockerfile agregue:
ARG CONFIGURATION=releaseRUN swift build -c ${CONFIGURATION}3. Contenerización de SwiftNIO y Vapor: trabajo con red
Las aplicaciones Swift de servidor a menudo usan SwiftNIO para el trabajo asíncrono con la red. En el contenedor, es importante configurar correctamente el entorno de red.
3.1. Configuración de Vapor en Docker
Vapor por defecto escucha en localhost:8080. En el contenedor, debe especificar 0.0.0.0 para aceptar conexiones externas. Agregue en configure.swift:
app.http.server.configuration.hostname = "0.0.0.0"app.http.server.configuration.port = 8080O use variables de entorno:
docker run -e HOST=0.0.0.0 -e PORT=8080 -p 8080:8080 my-swift-app3.2. SwiftNIO y healthcheck
Para monitoreo en producción, agregue un healthcheck en el Dockerfile:
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \ CMD curl -f http://localhost:8080/health || exit 1En el código de SwiftNIO, implemente el endpoint /health:
func healthHandler(request: Request) -> EventLoopFuture<String> { return request.eventLoop.makeSucceededFuture("OK")}4. Técnicas avanzadas: Docker Compose y compilación multiplataforma
4.1. Docker Compose para backend Swift
Si