- Как создать свою службу в Linux
- Шаг 1. Создание скрипта
- Список инструкций Данный список взят по одной из ссылок в конце записи, описания нуждаются в доработке
- Список виртуальных объектов Та же хрень, что и с предыдущим списком
- Шаг 2+3. Оформление скрипта в качестве службы
- Systemd: пишем собственные .service и .target
- Чего я хотел?
- Что я сделал?
- Что такое service?
- Что такое target?
- Как я это сделал?
- Какие были проблемы?
- Результат
- Cheat sheet
- Systemd за пять минут
Козлы и бараны — это совершенно разные люди!
Как создать свою службу в Linux
23.08.11 03:12 / Обновлено 25.08.11 20:59 | Версия для печати | Ubuntu | Debian |
- Создаём скрипт для управления сервисом, в специальном формате.
- Помещаем его в хранилище сервисных скриптов. Это каталог /etc/init.d
- Обрабатываем скрипт специальной утилитой update-rc.d (или insserv)
Шаг 1. Создание скрипта
Вот шаблон, чтобы не запутаться:
Шаблон от разработчиков находится по адресу /etc/init.d/skeleton
Обратите внимание на блок, ограниченный метками [ ### BEGIN INIT INFO ] и [ ### END INIT INFO ]. Он выделен жирным.
Этот блок полностью закомментирован, то есть при выполнении скрипта его содержимое игнорируется. Однако, он содержит инструкции для утилиты update-rc.d, которая прочитает их, поймёт и раскидает ссылки на этот скрипт по уровням запуска системы.
Подробнее про процессы, службы, уровни запуска и устройство каталогов запуска можно прочитать здесь.
Этот блок обязателен для скрипта, управляющего сервисом. Остановимся на нём подробнее.
### BEGIN INIT INFO — метка начала списка инструкций
### END INIT INFO — метка конца списка инструкций
Все строки между ними должны быть в формате:
# Инструкция: арг1 [арг2. ]
Строка начинается со знака # и последующего одного пробела. После инструкции должно стоять двоеточие, агрументы разделяются пробелами.
Список инструкций
Данный список взят по одной из ссылок в конце записи, описания нуждаются в доработке
Provides | Описывает предоставляемые этим скриптом объекты (арг1, агр2, . ) таким способом, что, когда скрипт запускается с аругментом start, данные объекты считаются существующими, и, следовательно, другие скрипты в init, которые требуют существование этих объектов, смогут запуститься на более поздней стадии. Обычно, можно использовать имя скрипта в качестве объекта, но так же можно использовать имя сервиса, которую он заменяет. Виртуальные объекты тут не указываются. Они определены вне скриптов init.d |
Required-Start | Задаёт объекты, которые должны существовать, чтобы запустить скрипт. Можно использовать при необходимости виртуальные объекты, как описано ниже. Если объекты не указаны, то этот скрипт может быть запущен сразу после старта, не требуя подключенных локальных файловых систем, запущенного системного журнала и т.д. |
Required-Stop | Задаёт объекты, используемые сервисом, предоставляемой скриптом. Объект, предоставляемый этим скриптом должен завершиться до завершения перечисленных здесь объектов, чтобы избежать конфликтов. Обычно, здесь указывают те же объекты, что и в Required-Start |
Should-Start | Задаёт объекты, которые, если существуют, должны должны быть запущены перед сервисом, предоставляемым данным скриптом. Это допускает слабые зависимости, которые не приводят сервис к ошибке, если объекты не доступны. Можно использовать при необходимости виртуальные объекты, как описано ниже. |
Should-Stop | Задаёт объекты, если существуют должны быть остановлены уже после данного сервиса. Обычно, здесь указывают те же объекты, что и в Should-Start |
Default-Start Default-Stop | Задаёт уровни запуска, на которых скрипт должен быть запущен (остановлен) по умолчанию. Например, если сервис должен быть запущен на только уровнях 3, 4 и 5, укажите «Default-Start: 3 4 5» и «Default-Stop: 0 1 2 6». |
Short-Description | Задаёт короткое описание действия скрипта. Ограничено одной строкой. |
Description | Задаёт более подробное описание действия скрипта. Может быть в несколько строк, в этом случае, каждая строка описания должна начинаться с символа # с последующим знаком табуляции или как минимум 2-мя символами пробела. Описание заканчивается перед линией, не совпадающим с этим условием. |
X-Start-Before X-Stop-After | Задаёт обратные зависимости, которые значат то же, как если бы они были указаны в should-start и should-stop в пакетах, указанных тут. |
Уровни запуска определяют, в какие из каталогов /etc/rcX.d будут помещены ссылки на текущий скрипт.
Для отслеживания зависимостей важны инструкции Provides, Required- и Should-. Остальные не используются. На основе зависимостей утилита update-rc.d (или insserv) упорядочивает скрипты в каталоге определённого уровня запуска ( /etc/rcX.d ).
Список виртуальных объектов
Та же хрень, что и с предыдущим списком
$local_fs | Все локальные фаловые системы подключены. Все скрипты, которые производят запись в /var/ должны зависеть от этого, если они уже не зависят от $remote_fs |
$network | низкоуровневая сеть, т.е. сетевые карты, может подразумеваться PCMCIA запущеной |
$named | Демоны, которые могут предоставлять разрешение доменных имён предполагаются запущенными. Например, DNS, NIS+ или LDAP |
$portmap | Демоны, предоставляющие сервис SunRPC/ONCRPC portmapping как указано в 1833 (если они есть) |
$remote_fs | Все файловые системы подключены. Скрипты, которые должны быть запущены во время остановки системы до того, как всем процессам будет отправлен сигнал уничтожения, должны зависеть от $remote_fs. |
$syslog | системный журнал функционирует |
$time | установленно корректное системное время, например, ntp или rdate, или RTC |
$all | Запускает скрипт как можно последним |
Шаг 2+3. Оформление скрипта в качестве службы
Допустим, имя нашего самописного скрипта somestuff. Именно так, somestuff, без всяких расширений.
Делаем его исполняемым:
chmod +x ./somestuff
Копируем в хранилище сервисных скриптов:
cp ./somestuff /etc/init.d
И делаем прописку в каталогах уровней запуска:
update-rc.d somestuff defaults
Полный путь давать не надо, только имя в /etc/init.d/
Для выписки (удаления всех симлинков на этот скрипт из всех каталогов уровней запуска) делаем:
Источник
Systemd: пишем собственные .service и .target
У меня появился Linux на домашнем компьютере, и я поспешил обжиться в новой ОС. Она была установлена с systemd init process. Это было мое первое знакомство с этим новым инструментом. Cвой ноутбук я использую для каждодневной жизни и для программирования. Мне хотелось включать рабочие программы (Apache2 и MySQL) только на время, пока я их использую, чтобы не тратить впустую ресурсы своего компьютера. Дополнительно, для тестирования я написал bash скрипт, который выгружает содержимое одной из MySQL БД c жесткого диска в ОЗУ (в tmpfs) – так тесты выполняются значительно быстрее. По идее, я мог бы начинать свой рабочий день вот так:
И заканчивать его:
Но мне хотелось сделать вещи “как надо”.
Чего я хотел?
Я хотел достичь 2 целей:
- Мне было лень писать 2 команды (запуск apache и запуск mysql), т.к. я знал, что обе программы всегда будут выключаться и включаться синхронно. Хотелось выполнять эту операцию одной командой.
- Дело попахивало неприятностями, если компьютер перезагрузится пока моя база данных будет сидеть в tmpfs – все файлы будут потеряны. Конечно, я делал бекапы, но мне опять же было лень восстанавливать их вручную после каждой непредвиденной перезагрузки.
Что я сделал?
В итоге я объединил Apache2 и MySQL в один target. Это позволило запускать оба сервиса одной командой. А свой mysqld-tmpfs скрипт я декларировал в виде сервиса в глазах systemd. Будучи сервисом, я уверен, что systemd выполнит его корректную остановку, если система пойдет на перезагрузку или еще в какую-то нештатную ситуацию, и моя БД без потерь сохранится на жесткий диск.
Что такое service?
Это некоторая программа, которая выполняется в фоне и предоставляет полезную функциональность. К примеру, Apache веб сервер. Сервисы можно запускать и останавливать. Некоторые сервисы могут запускаться и останавливаться автоматически по определенным событиям (загрузка ОС, выгрузка ОС и тп). Так же их можно запускать/останавливать вручную. Сервис декларируется в /etc/systemd/system/my-name.service файлах (с суффиксом “.service”).
Что такое target?
Target в systemd очень похож на runlevel в openRC, но это все-таки разные вещи. Во-первых, target позволяет группировать 1 и более сервисов в единый блок. Группируя сервисы в targets, ими проще управлять. Во-вторых, systemd автоматически включает/выключает targets по событиям. “Включение” target означает включение всех сервисов, которые он объединяет в себе. К примеру, если в systemd настроен target по умолчанию my-favorite.target, то при загрузке системы systemd включит все сервисы, которые задекларированы внутри my-favorite.target. В какой-то момент в консоли можно набрать:
Все сервисы из my-another.target будут включены, и все включенные сервисы не из my-another.target будут выключены. Это очень похоже на переключение runlevel в openRC. Однако, systemd поддерживает включение более чем 1 target. Вот пример:
После выполнения этих команд в системе будет работать объединение сервисов из my-favorite.target и my-another.target.
Как я это сделал?
В итоге у меня получился вот такой mysqld-tmpfs.service файл:
И вот такой programming.target файл:
Какие были проблемы?
При остановке programming.target почему-то нижележащие apache2.service и mysqld.service не останавливались. Почитав как следует man page, я нашел проблему: systemd останавливает сервисы “лениво” — если никто не требует запущенный сервис, и он не был запущен явным образом, а как зависимость для какого-то другого сервиса, то systemd остановит его только при одном из 3 обстоятельств:
- Запустится какой-то другой сервис, который в своей декларации указывает, что он конфликтует с нашим сервисом.
- Выполнится systemctl isolate some-another.target или systemctl stop this.service.
- Наш сервис может запросить в своей декларации останавливать себя не ленивым образом, а активным, добавив вот такую строку в [Unit] секцию: StopWhenUnneeded=true
Декларации “чужих” сервисов можно менять создавая файлы /etc/systemd/system/name-i-alter.service.d/*.conf. Я просто создал /etc/systemd/system/apache2.service/auto-stop.conf и /etc/systemd/system/mysqld.service.d/auto-stop.conf и поместил туда ту строку.
Другая проблема, на которую я, наткнулся была в том, что systemd не очень любит symlinks. Я не большой любитель “загаживать” системные директории типа /etc, /bin, /usr своими локальными продуктами жизнедеятельности, поэтому изначально я попытался свой /etc/systemd/system/mysqld-tmpfs.service сделать symlink на /root/scripts/mysqld-tmpfs.service файл, т.е. хранить сам файл в домашнем каталоге root пользователя. Но systemctl команда отказывалась работать с таким сервисом выдавая малопонятные ошибки. Оказалось, что определенную часть своей внутренней кухни systemd делает именно на symlinks, и ему тогда “трудно” отличать внутреннюю кухню (свои symlinks) от сторонних *.service файлов (если они тоже являются symlinks). Удалив symlink из /etc/systemd/system/mysqld-tmpfs.service и скопировав туда содержимое настоящего файла, я решил эту проблему. Более подробное описание этой проблемы можно прочитать тут: bugzilla.redhat.com/show_bug.cgi?id=955379
Результат
Я достиг своей цели. Начиная рабочий день:
Когда нужно выполнить тесты на своем проекте:
Когда я хочу демонтировать БД из tmpfs в жесткий диск (хотя на практике я так почти не делаю, а просто оставляю БД в tmpfs на целый день, и при выключении systemd за меня запускает демонтировку из tmpfs в жесткий диск):
Когда я закончил работать и хочу остановить рабочие программы:
Cheat sheet
Некоторые полезности при работе с systemd:
- Вызывайте systemctl daemon-reload, если вы изменили декларацию чего-либо (systemd считает файлы декларации заново)
- systemctl start my-name.(service|target) – запуск сервиса или target
- systemctl stop my-name.(service|target) – остановка сервиса или target
- systemctl enable my-name.service – сервисы могут декларировать при каких включенных targets они должны включаться. Для этого используется [Install] секция в файле декларации сервиса. Вы, как сисадмин, имеете власть на установку этого “пожелания” сервиса. Часто сервисы “устанавливаются” в target по умолчанию multi-user.target или в похожее.
- systemctl disable my-name.service – обратная операция по отношению к enable: деассоциировать связь между my-name.service и targets, которые он запросил в [Install] секции своей декларации.
- systemctl isolate my.target — включить все сервисы из my.target и выключить все остальные включенные сервисы.
- systemctl status my-name.(service|target) — узнать статус (запущен/остановлен) у сервиса или target.
Надеюсь, эта статья кому-то поможет при осваивании systemd. Я попытался сделать ее компактной, и если упустил из внимания какие-то дополнительные вопросы, спрашивайте в комментариях!
Источник
Systemd за пять минут
Наша компания занимается администрированием веб-серверов на базе CentOS. Довольно часто наши клиенты используют веб-приложения на базе python, ruby или java. Для автозапуска подобных приложений есть готовые шаблоны для написания стартап-скриптов. Но прогресс не стоит на месте, вышел уже второй релиз CentOS 7 и, следуя старой традиции «не ставить dot-zero релизы на продакшен», мы начинаем предлагать клиентам сервера на базе CentOS 7.1 (1503).
В CentOS7, так же как и в его родителе RHEL7, используется systemd — менеджер системы и служб для Linux, совместимый со скриптами инициализации SysV и LSB. systemd обеспечивает возможности агрессивной параллелизации и много всего прочего.
Огромный монстр с множеством возможностей, гибкими настройками и мегабайтами документации…
Но что делать, если стоит задача быстро-быстро, вот прямо вчера, сделать автозапуск некоего сервиса?
Давайте выжмем из документации минимально необходимый набор информации для создания простых старт-стоп скриптов.
Systemd запускает сервисы описанные в его конфигурации.
Конфигурация состоит из множества файлов, которые по-модному называют юнитами.
Все эти юниты разложены в трех каталогах:
/usr/lib/systemd/system/ – юниты из установленных пакетов RPM — всякие nginx, apache, mysql и прочее
/run/systemd/system/ — юниты, созданные в рантайме — тоже, наверное, нужная штука
/etc/systemd/system/ — юниты, созданные системным администратором — а вот сюда мы и положим свой юнит.
Юнит представляет из себя текстовый файл с форматом, похожим на файлы .ini Microsoft Windows.
[Название секции в квадратных скобках]
имя_переменной = значение
Для создания простейшего юнита надо описать три секции: [Unit], [Service], [Install]
В секции Unit описываем, что это за юнит:
Названия переменных достаточно говорящие:
Далее следует блок переменных, которые влияют на порядок загрузки сервисов:
Запускать юнит после какого-либо сервиса или группы сервисов (например network.target):
After=syslog.target
After=network.target
After=nginx.service
After=mysql.service
В итоге переменная Wants получается чисто описательной.
Если сервис есть в Requires, но нет в After, то наш сервис будет запущен параллельно с требуемым сервисом, а не после успешной загрузки требуемого сервиса
В секции Service указываем какими командами и под каким пользователем надо запускать сервис:
(по умолчанию): systemd предполагает, что служба будет запущена незамедлительно. Процесс при этом не должен разветвляться. Не используйте этот тип, если другие службы зависят от очередности при запуске данной службы.
systemd предполагает, что служба запускается однократно и процесс разветвляется с завершением родительского процесса. Данный тип используется для запуска классических демонов.
Также следует определить PIDFile=, чтобы systemd могла отслеживать основной процесс:
Команды на старт/стоп и релоад сервиса
ExecStart=/usr/local/bin/bundle exec service -C /work/www/myunit/shared/config/service.rb —daemon
ExecStop=/usr/local/bin/bundle exec service -S /work/www/myunit/shared/tmp/pids/service.state stop
ExecReload=/usr/local/bin/bundle exec service -S /work/www/myunit/shared/tmp/pids/service.state restart
Тут есть тонкость — systemd настаивает, чтобы команда указывала на конкретный исполняемый файл. Надо указывать полный путь.
Таймаут в секундах, сколько ждать system отработки старт/стоп команд.
Попросим systemd автоматически рестартовать наш сервис, если он вдруг перестанет работать.
Контроль ведется по наличию процесса из PID файла
В секции [Install] опишем, в каком уровне запуска должен стартовать сервис
multi-user.target или runlevel3.target соответствует нашему привычному runlevel=3 «Многопользовательский режим без графики. Пользователи, как правило, входят в систему при помощи множества консолей или через сеть»
Вот и готов простейший стартап скрипт, он же unit для systemd:
myunit.service
Кладем этот файл в каталог /etc/systemd/system/
Смотрим его статус systemctl status myunit
Видим, что он disabled — разрешаем его
systemctl enable myunit
systemctl -l status myunit
Если нет никаких ошибок в юните — то вывод будет вот такой:
Запускаем сервис
systemctl start myunit
Смотрим красивый статус:
systemctl -l status myunit
Если есть ошибки — читаем вывод в статусе, исправляем, не забываем после исправлений в юните перегружать демон systemd
Источник