GraalVM es una Maquina Virtual que es capaz de ejecutar código de diversos lenguajes de programación. Esto en principio nos puede sonar un poco raro porque ya existen maquinas virtuales que ejecutan código . Sin ir más lejos la propia maquina virtual de Java JVM es capaz de ejecutar código Java y otros lenguajes basados en su propia plataforma.
Sin embargo cada vez aparecen más opciones y una de las que hoy en día empieza a destacar es Graal.
GraalVM y sus ventajas
La ventaja de GraalVM como maquina virtual es que puede ejecutar código de diferentes lenguajes de programación , no solo de Java sino que puede ejecutar C++ , Python o JavaScript. Algo que abre las puertas a muchas cosas.
Es cierto que los desarrolladores Java estamos acostumbrados a usar la maquina virtual para ejecutar código en otros lenguajes cómo puede ser Kotlin o Scala. . Ahora bien siempre estamos ubicados en nuestro propio entorno es decir no podemos ejecutar código de Python o de JavaScript de una forma tan directa.
Graal es políglota
Con GraalVM las cosas cambian ya que esta máquina virtual es capaz de ejecutar código de muchos lenguajes de programación concretamente destacan aquellos bajo el paraguas de Node.js o de OpenJDK.
Esto la hace especialmente interesante ya que hoy por hoy muchos de los desarrolladores Java trabajamos como lenguaje complementario con JavaScript y plataformas como Node y viceversa muchos desarrolladores de Node trabajan con Java. El hecho de añadir a esta lista de lenguajes la capacidad de ejecutar código de python o código de C++ la hacen todavía más interesante ya que por ejemplo el mundo de BigData esta plagado de gente que programa con Python.
Un par de Ejemplos
Vamos a construir un par de ejemplos con GraalVM y ejecutarlos con un contenedor de Docker. Para que veamos la versatilidad que tiene el uso de esta Maquina Virtual. Para ello partiremos de un proyecto de servicio REST con Spring Boot . Veamos el contenido del pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.arquitecturajava</groupId> <artifactId>rest</artifactId> <version>0.0.1-SNAPSHOT</version> <name>rest</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
Una vez tenemos construido el pom.xml es momento de definir un servicio REST de holamundo.
package com.arquitecturajava.rest; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HolaService { @RequestMapping public String hola() { return "hola"; } }
Hechas estas dos cosas el siguiente paso es ejecutar mvn package y empaquetar la aplicación con Spring Boot como FatJAR.
Usando Docker
En este caso se trata del fichero rest-0.0.1.jar lo podemos copiar en cualquier carpeta. Lo que nos queda es tener instalado docker y construir un DockerFile. Recordemos que un DockerFile lo que hace es prepara los comandos elementales para poder lanzar un contenedor docker con la aplicación que nosotros necesitemos copiando los ficheros y ejecutando las ordenes que hagan falta ,veamos su contenido.
FROM oracle/graalvm-ce
COPY miproyecto /home
CMD java -jar /home/rest-0.0.1-SNAPSHOT.jar RestApplication
EXPOSE 8080
En este caso vamos a ver paso a paso que contiene el fichero de docker.
- En primer lugar esta el comando FROM que lo que hace es se descarga una imagen de la maquina virtual de GraalVM convertida ya en contenedor
- El segundo paso que realizo es usar el comando COPY para copiar mi FatJAR en la carpeta /home del contenedor que tiene una estructura Linux
- El tercer paso es abrir un terminal con el comando CMD y ejecutar el jar con:
java -jar /home/miproyecto/rest-0.0.1-SNAPSHOT.jar RestApplication
- Finalmente usamos el comando EXPOSE para exponer el puerto 8080 del contenedor en el sistema operativo principal.
Una vez disponemos del fichero docker file es suficiente con hacer
docker build . -t springboot
Construyendo el contenedor que despliega la máquina con nuestra aplicación dentro de ella. Por último ejecutaremos
docker run -p:8080:8080 springboot
El cual lanza el contenedor y publica la información en el puerto 8080 mapeandolo. Si cargamos la página de localhost en nuestro equipo podremos ver la aplicación de SpringBoot en funcionamiento.
Acabamos de desplegar nuestra primera aplicación de Spring Boot sobre GraalVM usando docker. El diagrama clarifica un poco la estructura del docker file.
Es momento de hacer algo similar con Node.js y ver el resultado:
GraalVM y Node.js
Vamos a construir el mismo ejemplo con Node.js .Al tratarse de JavaScript tendremos que construir una aplicación con el framework Express.js. En este caso lo que vamos a hacer es usar el ejemplo de Hello world.
var express = require('express'); var app = express(); app.get('/', function (req, res) { res.send('hola express desde graal'); }); app.listen(8081, function () { console.log('Example app listening on port 8081!'); });
Acabamos de construir nuestra aplicación de node en el puerto 8081 y disponemos de esta estructura de carpetas en la cual hemos ejecutado npm install express para que nos instale express.js como framework.
Es momento de disponer de un dockerfile que nos despliegue nuestra aplicación como contenedor. En este caso lo curioso es que aunque estemos trabajando con otro lenguaje el contenedor docker que usamos es el mismo.
FROM oracle/graalvm-ce
COPY miproyecto /home
CMD node /home/miproyecto/servidor.js
EXPOSE 8081
En este caso copiamos el contenido que tenemos en la carpeta de miproyecto a la carpeta /home del contenedor. Recordemos que el contenido son ficheros de Javascript. Ejecutamos y exponemos la aplicación en el puerto correspondiente. Para ello necesitaremos el siguiente dockerfile
FROM oracle/graalvm-ce
COPY miproyecto/* /home/miproyecto/
WORKDIR /home/miproyecto
COPY /miproyecto/package*.json ./
RUN npm install
CMD [ “node”, “servidor.js” ]
EXPOSE 8081
En este caso estamos realizando una operación similar a la anterior pero con unos comandos mas concretos para node y express.js que es el framework MVC de Node. Ejecutamos
docker build -t minode
Acto seguido
docker run -p:8081:8081 minode
Es momento de acceder a la aplicación en nuestro equipo a través del puerto 8081.
Acabamos de publicar dos aplicaciones en tecnologías diferentes utilizando el mismo sistema de maquina virtual o interprete a través de GraalVM.
Hola mira así me funcionó, con una IMAGE más pequeña.
PASO#1: (Dockerfile)
FROM findepi/graalvm
COPY dummy_GraalVM-1.0.0.jar /home
CMD java -jar /home/dummy_GraalVM-1.0.0.jar RestApplication
EXPOSE 8080
PASO#2:
Crear un directorio en cualquier ruta & poner ahí el .fatJAR del MICROSERVICIO & el ‘DOCKERFILE’ creado:
PASO#3: (ejecutar):
$ cd RUTA_DIRECTORIO
$ docker build . -t dummy_graalvm
$ docker run -p:8080:8080 dummy_graalvm
PASO#4:
Probar el ‘MICROSERVICO’ desplegado dentro del ‘CONTENEDOR’ (La IP del CONTENEDOR):
http://192.168.99.100:8080/
PASO#5: (Eliminar lo probado)
docker rmi -f ID_IMAGE
docker rm -f ID_CONTAINER
Saludos.
Hola que tal .., interesante tu post, el único tema que me inquieta es que esa IMAGE que se maneja para correr dentro de Contenedor pesa demasiado: 1.7GB waooo & la idea es que sea ligera para que las instancias elásticas puedan crecer & decrecer justamente, debe haber alguna que sea bastante más ligera en el mercado. Aquí tengo algunas pero solo funcionan para JDK8 no para GraalVM jústamente: #————– [PESOS DE IMAGENES] ———–# # maven:3-jdk-8 500MB # maven:3-jdk-8-alpine 122MB # openjdk:8 488MB # openjdk:8-slim 284MB # openjdk:8-alpine 105MB # openjdk:8-jdk-slim 284MB # openjdk:8-jdk-alpine 105MB # adoptopenjdk/openjdk8:alpine-slim 90.2MB #————– [PESOS… Read more »
Excelente explicación, GraalVM si que cambiara muchas cosas en el mercado del software.
gracias 🙂