در حال اجرا سیستم در یک ظرف

مدت هاست که موضوع استفاده از systemd در کانتینرها را دنبال می کنیم. در سال 2014، مهندس امنیت ما دنیل والش مقاله ای نوشت در حال اجرا در داخل یک Docker Container، و چند سال بعد - دیگری که نامیده شد در حال اجرا سیستم در یک کانتینر غیرمجاز، که در آن اظهار داشت که وضعیت چندان بهبود نیافته است. او به طور خاص نوشت که «متاسفانه، حتی دو سال بعد، اگر «سیستم داکر» را در گوگل جستجو کنید، اولین چیزی که می‌آید همان مقاله قدیمی او است. پس وقت آن است که چیزی را تغییر دهیم.» علاوه بر این، قبلاً در مورد آن صحبت کرده ایم تضاد بین Docker و توسعه دهندگان systemd.

در حال اجرا سیستم در یک ظرف

در این مقاله نشان خواهیم داد که چه چیزی در طول زمان تغییر کرده است و چگونه Podman می تواند به ما در این زمینه کمک کند.

دلایل زیادی برای اجرای systemd در داخل یک کانتینر وجود دارد، مانند:

  1. کانتینرهای چند سرویس - بسیاری از مردم می خواهند برنامه های چند سرویس خود را از ماشین های مجازی بیرون بکشند و آنها را در کانتینرها اجرا کنند. البته بهتر است که چنین برنامه هایی را به میکروسرویس ها تقسیم کنیم، اما همه هنوز نمی دانند چگونه این کار را انجام دهند یا به سادگی وقت ندارند. بنابراین، اجرای چنین برنامه هایی به عنوان سرویس هایی که توسط systemd از فایل های واحد راه اندازی می شوند کاملا منطقی است.
  2. فایل های واحد سیستم شده - اکثر برنامه های کاربردی که در داخل کانتینرها اجرا می شوند از کدهایی ساخته شده اند که قبلاً روی ماشین های مجازی یا فیزیکی اجرا می شدند. این برنامه ها دارای یک فایل واحد هستند که برای این برنامه ها نوشته شده است و نحوه راه اندازی آنها را درک می کند. بنابراین همچنان بهتر است به جای هک کردن سرویس init خود، خدمات را با استفاده از روش های پشتیبانی شده شروع کنید.
  3. Systemd یک مدیر فرآیند است. این سرویس خدمات (خاموش کردن، راه اندازی مجدد خدمات، یا کشتن فرآیندهای زامبی) را بهتر از هر ابزار دیگری مدیریت می کند.

با این حال، دلایل زیادی برای اجرا نشدن systemd در کانتینرها وجود دارد. نکته اصلی این است که systemd/journald خروجی کانتینرها و ابزارهایی مانند آن را کنترل می کند کوبرنیتس یا openshift انتظار دارید که کانتینرها log را مستقیماً در stdout و stderr بنویسند. بنابراین، اگر قصد دارید کانتینرها را از طریق ابزارهای ارکستراسیون مانند موارد ذکر شده در بالا مدیریت کنید، باید به طور جدی استفاده از کانتینرهای مبتنی بر سیستم را در نظر بگیرید. علاوه بر این، توسعه دهندگان Docker و Moby اغلب به شدت با استفاده از systemd در کانتینرها مخالف بوده اند.

آمدن پودمان

ما خوشحالیم که گزارش می دهیم که وضعیت در نهایت رو به جلو حرکت کرده است. تیم مسئول اجرای کانتینرها در Red Hat تصمیم گرفت توسعه یابد موتور کانتینر خودت. او یک نام گرفت پودمن و همان رابط خط فرمان (CLI) را به عنوان Docker ارائه می دهد. و تقریباً تمام دستورات Docker را می توان در Podman به همین صورت استفاده کرد. ما اغلب سمینارهایی را برگزار می کنیم که اکنون نامیده می شود تغییر Docker به Podman، و اولین اسلاید برای نوشتن نیاز دارد: مستعار docker=podman.

بسیاری از مردم این کار را انجام می دهند.

من و Podman من به هیچ وجه مخالف کانتینرهای مبتنی بر سیستم نیستیم. به هر حال، Systemd متداول‌ترین زیرسیستم لینوکس init است و اجازه ندادن به درستی کارکردن در کانتینرها به معنای نادیده گرفتن چگونگی عادت هزاران نفر به اجرای کانتینرها است.

Podman می داند که باید چه کاری انجام دهد تا systemd در یک ظرف به درستی کار کند. به چیزهایی مانند نصب tmpfs در /run و /tmp نیاز دارد. او دوست دارد محیط "containerized" را فعال کند و انتظار دارد مجوزهای نوشتن در قسمت خود از فهرست cgroup و پوشه /var/log/journald داشته باشد.

هنگامی که ظرفی را راه اندازی می کنید که در آن اولین دستور init یا systemd است، Podman به طور خودکار tmpfs و Cgroups را پیکربندی می کند تا مطمئن شود که systemd بدون مشکل شروع می شود. برای مسدود کردن این حالت راه اندازی خودکار، از گزینه --systemd=false استفاده کنید. لطفا توجه داشته باشید که Podman فقط زمانی از حالت systemd استفاده می کند که ببیند باید دستور systemd یا init را اجرا کند.

در اینجا گزیده ای از راهنما آمده است:

مرد پادمن اجرا
...

–systemd=true|نادرست

اجرای یک کانتینر در حالت 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 در فایل های systemd unit بهتر از داکر کار می کند

اگر زمانی که سیستم بوت می‌شود باید کانتینرها راه‌اندازی شوند، می‌توانید به سادگی دستورات Podman مناسب را در فایل unit systemd وارد کنید، که سرویس را شروع کرده و آن را نظارت می‌کند. Podman از مدل استاندارد fork-exec استفاده می کند. به عبارت دیگر، فرآیندهای کانتینری فرزندان فرآیند Podman هستند، بنابراین systemd به راحتی می تواند آنها را نظارت کند.

داکر از مدل کلاینت-سرور استفاده می کند و دستورات Docker CLI را نیز می توان مستقیماً در یک فایل واحد قرار داد. با این حال، هنگامی که مشتری Docker به Daemon Docker متصل می شود، آن (سرویس گیرنده) تبدیل به یک فرآیند دیگر در پردازش stdin و stdout می شود. به نوبه خود، systemd هیچ ایده ای در مورد ارتباط بین مشتری Docker و کانتینری که تحت کنترل Daemon Docker اجرا می شود ندارد، و بنابراین، در این مدل، systemd اساساً نمی تواند سرویس را نظارت کند.

فعال سازی systemd از طریق سوکت

Podman فعال سازی از طریق سوکت را به درستی انجام می دهد. از آنجایی که Podman از مدل fork-exec استفاده می کند، می تواند سوکت را به فرآیندهای محفظه فرزند خود ارسال کند. Docker نمی تواند این کار را انجام دهد زیرا از مدل مشتری-سرور استفاده می کند.

سرویس varlink که Podman برای برقراری ارتباط با مشتریان از راه دور به کانتینرها استفاده می کند، در واقع از طریق یک سوکت فعال می شود. بسته cockpit-podman که در Node.js نوشته شده و بخشی از پروژه کابین خلبان است، به افراد اجازه می دهد تا از طریق یک رابط وب با کانتینرهای Podman تعامل داشته باشند. شبح وب در حال اجرا cockpit-podman پیام‌هایی را به سوکت varlink ارسال می‌کند که systemd به آن گوش می‌دهد. سپس Systemd برنامه Podman را برای دریافت پیام ها و شروع مدیریت کانتینرها فعال می کند. فعال کردن systemd از طریق سوکت، نیاز به یک دیمون دائماً در حال اجرا را هنگام اجرای API های راه دور از بین می برد.

علاوه بر این، ما در حال توسعه یک کلاینت Podman دیگر به نام podman-remote هستیم که همان Podman CLI را پیاده‌سازی می‌کند اما varlink را برای اجرای کانتینرها فراخوانی می‌کند. Podman-Remote می تواند در بالای جلسات SSH اجرا شود و به شما این امکان را می دهد که به طور ایمن با کانتینرها در ماشین های مختلف تعامل داشته باشید. با گذشت زمان، ما قصد داریم که Podman-Remote را برای پشتیبانی از MacOS و Windows در کنار لینوکس فعال کنیم، به طوری که توسعه دهندگان در آن پلتفرم‌ها می‌توانند یک ماشین مجازی لینوکس را با Podman varlink اجرا کنند و تجربه کاملی را داشته باشند که کانتینرها در ماشین محلی اجرا می‌شوند.

SD_NOTIFY

Systemd به شما امکان می دهد راه اندازی سرویس های کمکی را تا زمانی که سرویس کانتینری مورد نیاز آنها شروع شود به تعویق بیندازید. Podman می تواند سوکت SD_NOTIFY را به سرویس کانتینری ارسال کند تا این سرویس به systemd اطلاع دهد که آماده کار است. و باز هم Docker که از مدل کلاینت-سرور استفاده می کند، نمی تواند این کار را انجام دهد.

در طرح ها

ما قصد داریم دستور podman generate systemd CONTAINERID را اضافه کنیم که یک فایل unit systemd برای مدیریت یک کانتینر مشخص شده ایجاد می کند. این باید در هر دو حالت روت و بدون روت برای کانتینرهای غیرمجاز کار کند. ما حتی درخواستی برای زمان اجرا systemd-nspawn سازگار با OCI دیده ایم.

نتیجه

اجرای سیستم در یک ظرف یک نیاز قابل درک است. و به لطف Podman، بالاخره یک Container Runtime داریم که با systemd تضادی ندارد، اما استفاده از آن را آسان می کند.

منبع: www.habr.com

اضافه کردن نظر