Ми давно стежимо за темою використання systemd у контейнерах. Ще у 2014 році наш інженер з безпеки Деніел Уолш (Daniel Walsh) написав статтю
У цій статті ми покажемо, що змінилося за минулий час і як нам може допомогти Podman.
Є багато причин для того, щоб запускати systemd всередині контейнера, такі як:
- Мультисервісні контейнери - багато хто хоче витягнути свої мультисервісні програми з віртуальних машин і запускати їх у контейнерах. Краще б, звичайно, розбити такі програми на мікросервіси, але не всі поки що це вміють або просто немає часу. Тому запуск таких додатків у вигляді сервісів, що запускаються systemd з юніт-файлів, цілком має сенс.
- Юніт-файли Systemd – більшість програм, що працюють усередині контейнерів, зібрані з коду, який раніше запускався на віртуальних або фізичних машинах. Ці програми мають юніт-файл, який писався під ці програми і розуміє, як їх треба запускати. Так що сервіси все ж таки краще запускати за допомогою підтримуваних методів, а не зламуючи свою власну init-службу.
- Systemd – це диспетчер процесів. Він здійснює управління сервісами (завершує роботу, перезапускає сервіси або викошує зомбі-процеси) краще ніж будь-який інший інструмент.
При цьому є багато причин для того, щоб не запускати systemd в контейнерах. Основна полягає в тому, що systemd/journald контролює виведення контейнерів, а інструменти начебто
Наступ Podman'а
З радістю повідомляємо, що ситуація нарешті зрушила з мертвої точки. Команда, яка відповідає у Red Hat за запуск контейнерів, вирішила розробити
Багато хто так і робить.
Ми зі своїм Podman'ом ні в якому разі не проти контейнерів на основі systemd. Адже Systemd найчастіше використовується як init-підсистеми Linux, і не давати їй нормально працювати в контейнерах означає ігнорувати те, як тисячі людей звикли запускати контейнери.
Podman знає, що треба робити, щоб системаd нормально працювала в контейнері. Їй потрібні такі речі, як монтування tmpfs на /run та /tmp. Їй подобається, коли включено «контейнерне» середовище, і воно чекає прав на запис у свою частину каталогу cgroup та в папку /var/log/journald.
При запуску контейнера, в якому першою командою йде init або systemd, Podman автоматично налаштовує tmpfs і Cgroups, щоб запуск systemd пройшов без проблем. Щоб заблокувати такий авторежим запуску, використовується опція systemd = false. Зауважте, що Podman використовує systemd-режим тільки тоді, коли бачить, що треба виконати команду systemd або init.
Ось витяг з мануалу:
man podman run
...-systemd = true | false
Запуск контейнера як systemd. За замовчуванням увімкнено.
Якщо всередині контейнера виконується команда systemd або init, Podman налаштує точки монтування tmpfs у наступних каталогах:
/run, /run/lock, /tmp, /sys/fs/cgroup/systemd, /var/lib/journal
Також як сигнал зупинки за замовчуванням використовуватиметься SIGRTMIN+3.
Все це дозволяє systemd працювати в замкнутому контейнері без будь-яких модифікацій.
ПРИМІТКА: systemd намагається виконати запис у файлову систему cgroup. Однак, SELinux за замовчуванням забороняє контейнерам це робити. Щоб дозволити запис, увімкніть логічний параметр container_manage_cgroup:
setsebool -P container_manage_cgroup true
Тепер подивіться, як виглядає Dockerfile для запуску systemd у контейнері при використанні Podman'а:
# cat Dockerfile
FROM fedora
RUN dnf -y install httpd; dnf clean all; systemctl enable httpd
EXPOSE 80
CMD [ "/sbin/init" ]
От і все.
Тепер збираємо контейнер:
# podman build -t systemd .
Говоримо SELinux дозволити systemd модифікувати конфігурацію Cgroups:
# setsebool -P container_manage_cgroup true
Багато хто, до речі, забуває про цей крок. На щастя, це достатньо зробити лише один раз і налаштування зберігається після перезавантаження системи.
Тепер просто запускаємо контейнер:
# podman run -ti -p 80:80 systemd
systemd 239 running in system mode. (+PAM +AUDIT +SELINUX +IMA -APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ +LZ4 +SECCOMP +BLKID +ELFUTILS +KMOD +IDN2 -IDN +PCRE2 default-hierarchy=hybrid)
Detected virtualization container-other.
Detected architecture x86-64.
Welcome to Fedora 29 (Container Image)!
Set hostname to <1b51b684bc99>.
Failed to install release agent, ignoring: Read-only file system
File /usr/lib/systemd/system/systemd-journald.service:26 configures an IP firewall (IPAddressDeny=any), but the local system does not support BPF/cgroup based firewalling.
Proceeding WITHOUT firewalling in effect! (This warning is only shown for the first loaded unit using IP firewalling.)
[ OK ] Listening on initctl Compatibility Named Pipe.
[ OK ] Listening on Journal Socket (/dev/log).
[ OK ] Started Forward Password Requests to Wall Directory Watch.
[ OK ] Started Dispatch Password Requests to Console Directory Watch.
[ OK ] Reached target Slices.
…
[ OK ] Started The Apache HTTP Server.
Все, сервіс запустився і працює:
$ curl localhost
<html xml_lang="en" lang="en">
…
</html>
ПРИМІТКА: Не намагайтеся повторити це на Docker! Там, як і раніше, потрібні танці з бубном, щоб запускати такого роду контейнери через демона. (Потрібні додаткові поля та пакети, щоб все це безшовно запрацювало в Docker, або треба буде запускати у привілейованому контейнері. Подробиці див.
Ще пара крутих речей про Podman та systemd
Podman працює краще Docker у юніт-файлах systemd
Якщо контейнери треба запускати при завантаженні системи, то можна просто вставити відповідні команди Podman у юніт-файл systemd, той запустить сервіс і моніторитиме. Podman використовує стандартну модель розгалуження під час виконання (fork-exec). Інакше кажучи, контейнерні процеси є дочірніми щодо процесу Podman'а, тому systemd легко може їх моніторити.
Docker використовує модель клієнт-сервер, і CLI-команди Docker також можна розміщувати прямо в юніт-файлі. Однак після того, як Docker-клієнт підключається до Docker-демона, він (клієнт) стає просто ще одним процесом, що обробляє stdin та stdout. У свою чергу, systemd уявлення не має про зв'язок між Docker-клієнтом і контейнером, який працює під керуванням Docker-демона, і тому в рамках цієї моделі systemd принципово не може моніторити сервіс.
Активація systemd через сокет
Podman коректно відпрацьовує активування через сокет. Оскільки Podman використовує модель fork-exec, він може прокидати сокет своїм дочірнім контейнерним процесам. Docker не вміє, оскільки використовує модель клієнт-сервер.
Сервіс varlink, який Podman використовує для взаємодії віддалених клієнтів із контейнерами, насправді активується через сокет. Пакет cockpit-podman, написаний на Node.js і що входить до проекту cockpit, дозволяє людям взаємодіяти з контейнерами Podman через веб-інтерфейс. Веб-демон, на якому крутиться cockpit-podman, посилає повідомлення на varlink-сокет, який прослуховується systemd. Після цього systemd активує програму Podman для отримання повідомлень та початку управління контейнерами. Активація systemd через сокет дозволяє уникнути постійно працюючого демона під час реалізації віддалених API.
Крім того, ми розробляємо ще один клієнт для Podman'а під назвою podman-remote, який реалізує той же Podman CLI, але викликає varlink для запуску контейнерів. Podman-remote може працювати поверх SSH-сеансів, що дозволяє безпечно взаємодіяти із контейнерами на різних машинах. Згодом ми плануємо задіяти podman-remote для підтримки MacOS і Windows поряд з Linux, щоб розробники на цих платформах могли запускати віртуальну машину Linux із працюючим Podman varlink і мати повне відчуття, що контейнери виконуються на локальній машині.
SD_NOTIFY
Systemd дозволяє відкласти запуск допоміжних сервісів доти, доки стартує необхідний їм контейнеризований сервіс. Podman може прокинути сокет SD_NOTIFY у контейнеризований сервіс, щоб цей сервіс повідомив systemd про готовність до роботи. І знову ж таки Docker, що використовує модель клієнт-сервер, так не вміє.
В планах
Ми плануємо додати команду podman generate systemd CONTAINERID, який генеруватиме юніт-файл systemd для керування конкретним заданим контейнером. Це має працювати як у root-, так і в rootless-режимах для непривілейованих контейнерів. Ми навіть бачив запит на створення OCI-сумісного середовища виконання systemd-nspawn.
Висновок
Запуск systemd у контейнері – це цілком зрозуміла потреба. І завдяки Podman у нас нарешті є середовище запуску контейнерів, яке не ворогує із systemd, а дозволяє легко його використовувати.
Джерело: habr.com