Ya llevamos tiempo con los nuevos Mac con chip M1 (aarch64) en el mercado y la verdad es que tenemos muchísimas aplicaciones ya migradas y las que no funcionan bastante bien con Rosetta (de hecho en lagunas ocasiones funciona hasta mejor 🤯).
Sin embargo todavía hay cosas que no van finas y ese es el caso si usas: Maven + Mac M1 aarch64 + io.fabric8 docker-maven-plugin.
En este tutorial vamos a ver como solucionarlo.
Índice
- El problema
- La solución: Redireccionar la comunicación a través de socat
- Bola extra
- Conclusiones
- Sobre el autor
1. El problema
Cuando ejecutas Maven verás que da un error similar a:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[INFO] --- docker-maven-plugin:0.37.0:start (start) @ test-project --- [INFO] ------------------------------------------------------------------------ [INFO] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] Total time: 4.660 s [INFO] Finished at: 2021-11-09T10:02:52+01:00 [INFO] ------------------------------------------------------------------------ [ERROR] Failed to execute goal io.fabric8:docker-maven-plugin:0.37.0:start (start) on project test-project: Execution start of goal io.fabric8:docker-maven-plugin:0.37.0:start failed: An API incompatibility was encountered while executing io.fabric8:docker-maven-plugin:0.37.0:start: java.lang.UnsatisfiedLinkError: could not load FFI provider jnr.ffi.provider.jffi.Provider [ERROR] ----------------------------------------------------- [ERROR] realm = plugin>io.fabric8:docker-maven-plugin:0.37.0 [ERROR] strategy = org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy [ERROR] urls[0] = file:~/.m2/repository/io/fabric8/docker-maven-plugin/0.37.0/docker-maven-plugin-0.37.0.jar ... [ERROR] : could not get native definition for type `POINTER`, original error message follows: java.lang.UnsatisfiedLinkError: Unable to execute or load jffi binary stub from `/var/folders/8r/_69wbkk57hj3x489xf8r0fh40000gn/T/`. Set `TMPDIR` or Java property `java.io.tmpdir` to a read/write path that is not mounted "noexec". [ERROR] ~/src/test-project/jffi13802233681866717935.dylib: dlopen(~/src/test-project/jffi13802233681866717935.dylib, 0x0001): tried: '~/src/test-project/jffi13802233681866717935.dylib' (fat file, but missing compatible architecture (have 'i386,x86_64', need 'arm64e')), '/usr/lib/jffi13802233681866717935.dylib' (no such file) [ERROR] at com.kenai.jffi.internal.StubLoader.tempLoadError(StubLoader.java:424) [ERROR] at com.kenai.jffi.internal.StubLoader.loadFromJar(StubLoader.java:409) ... |
Este error ocurre porque la librería de bajo nivel que usa Fabric8 para conectar con el socket del Docker daemon todavía no está adaptada a la nueva arquitectura, así que básicamente el problema es que el docker-maven-plugin
de Fabric8 no es capaz de encontrar al Docker daemon.
Puedes encontrar más información de este problema en: https://github.com/fabric8io/docker-maven-plugin/issues/1257
Y también en: https://github.com/jnr/jnr-unixsocket/issues/95
Y un poco más en: https://github.com/jnr/jffi/issues/105
Así que seguramente es cuestión de tiempo que acaben dando soporte (si alguien se anima igual les puede echar una mano 😉).
Pero mientras tanto aquí podemos ver una solución (que encontraréis en inglés en los enlaces anteriores).
2. La solución: Redireccionar la comunicación a través de socat
El truco que vamos a hacer es “engañar” a docker-maven-plugin
para que haga la comunicación a través de URL en lugar de socket directo. Y para ello vamos usar la utilidad de línea de comandos socat
.
socat
se define a sí mismo como:
Socat is a command line based utility that establishes two bidirectional byte streams and transfers data between them.
¿Qué quiere decir esto? Que con socat
vamos a ser capaces de redireccionar el tráfico entre docker-maven-plugin
y el Docker daemon.
Primero tendremos que instalar socat
ya que no viene en el sistema operativo:
1 |
brew install socat |
Ahora tendremos que definir la variable de entorno:
1 |
export DOCKER_HOST=tcp://127.0.0.1:2375 |
Con esto le estamos diciendo a docker-maven-plugin
que en vez de usar el socket use la URL indicada en la variable DOCKER_HOST
. Ojo con esta variable porque si la ponéis en los scripts .xxxrc
de vuestra shell entonces todos los clientes de Docker van a intentar usar esa URL y esto igual no os interesa, ahí cada uno verá que se adapta mejor al uso que le quiere dar.
Ahora levantamos socat
:
1 |
socat TCP-LISTEN:2375,range=127.0.0.1/32,reuseaddr,fork UNIX-CLIENT:/var/run/docker.sock |
Con esto lo que estamos haciendo es que todo el tráfico que vaya a la URL que habíamos definido con DOCKER_HOST
, socat
se va a encargar de mandarlo al puerto del Docker dameon, y viceversa (es decir socat
establece una comunicación bidireccional).
Este comando lo podemos lanzar en otra pestaña de nuestro terminal o incluso en background añadiendo la clásica “&
” al final, aunque a mi esta opción no me gusta mucho ya que no quiero dejar encendido siempre el socat
, sino levantarlo o tirarlo en función de cuando lo necesito.
Y con esto estaría todo, ya deberíamos poder lanzar Maven para ejecutar el plugin docker-maven-plugin
con normalidad 👌.
3. Bola extra
Simplemente para que tengáis una referencia rápida, os dejo aquí un ejemplo de configuración del pom.xml
de Maven con el plugin docker-maven-plugin
para, por ejemplo, levantar una base de datos PostgreSQL para ejecutar los tests de integración.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
... <properties> ... <docker-maven-plugin.version>0.37.0</docker-maven-plugin.version> <it.postgresql.image>postgres:13.3-alpine</it.postgresql.image> <it.postgresql.port>5432</it.postgresql.port> <it.postgresql.db>postgres</it.postgresql.db> <it.postgresql.password>indescifrable</it.postgresql.password> <it.postgresql.removeVolumesOnStop>true</it.postgresql.removeVolumesOnStop> ... </properties> ... <plugin> <!-- Run database Docker container during integration tests--> <groupId>io.fabric8</groupId> <artifactId>docker-maven-plugin</artifactId> <version>${docker-maven-plugin.version}</version> <configuration> <images> <image> <name>${it.postgresql.image}</name> <run> <ports> <port>${it.postgresql.port}:5432</port> </ports> <env> <POSTGRES_DB>${it.postgresql.db}</POSTGRES_DB> <POSTGRES_PASSWORD>${it.postgresql.password}</POSTGRES_PASSWORD> </env> <wait> <log>database system is ready to accept connections</log> </wait> </run> </image> </images> </configuration> <executions> <execution> <id>start</id> <phase>pre-integration-test</phase> <goals> <goal>start</goal> </goals> </execution> <execution> <id>stop</id> <phase>post-integration-test</phase> <goals> <goal>stop</goal> </goals> <configuration> <removeVolumes>${it.postgresql.removeVolumesOnStop}</removeVolumes> </configuration> </execution> </executions> </plugin> ... |
4. Conclusiones
No siempre tenemos el tiempo para esperar a la solución oficial, pero siempre podemos buscarnos las mañas para conseguir un workaround, mas en el mundo Unix donde las opciones son prácticamente infinitas.
5. Sobre el autor
Alejandro Pérez García (@alejandropgarci).
Ingeniero en Informática (especialidad de Ingeniería del Software) y Certified ScrumMaster.
Socio fundador de Autentia Real Business Solutions S.L. – “Soporte a Desarrollo”.
I spent hours trying to solve this. Works great. Thanks for your help Alejandro!
El problema del socket está arreglado a partir de la versión 0.38.1 del plugin 🥳
https://github.com/fabric8io/docker-maven-plugin/releases
Buenas,
disculpas sabes que tengo un problema al correr el mvn. Me podrias orientar por favor.. Actualmente estoy levante con soncat paralelo pero tiene un problema l buscar cp-base-new
FO]
[INFO] Image will be built as nxt/confluentinc/cp-base-new:7.0.0-ubi8
[INFO]
[INFO] ————————————————————————
[INFO] Reactor Summary for common-docker 7.0.0:
[INFO]
[INFO] common-docker ……………………………….. SUCCESS [ 2.210 s]
[INFO] utility-belt ………………………………… SUCCESS [ 1.858 s]
[INFO] docker-utils ………………………………… SUCCESS [ 4.170 s]
[INFO] cp-base-new …………………………………. FAILURE [ 14.628 s]
[INFO] cp-jmxterm ………………………………….. SKIPPED
[INFO] ————————————————————————
[INFO] BUILD FAILURE
[INFO] ————————————————————————
[INFO] Total time: 23.152 s
[INFO] Finished at: 2022-02-24T13:30:50-03:00
[INFO] ————————————————————————
[ERROR] Failed to execute goal com.spotify:dockerfile-maven-plugin:1.4.13:build (package) on project cp-base-new: Could not build image: java.util.concurrent.ExecutionException: com.spotify.docker.client.shaded.javax.ws.rs.ProcessingException: java.lang.UnsatisfiedLinkError: could not load FFI provider com.spotify.docker.client.shaded.jnr.ffi.provider.jffi.Provider: ExceptionInInitializerError: Can’t overwrite cause with java.lang.UnsatisfiedLinkError: java.lang.UnsatisfiedLinkError: Can’t load library: /var/folders/zz/zyxvpxvq6csfxvn_n0000000000000/T/jffi6854678401596354910.dylib
Hola Francisco,
Pues complicado de saber, porque ni siquiera es el mismo plugin, sino que parece que el problema viene de
com.spotify.docker.client.shaded....
. Así que habría que analizar con calma lo que está pasando ahí.Por lo pronto asegúrate de que tienes bien definida la variable de entorno
export DOCKER_HOST=tcp://127.0.0.1:2375
en la shell donde estás ejecutando eso.Por otro lado si lo que quieres es simplemente levantar contenedores Docker y no construirlos, igual puedes buscar otras alternativas como esta que explico en este otro tutorial https://www.adictosaltrabajo.com/2021/11/12/como-ejecutar-contenedores-de-docker-con-maven-exec-maven-plugin/
Saludo y suerte!