Introdução: por que um desenvolvedor Swift precisa de Docker?
Muitos desenvolvedores Swift (especialmente no mundo iOS e macOS) estão acostumados com Xcode, simuladores e gerenciamento manual de dependências. No entanto, com o crescimento da popularidade do Swift no servidor (por exemplo, o framework Vapor) e a necessidade de automatizar testes e deploys, a conteinerização se torna uma habilidade obrigatória.
O Docker permite empacotar sua aplicação Swift junto com todas as dependências em um contêiner isolado. Isso garante que o código funcione da mesma forma na máquina local, no servidor CI/CD e em produção. Neste artigo, vamos entender como criar uma imagem Docker para um projeto Swift, otimizar seu tamanho e executá-lo em um contêiner.
1. Fundamentos do Docker para Swift: Dockerfile e build multi-estágio
A abordagem padrão para conteinerizar aplicações Swift é usar o build multi-estágio. Isso permite separar a etapa de compilação (onde são necessárias as ferramentas pesadas do Swift) da etapa de execução (onde apenas o binário compilado é suficiente).
Vamos considerar um projeto Swift típico, por exemplo, um servidor em Vapor. Criaremos um arquivo Dockerfile na raiz do projeto:
# Primeiro estágio: buildFROM swift:5.9-focal AS build
WORKDIR /app
# Copiamos os arquivos Package.swift e Package.resolved para cachear as dependênciasCOPY Package.swift Package.resolved ./RUN swift package resolve
# Copiamos o código fonteCOPY . .
# Build em modo releaseRUN swift build -c release --static-swift-stdlib
# Segundo estágio: imagem mínima para execuçãoFROM ubuntu:focal
WORKDIR /app
# Copiamos o binário compilado do primeiro estágioCOPY --from=build /app/.build/release/Run .
# Abrimos a porta (por padrão, Vapor usa a 8080)EXPOSE 8080
# Iniciamos a aplicaçãoCMD ["./Run"]Pontos importantes:
--static-swift-stdlib— a linkagem estática da biblioteca padrão do Swift reduz as dependências na etapa de execução.- Copiar o
Package.swiftantes dos fontes é um truque para cachear a camada de dependências do Docker. - A imagem base
ubuntu:focalé leve, mas contém todas as bibliotecas de sistema necessárias.
2. Build e execução do contêiner
Agora que o Dockerfile está pronto, vamos construir a imagem e executar o contêiner:
# Build da imagemdocker build -t my-swift-app .
# Executar o contêiner em segundo plano com redirecionamento de portadocker run -d -p 8080:8080 --name swift-container my-swift-app
# Verificar os logsdocker logs swift-container
# Testar (se a aplicação for um servidor HTTP)curl http://localhost:8080/Se você estiver usando o Swift Package Manager sem o Vapor, o binário pode ter um nome diferente. Nesse caso, altere o caminho em COPY --from=build e no CMD. Você pode descobrir o nome exato do binário com o comando:
swift build -c release --show-bin-path2.1. Trabalhando com .dockerignore
Para não copiar arquivos desnecessários para o contexto do build (por exemplo, .git, build/), crie um arquivo .dockerignore:
.git.gitignore.build/*.xcodeproj*.xcworkspace.DS_StoreREADME.md3. Docker Compose para aplicações Swift
Se o seu projeto usa um banco de dados (PostgreSQL, Redis) ou vários microsserviços, é conveniente usar o docker-compose.yml. Exemplo para Vapor + PostgreSQL:
version: '3.8'
services: app: build: . ports: - "8080:8080" environment: - DATABASE_HOST=db - DATABASE_PORT=5432 - DATABASE_NAME=vapor_db - DATABASE_USER=vapor_user - DATABASE_PASSWORD=secret depends_on: - db
db: image: postgres:15 environment: POSTGRES_DB: vapor_db POSTGRES_USER: vapor_user POSTGRES_PASSWORD: secret ports:
```