Запустить jar как сервис linux

Jar файл как служба Ubuntu

Jan 13, 2020 · 10 min read

Чтобы создать кастомную службу, нужно чуточку знать о systemd.

systemd — это набор базовых утилит для Linux. Набор предоставляет диспетчер системы и служб, запаускаемый с PID 1, и запускающий остальную часть системы. Systemd управляет службами, запускает демонов, поддерживает монтирование, управляет конфигурацией системы, ведёт логи, отслеживает входящих в систему юзеров, запуск контейнеров и вирт машин и т.д.

С 8-ой версии Debian по умолчанию используется systemd вместо init.d, который использует unit-файлы из каталога /etc/systemd/system, вместо скриптов из /etc/init.d/.

systemctl — основная команда для контроля и отслеживания состояния systemd. С помощью неё мы и будем запускать кастомные службы.

1) Пара слов о systemctl.

Тольк о касательно запуска служб, ибо возможностей у неё гораздо больше.

Вывести список служб ( — no-legend — без шапки и пояснений; — all — не только активные, а все):

Юнитами могут быть как службы (.services), так и точки монтирования (.mount), устройства (.device) или сокеты (.socket)

если интересны только зафэйлиные службы:

Если юнит не указан, то считается, что это . service, в данном случае smbd. service; также точки монтирования преобразуются из / home в home. mount, а имена устройств в юниты-девайсы: /dev/sda2 в dev-sda2.device. При выводе статуса перечисляются все запущенные службой процессы и выводится последняя часть лога.

Посмотреть службы и зависящие процессы юзера:

Если в имени юнита содержится @, значит это экземпляр юнита-шаблона. Например, user@1000.service — экземпляр шаблона user@.service. Экземпляры отличаются идентификаторами, указывающимися после @.

После создания службы может потребоваться перезагрузка настроек systemd:

А также добавить службу в автозапуск при загрузке системы:

2) Создание службы.

А) Где?

Для начала надо создать файл описания службы. Где это сделать? Посмотрим, откуда вообще загружаются юниты:

Положим в приоритетную папку /etc/systemd/system.

Б) Как?

Посмотреть пример имеющейся службы можно так:

А вообще у нас как минимум два варианта:

— Запуск джавы напрямую (не надо никаких доп скриптов)

— Запуск скрипта, который запускает джаву (можно описать доп действия в скрипте)

Б.0) Создаём файл

Б.1) Запуск java напрямую

Б.2) Запуск sh-скрипта

Б.3) Что тут к чему?

Часть строк интуитивно понятна, а часть не очень. Разберём же их (и немного того, что ещё можно указать в файле службы):

Description = Java Test Service

# Зависимость службы от подключения к сети (или любой другой службы):

# если зависимость необходима

# если зависимость не обязательна (взаимоисключаемо с requires)

# если нужно запускать только после подключения к сети. Если не указать, то служба может быть запущена параллельно с поднятием сети (или любой другой указанной службой)

# Type — без указания будет simple — заупск cmd и получение от неё ответа об ЕЁ успешном запуске.

# exec — запуск cmd и получение ответа об успешном запуске именно итогового приложения.

# forking — ждёт, когда указанная служба сделает fork() от главного процесса, потом ждёт завершения главного процесса и стартует последующие за ним юниты.

Читайте также:  Снижается яркость экрана ноутбука windows 10

(дальше в разделе «нюансы» будет объяснено дополнительно)

# пользователь, под которым запускать приложение

# Рабочая директория вашего приложения. Т.е. при получении внешних файлов ваша программа будет отталкиваться от указанной здесь папки.

WorkingDirectory=/home/snatchub/Documents/Share/jar as service test

# Путь к исполняемому файлу — либо к java с параметрами, либо к скрипту, запускающему ваше приложение.

ExecStart=/home/snatchub/Documents/Share/jar as service test/my_amazing.sh

# Скрипт, запускаемый по команде «Стоп». Если не указан, то просто шлётся SIGTERM

ExecStop = /home/snatchub/Documents/Share/jar as service test/my_amazing.sh stop

# скрипт для перезапуска

ExecReload = /home/snatchub/Documents/Share/jar as service test/my_amazing.sh reload

# Путь к PID файлу сервиса (туда пишется PID процесса приложения). Нужен для отслеживания основного процесса.

# Сервис менеджер сам не пишет в этот файл, только читает оттуда pid, а также удаляет его после завершения приложения.

# Это дополнительный «успешный» статус окончания работы приложения.

( дальше в разделе «нюансы» будет объяснено дополнительно)

# Если указан тайм-аут, то сначала мы ждём указанное время окончания выполнения команды ExecStop, потом если служба не остановилась, шлём SIGTERM и ждём указанное время, после чего шлём SIGKILL, если у нас очень живучее приложение.

# on-failure — перезапускать приложение при ошибке (Unclean exit code, Unclean signal, Timeout, Watchdog).

# Можно указать always , если приложение всегда должно быть запущено. На команду “ systemctl stop” это не распространяется.

( дальше в разделе «нюансы» будет объяснено дополнительно)

# Через какое время после остановки произойдёт рестарт. По дефолту время перезапуска 100ms

# Служба должна регулярно вызывать sd_notify(3) с “ WATCHDOG=1” (считай это “ keep-alive ping”-ом).

# Если время между двумя вызовами больше указанного, то служба считается «нерабочей» и останавливается сигналом SIGABRT или тем, что указан в « WatchdogSignal».

# По дефолту в значении control-group , что значит при остановке службы будут убиты все оставшиеся процессы в контрольной группе основного процесса (после выполнения скрипта, указанного в команде « ExecStop»).

# Если указан « process », то только главный процесс будет убит.

# уровень запуска. multi-user.target соответствует уровню runlevel3 и значит, что служба запустится ещё до запуска графической оболочки, но когда юзеры смогут входить в систему через консоль.

Кратко: если pid файл не существует, то стартуем джава-процесс и пишем его pid в файл, иначе убиваем процесс по pid’у из файла и удаляем файл.

if [ ! -f $PID_PATH_NAME ]; then

-f — истина, если файл существует.

nohup “$PATH_TO_JAVA” $JVM_PARAMS -jar $PATH_TO_JAR $APP_PARAMS >> amazing.out 2>&1 &

nohup — утилита, запускающая указанную команду с игнорированием сигналов потери связи (SIGHUP). Т.е. команда будет продолжать выполняться в фоновом режиме даже после выхода юзера из системы.

Знак & в конце означает старт джава-приложения как демона, т.е. скрипт не будет дожидаться окончания джава приложения и пойдёт дальше по своим командам.

2>&1 означает stderr(2) перенаправляется (>) в stdout(&1). Здесь & указывает, что последующую за ней единицу следует воспринимать как дескриптор файла, а не как имя файла.

>> amazing.out — перенаправление потоков в файл.

“ ! — Expands to the process ID of the most recently executed background (asynchronous) command.” (с) man bash

Из этой ни разу не витиеватой фразы делаем глубокомысленный вывод, что $! содержит ID процесса.

2>/dev/null — перенаправление потока ошибок в никуда. Просто подавление ошибок.

Б.4) Нюансы.

SuccessExitStatus=143

Как я уже упомянул — это дополнительный «успешный» статус окончания работы приложения, от которого зависит будет ли ваше приложение перезапускаться или нет в случае ошибки, т.е. когда указано Restart=on-failure или always.

Читайте также:  Грустная рожица windows 10

Когда мы останавливаем службу « systemctl stop», то шлётся сигнал SIGTERM с кодом завершения приложения 15, который по умолчанию для службы считается успешным. Но джава завершается с кодом 143 вместо 15, поэтому нужно сказать убунте, что наш код 143 тоже должен считаться успешным.

Если не добавить этот статус, то при попытке остановить службу « systemctl stop», мы увидим статус “ failed” вместо “ inactive”, якобы приложение завершилось по ошибке, и если у нас стоит рестарт, то оно будет перезапущено, вместо остановки.

Почему 143? Потому что по Posix спецификации “The exit status of a command that terminated because it received a signal shall be reported as greater than 128”.

Джава решила следовать спецификации и, таким образом, 143–128 = 15 — это сигнал SIGTERM.

Type = forking

Если запустить джаву напрямую с типом « forking», то увидим такую картину в статусе службы:

Через 1,5 минуты служба отвалится по тайм-ауту и попробует рестартовать приложение, но снова безуспешно. Джава не может форкнуться. Поэтому если в службе вызываете джаву напрямую, то либо уберите этот пункт, либо ставьте явно « Type=simple».

Если запускаете .sh скрипт, то вот что будет при Type=forking:

Скрипт успешно завершился, а джава продолжает работать и может быть остановлена менеджером служб.

Основной процесс мёртв и джава не работает в фоне.

Если type=simple, то служба должна запускать джава приложение напрямую, либо через .sh-скрипт, который должен работать и ждать окончания джава приложения (т.е. не запускать её демоном).

Если type=forking, служба должна стартовать .sh-скрипт, который может завершиться после запуска джава-приложения (т.е. можно запустить её демоном).

Остановка и рестарт приложения по эксепшену.

Давайте сымитируем ошибку в пользовательском треде (не демоническом) и посмотрим, перезапустится ли служба ( пример кода на гитхабе). Если сделать это прямо в main-thread, то всё работает, как надо, приложение возвращает статус ошибки и перезапускается. Вот только main-thread как правило заканчивает свою жизнь быстро по выходу из метода main(String[] args), а приложение дальше живёт на остальных пользовательских тредах. Создадим такой отдельный тред и вызовем ошибку там. Оп-па, приложение завершается, вот только успешно, а не с ошибкой! Соответственно, перезапущена она при « Restart=on-failure» не будет. И если у нас не установлен общий « uncaught exception handler», мы даже увидим эту ошибку в логах службы:

Почему же так происходит?

Потому что джава возвращает только статус окончания main-треда и игнорирует статусы остальных пользовательских тредов.

Если ваше приложение должно всегда работать, то можно выставить « Restart=always». В ином случае рассмотрите вариант использования « Thread.setDefaultUncaughtExceptionHandler()» с логированием ошибки и « System.exit(1)» внутри этого обработчика.

В) Ошибки.

В.1) Как смотреть?

После запуска не забываем смотреть статус службы, там могли быть проблемы:

В таком случае стопорим службу (дабы не перезапускалась) и смотрим снова статус и последние строки лога службы:

Более полный лог можно посмотреть с помощью команды:

Также можем отслеживать живой лог с опцией -f :

В.2) Кавычки!

Важно не забывать знак равенства и не напутать с кавычками, если у вас в путях есть пробелы. Где-то кавычки нужны (в exec секции), а где-то нет (в working directory). Иначе рискуете наткнуться на кучу сообщений об ошибках, типа:

Читайте также:  Eiffel tower from windows

В.3) Права доступа.

Не забудьте добавить прав на запуск скрипта.

u означает чьи права на доступ будут изменены: юзера-владельца в данном случае.

Оператор + вызывает добавление выбранных битов режима файла к существующим. Т.е. добавляет права, а не переписывает их.

x — само право: выполнение (или поиск для директорий)

Если дали прав на скрипт, но не дали на директорию с логами и ресурсами:

Проверяем рестарт службы по ошибке:

Видно, что служба вылетела по ошибке и рестартовала через 5 секунд.

Общий вывод.

Встроенных средств в убунту достаточно для запуска jar-ников как служб, используем systemctl и радуемся безотказной работе!

Примеры кода лежат на гитхабе

Не стесняйтесь пожать руку на Medium или Telegram.

Источник

Как запустить jar в Linux

Java — это кроссплатформенный язык программирования, благодаря которому программы, написанные один раз, можно запускать в большинстве операционных систем: в Windows, Linux и даже MacOS. И всё это без каких-либо изменений.

Но программы, написанные на Java, распространяются в собственном формате .jar, и для их запуска необходимо специальное ПО — Java-машина. В этой небольшой статье мы рассмотрим, как запустить jar-файл в Linux.

Как запустить jar Linux

Как я уже сказал, для запуска jar-файлов нам необходимо, чтобы на компьютере была установлена Java-машина. Если вы не собираетесь ничего разрабатывать, вам будет достаточно Java Runtime Environment или JRE. Что касается версии, то, обычно, большинство программ работают с 7 или 8 версией. Если нужна только восьмая, то разработчики прямо об этом сообщают. Посмотреть версию Java и заодно убедиться, что она установлена в вашей системе, можно с помощью команды:

У меня установлена восьмая версия, с пакетом обновлений 171. Если вы получаете ошибку, что команда не найдена, то это значит, что вам нужно установить java. В Ubuntu OpenJDK JRE можно установить командой:

sudo apt install openjdk-8-jre

Если вы хотите скомпилировать пример из этой статьи, то вам понадобиться не JRE, а JDK, её можно установить командой:

sudo apt install openjdk-8-jdk-headless

Чтобы узнать, как установить Java в других дистрибутивах, смотрите статью по ссылке выше. Когда Java будет установлена, вы можете очень просто запустить любой jar-файл в Linux, передав путь к нему в качестве параметра Java-машине. Давайте для примера создадим небольшое приложение:

public class Main <
public static void main(String[] args) <
System.out.println(» Losst test app! «);
>
>

Затем скомпилируем наше приложение в jar-файл:

javac -d . Main.java
jar cvmf MANIFEST.MF main.jar Main.class

Теперь можно запустить наш jar-файл командой java с параметром -jar:

java -jar main.jar

Таким образом вы можете запустить любой jar-файл, который собран для вашей версии Java. Но не очень удобно каждый раз открывать терминал и прописывать какую-либо команду. Хотелось бы запускать программу по щелчку мышки или как любую другую Linux-программу — по имени файла.

Если мы дадим программе право на выполнение:

chmod u+x ./main.jar

И попытаемся её запустить, то получим ошибку:

Чтобы её исправить, нам понадобиться пакет jarwrapper:

sudo apt install jarwrapper

Теперь можно запускать java в Linux по щелчку мыши или просто командой.

Выводы

В этой небольшой статье мы рассмотрели, как запустить jar Linux с помощью java-машины, а также как упростить команду запуска. Если у вас остались вопросы, спрашивайте в комментариях!

Источник

Оцените статью