Cómo conectarse a instancias Java que se ejecutan en EC2 mediante JMX


Tenemos problemas para conectarnos a nuestras aplicaciones Java que se ejecutan en el clúster EC2 de Amazon. Definitivamente hemos permitido tanto el "puerto JMX" (que suele ser el puerto de registro RMI) y el puerto del servidor (que hace la mayor parte del trabajo) al grupo de seguridad para las instancias en cuestión. Jconsole se conecta pero parece colgar y nunca mostrar ninguna información.

Estamos ejecutando nuestro java con algo como lo siguiente:

java -server -jar foo.jar other parameters here > java.log 2>&1

Tenemos intentado:

  • Telnets a los puertos conectar pero no se muestra información.
  • Podemos ejecutar jconsole en la propia instancia usando remote-X11 sobre ssh y se conecta y muestra información. Así que el JRE es exportándolo localmente.
  • Abriendo todos los puertos del grupo de seguridad. Weeee.
  • Usando tcpdump para asegurarse de que el tráfico no va a otros puertos.
  • Simulándolo localmente. Siempre podemos conectarnos con nuestros JRE locales o aquellos que se ejecutan en otra parte de nuestra red utilizando los mismos parámetros de aplicación.

java -version salidas:

OpenJDK Runtime Environment (IcedTea6 1.11.5) (amazon-53.1.11.5.47.amzn1-x86_64)
OpenJDK 64-Bit Server VM (build 20.0-b12, mixed mode)

Como un aparte, estamos utilizando mi Simple JMX paquete que nos permite establecer ambos el registro RMI y los puertos de servidor que normalmente son elegidos semi-aleatoriamente por el registro RMI. También puede forzar esto con algo como el siguiente URI JMX:

service:jmx:rmi://localhost:" + serverPort + "/jndi/rmi://:" + registryPort + "/jmxrmi"

En estos días usamos el mismo puerto tanto para el servidor como para el registro. En el pasado hemos utilizado X como el puerto del registro y X+1 para el puerto del servidor para facilitar las reglas del grupo de seguridad. Se conecta al puerto de registro en jconsole o al cliente JMX que esté utilizando.

Author: Gray, 2012-12-06

4 answers

Tenemos problemas para conectarnos a nuestras aplicaciones Java que se ejecutan en el clúster EC2 de Amazon.

Resulta que el problema era una combinación de dos ajustes faltantes. La primera fuerza al JRE a preferir ipv4 y no v6. Esto era necesario (supongo) ya que estamos tratando de conectarnos a él a través de una dirección v4:

-Djava.net.preferIPv4Stack=true

El verdadero bloqueador fue el hecho de que JMX funciona primero contactando el puerto RMI que responde con el hostname y puerto para que el cliente JMX se conecte. Sin ajustes adicionales, utilizará la IP local de la caja, que es una dirección virtual 10.X.X.X a la que un cliente remoto no puede enrutar. Necesitábamos añadir la siguiente configuración que es external hostname o IP del servidor -- en este caso es el elastic hostname del servidor.

-Djava.rmi.server.hostname=ec2-107-X-X-X.compute-1.amazonaws.com

El truco, si está tratando de automatizar sus instancias EC2 (y por qué diablos no lo haría), es cómo encontrar esta dirección en tiempo de ejecución. Hacer que necesita poner algo como lo siguiente en nuestro script de arranque de la aplicación:

# get our _external_ hostname
RMI_HOST=`wget -q -O - http://169.254.169.254/latest/meta-data/public-hostname`
...
java -server \
    -Djava.net.preferIPv4Stack=true -Djava.rmi.server.hostname=$RMI_HOST \
    -jar foo.jar other parameters here > java.log 2>&1

La misteriosa IP 169.254.169.254 en el comando wget anterior proporciona información que la instancia EC2 puede solicitar sobre sí misma. Me decepciona que no incluya etiquetas que solo están disponibles en una llamada autenticada.

Inicialmente estaba usando la dirección ipv4 externa, pero parece que el JDK intenta hacer una conexión con el puerto del servidor cuando se inicia. Si utiliza la IP externa entonces esto estaba ralentizando el tiempo de arranque de nuestra aplicación hasta que se agotó el tiempo. El public-hostname se resuelve localmente en la dirección 10-net y externamente en el public-ipv4. Así que la aplicación ahora se está iniciando rápidamente y los clientes JMX todavía funcionan. Woo hoo!

Espero que esto ayude a alguien más. Me costó 3 horas hoy.

Para forzar a su servidor JMX a iniciar el servidor y el registro RMI en los puertos designados para que pueda bloquearlos en los grupos de seguridad EC2, consulte esto respuesta:

¿Cómo cerrar rmiregistry que se ejecuta en un puerto particular?

Editar:

Acabamos de tener este problema volver a ocurrir. Parece que el código Java JMX está haciendo algunas búsquedas de nombre de host en el nombre de host de la caja y usándolas para intentar conectarse y verificar la conexión JMX.

El problema parece ser un requisito que el nombre de host local de la caja debe resolver a la ip local de la caja. Por ejemplo, si su /etc/sysconfig/network tiene HOSTNAME=server1.foobar.com luego, si hace una búsqueda de DNS en server1.foobar.com, debe llegar a la dirección virtual de 10 REDES. Estábamos generando nuestro propio archivo /etc/hosts y el nombre de host del host local faltaba en el archivo. Esto causó que nuestras aplicaciones se detuvieran en el inicio o no se iniciaran en absoluto.

Por último

Una forma de simplificar la creación de JMX es usar mi paquete SimpleJMX.

 36
Author: Gray,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:27

Por la segunda respuesta ¿Por qué falla la conexión JMX a Amazon EC2?, la dificultad aquí es que, por defecto, el puerto RMI se selecciona al azar, y los clientes necesitan acceso a los puertos JMX y RMI. Si está ejecutando jdk7u4 o posterior, el puerto RMI se puede especificar a través de una propiedad app. Iniciar mi servidor con la siguiente configuración de JMX me funcionó:

Sin autenticación:

-Dcom.sun.management.jmxremote 
-Dcom.sun.management.jmxremote.port=9999 
-Dcom.sun.management.jmxremote.rmi.port=9998 
-Dcom.sun.management.jmxremote.ssl=false 
-Dcom.sun.management.jmxremote.authenticate=false 
-Djava.rmi.server.hostname=<public EC2 hostname>

Con autenticación:

-Dcom.sun.management.jmxremote 
-Dcom.sun.management.jmxremote.port=9999 
-Dcom.sun.management.jmxremote.rmi.port=9998 
-Dcom.sun.management.jmxremote.ssl=false 
-Dcom.sun.management.jmxremote.authenticate=true 
-Dcom.sun.management.jmxremote.password.file=/path/to/jmxremote.password
-Djava.rmi.server.hostname=<public EC2 hostname>

También abrí los puertos 9998-9999 en el Grupo de seguridad EC2 para mi ejemplo.

 12
Author: mmindenhall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:02

Un enfoque un poco diferente mediante el uso de túneles ssh

  1. (En la máquina remota) Pase las siguientes banderas a la JVM

-Dcom.sun.management.jmxremote.port=1099 -Djava.net.preferIPv4Stack=true -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=127.0.0.1

  1. (En la máquina remota) Compruebe qué puertos java comenzó a usar

netstat -tulpn | grep java

tcp 0 0 0.0.0.0:37484 0.0.0.0:* LISTEN 2904/java tcp 0 0 0.0.0.0:1099 0.0.0.0:* LISTEN 2904/java tcp 0 0 0.0.0.0:45828 0.0.0.0:* LISTEN 2904/java

  1. (En la máquina local) Hacer túneles ssh para todos los puertos

ssh -N -L 1099:127.0.0.1:1099 ubuntu@<ec2_ip> ssh -N -L 37484:127.0.0.1:37484 ubuntu@<ec2_ip> ssh -N -L 45828:127.0.0.1:45828 ubuntu@<ec2_ip>

  1. (En la máquina local) Conectar por Java Mission Control a "localhost: 1099"
 4
Author: Tim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-25 23:36:08

La respuesta dada por Gray funcionó para mí, sin embargo, encuentro que tengo que abrir los puertos TCP 0 a 65535 o no entro. Yo creo que puede conectarse en el puerto principal JMX, y luego obtener otro asignado. Obtuve eso de esta entrada de blog que siempre ha funcionado bien para mí.

 1
Author: Eric Pugh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-21 01:46:50