Администрирование нагруженных серверов
237,14
рейтинг
15 апреля 2015 в 16:54

Разное → Systemd за пять минут

Наша компания занимается администрированием веб-серверов на базе CentOS. Довольно часто наши клиенты используют веб-приложения на базе python, ruby или java. Для автозапуска подобных приложений есть готовые шаблоны для написания стартап-скриптов. Но прогресс не стоит на месте, вышел уже второй релиз CentOS 7 и, следуя старой традиции «не ставить dot-zero релизы на продакшен», мы начинаем предлагать клиентам сервера на базе CentOS 7.1 (1503).

В CentOS7, так же как и в его родителе RHEL7, используется systemd — менеджер системы и служб для Linux, совместимый со скриптами инициализации SysV и LSB. systemd обеспечивает возможности агрессивной параллелизации и много всего прочего.

image

Огромный монстр с множеством возможностей, гибкими настройками и мегабайтами документации…

Но что делать, если стоит задача быстро-быстро, вот прямо вчера, сделать автозапуск некоего сервиса?
Давайте выжмем из документации минимально необходимый набор информации для создания простых старт-стоп скриптов.

Systemd запускает сервисы описанные в его конфигурации.
Конфигурация состоит из множества файлов, которые по-модному называют юнитами.

Все эти юниты разложены в трех каталогах:

/usr/lib/systemd/system/ – юниты из установленных пакетов RPM — всякие nginx, apache, mysql и прочее
/run/systemd/system/ — юниты, созданные в рантайме — тоже, наверное, нужная штука
/etc/systemd/system/ — юниты, созданные системным администратором — а вот сюда мы и положим свой юнит.

Юнит представляет из себя текстовый файл с форматом, похожим на файлы .ini Microsoft Windows.

[Название секции в квадратных скобках]
имя_переменной = значение


Для создания простейшего юнита надо описать три секции: [Unit], [Service], [Install]

В секции Unit описываем, что это за юнит:
Названия переменных достаточно говорящие:

Описание юнита:
Description=MyUnit

Далее следует блок переменных, которые влияют на порядок загрузки сервисов:

Запускать юнит после какого-либо сервиса или группы сервисов (например network.target):
After=syslog.target
After=network.target
After=nginx.service
After=mysql.service

Для запуска сервиса необходим запущенный сервис mysql:
Requires=mysql.service

Для запуска сервиса желателен запущенный сервис redis:
Wants=redis.service

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

В секции Service указываем какими командами и под каким пользователем надо запускать сервис:

Тип сервиса:
Type=simple
(по умолчанию): systemd предполагает, что служба будет запущена незамедлительно. Процесс при этом не должен разветвляться. Не используйте этот тип, если другие службы зависят от очередности при запуске данной службы.

Type=forking
systemd предполагает, что служба запускается однократно и процесс разветвляется с завершением родительского процесса. Данный тип используется для запуска классических демонов.

Также следует определить PIDFile=, чтобы systemd могла отслеживать основной процесс:
PIDFile=/work/www/myunit/shared/tmp/pids/service.pid

Рабочий каталог, он делается текущим перед запуском стартап команд:
WorkingDirectory=/work/www/myunit/current

Пользователь и группа, под которым надо стартовать сервис:
User=myunit
Group=myunit


Переменные окружения:
Environment=RACK_ENV=production

Запрет на убийство сервиса вследствие нехватки памяти и срабатывания механизма OOM:
-1000 полный запрет (такой у sshd стоит), -100 понизим вероятность.
OOMScoreAdjust=-100

Команды на старт/стоп и релоад сервиса

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 отработки старт/стоп команд.
TimeoutSec=300


Попросим systemd автоматически рестартовать наш сервис, если он вдруг перестанет работать.
Контроль ведется по наличию процесса из PID файла
Restart=always


В секции [Install] опишем, в каком уровне запуска должен стартовать сервис

Уровень запуска:
WantedBy=multi-user.target

multi-user.target или runlevel3.target соответствует нашему привычному runlevel=3 «Многопользовательский режим без графики. Пользователи, как правило, входят в систему при помощи множества консолей или через сеть»

Вот и готов простейший стартап скрипт, он же unit для systemd:
myunit.service

[Unit]
Description=MyUnit
After=syslog.target
After=network.target
After=nginx.service
After=mysql.service
Requires=mysql.service
Wants=redis.service

[Service]
Type=forking
PIDFile=/work/www/myunit/shared/tmp/pids/service.pid
WorkingDirectory=/work/www/myunit/current

User=myunit
Group=myunit

Environment=RACK_ENV=production

OOMScoreAdjust=-1000

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
TimeoutSec=300

[Install]
WantedBy=multi-user.target 

Кладем этот файл в каталог /etc/systemd/system/

Смотрим его статус systemctl status myunit

myunit.service - MyUnit
   Loaded: loaded (/etc/systemd/system/myunit.service; disabled)
   Active: inactive (dead)

Видим, что он disabled — разрешаем его
systemctl enable myunit
systemctl -l status myunit


Если нет никаких ошибок в юните — то вывод будет вот такой:

myunit.service - MyUnit
   Loaded: loaded (/etc/systemd/system/myunit.service; enabled)
   Active: inactive (dead)


Запускаем сервис
systemctl start myunit

Смотрим красивый статус:
systemctl -l status myunit

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

systemctl daemon-reload

Теперь, после ознакомительного окунания в systemd можно, начинать самостоятельные заплывы.
Если вдруг появятся вопросы, рады будем ответить на ваши письма по адресу ask@centos-admin.ru
Автор: @LuckySB
Centos-admin.ru
рейтинг 237,14
Администрирование нагруженных серверов

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

  • +1
    systemd хорошо и годно, но почему легаси разломали? допустим, есть у меня старые скрипты, где есть /etc/init.d/blabla start.

    что мешает systemd научиться обрабатывать симлинки из /etc/init.d/? или быть может уже есть правильный путь для этого? те же upstart или runit симлинки полностью поддерживают.
    • +10
      Может чтобы ускорить миграцию на systemd? Про хорошо и годно вопрос не однозначный. Я вот с грустью смотрю на список дистрибутивов без systemd и размышляю куда мигрировать.
      • +5
        Gentoo — OpenRC просто шикарен и, к счастью, systemd там не светит.
        • +2
          Что значит «не светит»? О_о
          В Gentoo он, SystemD, как и многие другие «альтернативы», как всегда, «на выбор». Если пользователь хочет — он может сам его поставить и настроить. Раньше ещё был eselect init, но, что-то, забили на него и выпилили :) Всё равно ядру «лучше» по совету апстрима не симлинк указывать, а путь до реального бинарника :)
        • 0
          Почему systemd это плохо?
          Да, я понимаю, что systemd не соответствует linux(unix?) философии, но ведь это шикарная вещь!
          cc kt97679
          • +7
            Я далек от того, чтобы утверждать, что systemd это плохо. Давайте проведем аналогию с автомобилями. Машину выпуска 80-х годов прошлого века можно починить в гараже своими силами. Машину выпуска 2010-го года в гараже починить не реально. Это не хорошо и не плохо, это то, куда пришло автопроизводство исходя из рыночной коньюктуры. Если бы люди покупали только машины, которые можно ремонтировать в гараже, то ситуация была бы другая. Но люди охотно покупают машины, которые можно ремонтировать исключительно методом агрегатной замены и исключительно у дилера. Что касается systemd то тут похожая ситуация с той разницей, что возможность ремонта в гараже для многих критична. systemd достаточно новая штука. Им предполагается заменить ключевые компоненты системы, обкатанные годами. Негативный опыт использования systemd, в том числе моими непосредственными коллегами, уже есть. Мне очень не комфортно использовать в продакшене систему, которую я не могу починить отредактировав пару скпиптов. А в том, что она не будет ломаться, у меня пока что уверенности нет. Вот такая моя личная позиция, которую я ни в коем случае никому не навязываю.
            • –1
              Машину выпуска 2010-го года в гараже починить не реально.

              Всё реально. Было бы желание и гараж ))
              Ничего существенно нового в новых машинах нет. Чуть больше электроники, чуть больше датчиков, но по сути движок тот же. Всё дело в наличии документации (которую можно найти в интернете) и инструментов.
              • +1
                Вот только когда после замены какой-нить фигни, машина не заводится, потому что электроника кричит «сбросьте датчик специальным лицензированным ПО от официального сервис центра», начинаешь понимать, что в такой «гараж» следует вложить столько, что проще уже в сервис центр.
                • 0
                  Сбрасывание этих датчиков — это не необходимость рынка или развития автопрома. Это сбоку и специально так сделано. Невозможность ремонта тут не при чем…
                  • +2
                    > «Это сбоку и специально так сделано.»
                    Но машина тем не менее не заведется, если я просто куплю запчасть и сам ее в гараже поменяю. Мне нужно будет и программатор завести и разобраться со всеми этими датчиками, то есть кроме замены запчасти, что можно сделать чисто интуитивным путем, если руки из нужного места растут, необходима процедура, которая не интуитивна, и отверткой не решается.

                    P.S. Кстати почему это не необходимость рынка? Как раз для развития официальных сервисов и сделано. Как чипованные картриджи.
                    • 0
                      Я же выше написал:
                      >> Было бы желание
                      >> Всё дело в наличии документации (которую можно найти в интернете) и инструментов

                      Сейчас все машины свежие выпускаются с единой цифровой шиной, через которую и диагностику проводят и ошибки сбрасывают. И софт на спецфорумах можно накопать и переходники для подключения к ПК найти.
                      Ну а на крайний случай для сброса ошибки можно и в сервис отвезти. Сброс ошибки будет стоить гораздо меньше чем работы по замене узлов, сами узлы у диллеров и т.п.
                      Пока машина на гарантии я в неё не лезу — диллер пусть делает, а вот когда гарантия закончится, при надобности буду разбираться. Предыдущую машину ремонтировал вплоть до перебора и замены движка, так что опыт есть.
          • +4
            Я вот не понимаю людей, которые вопят как потерпевшие о том, что systemd, типа, не соответствует unix-философии потому, что, панимаэш, в одном пакеты живут десятки самых разнообразных утилит, которые работают только друг с другом.

            Люди, вы вообще когда-нибудь видели исходники UNIX? Нет? Сходите, полюбуйтесь.

            Каждая версия содержит в одном «пакете» десятки утилит, которые рассчитаны на совместную работу и которые нигде, кроме как в соответствующей системе, не собираются и в комплекте с «чужими» утилитами не работают — точь-в-точь как в systemd.

            Или у нас теперь уже сам UNIX перестал соответствовать философии UNIX??? Оксюморон же!

            Другое дело, что кой-какие вещи, может быть, засунуты зря в основной демон systemd, но там зачастую оказывается что выделить что-то в отдельную утилиту — себе дороже: будет больше кода, общающегося с утилитой, чем кода, который реализуется нужный функционал напрямую. А так… systemd — это как раз скорее попытка «вернуться к истокам»…
            • +4
              вы говорите так, будто какие-то факты этим людям интересны
            • +1
              Наоборот, пусть вопят. Их так очень удобно отсеивать и не читать. И уж тем более не спорить.
          • –3
            Я, как пример, уже начал запинаться об ошмётки systemd с проблемой вроде “не получается отключить уход в спящий режим при закрытии крышки ноутбука”. Пришлось решать смотрением в логи (которые потом могут оказаться ещё и бинарными), редактированием конфига в /etc (и это – пользовательскодружелюбный ubuntu) и ещё и перезапуском всего (“что рестартнуть чтобы сработало и не перестало работать всё остальное” оказалось более нетривиальным вопросом).

            Предсказываю, что проблем вроде такой будет всё больше, особенно из-за вышеупомянутого забивания на совместимость с тем что было раньше (вроде init.d-скриптов).
            • 0
              С каких пор systemd не поддерживает sysv init-скрипты?
              • –3
                Не знаю про поддержку чего именно речь, но некоторые упоминания вроде того что в корне этой ветки комментариев попадаются.

                То есть да, я просто косо на всё это смотрю и не питаю надежд.
      • 0
        У меня любые несовместимости вызывают желание выпилить системд. Если честно, я вообще не понимаю, зачем он нужен. У меня Sys V отлично работает.
        • +1
          Давно ли вы писали init-скрипты?
          • 0
            бывает иногда. Вообще, есть же генератор инит-скриптов.
            • +1
              Поделитесь, то куча народу мучается.
              Кстати, сколько major версий основных массовых дистрибутивов (Debian, RHEL, Ubuntu, SLES) поддерживает? Upstart-скрипты генерирует? Таблицы имён сервисов и базовых зависимостей за пределами LSB, от которых может зависеть конкретный инит-скрипт имеет?
              • –1
                metainit

                > Кстати, сколько major версий основных массовых дистрибутивов (Debian, RHEL, Ubuntu, SLES) поддерживает?
                Понятия не имею. Пользуюсь только дебианом

                > Upstart-скрипты генерирует?
                Не пользуюсь, не знаю.

                > Таблицы имён сервисов и базовых зависимостей за пределами LSB, от которых может зависеть конкретный инит-скрипт имеет?
                Не интересовался. Необходимые зависимости можно руками дописать.

                Вообще, задача написания инит-скриптов столь редко возникает, что мне непонятны все эти сложности. Хотя, это только на дебиане так, наверное. Во всяких рхелах 90% софта приходится руками собирать, т.к. нет в дистрибутиве.
                • +1
                  metainit
                  debian-specific, только для простых скриптов. Этакий boilerplate-generator, почти не снижающий затраты на поддержку в простых случаях и повышающий их в чуть более сложных.

                  Вообще, задача написания инит-скриптов столь редко возникает, что мне непонятны все эти сложности. Хотя, это только на дебиане так, наверное.
                  Если вы:
                  — пишите свой софт,
                  — имеете нетривиальную конфигурацию софта из дистрибутива (например, несколько копий исходного сервиса),
                  — используете сторонний коммерческий софт, для которого не озаботились написанием нормального инит-скрипта,
                  то их писать приходится.

                  Во всяких рхелах 90% софта приходится руками собирать, т.к. нет в дистрибутиве.
                  Не знаю, откуда вы взяли такую статистику, учитывая, что rhel/centos не пользуетесь. У меня возникает потребность в инит-скриптах для софта, которого нет ни в репозиториях centos, ни в debian.
          • 0
            может ли systemd работать с конфигами, сгенерированными на лету и находяшимися в произвольных директориях?
            • 0
              Да. Можно импортировать файл с парами ключ-значение (которые обычно хранятся в /etc/sysconfig/ или /etc/default/) в environment, можно запускать любые скрипты перед запуском, после запуска, после остановки (ExecStartPre, ExecStartPost, ExecStopPost) для генерации чего угодно.
              • 0
                Прошу прощения, надо было сформулировать более развернутый вопрос. Могу ли я при помощи systemd организовать мета сервис, который бы при запуске генерировал конфиги для компонент и запускал бы их, перезапускал компоненты в случае их аварийного завершения, при остановке мета сервиса останавливал бы запущенные компоненты и удалял бы сгенерированные конфиги? Я хочу зарегистрировать в systemd только метасервис, работа с компонентами должна происходить автоматически без человеческого вмешательства. Что произойдет, если при запущенном метасервисе машина внезапно уйдет на перезагрузку? При следующем старте компоненты будут запущены даже если метасервис запускается в ручном режиме? Отдельный вопрос: может ли systemd проверять живость сервиса при помощи сетевого запроса?
                • 0
                  Всё равно не очень ясно чего вы хотите. Зачем вам какой-то загадочный метасервис если вы можете просто положить конфиги в /run/systemd/system? Тогда systemd их подхватит. Или, может, вы хотите себе завести «дочерний» systemd? Запустите его с опцией --user и не забудьте установить переменные XDG_*. Если вам нужна какая-то хитрая логика по проверке живости сервера, то вам придётся создать отдельный процесс, который будет периодически сообщать systemd о том, что ваш сервис ещё жив.

                  Сформулируйте задачу — и её можно будет обсуждать. Потому что пока что её формулировка слишком близка к «пойди туда, не знаю куда, принеси то, не знаю что».
                  • 0
                    Я хочу понять можно ли перевести на systemd вот это: github.com/hulu/statsd-router/blob/master/example/init.d.sh. И что я при этом выиграю.
                    • 0
                      От простого перевода одного скрипта — скорее всего ничего не выиграете. Просто один набор костылей заменится на другой. Нужно более аккуратно смотреть на архитектуру statsd и его потребности.

                      Хотя я на тему использования systemd для управления демонами в кластере как-то вообще не думал, не знаю можно ли от него получить вообще хоть какую-то пользу в таком раскладе. Всё-таки он больше для управления локальными процессами.
                      • 0
                        > systemd для управления демонами в кластере

                        github.com/coreos/fleet
                • 0
                  Вы можете спокойно генерировать новые unit'ы в /run/systemd/system/. Они будут видны systemd автоматически, без systemctl daemon-reload. Кроме того, эти конфиги автоматически удалятся при перезагрузке сервера.
                  Каждому сгенерированному «компоненту» можно указать политику перезапуска (параметр Restart, см man 5 systemd.service), но мониторинг сервиса (кроме стандартных механизмов — sigchld, pid file, dbus) — внешняя относительно systemd задача.
                  Также вам стоит разобраться с тем, какие части от каких будут зависеть. У systemd очень развитая система зависимостей (Requires, Wants, BindTo, PartOf и ещё некоторые, см man 5 systemd.unit).
    • –1
      Потому что в каждом дистрибутиве был свой велосипед из этих init скриптов. И если есть скрипт для debian, то для того чтобы запустить его в suse надо было прыгать с бубном.
    • 0
      Чисто для справки: System V init Scripts systemd полностью поддерживает. По крайней мере в RHEL7:
      Although not encouraged, System V init scripts are still supported. There are still some services in RHEL 7 that are implemented in System V init scripts.
      Тут скорее проблема в другом: во многих случаях эти самые скрипты написаны через одно место, неправильно описывают зависимости от других компонент и работают, в общем-то, по чистой случайности.

      Если же у вас нормальные скрипты, то с ними на в RHEL7, ни в CentOSе ничего случиться не должно.
      • 0
        Вопрос не об этом. Еще раз объясняю. ivlis это тоже касается.

        В дебиане/убунте когда обычный систем-файв инитскрипт заменялся на апстарт-джоб, файл /etc/init.d/servicename заменялся на симлинк на исполняемый файл upstart, например:

        lrwxrwxrwx   1 root    root       21 окт.   2  2014 rsyslog -> /lib/init/upstart-job


        root@hostname:/# /etc/init.d/rsyslog status
        Rather than invoking init scripts through /etc/init.d, use the service(8)
        utility, e.g. service rsyslog status
        
        Since the script you are attempting to invoke has been converted to an
        Upstart job, you may also use the status(8) utility, e.g. status rsyslog
        rsyslog start/running, process 5119
        


        То же самое если я хочу использовать runit вместо init.

        lrwxrwxrwx 1 root root 11 апр.  15 20:55 /etc/init.d/xupnpd -> /usr/bin/sv


        root@hostname:/# /etc/init.d/xupnpd status
        run: xupnpd: (pid 10356) 963840s; run: log: (pid 6347) 1532294s
        


        Cоответственно, я и какие-то древние скрипты можем вызвать /etc/init.d/rsyslog start и запустится rsyslogd.
        теперь понятно, о чем речь?
    • +1
      В Ubuntu 15.04 уже переползли на systemd, но привычные service webserver start (мой пользовательский сервис из /etc/init.d) всё ещё работают нормально.
      • 0
        ну хвала богам, если бы rpm- и deb- линуксы еще и в ините/конфигах разошлись, это было бы уже две разных оси
        • –2
          Вчера на тостере видел линк на wikipedia со всем зоопарком
          Я бы так не радовался)

          image
  • 0
    Чем не тоже самое?
    www2.kangran.su/~nnz/pub/s4a/s4a_latest.pdf
    • 0
      ну уж точно размером.
      • 0
        ну не 5 минут, минут 90 возможно.
  • +11
    Серьёзное замечание по поводу OOMScoreAdjust=-1000.

    Это ОЧЕНЬ серьёзно. Это настолько серьёзно, что утверждение следует читать так:

    убейте пожалуйста всё, включая базу данных, getty, udev, ssh, но не убийвайте мой сервис. Заметим, если в системе всё настолько плохо, то следующим умрёт и этот сервис.

    И нафига нам такой сервер в продакшене? Может, лучше, мирно сковырнуться по oom'у и перезапуститься?

    OOMScoreAdjust надо делать в пределах нескольких сотен (меньше 500), иначе можно получить «странно залипший сервер». И, вообще, если это не системный сервис крайне высокой важности (ssh, udev), то разработчику лучше не трогать этот параметр, оставляят такие вещи на усмотрение системного администратора. Что для одного бизнес-критикал — для другого побрякушка.
    • 0
      спасибо. внес дополнения
  • 0
    Ещё полезная опция

    [Service]
    Type=notify
    

    Позволяет в вашем приложении-сервисе сделать начальную инициализацию, которая необходима перед продолжением загрузки системы, а потом вызвать из сервиса
    sd_notify(0, "READY=1");
    

    и загрузка пойдёт дальше вместе с вашим работающим сервисом.
  • +4
    Скажите мне главное: зачем нужен системд?
  • –3
    > разложены в трех каталогах: /usr/lib/systemd/system/
    Гребанные хипстеры!
    Я монтирую /usr в readonly (за исключением момента установки софта или апгрейда), а они туда изменяемые конфигурации выносят!
    • +3
      В /usr/lib/systemd/system лежат как раз неизменяемые файлы конфигурации, поставляемые с дистрибутивом. Если вы хотите что-то изменить, то вы должны скопировать файл в /etc/systemd/system и изменить его уже там.

      Этот подход задолго до systemd появился. Никогда не обращали внимание, скажем, на файлик /lib/init/fstab? А он таки там есть… по крайней мере на Ubuntu 14.04, где systemd ещё и не пахнет.
      • 0
        Что происходит, когда ты желаешь отключить автостарт сервиса «из коробки»?
        • 0
          systemctl disable <name>
          
          ?
        • 0
          При вызове systemctl disable some-service.service удаляется симлинк из /etc/systemd/system/multi-user.target.wants/some-service.service (указывающий на файл /etc/systemd/system/some-service.service или /usr/lib/systemd/system/some-service.service, если первый не существует).

          Из какой директории удалять определяется target'ом в котором этот сервис требовался. systemctl enable работает аналогично — создаёт симлинк.
        • 0
          systemctl mask <name>
          
          • 0
            Я спросил «что происходит» а не «что нужно выполнить».
            • +2
              Создаеся симлинк /etc/systemd/system/<vendor.service> который указывает на /dev/null.
              Именно такое сообщение выводится на английском языке, из команды за которую кто-то поставил минус.
              • 0
                И это же написано в man systemctl:
                mask NAME...
                    Mask one or more unit files, as specified on the command line. This will link these units to /dev/null, making it impossible to start them. This is a stronger version of disable, since it prohibits
                    all kinds of activation of the unit, including enablement and manual activation. Use this option with care. This honors the --runtime option to only mask temporarily until the next reboot of the
                    system. The --now option can be used to ensure that the units are also stopped.
      • 0
        И да, cannot open `/lib/init/fstab' (No such file or directory)
      • 0
        Если вы хотите что-то изменить, то вы должны скопировать файл в /etc/systemd/system и изменить его уже там.
        Или использовать drop-in в /etc/systemd/system/service-name.service.d/some-conf.conf
  • 0
    А подскажите мне пожалуйста: есть у меня самописный юнит, который просто дёргает bash скрипт. Всё работает, но при запуске не возвращает консоль. Что не так?
    • 0
      а какой тип вы указали?

      я телепатирую, что надо бы type=simple
      и никаких & в конце ExecStart

      systemd сам оставит выполнятся сервис в background
      • 0
        Да, помогло, спасибо. А то с типами я что то так и не разобрался, стоял oneshot

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

Самое читаемое Разное