Aplicación Spring Boot como servicio

¿Cómo configurar bien la aplicación Spring Boot empaquetada como un archivo ejecutable como un servicio en el sistema Linux? ¿Es este enfoque recomendado, o debo convertir esta aplicación a guerra e instalarla en Tomcat?

Actualmente puedo ejecutar la aplicación de arranque de Spring desde screen sesión de la screen , lo que es bueno, pero requiere un inicio manual después del reinicio del servidor.

Lo que estoy buscando es consejo general / dirección o script de muestra init.d , si mi enfoque con el archivo ejecutable es correcto.

Los siguientes trabajos para Springboot 1.3 y superior:

Como servicio init.d

El archivo ejecutable tiene los comandos habituales de inicio, detención, reinicio y estado. También configurará un archivo PID en el directorio usual / var / run e iniciará sesión en el directorio habitual / var / log de forma predeterminada.

Solo necesita vincular simbólicamente su jar en /etc/init.d como tal

 sudo link -s /var/myapp/myapp.jar /etc/init.d/myapp 

O

 sudo ln -s ~/myproject/build/libs/myapp-1.0.jar /etc/init.d/myapp_servicename 

Después de eso puedes hacer lo usual

 /etc/init.d/myapp start 

A continuación, configure un enlace en el nivel de ejecución en el que desee que la aplicación inicie / finalice en el inicio, si así lo desea.


Como un servicio systemd

Para ejecutar una aplicación Spring Boot instalada en var / myapp, puede agregar la siguiente secuencia de comandos en /etc/systemd/system/myapp.service:

 [Unit] Description=myapp After=syslog.target [Service] ExecStart=/var/myapp/myapp.jar [Install] WantedBy=multi-user.target 


Referencia

http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/html/deployment-install.html#deployment-service

Lo que sigue es la forma más fácil de instalar una aplicación Java como servicio del sistema en Linux.

Supongamos que está usando systemd (que cualquier distribución moderna hoy en día):

En primer lugar, crea un archivo de servicio en /etc/systemd/system llamado eg javaservice.service con este contenido:

 [Unit] Description=Java Service [Service] User=nobody # The configuration file application.properties should be here: WorkingDirectory=/data ExecStart=/usr/bin/java -Xmx256m -jar application.jar SuccessExitStatus=143 TimeoutStopSec=10 Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target 

En segundo lugar, notifique systemd del nuevo archivo de servicio:

 systemctl daemon-reload 

y lo habilita, por lo que se ejecuta en el arranque:

 systemctl enable javaservice.service 

Eventualmente, puede usar los siguientes comandos para iniciar / detener su nuevo servicio:

 systemctl start javaservice systemctl stop javaservice systemctl restart javaservice systemctl status javaservice 

Siempre que esté utilizando systemd , esta es la forma más no intrusiva y limpia de configurar una aplicación Java como servicio del sistema.

Lo que más me gusta de esta solución es el hecho de que no necesita instalar y configurar ningún otro software. El systemd enviado hace todo el trabajo por usted, y su servicio se comporta como cualquier otro servicio del sistema. Lo uso en producción por un tiempo, en diferentes distros, y funciona como es de esperar.

Otra ventaja es que, al usar /usr/bin/java , puede agregar fácilmente parámetros jvm como -Xmx256m .

Lea también la parte systemd en la documentación oficial de Spring Boot: http://docs.spring.io/spring-boot/docs/current/reference/html/deployment-install.html

También puede usar supervisord, que es un daemon muy útil, que se puede usar para controlar fácilmente los servicios. Estos servicios se definen mediante simples archivos de configuración que definen qué ejecutar con qué usuario en qué directorio y demás, hay un millón de opciones. supervisord tiene una syntax muy simple, por lo que es una muy buena alternativa para escribir scripts de inicio SysV.

Aquí un archivo de configuración de supervisión simple para el progtwig que está intentando ejecutar / controlar. ( Pon esto en /etc/supervisor/conf.d/yourapp.conf )

/etc/supervisor/conf.d/yourapp.conf

 [program:yourapp] command=/usr/bin/java -jar /path/to/application.jar user=usertorun autostart=true autorestart=true startsecs=10 startretries=3 stdout_logfile=/var/log/yourapp-stdout.log stderr_logfile=/var/log/yourapp-stderr.log 

Para controlar la aplicación, necesitaría ejecutar supervisorctl , que le presentará un mensaje donde podría iniciar, detener, el estado de su aplicación.

CLI

 # sudo supervisorctl yourapp RUNNING pid 123123, uptime 1 day, 15:00:00 supervisor> stop yourapp supervisor> start yourapp 

Si el demonio supervisord ya se está ejecutando y ha agregado la configuración para su servicio sin reiniciar el daemon, simplemente puede hacer un comando de reread a reread y update en el shell supervisorctl .

Esto realmente le da todas las flexibilidades que tendría al usar los scripts de SysV Init, pero es fácil de usar y controlar. Eche un vistazo a la documentación .

Acabo de hacer esto yo mismo, así que lo que sigue es lo que llevo en términos de un script de controlador de servicio init.d de CentOS. Está funcionando muy bien hasta ahora, pero no soy el hacker de Bash, así que estoy seguro de que hay margen de mejora, por lo que las ideas para mejorarlo son bienvenidas.

Antes que nada, tengo un pequeño script de configuración /data/svcmgmt/conf/my-spring-boot-api.sh para cada servicio, que configura las variables de entorno.

 #!/bin/bash export JAVA_HOME=/opt/jdk1.8.0_05/jre export APP_HOME=/data/apps/my-spring-boot-api export APP_NAME=my-spring-boot-api export APP_PORT=40001 

Estoy usando CentOS, así que para asegurar que mis servicios se inicien después de reiniciar el servidor, tengo un script de control de servicio en /etc/init.d/my-spring-boot-api :

 #!/bin/bash # description: my-spring-boot-api start stop restart # processname: my-spring-boot-api # chkconfig: 234 20 80 . /data/svcmgmt/conf/my-spring-boot-api.sh /data/svcmgmt/bin/spring-boot-service.sh $1 exit 0 

Como puede ver, eso llama al script de configuración inicial para configurar variables de entorno y luego llama a un script compartido que utilizo para reiniciar todos mis servicios de Spring Boot. Ese script compartido es donde se puede encontrar la carne de todo:

 #!/bin/bash echo "Service [$APP_NAME] - [$1]" echo " JAVA_HOME=$JAVA_HOME" echo " APP_HOME=$APP_HOME" echo " APP_NAME=$APP_NAME" echo " APP_PORT=$APP_PORT" function start { if pkill -0 -f $APP_NAME.jar > /dev/null 2>&1 then echo "Service [$APP_NAME] is already running. Ignoring startup request." exit 1 fi echo "Starting application..." nohup $JAVA_HOME/bin/java -jar $APP_HOME/$APP_NAME.jar \ --spring.config.location=file:$APP_HOME/config/ \ < /dev/null > $APP_HOME/logs/app.log 2>&1 & } function stop { if ! pkill -0 -f $APP_NAME.jar > /dev/null 2>&1 then echo "Service [$APP_NAME] is not running. Ignoring shutdown request." exit 1 fi # First, we will try to trigger a controlled shutdown using # spring-boot-actuator curl -X POST http://localhost:$APP_PORT/shutdown < /dev/null > /dev/null 2>&1 # Wait until the server process has shut down attempts=0 while pkill -0 -f $APP_NAME.jar > /dev/null 2>&1 do attempts=$[$attempts + 1] if [ $attempts -gt 5 ] then # We have waited too long. Kill it. pkill -f $APP_NAME.jar > /dev/null 2>&1 fi sleep 1s done } case $1 in start) start ;; stop) stop ;; restart) stop start ;; esac exit 0 

Al detenerse, intentará usar el Actuador de arranque de resorte para realizar un apagado controlado. Sin embargo, en caso de que el Actuador no esté configurado o no se cierre dentro de un marco de tiempo razonable (le doy 5 segundos, lo cual es un poco corto realmente), el proceso será cancelado.

Además, el script supone que el proceso java que ejecuta la aplicación será el único con “my-spring-boot-api.jar” en el texto de los detalles del proceso. Esta es una suposición segura en mi entorno y significa que no necesito hacer un seguimiento de los PID.

Si desea utilizar Spring Boot 1.2.5 con Spring Boot Maven Plugin 1.3.0.M2, aquí está la solución:

  org.springframework.boot spring-boot-starter-parent 1.2.5.RELEASE     org.springframework.boot spring-boot-maven-plugin 1.3.0.M2  true       spring-libs-milestones http://repo.spring.io/libs-milestone   

A continuación, compile como ususal: mvn clean package , mvn clean package un enlace simbólico ln -s /.../myapp.jar /etc/init.d/myapp , haga que sea ejecutable chmod +x /etc/init.d/myapp y lo inicie service myapp start (con Ubuntu Server)

Sé que esta es una pregunta más antigua, pero quería presentar otra forma, que es el appassembler-maven-plugin . Aquí está la parte relevante de mi POM que incluye una gran cantidad de valores de opciones adicionales que encontramos útiles:

  org.codehaus.mojo appassembler-maven-plugin  true flat true true config ${project.build.directory}   ${installer-target} ${mainClass}  --spring.profiles.active=dev --logging.config=${rpmInstallLocation}/config/${installer-target}-logback.xml   jsw    jsw  linux-x86-64    wrapper.logfile logs/${installer-target}-wrapper.log   wrapper.logfile.maxsize 5m   run.as.user.envvar ${serviceUser}   wrapper.on_exit.default RESTART      256M 1024M  -server        generate-jsw-scripts package  generate-daemons     

En esta pregunta, la respuesta de @PbxMan debería comenzar:

Ejecuta una aplicación Java como un servicio en Linux

Editar:

Hay otra manera menos agradable de iniciar un proceso al reiniciar, usando cron:

 @reboot user-to-run-under /usr/bin/java -jar /path/to/application.jar 

Esto funciona, pero no le ofrece una buena interfaz de inicio / detención para su aplicación. Todavía puedes simplemente kill todos modos …

Mi script SysVInit para Centos 6 / RHEL (aún no ideal). Este script requiere ApplicationPidListener .

Fuente de /etc/init.d/app

 #!/bin/sh # # app Spring Boot Application # # chkconfig: 345 20 80 # description: App Service # ### BEGIN INIT INFO # Provides: App # Required-Start: $local_fs $network # Required-Stop: $local_fs $network # Default-Start: 3 4 5 # Default-Stop: 0 1 2 6 # Short-Description: Application # Description: ### END INIT INFO # Source function library. . /etc/rc.d/init.d/functions # Source networking configuration. . /etc/sysconfig/network exec="/usr/bin/java" prog="app" app_home=/home/$prog/ user=$prog [ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog lockfile=/var/lock/subsys/$prog pid=$app_home/$prog.pid start() { [ -x $exec ] || exit 5 [ -f $config ] || exit 6 # Check that networking is up. [ "$NETWORKING" = "no" ] && exit 1 echo -n $"Starting $prog: " cd $app_home daemon --check $prog --pidfile $pid --user $user $exec $app_args & retval=$? echo [ $retval -eq 0 ] && touch $lockfile return $retval } stop() { echo -n $"Stopping $prog: " killproc -p $pid $prog retval=$? [ $retval -eq 0 ] && rm -f $lockfile return $retval } restart() { stop start } reload() { restart } force_reload() { restart } rh_status() { status -p $pid $prog } rh_status_q() { rh_status >/dev/null 2>&1 } case "$1" in start) rh_status_q && exit 0 $1 ;; stop) rh_status_q || exit 0 $1 ;; restart) $1 ;; reload) rh_status_q || exit 7 $1 ;; force-reload) force_reload ;; status) rh_status ;; condrestart|try-restart) rh_status_q || exit 0 restart ;; *) echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}" exit 2 esac exit $? 

Ejemplo de archivo de configuración /etc/sysconfig/app :

 exec=/opt/jdk1.8.0_05/jre/bin/java user=myuser app_home=/home/mysuer/ app_args="-jar app.jar" pid=$app_home/app.pid 

Aquí hay un script que implementa un jar ejecutable como un servicio systemd.

Crea un usuario para el servicio y el archivo .service, y coloca el archivo jar en / var, y realiza algunos lockings básicos de privilegios.

 #!/bin/bash # Argument: The jar file to deploy APPSRCPATH=$1 # Argument: application name, no spaces please, used as folder name under /var APPNAME=$2 # Argument: the user to use when running the application, may exist, created if not exists APPUSER=$3 # Help text USAGE=" Usage: sudo $0    If an app with the name  already exist, it is stopped and deleted. If the  does not already exist, it is created. " # Check that we are root if [ ! "root" = "$(whoami)" ]; then echo "Must be root. Please use eg sudo" echo "$USAGE" exit fi # Check arguments if [ "$#" -ne 3 -o ${#APPSRCPATH} = 0 -o ${#APPNAME} = 0 -o ${#APPUSER} = 0 ]; then echo "Incorrect number of parameters." echo "$USAGE" exit fi if [ ! -f $APPSRCPATH ]; then echo "Can't find jar file $APPSRCPATH" echo "$USAGE" exit fi # Infered values APPFILENAME=$(basename $APPSRCPATH) APPFOLDER=/var/javaapps/$APPNAME APPDESTPATH=$APPFOLDER/$APPFILENAME # Stop the service if it already exist and is running systemctl stop $APPNAME >/dev/null 2>&1 # Create the app folder, deleting any previous content rm -fr $APPFOLDER mkdir -p $APPFOLDER # Create the user if it does not exist if id "$APPUSER" >/dev/null 2>&1; then echo "Using existing user $APPUSER" else adduser --disabled-password --gecos "" $APPUSER echo "Created user $APPUSER" fi # Place app in app folder, setting owner and rights cp $APPSRCPATH $APPDESTPATH chown $APPUSER $APPDESTPATH chmod 500 $APPDESTPATH echo "Added or updated the $APPDESTPATH file" # Create the .service file used by systemd echo " [Unit] Description=$APPNAME After=syslog.target [Service] User=$APPUSER ExecStart=/usr/bin/java -jar $APPDESTPATH SuccessExitStatus=143 [Install] WantedBy=multi-user.target " > /etc/systemd/system/$APPNAME.service echo "Created the /etc/systemd/system/$APPNAME.service file" # Reload the daemon systemctl daemon-reload # Start the deployed app systemctl start $APPNAME systemctl status $APPNAME 

Ejemplo: enter image description here

Estoy tratando de hacer aplicaciones Springboot que se presentan como un script de shell de estilo “init.d” con una aplicación comprimida java añadida al final

Al vincular simbólicamente estos scripts desde /etc/init.d/spring-app a /opt/spring-app.jar y modificar el archivo jar para que sea ejecutable, es posible hacer que “/etc/init.d/spring-app start “” /etc/init.d/spring-app stop “y otras posibilidades como el trabajo de estado

Presumiblemente, como los scripts de estilo init.d de springboot parecen tener las cadenas mágicas necesarias (como # Default-Start: 2 3 4 5 ) chkconfig podría agregarlo como un “servicio”

Pero quería que funcionara con systemd

Para que esto funcione intenté muchas de las recetas en las otras respuestas anteriores, pero ninguna de ellas funcionó para mí en Centos 7.2 con Springboot 1.3. Principalmente iniciarían el servicio pero no podrían rastrear el pid.

Al final encontré que lo siguiente funcionó para mí, cuando el enlace /etc/init.d también estaba en su lugar. Se debe instalar un archivo similar al siguiente como /usr/lib/systemd/system/spring-app.service

 [Unit] Description=My loverly application After=syslog.target [Service] Type=forking PIDFile=/var/run/spring-app/spring-app.pid ExecStart=/etc/init.d/spring-app start SuccessExitStatus=143 [Install] WantedBy=multi-user.target 

No conozco una forma “estándar” de envoltura para hacer eso con una aplicación Java, pero definitivamente es una buena idea (desea beneficiarse de las capacidades de mantenimiento y monitoreo del sistema operativo si están allí) . Se encuentra en la hoja de ruta para proporcionar algo del soporte de la herramienta Spring Boot (maven y gradle), pero por ahora probablemente tenga que hacer la suya propia. La mejor solución que conozco en este momento es Foreman , que tiene un enfoque declarativo y comandos de una línea para el empaquetado de scripts de inicio para varios formatos de sistema operativo estándar (monit, sys V, upstart, etc.). También hay evidencia de personas que han creado cosas con gradle (por ejemplo, aquí ).

¿Estás usando Maven? Entonces deberías probar el complemento AppAssembler:

El complemento Application Assembler es un plugin de Maven para generar scripts para iniciar aplicaciones java. … Todos los artefactos (dependencias + el artefacto del proyecto) se agregan a la ruta de clase en las secuencias de comandos bin generadas.

Plataformas compatibles:

Variantes de Unix

Windows NT (Windows 9x NO es compatible)

Java Service Wrapper (JSW)

Ver: http://mojo.codehaus.org/appassembler/appassembler-maven-plugin/index.html

Se puede hacer usando el servicio Systemd en Ubuntu

  [Unit] Description=A Spring Boot application After=syslog.target [Service] User=baeldung ExecStart=/path/to/your-app.jar SuccessExitStatus=143 [Install] WantedBy=multi-user.target  

Puede seguir este enlace para obtener una descripción más elaborada y diferentes formas de hacerlo. http://www.baeldung.com/spring-boot-app-as-a-service

COMO SERVICIO DE WINDOWS

Si desea que esto se ejecute en el equipo de Windows, descargue el winsw.exe de

  http://repo.jenkins-ci.org/releases/com/sun/winsw/winsw/2.1.2/ 

Después de eso, renómbrelo al nombre de archivo jar (p. Ej .: your-app .jar)

 winsw.exe -> your-app.exe 

Ahora crea un archivo xml your-app.xml y copia el siguiente contenido a ese

 < ?xml version="1.0" encoding="UTF-8"?>  your-app your-app your-app as a Windows Service java -jar "your-app.jar" rotate  

Asegúrese de que el exe y xml junto con jar estén en la misma carpeta.

Después de este abrir el símbolo del sistema en Administrator previlege e instalarlo en el servicio de Windows.

 your-app.exe install eg -> D:\Springboot\your-app.exe install 

Si falla con

 Error: Registry key 'Software\JavaSoft\Java Runtime Environment'\CurrentVersion' has value '1.8', but '1.7' is required. 

Luego intente lo siguiente:

 Delete java.exe, javaw.exe and javaws.exe from C:\Windows\System32 

Eso es 🙂 .

Para desinstalar el servicio en Windows

 your-app.exe uninstall 

Para ver / ejecutar / detener el servicio: gane + r y escriba Herramientas administrativas, luego seleccione el servicio de esa. A continuación, haga clic con el botón derecho y seleccione la opción – ejecutar / detener

Siguiendo la excelente respuesta de Chad, si obtiene un error de “Error: no se pudo encontrar o cargar la clase principal” , y pasa un par de horas tratando de solucionarlo, ya sea ejecutando un script de shell que inicie su aplicación java o la inicie desde el sistema mismo, y usted sabe que su ruta de clase es 100% correcta, por ejemplo, ejecutar manualmente la secuencia de comandos de la shell funciona, así como ejecutar lo que tiene en systemd execstart. ¡Asegúrate de ejecutar las cosas como el usuario correcto! En mi caso, probé con diferentes usuarios, después de bastante tiempo de resolución de problemas, finalmente tuve un presentimiento, me puse a rootear como usuario – voila, la aplicación comenzó correctamente. Después de determinar que era un problema de usuario incorrecto, seleccioné chown -R user:user la carpeta y las subcarpetas y la aplicación se ejecutó correctamente como el usuario y grupo especificado, por lo que ya no es necesario ejecutarla como raíz (mala seguridad).

En los archivos de unidades systemd puede establecer el directorio de variables de entorno oa través de un EnvironmentFile . Yo propondría hacer las cosas de esta manera, ya que parece ser la menor cantidad de fricción.

Archivo de unidad de muestra

 $ cat /etc/systemd/system/hello-world.service [Unit] Description=Hello World Service After=systend-user-sessions.service [Service] EnvironmentFile=/etc/sysconfig/hello-world Type=simple ExecStart=/usr/bin/java ... hello-world.jar 

A continuación, configure un archivo en /etc/sysconfig/hello-world que incluya nombres en mayúscula de sus variables Spring Boot. Por ejemplo, una variable llamada server.port seguiría el formulario SERVER_PORT como una variable de entorno:

 $ cat /etc/sysconfig/hello-world SERVER_PORT=8081 

El mecanismo que se explota aquí es que las aplicaciones Spring Boot tomarán la lista de propiedades y luego las traducirán, haciendo que todo sea mayúscula y reemplazando los puntos con guiones bajos. Una vez que la aplicación Spring Boot realiza este proceso, busca las variables de entorno que coinciden y utiliza las que se encuentren en consecuencia.

Esto se resalta con más detalle en este SO Q & A titulado: ¿Cómo establecer una propiedad Spring Boot con un guión bajo en su nombre a través de variables de entorno?

Referencias

  • Parte IV. Funciones de Spring Boot – 24. Configuración externalizada