Docker e Kotlin: conteinerização de aplicações em nível avançado
\n\nA conteinerização tornou-se uma parte essencial do desenvolvimento moderno, e Kotlin não é exceção. Se você já domina Dockerfiles básicos e a execução de arquivos JAR simples, é hora de passar para o próximo nível. Neste artigo, exploraremos técnicas avançadas de conteinerização de aplicações Kotlin: builds multi-estágio (multi-stage builds), otimização do tamanho das imagens, configuração para os frameworks Ktor e Spring Boot, além do trabalho com Docker Compose em ambiente de produção.
\n\n1. Build multi-estágio de aplicações Kotlin
\n\nUm dos principais problemas ao conteinerizar aplicações JVM é o tamanho da imagem final. Uma imagem OpenJDK padrão pode pesar mais de 400 MB. O build multi-estágio permite separar o ambiente de compilação do ambiente de execução, utilizando uma imagem runtime mínima.
\n\nExemplo para projeto Gradle em Kotlin
\n\n# Primeiro estágio: compilação\nFROM gradle:8.5-jdk21 AS builder\nWORKDIR /app\nCOPY build.gradle.kts settings.gradle.kts ./\nCOPY src ./src\nRUN gradle build --no-daemon -x test\n\n# Segundo estágio: execução\nFROM eclipse-temurin:21-jre-alpine\nWORKDIR /app\nCOPY --from=builder /app/build/libs/app.jar app.jar\nEXPOSE 8080\nENTRYPOINT ["java", "-jar", "app.jar"]\n\nO uso de eclipse-temurin:21-jre-alpine em vez da imagem JDK completa reduz o tamanho de 400+ MB para ~120 MB. Para aplicações Kotlin com Ktor ou Spring Boot, isso é mais que suficiente.
Otimização do cache de camadas
\n\nPara acelerar compilações repetidas, copie as dependências separadamente do código fonte:
\n\nFROM gradle:8.5-jdk21 AS builder\nWORKDIR /app\n# Primeiro as dependências (armazenadas em cache se não forem alteradas)\nCOPY build.gradle.kts settings.gradle.kts ./\nRUN gradle dependencies --no-daemon\n# Depois os fontes\nCOPY src ./src\nRUN gradle build --no-daemon -x test\n\nEsta técnica é especialmente útil em CI/CD, onde cada segundo conta.
\n\n2. Otimização de imagens para aplicações Kotlin
\n\nAlém da escolha da imagem base, existem técnicas adicionais para reduzir o tamanho e aumentar a segurança.
\n\nUso do JLink para criar uma JRE personalizada
\n\nSe sua aplicação utiliza apenas parte dos módulos do JDK, você pode criar uma JRE mínima:
\n\n# No estágio builder\nRUN jlink --module-path /opt/java/openjdk/jmods \\\n --add-modules java.base,java.logging,java.naming,java.sql \\\n --output /custom-jre \\\n --strip-debug --no-man-pages --no-header-files\n\n# No estágio runtime\nFROM alpine:3.19\nCOPY --from=builder /custom-jre /opt/jre\nCOPY --from=builder /app/build/libs/app.jar /app/app.jar\nENV PATH="/opt/jre/bin:${PATH}"\nEXPOSE 8080\nCMD ["java", "-jar", "/app/app.jar"]\n\nO tamanho da imagem pode ser reduzido para 50-70 MB.
\n\nArquitetura em múltiplas camadas com Spring Boot
\n\nSpring Boot 3+ suporta a divisão do fat JAR em camadas. Use isso no Dockerfile:
\n\nFROM eclipse-temurin:21-jre-alpine AS builder\nWORKDIR /app\nCOPY build/libs/*.jar app.jar\nRUN java -Djarmode=layertools -jar app.jar extract\n\nFROM eclipse-temurin:21-jre-alpine\nWORKDIR /app\nCOPY --from=builder /app/dependencies/ ./\nCOPY --from=builder /app/spring-boot-loader/ ./\nCOPY --from=builder /app/snapshot-dependencies/ ./\nCOPY --from=builder /app/application/ ./\nENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]\n\nAgora, ao alterar o código, apenas a camada application é reconstruída, enquanto as dependências e o Spring Boot loader permanecem em cache.
3. Conteinerização de aplicações Ktor
\n\nKtor é um framework leve para criar servidores assíncronos em Kotlin. Sua conteinerização tem características próprias.
\n\nDockerfile para Ktor com Netty
\n\nFROM gradle:8.5-jdk21 AS builder\nWORKDIR /app\nCOPY build.gradle.kts settings.gradle.kts ./\nRUN gradle dependencies --