Запуск java приложения как сервис linux

Обновлено: 19.09.2024

По профилю работы DevOps-инженером я часто занимаюсь автоматизацией установки и настройки разнообразных IT-систем в различных средах: от контейнеров до облака. Приходилось работать со многими системами, основанными на Java-стеке: от небольших (вроде Tomcat), до масштабных (Hadoop, Cassandra и др.).

При этом почти каждая такая система, даже самая простая, почему-то имела сложную неповторимую систему запуска. Как минимум, это были многострочные shell-скрипты, как в Tomcat, а то и целые фреймворки, как в Hadoop. Мой нынешний "пациент" из этой серии, вдохновивший меня на написание этой статьи — хранилище артефактов Nexus OSS 3, скрипт запуска которого занимает

Непрозрачность, избыточность, запутанность startup-скриптов создает проблемы даже при ручной установке одного компонента на локальной системе. А теперь представьте, что набор таких компонентов и сервисов нужно запаковать в Docker-контейнер, попутно написав еще один слой абстракции для мало-мальски адекватного оркестрирования, развернуть в Kubernetes-кластере и реализовать этот процесс в виде CI/CD-пайплайна.

Короче говоря, давайте на примере упомянутого Nexus 3 разберемся, как вернуться из лабиринта shell-скриптов к чему-то более похожему на java -jar <program.jar> , учитывая наличие удобных современных DevOps-инструментов.

Если в двух словах, то в древние времена, когда при упоминании UNIX не переспрашивали: "в смысле, Linux?", не было Systemd и Docker и др., для управления процессами использовались переносимые shell-скрипты (init-скрипты) и PID-файлы. Init-скрипты задавали необходимые настройки окружения, которые в разных UNIX-ах были свои, и, в зависимости от аргументов, запускали процесс или перезапускали/останавливали его с помощью ID из PID-файла. Подход простой и понятный, но эти скрипты переставали работать при каждой нестандартной ситуации, требуя ручного вмешательства, не позволяли запустить несколько копий процесса… но не суть.

Так вот, если внимательно посмотреть на упомянутые выше startup-скрипты в Java-проектах, то можно в них разглядеть явные признаки этого доисторического подхода, включая даже упоминания SunOS, HP-UX и других UNIX-ов. Как правило, такие скрипты делают примерно следующее:

  • используют синтаксис POSIX shell со всеми его костылями для UNIX/Linux-переносимости
  • определяют версию и релиз ОС через uname , /etc/*release и т.п.
  • ищут JRE/JDK в укромных уголках файловой системы и выбирают наиболее "подходящую" версию по хитрым правилам, иногда еще и специфичным для каждой ОС
  • рассчитывают числовые параметры JVM, например, размер памяти ( -Xms , -Xmx ), количество потоков GC и др.
  • оптимизируют JVM через -XX -параметры с учетом специфики выбранной версии JRE/JDK
  • отыскивают свои компоненты, библиотеки, пути к ним по окружающим директориям, конфигурационным файлам и т.п.
  • настраивают окружение: ulimits, переменные среды и т.п.
  • генерируют CLASSPATH циклом типа: for f in $path/*.jar; do CLASSPATH="$:$f"; done
  • парсят аргументы командной строки: start|stop|restart|reload|status|.
  • собирают Java-команду, которую в итоге нужно выполнить, из перечисленного выше
  • и, наконец, выполняют эту Java-команду. Зачастую при этом явно или неявно используются все те же пресловутые PID-файлы, & , nohup , специальные TCP-порты и прочие трюки из прошлого столетия (см. пример из Karaf)

Упомянутый скрипт запуска Nexus 3 — подходящий пример такого скрипта.

По сути, вся перечисленная выше скриптовая логика, как бы, пытается заменить системного администратора, который бы установил и настроил все вручную под конкретную систему от начала до конца. Но вообще любые требования самых разнообразных систем учесть, в принципе, невозможно. Поэтому получается, наоборот, головная боль, как для разработчиков, которым нужно поддерживать эти скрипты, так и для системных инженеров, которым в этих скриптах потом нужно разбираться. С моей точки зрения, системному инженеру гораздо проще один раз разобраться в параметрах JVM и настроить ее как надо, чем каждый раз при установке новой системы разбираться в тонкостях ее startup-скриптов.

  • за очень редким исключением, UNIX == Linux
  • задача управления процессами решена как для отдельного сервера (Systemd, Docker), так и для кластеров (Kubernetes и т.п.)
  • появилась куча удобных инструментов управления конфигурациями (Ansible и др.)
  • в администрирование пришла и уже основательно закрепилась тотальная автоматизация: вместо ручной настройки хрупких неповторимых "серверов-снежинок" теперь можно автоматически собирать унифицированные репродуцируемые виртуальные машины и контейнеры с помощью целого ряда удобных инструментов, включая упомянутые выше Ansible и Docker
  • повсеместно используются инструменты сбора runtime-статистики, как для самой JVM (пример), так и для Java-приложения (пример)
  • и, самое главное, появились специалисты: системные и DevOps-инженеры, которые умеют использовать перечисленные выше технологии и понимают, как правильно установить JVM на конкрентной системе и впоследствии подстроить ее с учетом собранной runtime-статистики

Так что давайте снова пройдемся по функционалу startup-скриптов еще раз с учетом перечисленных пунктов, не пытаясь при этом делать работу за системного инженера, и уберем оттуда все "лишнее".

  • синтаксис POSIX shell ⇒ /bin/bash
  • определение версии ОС ⇒ UNIX == Linux, если есть ОС-специфичные параметры, можно описать их в документации
  • поиск JRE/JDK ⇒ у нас единственная версия, и это OpenJDK (ну или Oracle JDK, если уж очень нужно), java и компания есть в стандартном системном пути
  • расчет числовых параметров JVM, тюнинг JVM ⇒ это можно описать в документации по скалированию приложения
  • поиск своих компонентов и библиотек ⇒ описать структуру приложения и способы ее настройки в документации
  • настройка окружения ⇒ описать в документации требования и особенности
  • генерация CLASSPATH ⇒ -cp path/to/my/jars/* или даже, вообще, Uber-JAR
  • парсинг аргументов командной строки ⇒ аргументов не будет, т.к. обо всем, кроме запуска, позаботится менеджер процессов
  • сборка Java-команды
  • выполнение Java-команды

В итоге, нам нужно просто собрать и выполнить Java-команду вида java <opts> -jar <program.jar> с помощью выбранного менеджера процессов (Systemd, Docker и т.п.). Все параметры и опции ( <opts> ) мы оставляем на усмотрение системного инженера, который подстроит их под конкретную среду. Если список опций <opts> довольно длинный, можно вновь вернуться к идее startup-скрипта, но, в этом случае, максимально компактного и декларативного, т.е. не содержащего никакой программной логики.

В качестве примера давайте посмотрим, как можно упростить скрипт запуска Nexus 3.

Самый простой вариант, чтобы не залезать в дебри этого скрипта — просто запустить его в реальных условиях ( ./nexus start ) и посмотреть на результат. Например, можно найти полный список аргументов запущенного приложения в таблице процессов (через ps -ef ), или запустить скрипт в режиме отладки ( bash -x ./nexus start ), чтобы наблюдать весь процесс его выполнения и в самом конце — команду запуска.

У меня в итоге получилась следующая Java-команда

Вначале применим к ней пару простых приемов:

  • поменяем /the/long/and/winding/road/to/my/java на java , ведь она есть в системном пути
  • поместим список Java-параметров в отдельный массив, отсортируем его и уберем дубликаты

Теперь можно идти в глубину.

Install4j — это такой графический Java-инсталлятор. Похоже, что он используется для начальной установки системы. На сервере он нам не нужен, убираем.

Договоримся о размещении компоненты и данные Nexus на файловой системе:

  • поместим само приложение в /opt/nexus-<version>
  • для удобства создадим символическую ссылку /opt/nexus -> /opt/nexus-<version>
  • сам скрипт разместим вместо исходного как /opt/nexus/bin/nexus
  • все данные нашего Nexus будут лежать на отдельной файловой системе, смонтированной как /data/nexus

Само создание каталогов и ссылок — удел систем управления конфигурациями (на все про все 5-10 строчек в Ansible), поэтому оставим эту задачу системным инженерам.

Пусть наш скрипт при запуске меняет рабочий каталог на /opt/nexus — тогда мы сможем поменять пути к компонентам Nexus на относительные.

Опции вида -Dkaraf.* — это настройки Apache Karaf, OSGi-контейнера, в который, очевидно, "запакован" наш Nexus. Поменяем karaf.home , karaf.base , karaf.etc и karaf.data соответственно размещению компонентов, по возможности используя относительные пути.

Видя, что CLASSPATH состоит из списка jar-файлов, которые лежат в одном каталоге lib/ , заменим весь этот список на lib/* (придется также выключить wildcard expansion с помощью set -o noglob ).

Поменяем java на exec java , чтобы наш скрипт на запускал java как дочерний процесс (менеджер процессов этот дочерний процесс просто не увидит), а "заменял" себя на java (описание exec).

Посмотрим, что нас получилось:

Итого всего 27 строчек вместо >400, прозрачно, понятно, декларативно, никакой лишней логики. При необходимости этот скрипт легко превратить в темплейт для Ansible/Puppet/Chef и добавить туда только ту логику, которая нужна для конкретной ситуации.

Этот скрипт можно использовать в качестве ENTRYPOINT в Dockerfile или вызывать в unit-файле Systemd, заодно подстроив там ulimits и другие системные параметры, например:

Какие выводы можно сделать из этой статьи? В принципе, все сводится к паре пунктов:

  1. У каждой системы свое предназначение, т.е., не нужно забивать гвозди микроскопом.
  2. Простота (KISS, YAGNI) рулит — реализовывать только то, что нужно для данной конкретной ситуации.
  3. И самое главное: круто, что есть IT-специалисты разного профиля. Давайте будем взаимодействовать и делать наши IT-системы проще, понятнее и лучше! :)

Спасибо за внимание! Буду рад обратной связи и конструктивной дискуссии в комментариях.

Я написал приложение сервера Java, которое работает на стандартном виртуальном размещенном Linux-решении. Приложение работает все время, слушая подключения сокетов и создавая для них новые обработчики. Это реализация на стороне сервера для клиент-серверного приложения.

Способ, которым я начинаю, заключается в том, чтобы включить его в начало rc.local script сервера. Однако как только я начал работать, я не знаю, как получить к нему доступ, чтобы остановить его, и если я хочу установить обновление, поэтому мне нужно перезагрузить сервер, чтобы перезапустить приложение.

На компьютере с Windows для этого типа приложения я могу создать службу Windows, а затем я могу остановить и запустить ее, как я хочу. Есть ли что-то подобное в ящике Linux, поэтому, если я запустил это приложение, я могу остановить его и перезапустить, не выполняя полный перезапуск сервера.

Мое приложение называется WebServer.exe. Он запускается при запуске сервера, включая его в мой rc.local как таковой:

ОТВЕТЫ

Ответ 1

Я написал еще одну простую оболочку:

Вы можете выполнить полный учебник по init.d здесь и для systemd (ubuntu 16+) здесь

Если вам нужен выходной журнал, замените 2

Ответ 2

Простым решением является создание script start.sh, который запускает Java через nohup, а затем сохраняет PID в файл:

Затем ваша остановка script stop.sh будет читать PID из файла и убить приложение:

Конечно, я не рассмотрел некоторые детали, например, проверить, существует ли процесс и удалить pid.txt , если вы закончили.

Ответ 3

Служба Linux init script хранится в /etc/init.d . Вы можете скопировать и настроить файл /etc/init.d/skeleton , а затем вызвать

Ответ 4

Возможно, это не лучшее решение для разработчиков, но полезно для общего использования сервера для локальной группы или аналогичного.

Используйте screen , чтобы запустить ваш сервер, а затем отсоединиться до выхода из системы, это приведет к тому, что процесс будет запущен, после чего вы сможете снова подключиться в любой момент.

Workflow:

Запустите экран: screen

Запустите свой сервер: java -jar minecraft-server.jar

Отсоедините, нажав: Ctl-a , d

Повторно присоединить: screen -r

Ответ 5

Другим альтернативом, который также довольно популярен, является Java Service Wrapper. Это также довольно популярно вокруг сообщества OSS.

Ответ 6

Ссылаясь на Spring приложение для загрузки как службы, я бы пошел на systemd поскольку он является самым легким, наименее многословным и наилучшим образом интегрирован в современные дистрибутивы (и даже не очень современные, такие как CentOS 7.x).

Ответ 7

Из Spring Загрузите приложение как услугу, я могу порекомендовать приложение supervisord на основе Python, См. Этот вопрос о переполнении стека для получения дополнительной информации. Это очень просто настроить.

Ответ 8

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

Ответ 9

Ответ 10

Вы можете использовать Thrift server или JMX для связи с вашей службой Java.

Ответ 11

В других ответах хорошая работа дает настраиваемые сценарии и настройки в зависимости от вашей платформы. В дополнение к этим, вот зрелые, специальные программы, о которых я знаю:

  • JSW от TanukiSoftware
  • YAJSW - это клон с открытым исходным кодом из вышеперечисленного. Он написан на Java, и это процесс няни, который управляет дочерним процессом (ваш код) в соответствии с конфигурациями. Работает на windows/linux.
  • JSVC является родным приложением. Это также процесс няньки, но он вызывает ваше дочернее приложение через JNI, а не как подпроцесс.

Ответ 12

Чтобы запустить Java-код в качестве демона (службы), вы можете написать заглушку на основе JNI.

для примера кода, основанного на JNI. В этом случае вы демонизируете код, который был запущен как Java, а основной цикл выполняется в C. Но также можно поместить основной цикл daemon в Java.

Получайте удовольствие от JNI!

Ответ 13

Однако как только я начал работать, я не знаю, как получить к нему доступ, чтобы остановить его.

Вы можете написать простой stop script, который будет использоваться для вашего java-процесса, извлекает PID и вызывает kill на нем. Это не фантазия, но это прямо. Что-то вроде этого может помочь в начале:

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 можно установить командой:

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

Чтобы узнать, как установить 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

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

sudo apt install jarwrapper

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

Выводы

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

Нет похожих записей


Статья распространяется под лицензией Creative Commons ShareAlike 4.0 при копировании материала ссылка на источник обязательна.

Я написал серверное приложение Java, которое работает на стандартном виртуальном размещенном решении Linux. Приложение работает все время прослушивания сокетов соединений и создания новых обработчиков для них. Это реализация на стороне сервера для клиент-серверного приложения.

Как я начинаю это, включив его в start up rc.местные скрипт сервера. Однако после запуска я не знаю, как получить к нему доступ, чтобы остановить его, и если я хочу установить обновление, поэтому я должен перезапустить сервер, чтобы перезапустить приложение.

на ПК с windows для этого типа приложений я мог бы создать службу windows, а затем я могу остановить и запустить ее, как я хочу. Есть ли что-то подобное в Linux box, чтобы при запуске этого приложения я мог остановить его и перезапустить без полного перезапуска сервера.

мое приложение называется WebServer.исполняемый. Он запускается при запуске сервера, включая его в мой rc.местные такие как:

Я написал здесь еще одну простую обертку:

вы можете следовать полный учебник для init.d здесь и для systemd (ubuntu 16+) здесь

Если вам нужен журнал вывода, замените 2

простым решением является создание скрипта start.sh это запускает Java через nohup, а затем сохраняет PID в файл:

тогда ваш сценарий остановки stop.sh прочитал бы PID из файла и убил бы приложение:

конечно, я опустил некоторые детали, например, проверить, существует ли процесс и удалить pid.txt Если вы закончили.

скрипт инициализации службы Linux хранятся в /etc/init.d . Вы можете копировать и настраивать /etc/init.d/skeleton файл, а затем вызов

возможно, не лучшее решение для dev-ops, но хорошо для общего использования сервера для вечеринки lan или аналогичного.

использовать screen для запуска сервера, а затем отсоединить перед выходом из системы, это будет держать процесс работает, вы можете повторно подключить в любой момент.

документооборот:

пуск экран: screen

запустить сервер: java -jar minecraft-server.jar

отключить нажатием: Ctl-a , d

прикрепления: screen -r

Другой альтернативой, которая также довольно популярна, является Java Service Wrapper. Это также довольно популярно в сообществе OSS.

ссыль приложение Spring Boot в качестве службы а также, я бы пошел на systemd версия, так как это самый простой, наименее подробный и лучше всего интегрирован в современные дистрибутивы (и даже не очень современные, такие как CentOS 7.икс.)

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

С приложение Spring Boot в качестве службы, Я могу порекомендовать Python-based supervisord приложение. См. этот вопрос переполнения стека для получения дополнительной информации. Это очень просто настроить.

Я написал серверное приложение Java, работающее на стандартном виртуальном хостинге Linux. Приложение все время работает, отслеживая соединения сокетов и создавая для них новые обработчики. Это серверная реализация клиент-серверного приложения.

Я начинаю это с включения в сценарий запуска rc.local сервера. Однако после запуска я не знаю, как получить к нему доступ, чтобы остановить его, и хочу ли я установить обновление, поэтому мне нужно перезапустить сервер, чтобы перезапустить приложение.

На ПК с Windows для этого типа приложений я мог бы создать службу Windows, а затем остановить и запустить ее по своему усмотрению. Есть ли что-нибудь подобное в коробке с Linux, чтобы, если я запустил это приложение, я мог остановить его и перезапустить, не выполняя полного перезапуска сервера.

Мое приложение называется WebServer.exe. Он запускается при запуске сервера, включая его в мой rc.local как таковой:

Я немного новичок в Linux, поэтому любой пример будет оценен с любыми постами. Однако у меня есть SSH и полный FTP-доступ к устройству для установки любых обновлений, а также доступ к панели Plesk.

Ответы

PbxMan picture

Я написал здесь еще одну простую оболочку:

Вы можете ознакомиться с полным руководством для init.d здесь и для systemd (ubuntu 16+) здесь.

Если вам нужен выходной журнал, замените 2

Wernsey picture

Простое решение - создать скрипт start.sh, который запускает Java через nohup, а затем сохраняет PID в файл:

Затем ваш стоп-скрипт stop.sh прочитает PID из файла и убьет приложение:

Конечно, я упустил некоторые детали, такие как проверка существования процесса и удаление pid.txt если вы закончили.

Arcadien picture

Сценарий инициализации службы Linux хранится в /etc/init.d . Вы можете скопировать и настроить файл /etc/init.d/skeleton , а затем вызвать

Может быть, это не лучшее решение для разработчиков, но подходит для общего использования сервера для локальной вечеринки или чего-то подобного.

Используйте screen для запуска вашего сервера, а затем отсоедините его перед выходом из системы, это будет поддерживать процесс, и вы можете повторно подключиться в любой момент.

Читайте также: