Eliana Mariel

Eliana Mariel

Senior Devops/SRE Engineer
Related topics: GraalVM

GraalVM y las imágenes nativas

14 junio 2022
2 minutos

GraalVM, es una máquina virtual creada por Oracle Labs como podría serlo la tradicional JVM. No obstante, cabe destacar algunas mejoras. En primer lugar, es políglota, es decir, una máquina virtual capaz de ejecutar código en diversos lenguajes de programación. Por otro lado, incorpora un nuevo concepto de compilador de tipo AOT (ahead of time) que predice todo lo que nuestro código necesitará para ejecutarse. Esto último podría pasar desapercibido hasta que hablemos de la utilidad de “native images” que veremos más adelante.

Desde Java 8 tenemos la posibilidad de probar este nuevo concepto de trabajo, aunque el JDK se presenta con la JVM tradicional, trae consigo algunos features específicos para trabajar con Graal.

Si bien la JVM tradicional nos deja trabajar con lenguajes como Java, Groove, Scala y Kotlin, Graal nos permite incorporar Node, JS, Ruby, Python, R y C++ entre otros.

La forma más simple de entender el concepto de Graal es verla como una JVM vitaminada que en el proceso de compilación aplica ciertas optimizaciones que, dependiendo de los casos de uso, hacen que el rendimiento de los programas sea significativamente mejor. Es en los programas de larga ejecución donde se aprecian las mejoras de rendimiento de Graal.

 

¿En qué consiste?

Antes de seguir adelante es importante aclarar que, en definitiva, GraalVM es un conjunto de herramientas que puede ser utilizado de diversas formas.

1. Graal puede entenderse como un compilador de código Java tradicional, con la diferencia que podemos “elegir” cómo compilar ese código ya que nos deja usar el tipo de compilación JIT (just in time) o el nuevo AOT (ahead of time).

2. SubstrateVM es un runtime necesario para ejecutar el AOT compiler de la JVM y generar las imágenes nativas.

3. Truffle es un framework usado dentro de GraalVM como interprete de otros lenguajes como Ruby, R, Python, entre otros.

4. GraalVM: el paquete completo de tecnologías que puede ser utilizado para diversos casos de uso, ejecutar código Java compilado con JIT, con AOT, ejecutar otros lenguajes, ejecutar código mezclando lenguajes, etc.

 

Native Images

La ejecución de una aplicación dentro de una máquina virtual Java conlleva costos de inicio y de espacio. GraalVM tiene la capacidad, junto con Substrate VM de crear imágenes nativas. En el proceso de generación de imágenes nativas emplea un análisis estático para encontrar cualquier código accesible desde el método principal de Java y luego realiza una compilación completa con anticipación (AOT). El binario nativo resultante contiene todo el programa en forma de código máquina para su ejecución inmediata.  Así, con Graal, podemos crear imágenes nativas para procesos que requieren un startup rápido y además con la mínima necesidad de dependencias ya que, como hemos dicho, todo se traduce en lenguaje nativo.

Crear imágenes nativas significa crear un ejecutable self-contained, es decir, que contenga todas las herramientas y librerías necesarias para ser ejecutado sin necesidad de nada más. Generando imágenes nativas, no necesitaríamos ningún runtime específico para que un programa pueda ser ejecutado.

La herramienta de Graal que hace uso del compilador “ahead of time” y nos permite generar este tipo de ejecutables es “native-images”. Esta se encarga de analizar todas las clases necesarias, y analiza todos los métodos que serán utilizados durante la ejecución dando como resultado un ejecutable hecho a medida para la arquitectura del sistema operativo que la ha generado. 

 

Dockerizar imágenes nativas

En primer lugar, podríamos pensar que al generar una imagen nativa, podemos copiar el ejecutable dentro de una imagen Docker y simplemente ejecutarla, pues como hemos dicho no nos hace falta absolutamente mas nada ya que todas las clases y dependencias están auto-contenidas en el ejecutable. No obstante, es importante tener en cuenta que cuando se genera una imagen nativa esta no es cross-platform. Es decir, si he generado mi imagen en una plataforma específica, por ejemplo macOS, cuando intente ejecutar esta imagen dentro de un contenedor Linux, esta no funcionará.

Para ello, la imagen nativa deberá generarse dentro de un contenedor Linux que, por supuesto también tenga GraalVM y native-images. Después de generarla, sería tan sencillo como copiar este ejecutable a otro contenedor con la misma arquitectura y lo más minimalista posible.

Las limitaciones que podemos encontrarnos respecto a la creación de imágenes nativas son debidas a las implicaciones que tiene usar un compilador de tipo “ahead of time”. Es decir, compilar asumiendo un escenario cerrado. Entre las limitaciones más conocidas se encuentran el uso de clases Java que usen reflection y proxys dinámicos. Estas limitaciones, no obstante, pueden mitigarse con configuraciones avanzadas.

El siguiente paso

Dado a que el recibimiento de la comunidad ante Graal y todo lo que implica ha sido bastante exitoso, no han tardado en surgir nuevos frameworks para facilitar la utilización de todo lo que Graal nos propone.

  • Micronaut: Framework basado en Java para crear aplicaciones y micro-servicios ligeros y modulares con un startup rápido.
  • SpringNative: Framework para compilar aplicaciones tradicionales de Spring en ejecutables nativos haciendo uso del native-images de Graal.
  • Quarkus: Framework enfocado específicamente a contenedores y aplicaciones Cloud y serverless de enfoque nativo.

La posibilidad de probar las funcionalidades de Graal vienen incorporadas desde el JDK 8, por lo tanto, es solo cuestión de configurar los comandos java tradicionales para empezar a experimentar con sus funcionalidades.