Запускаем systemd у кантэйнеры

Мы даўно сочым за тэмай выкарыстання systemd у кантэйнерах. Яшчэ ў 2014 годзе наш інжынер па бяспецы Дэніэл Уолш (Daniel Walsh) напісаў артыкул Running systemd within a Docker Container, а яшчэ праз пару гадоў - іншую, якая называлася Running systemd in non-privileged container, у якой ён канстатаваў, што сітуацыя не вельмі і палепшылася. У прыватнасці, ён пісаў, што «на жаль, і праз два гады, калі трошкі «Docker system», то перш за ўсё ўсплывае ўсё той жа яго стары артыкул. Значыць, час нешта мяняць». Акрамя таго, мы ўжо неяк расказвалі пра канфлікце паміж распрацоўшчыкамі Docker і systemd.

Запускаем systemd у кантэйнеры

У гэтым артыкуле мы пакажам, што змянілася за мінулы час і як нам можа дапамагчы ў гэтым пытанні Podman.

Ёсць шмат прычын для таго, каб запускаць systemd ўнутры кантэйнера, такія як:

  1. Мультысэрвісныя кантэйнеры - шматлікія жадаюць выцягнуць свае мультысэрвісныя прыкладанні з віртуальных машын і запускаць іх у кантэйнерах. Лепш бы, вядома, разбіць такія прыкладанні на мікрасэрвісы, але не ўсё пакуль гэта ўмеюць ці проста няма часу. Таму запуск такіх прыкладанняў у выглядзе сэрвісаў, якія запускаюцца systemd з юніт-файлаў, цалкам мае сэнс.
  2. Юніт-файлы Systemd - Большасць прыкладанняў, якія працуюць усярэдзіне кантэйнераў, сабраны з кода, які да гэтага запускаўся на віртуальных або фізічных машынах. У гэтых прыкладанняў ёсць юніт-файл, які пісаўся пад гэтыя прыкладанні і разумее, як іх трэба запускаць. Так што сэрвісы ўсё ж лепш запускаць з дапамогай падтрымліваемых метадаў, а не ўзломваючы сваю ўласную init-службу.
  3. Systemd - гэта дыспетчар працэсаў. Ён ажыццяўляе кіраванне сэрвісамі (завяршае працу, перазапускае сэрвісы або выкошвае зомбі-працэсы) лепш, чым любая іншая прылада.

Пры гэтым ёсць і шмат чыннікаў для таго, каб не запускаць systemd у кантэйнерах. Асноўная складаецца ў тым, што systemd/journald кантралюе выснову кантэйнераў, а прылады накшталт Kubernetes або OpenShift разлічваюць, што кантэйнеры будуць пісаць лог непасрэдна ў stdout і stderr. Таму, калі вы збіраецеся кіраваць кантэйнерамі праз сродкі аркестрацыі тыпу паказаных вышэй, тое трэба сур'ёзна абдумаць пытанне выкарыстання кантэйнераў на базе systemd. Акрамя таго, распрацоўшчыкі Docker і Moby часта былі рэзка супраць выкарыстання systemd у кантэйнерах.

Прышэсце Podman'а

З радасцю паведамляем, што сітуацыя нарэшце зрушылася з мёртвай кропкі. Каманда, якая адказвае ў Red Hat за запуск кантэйнераў, вырашыла распрацаваць свой уласны кантэйнерных рухавічок. Ён атрымаў імя Падман і прапануе такі ж інтэрфейс каманднага радка (CLI) як у Docker'а. І практычна ўсе каманды Docker сапраўды гэтак жа можна выкарыстоўваць у Podman. Мы часта праводзім семінары, якія зараз называюцца Змяняем Docker на Podman, і першы ж слайд заклікае прапісаць: alias docker=podman.

Многія так і робяць.

Мы са сваім Podman'ам ні ў якай меры не супраць кантэйнераў на аснове systemd. Бо Systemd часцей за іншых выкарыстоўваецца ў якасці init-падсістэмы Linux, і не даваць ёй нармальна працаваць у кантэйнерах значыць ігнараваць тое, як тысячы людзей прывыклі запускаць кантэйнеры.

Podman ведае, што трэба рабіць, каб systemd нармальна працавала ў кантэйнеры. Ёй патрэбныя такія рэчы, як мантаванне 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

Дадаць каментар