¿Por qué la contenerización de Kotlin es imprescindible para el desarrollador moderno?
\n\nImagine esta situación: ha escrito una increíble aplicación Kotlin en su MacBook. Todo funciona perfectamente. Le envía el código a un colega con Windows: la base de datos no se conecta. A otro con Linux: la versión de JDK no es la correcta. ¿Le suena familiar? Docker resuelve este problema de una vez por todas.
\n\nLa contenerización de aplicaciones Kotlin no es solo una tendencia de moda. Es una forma de garantizar que su aplicación funcione igual en cualquier máquina: desde el portátil de un junior hasta un servidor de producción en AWS. En este artículo, analizaremos cómo empaquetar correctamente una aplicación Kotlin en una imagen Docker, optimizar su tamaño y configurarla para producción.
\n\nQué aprenderá:
\n- \n
- Cómo escribir un Dockerfile para una aplicación Kotlin con Gradle \n
- Cómo reducir el tamaño de la imagen entre 3 y 5 veces \n
- Cómo usar Docker Compose para la combinación Kotlin + base de datos \n
- Best practices para la contenerización en producción \n
Dockerfile básico para una aplicación Kotlin
\n\nEmpecemos con algo simple. Supongamos que tiene una aplicación Kotlin estándar que se compila con Gradle. Lo primero que viene a la mente es tomar una imagen JDK, copiar los fuentes y ejecutar la compilación. Hagámoslo así, pero desde el principio teniendo en cuenta las best practices.
\n\nCompilación monolítica (no lo haga en producción)
\n\n# Dockerfile\nFROM gradle:7.6-jdk17 AS build\nWORKDIR /app\nCOPY build.gradle.kts settings.gradle.kts ./\nCOPY src ./src\nRUN gradle build --no-daemon -x test\n\nFROM openjdk:17-jdk-slim\nWORKDIR /app\nCOPY --from=build /app/build/libs/app.jar app.jar\nEXPOSE 8080\nCMD ["java", "-jar", "app.jar"]\n\nEste enfoque funciona, pero tiene tres graves inconvenientes:
\n- \n
- Tamaño: la imagen pesa alrededor de 400-500 MB debido al JDK completo \n
- Almacenamiento en caché: ante cualquier cambio en los fuentes, Gradle descargará las dependencias de nuevo \n
- Seguridad: en la imagen quedan herramientas de compilación que no son necesarias en tiempo de ejecución \n
Compilación optimizada: multi-etapa y almacenamiento en caché
\n\nLa verdadera magia de Docker son las multi-stage builds. Dividiremos el proceso en tres etapas:
\n- \n
- Descarga de dependencias (se almacena en caché) \n
- Compilación de la aplicación \n
- Imagen final mínima \n
Dockerfile correcto para Kotlin + Gradle
\n\n# Stage 1: Descarga de dependencias\nFROM gradle:7.6-jdk17 AS dependencies\nWORKDIR /app\nCOPY build.gradle.kts settings.gradle.kts ./\nRUN gradle dependencies --no-daemon\n\n# Stage 2: Compilación\nFROM dependencies AS build\nCOPY src ./src\nRUN gradle build --no-daemon -x test\n\n# Stage 3: Imagen final\nFROM eclipse-temurin:17-jre-alpine\nWORKDIR /app\nCOPY --from=build /app/build/libs/app.jar app.jar\nRUN addgroup -S appgroup && adduser -S appuser -G appgroup\nUSER appuser\nEXPOSE 8080\nCMD ["java", "-jar", "app.jar"]\n\n¿Qué ha cambiado?
\n- \n
- Alpine JRE en lugar del JDK completo: la imagen se reduce a ~120 MB \n
- Almacenamiento en caché de dependencias: si no cambia build.gradle.kts, Docker usa la caché \n
- Seguridad: la aplicación se ejecuta con un usuario no privilegiado \n
Compilación con Gradle Wrapper (método recomendado)
\n\nSi usa Gradle Wrapper (¡y debería!), el Dockerfile se vuelve aún más elegante. Wrapper garantiza que la versión de Gradle sea la misma para todos los desarrolladores y en CI/CD.
\n\nFROM eclipse-temurin:17-jdk-alpine AS builder\nWORKDIR /app\nCOPY gradlew gradlew.bat ./\nCOPY gradle ./gradle\nRUN ./gradlew --version\n\nCOPY build.gradle.kts settings.gradle.kts ./\nRUN ./gradlew dependencies --no-daemon\n\nCOPY src ./src\nRUN ./gradlew build --no-daemon -x test\n\nFROM eclipse-temurin:17-jre-alpine\nWORKDIR /app\nCOPY --from=builder