В статье речь будет идти о systemd, который вошел в Debian 8 jessie. Я пишу о Debian, потому что пользуюсь именно им. Пишу о systemd не потому что его фанат, но некоторые вещи меня действительно радуют.

Так почему же больше не нужны supervisord и forever?

Задача supervisord и его аналога написанного на nodejs — forever: демонизировать недемонизируемое и поднимать, когда оно падает. Эти задачи сейчас выполняет штатный systemd. Что в этом хорошего? Ну как минимум не нужны дополнительные инструменты (которых кстати не только два упомянутых) и есть штатное системное средство для решения таких задач.

Например очень легко настроить таким образом sshd, чтобы он поднимался в случае падения или kill. Нам нужно найти .service файл нашего sshd. Для этого выполним:
root@lynx:~# systemctl status ssh
? ssh.service - OpenBSD Secure Shell server
   Loaded: loaded (/lib/systemd/system/ssh.service; enabled)
   Active: active (running) since Fri 2015-10-09 20:09:29 UTC; 55s ago
 Main PID: 26884 (sshd)
   CGroup: /system.slice/ssh.service
           L-26884 /usr/sbin/sshd -D

Oct 09 20:09:29 lynx sshd[26884]: Server listening on 0.0.0.0 port 22.
Oct 09 20:09:29 lynx sshd[26884]: Server listening on :: port 22.

Собственно /lib/systemd/system/ssh.service и есть нужный нам файл. Вот такое его содержимое по умолчанию:
[Unit]
Description=OpenBSD Secure Shell server
After=network.target auditd.service
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run

[Service]
EnvironmentFile=-/etc/default/ssh
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure

[Install]
WantedBy=multi-user.target
Alias=sshd.service

В секции Service есть специальная опция “Restart” она как раз указывает, в каких случаях перезапускать сервис автоматически. Мы можем написать туда:
Restart=always

И при любом падении или попытке не взлететь sshd будет перезапущен. Чтобы изменения в файле вступили в силу, нужно выполнить:
root@lynx:~# systemctl daemon-reload
root@lynx:~# systemctl restart ssh

Теперь проверим, что сервис работает:
root@lynx:~# systemctl status ssh
? ssh.service - OpenBSD Secure Shell server
   Loaded: loaded (/lib/systemd/system/ssh.service; enabled)
   Active: active (running) since Fri 2015-10-09 20:09:29 UTC; 3min 58s ago
 Main PID: 26884 (sshd)
   CGroup: /system.slice/ssh.service
           L-26884 /usr/sbin/sshd -D

Oct 09 20:09:29 lynx sshd[26884]: Server listening on 0.0.0.0 port 22.
Oct 09 20:09:29 lynx sshd[26884]: Server listening on :: port 22.

Давайте убьем sshd и посмотрим как он запустился снова:
root@lynx:~# killall sshd
root@lynx:~# systemctl status ssh
? ssh.service - OpenBSD Secure Shell server
   Loaded: loaded (/lib/systemd/system/ssh.service; enabled)
   Active: active (running) since Fri 2015-10-09 20:13:47 UTC; 1s ago
 Main PID: 14123 (sshd)
   CGroup: /system.slice/ssh.service
           L-14123 /usr/sbin/sshd -D

Oct 09 20:13:47 lynx sshd[14123]: Server listening on 0.0.0.0 port 22.
Oct 09 20:13:47 lynx sshd[14123]: Server listening on :: port 22.


Видите, PID отличается — это говорит о том, что sshd действительно сдох, а systemd запустил его заново.

Поздравляю! Теперь Ваш sshd будет невозможно убить kill-ом и Ваш сервер будет доступен по ssh даже в случае убийства sshd.

Комментарии (65)


  1. tonymadbrain
    10.10.2015 16:41
    +6

    Хабротвит?


  1. pfactum
    10.10.2015 16:42
    +26

    Править сервис-файлы в [/usr]/lib/systemd/system — очень плохая идея, т.к. это vendor-файл, который перезапишется при обновлении.

    Для оверрайда используется /etc/systemd/system, куда можно поместить свой вариант сервис-файла. Но и это не совсем правильно, т.к. требует дублирования всего файла (можно решить инклюдом, но это костыльно).

    Самый правильный подход для указанной в статье задачи — использовать drop-in'ы:

    coreos.com/os/docs/latest/using-systemd-drop-in-units.html

    Ну и, конечно, читать документацию. В systemd есть много чего интересного и полезного, благодаря чему не нужно танцевать с бубном, как это делалось раньше.


  1. Tanner
    10.10.2015 16:46

    Я ничего не имею против systemd, но, чтобы заменить supervisor, он должен обслуживать таски простых пользователей от их имени, не от root и без помощи sudo. Возможно ли такое?


    1. farcaller
      10.10.2015 16:48
      +6

      Да, вот ман например: wiki.archlinux.org/index.php/Systemd/User


      1. Tanner
        10.10.2015 17:06

        Спасибо. Это действительно то, что нужно. systemd развивается так быстро, что за всеми фичами не уследишь. :) Я сейчас запускаю uwsgi, paster, celeryd, haystack на своём хостинге (Debian 8) при помощи supervisord. Пожалуй, стоит перенастроить это всё на systemd и сэкономить немного памяти.

        Теперь попросим piromanlynx обновить соответствующим образом примеры в этой статье. Иначе заявленная тема не будет раскрыта. :)


        1. piromanlynx
          10.10.2015 17:24
          -1

          Ну в случае как раз Ваших сервисов, которых еще нет в systemd (в отличне от ssh), Вы должны как раз класть конфиги в /lib/systemd/system/*.service

          Так что, для Вас тема раскрыта ;-)


          1. Tanner
            10.10.2015 17:37

            Как я понимаю, мне нужен не /lib/systemd/system/, а ~/.config/systemd/user/, и ещё “systemd --user” плюс нечто, называемое lingering. Именно это заменяет supervisord в его главном назначении: демонизировать пользовательские скрипты, отделяя их от пользовательской сессии.


            1. piromanlynx
              10.10.2015 17:44

              демонизировать пользовательские скрипты, отделяя их от пользовательской сессии

              т.е. Вам интересно запускать демон от имени пользователя или заменить nohup… &?


          1. Tanner
            10.10.2015 17:44

            Я имею в виду, что ещё не разобрался до конца, как это сделать, но зато хорошо знаю, что я хочу. Смысл всей этой возни ? запускать и перезапускать бэкенд-вебсервер и периодические задачи, обслуживающие сайт, от лица того же пользователя, который осуществляет деплоймент и тестирование. Это должен быть пользователь с минимальными привилегиями. Это ниша supervisor.


            1. piromanlynx
              10.10.2015 17:45

              Есть еще вариант в секции [Service] добавить

              User=<username to run as>


            1. piromanlynx
              10.10.2015 17:46

              т.е. если Ваша задача просто запускать демон от юзера — то все что написано выше в /lib/systemd/system/ только добавить опцию User= в секцию Service. Или я Вас не понял


              1. Tanner
                10.10.2015 18:00

                Да, технически этого достаточно.

                Административно ? если на одном сервере несколько пользователей (команд) осуществляют развёртывание, то логичнее будет использовать для них отдельные места для хранения юнитов. Наверное.

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


                1. piromanlynx
                  10.10.2015 18:03

                  Пожалуйтса!

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


                  1. khim
                    11.10.2015 23:41

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


              1. Cykooz
                10.10.2015 18:18
                +1

                У меня аналогичная задача. Постараюсь описать её понятнее.
                Имеется некая система состоящая из нескольких приложений (веб-приложение, менеджер фоновых задач, какой нибудь асинхронный нотификатор на веб-сокетах и т.д.). Требуется работать с этой системой имея только права обычного пользователя. Для этого нужно, что бы пользователь мог запускать/останавливать любые процессы, добавлять/редактировать/удалять список запускаемых процессов.
                Именно эти хотелки хорошо решал supervisor. Достаточно было один раз настроить из под root-а авто-запуск под указанным пользователем отдельного процесса supervisord, конфигурация которого доступна для редактирования пользователю.
                Если в systemd есть аналогичные возможности, тогда действительно — supervisor «больше не нужен».


                1. piromanlynx
                  10.10.2015 18:22

                  Да, как раз для Вашей задачи привели выше ссылочку на wiki.archlinux.org


                1. khim
                  11.10.2015 23:43

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


                1. sply
                  12.10.2015 00:21
                  +1

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

                  loginctl enable-linger _USERNAME_
                  


                  А потом пользователь сам может определять правила старта процессов через юниты: ~/.config/systemd/user/

                  wiki.archlinux.org/index.php/Systemd/User#Automatic_start-up_of_systemd_user_instances


          1. pfactum
            10.10.2015 21:24
            +4

            Размещать конфиги в /lib/systemd/system/*.service, т.е. там, где их не стоит размещать — неправильно. Кастомные конфиги живут в /etc.


          1. Googolplex
            11.10.2015 12:51

            del


  1. namespace
    10.10.2015 19:53
    +3

    [irony] Ухты, systemd добрался до супервизоров! Поттеринг, гори в аду! [/irony]


  1. youlose
    11.10.2015 00:49
    -4

    Громкое заявление, но есть такие как я и мои коллеги, например, у нас куча серверов маков и линуксов и тупо не получиться пользоватся systemd по причине того что он не везде есть и его нельзя поставить. Мы сами пользуемся супервизором https://github.com/kostya/eye, и там есть такие замечательные вещи как задержки при запуске/останове приложений, обновление множества конфигов одним действием, цепочки сигналов (чтобы прибивать процессы которые повисли разными способами с таймаутами), опять же всё это нужно с правами обычных юзеров и много чего другого. Не знаю насколько типичны или нетипичны наши хотелки, но нам systemd в принципе не сможет подойти.


    1. piromanlynx
      11.10.2015 00:55
      +2

      задержки при запуске/останове приложений

      И это есть в systemd

      обновление множества конфигов одним действием

      Не очень понял в чем суть

      опять же всё это нужно с правами обычных юзеров

      И это может systemd

      цепочки сигналов (чтобы прибивать процессы которые повисли разными способами с таймаутами)

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

      серверов маков и линуксов и тупо не получиться пользоватся systemd по причине того что он не везде есть и его нельзя поставить

      Ну проблема разрозненной инфрастуктуры… В ней обычно не systemd и не какое то другое решение виноваты. Это обычная ситуация в российском IT, ну как проблема с дорогами в РФ.


      1. youlose
        11.10.2015 01:48

        «задержки при запуске/останове приложений»
        То есть мне нужно запустить 20 экземпляров какого-то скрипта и чтобы он не сразу их все стартовал/останавливал, а по очереди? Есть такое?

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

        «опять же всё это нужно с правами обычных юзеров

        И это может systemd»
        я был бы благодарен за ссылку как это сделать

        «А это вообще может быть очень чревато и печально, особенно если SIGKILL, особенно если на высокой нагрузке.»
        В принципе согласен, но есть, например, скрипт (или группа скриптов), который ненадёжен, может повиснуть или течёт по памяти, на правку времени ни у кого нет, а работать должен прямо сейчас.

        «Ну проблема разрозненной инфрастуктуры… В ней обычно не systemd и не какое то другое решение виноваты. Это обычная ситуация в российском IT, ну как проблема с дорогами в РФ.»
        Это проблема в любом более менее долгоживущем IT в компаниях где меняются люди отвечающие за закупку железа и/или софта.


        1. kay
          12.10.2015 11:17

          1) есть. пишешь несколько сервисов, описываешь Require и After для каждого и устанавливаешь TimeoutStartSec.
          2) тут не совсем понял, но для этого существуют target: www.freedesktop.org/software/systemd/man/systemd.target.html
          3) www.freedesktop.org/software/systemd/man/systemd.exec.html#User=
          4) Пишешь задачу timers (замена cron, примеры тут coreos.com/os/docs/758.1.0/scheduling-tasks-with-systemd-timers.html) и выполняешь необходимую последовательность действий. Хоть каждую секунду, чего cron не позволял сделать.
          5) В systemd еще остались косяки, но сильно жить в production они уже не мешают. И всё-таки я за то, чтобы systemd заменил всяческие питоновые supervisord или, простите, nodejs.


          1. baldr
            12.10.2015 15:26

            Для youlose нужен, все-таки, самописный скрипт, который конкретно его задачи будет решать. Слишком специфичные вещи не стоит костылить. Частично systemd, но над ним можно еще что-то.

            Но хотелось бы задать автору вопрос именно по (1). Есть задача — запустить 20 копий скрипта myscript.py. И контролировать что они не упали — перезапускать. Как может systemd это сделать? Писать 20 одинаковых сервисов? Но это, извините, как раз и костыль. Негибко совсем.


            1. evg_krsk
              12.10.2015 16:52
              +1

              Для этого есть шаблоны. systemd.unit(5), смотреть на тему template.


  1. mickvav
    11.10.2015 21:33
    +3

    Слушайте, а я чего-то не понимаю, или это было уже в старом добром init? Ну, типа, вписать нужную строчку в inittab и init сам что надо поперезапускает?


    1. khim
      12.10.2015 00:12
      +5

      Именно так. Изначально SysV init начался ровно с этой идеи. Но там не было обработки зависимостей. И вместо того, чтобы придумать как добавить в SysV init обработку зависимостей большинство демонов начало запускаться одним развесистым скриптом с кучей костылей занимающим одну строчку в inittab. А потом туда добавили ещё костылей. И ещё. Через многие годы люди уже и забыли о том, какая изначально под всем эти лежала идея и что «костыли сбоку» — это всего лишь костыли.

      А systemd — это, в приципе, «возвращение к истокам».


      1. mickvav
        12.10.2015 13:16
        +1

        При этом никто не пошёл в список рассылки/багзиллу sysvinit и не попытался его проапгрейдить. Not invented here, чо… Грустно это всё как-то…


        1. khim
          12.10.2015 16:57

          Я редко видел, чтобы проекты «ушедшие не туда» кто-то поднимал бы «под старым знаменем». Лучший вариант — это когда новому проекту в какой-то момент дают имя старого (gcc 2.95, да), но чаще — просто создаётся новый проект. Всё просто: если вы планируете радикальную переделку, то в старом проекте вам, скорее всего, никто это делать не даст (в лучшем случае в какой-нибудь экспериментальной ветке попробовать разрешат), а когда оно уже «взлетело»… зачем вам старый проект-то?


  1. lubezniy
    12.10.2015 00:17
    +1

    «Неубиваемые» сервисы без постоянного мониторинга — это палка о двух концах. Достаточно кому-то накосячить (например, в конфигах), чтобы сервис не мог запуститься — и его всё время будут перезапускать, забивая дисковое пространство под логи ошибками. Если этого вовремя не заметить, могут быть уже проблемы нехватки этого пространства. Полагаю, на продакшнах правильнее уведомлять админа об остановке сервиса, чтобы тот посмотрел на причины и принял соответствующее решение.


    1. piromanlynx
      12.10.2015 03:17
      +1

      Ну наверное попытася взлететь назад — это первое что должно случится с сервисом. А мониторинг никто и не отменял. Ну придет админ через полчаса, увидит, что сервис работает — пойдет курить дальше. Или увидит что не работает, но полчаса ничего не испортят. А пытатся взлететь надо.
      А мониторить это совсем легко и просто. Есть отличная метрика — сколько новых pid появилось по сравнению с предыдушим состоянием и сколько пропало. Я эту метрику собираю со всех машин — для разных ролей машин конечно движуха разная, например на машинах с nginx пиды почти не меняются, а вот с php-fpm или postfix есть регулярная ротация. Главное корректно настроить Alert в мониторинге.


      1. evg_krsk
        12.10.2015 05:19

        А каким образом собираете, если не секрет?


        1. piromanlynx
          12.10.2015 10:31
          +1

          zabbix + просто скрипт на bash, который использует ps aux, grep и временный файлик. Скрипт отдает 2 числа — сколько прибавилось, сколько убавилось.


      1. lubezniy
        12.10.2015 08:17
        -4

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


        1. evg_krsk
          12.10.2015 08:52
          +1

          Ну так ограничивать нужно количество попыток.


          1. lubezniy
            12.10.2015 10:24

            Про что и речь. Такие возможности у того же systemd есть?


            1. evg_krsk
              12.10.2015 10:41
              +5

              Конечно есть. Странно, что автор их не указал. StartLImit* в systemd.service(5). Более того, лимиты по умолчанию включены, так что если включаешь рестар, оно не будет тупо бесконечно дёргаться.

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


            1. kay
              12.10.2015 11:23

        1. piromanlynx
          12.10.2015 10:27

          Ну странный у вас /var/log какой то, что его за полчаса забило.
          Да и умные люди делают по умному — один сервис — одна виртуалка. И пусть она хоть упадет.


          1. kay
            12.10.2015 11:30

            journald автоматически ротейтит бинарные логи. размер высчитвывается следующим образом: www.freedesktop.org/software/systemd/man/journald.conf.html#SystemMaxUse=

            Но в journald есть один фатальный недостаток. Если логи сыпятся слишком быстро, то он их может пропускать и не обрабатывать. С другой стороны, может это и не недостаток, а достоинство.


            1. grossws
              12.10.2015 16:27

              Если посмотреть на mature syslog (rsyslog, syslog-ng), то они при burst'е (например, при когда что-то сломалось и привело к обильному срачу в логах) прекрасно теряют данные. Так что это имплицитная особенность основных систем логгирования.


            1. saamich
              12.10.2015 20:59
              +1

              Даже с выключенными настройками

              RateLimitInterval=30s
              RateLimitBurst=1000

              ?


          1. lubezniy
            12.10.2015 11:43

            Ну не обязательно за полчаса — выходные, например, длятся несколько дольше. А архитектура сервисов бывает всякая — кто во что горазд.


  1. Alex_At_Net
    12.10.2015 05:50

    ок, пробую systemd. Как я понял, надо добавить пользователя в linger sudo loginctl enable-linger alex, что бы сессия для пользователя запускалась при загрузке системы.

    Потом добавил ~/.config/systemd/user/server

    $ cat .config/systemd/user/server
    [Unit]
    Description=server
    [Service]
    ExecStart=/srv/www/server/server/bin/server
    


    Теперь надо запустить?

    $ systemctl --user start server
    Failed to get D-Bus connection: Connection
    


    Что-то недопонял я?


    1. kay
      12.10.2015 11:32

      У тебя лог не полный.


      1. Alex_At_Net
        12.10.2015 12:26

        А как полный получить?


        1. kay
          12.10.2015 12:33

          Ну посмотри, у тебя он на Connection обрывается. Дальше то что?


          1. Alex_At_Net
            12.10.2015 12:51

            Так дальше-то только alex@playground $ | — prompt и курсор.


            1. kay
              12.10.2015 12:57

              «Failed to get D-Bus connection: Connection». А дальше что? Только слово Connection? Больше никакой информации не было?


  1. saksmt
    12.10.2015 07:57
    +1

    Это всё конечно замечательно, пока не выходит на сцену какой-нибудь этак docker…


    1. isden
      12.10.2015 10:23
      +2

      А что с ним не так? У меня прекрасно контейнеры докера из-под сабжа запускаются.


      1. piromanlynx
        12.10.2015 10:29
        -5

        Не говорите слово docker. Он настолько адски сырой, что его и трогать пока страшно.
        P.S. потрогал, посмотрел, попользовал — отложил в ящик, через 1-2 года достану.


        1. isden
          12.10.2015 10:42
          +2

          У меня на паре некритичных вещей работает — вроде нормально пока. Более серьезные товарищи его и в продакшн используют по слухам.


          1. saksmt
            13.10.2015 08:47

            Слухи подтверждаю.


          1. grossws
            13.10.2015 16:58

            У нас небольшая часть продакшна в докере, но не всё гладко.
            При большой cpu-активности была неприятная бага в cpuacct cgroups, приводившая к панике. Словить её можно и просто используя cgroups.
            Также нарывались на панику при использовании короткоживущих контейнеров (бага с удалением netns при использовании conntrack+nat).


        1. kay
          12.10.2015 11:32
          +1

          А что именно с ним не так?


          1. shuron
            12.10.2015 22:53

            Давсе так… Уже год в продакшене у меня… Кака часы…


      1. saksmt
        13.10.2015 08:46

        У меня были проблемы с запуском (systemd не запущен, а как его там запустить — не понятно) + как вы решили проблему с уничтожением контейнера? «while true»?

        Или вы про запуск контейнеров через systemd? Я вот пишу про запуск юнитов изнутри контейнера…


        1. isden
          13.10.2015 10:07

          > Или вы про запуск контейнеров через systemd?

          Да, именно так.
          Внутри контейнеров пока supervisor, руки все никак не дойдут там поковырять.


          1. saksmt
            13.10.2015 22:10

            Что ж тогда понятно.

            Удачи вам, от всей души, это, как оказалось, не самое простое занятие :(


            1. isden
              13.10.2015 22:21

              Меня вот терзает мысль, а может быть внутри контейнеров вообще убрать его нафиг и ничем не заменять?
              Наличие процесса внутри можно проверять, например, через /proc/1/cgroup. Тут нужно покопаться еще.


              1. youlose
                14.10.2015 14:05

                Так контейнер сразу же останавливается, вроде бы, если процесс с pid 1 завершается?


  1. nefelim4ag
    13.10.2015 11:52

    Для этих целей, есть ssh.socket
    А авторестарт такого сервиса — избыточно