Monitorizar consumo de memoria (Java Thread Dump)
Estamos trabajando contentos y felices en nuestro proyecto cuando, al intentar ejecutar el comando jstack sobre un proceso PID=1, nos encontramos con el siguiente error: «Unable to get pid of LinuxThreads manager thread».
Esto sucede debido a que el proceso PID=1 es un proceso reservado del sistema operativo. Entonces, ¿cómo podemos solucionarlo?
Para poder ejecutar el proceso, vamos a realizar una serie de cambios en el proyecto para arrancar primero un proceso «sh» que posteriormente arranque el proceso Java, quedando de la siguiente manera:
Pasos a realizar
1.- Crear fichero startJava.sh
Tendrá que ser a la altura de src, Docker, JenkinsFile, con la línea de arranque de Spring Boot y perfil:
java -Djava.security.egd=file:/dev/./urandom -Dspring.profiles.active=docker,securityOff -jar /app.jar
2.- Añadir el fichero startJava.sh al contenedor
Esto lo haremos tras el proceso de empaquetado (mvn).
Editaremos el fichero pom.xml para añadir en la etapa build un plugin que copie los ficheros .sh a la carpeta «target» para que posteriormente pueda incluirse este fichero en la imagen Docker junto con el .jar de la aplicación.
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>copy-resource-one</id>
<phase>package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>target/</outputDirectory>
<resources>
<resource>
<directory>.</directory>
<includes>
<include>*.sh</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
3.- Modificar fichero DockerFile
Añadiremos la siguiente línea tras añadir la aplicación Spring Boot; es decir, detrás de esta línea, ADD *-exec.jar app.jar
ADD startJava.sh startJava.sh
Concederemos permisos de ejecución y permisos propietarios al fichero creado:
RUN chown onesait:onesait startJava.sh && \
chmod +x startJava.sh
Comentaremos la línea «ENTRYPOINT» y añadiremos la siguiente línea:
CMD ["/bin/sh","-c", "/startJava.sh"]
Un ejemplo DockerFile completo sería el siguiente:
ADD *-exec.jar app.jar
ADD startJava.sh startJava.sh
# logs folder
RUN mkdir -p /var/logs/smartindustry/scheduler/transport-etl && \
mkdir ./target
# create onesait user/group
RUN addgroup -S onesait -g 433 && adduser -u 431 -S -g onesait -h /usr/local -s /sbin/nologin onesait
RUN chown -R onesait:onesait /usr/local && \
chown -R onesait:onesait /var/logs/smartindustry/scheduler/transport-etl && \
chown -R onesait:onesait ./target && \
chown onesait:onesait app.jar && \
chmod -R 777 ./target && \
chmod -R 777 /var/logs && \
chmod -R 777 /usr/local && \
chown onesait:onesait startJava.sh && \
chmod +x startJava.sh
VOLUME ["/tmp", "/var/logs/smartindustry/scheduler/transport-etl"]
USER onesait
EXPOSE 8082
ENV JAVA_OPTS="$JAVA_OPTS -Xms512M -Xmx1G" \
SERVER_NAME=localhost
#ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-Dspring.profiles.active=docker,securityOff","-jar","/app.jar"]
#ENTRYPOINT ["/bin/sh", "-c", "set -e && java -Djava.security.egd=file:/dev/./urandom -Dspring.profiles.active=docker,securityOff -jar /app.jar"]
CMD ["/bin/sh","-c", "/startJava.sh"]
#CMD ["/startJava.sh"]
Tras realizar estos pasos, se cambiará el PID:
Y ya está; ya sería posible ejecutar el comando sobre el PID deseado:
$ jstack -l 10
2020-06-28 12:36:56
Full thread dump OpenJDK 64-Bit Server VM (25.212-b04 mixed mode):
"Attach Listener" #40 daemon prio=9 os_prio=0 tid=0x00005576e5349000 nid=0x4c waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"DestroyJavaVM" #39 prio=5 os_prio=0 tid=0x00005576e81ae000 nid=0xc waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
Imagen de cabecera: Reka Illyes en Unsplash