Cgroup و Namespace در کانتینرها

فرقی نمی‌کند که با شنیدن واژه کانتینر به Docker ،Kubernetes ،Silverblue ،CoreOS یا Flatpak فکر کنید، واضح است که اپلیکیشن‌های تازه برای راحتی، امنیت و مقیاس پذیری از کانتینرها استفاده می‌کنند.

کانتینرها مفهوم پیچیده و گیج کننده‌ای هستند و هرچه مطالعه بیشتری در این زمینه انجام دهید، بی شک سوالات بیشتری برای شما به وجود خواهد آمد. مثلاً اینکه یک اپلیکیشن در کانتینر اجرا می‌شود، به چه معناست؟ یا اینکه چطور پراسس‌های یک کانتینر با بخش‌های دیگر کامپیوتر تعامل دارند؟ از آنجایی که من شخصاً از پیدا کردن جواب سوالات لذت می‌برم، بنابراین در ادامه مقاله مختصراً پشت پرده تکنولوژی کانتینرها را شرح خواهم داد.

Cgroup و Namespace واقعاً چه هستند؟

Cgroups یا Control Groups، مکانیزم کرنل لینوکس برای محدود کردن و اندازه‌گیری کل منابع در حال استفاده، توسط گروهی از پراسس‌های اجرایی روی سیستم است. برای مثال با استفاده از cgroup می‌توانید منابع سیستم‌های لینوکسی خود مثل RAM ،CPU یا IO را کنترل کنید. Cgroups در ابتدا توسط Paul Menage و Rohit Seth در گوگل ساخته شد و اولین قابلیت‌های آن در لینوکس 2.6.24 ادغام شد.

از طرف دیگر Namespaces مکانیزم دیگری در کرنل برای محدود کردن دید گروهی از پراسس‌ها، نسبت به بقیه سیستم است. برای مثال می‌توان یک یا چند پراسس را محدود کرد تا امکان دیدن و تعامل با بقیه پراسس‌های در حال اجرا روی سیستم یا مثلاً امکان دسترسی به فایل سیستم‌های مانت شده را نداشته باشد.

Namespaceها در ابتدا توسط Eric Biederman توسعه پیدا کردند و در لینوکس 3.8 ادغام شدند.

کانتینرها چطور از cgroup و Namespace استفاده می‌کنند؟

همانطور که گفته شد، با استفاده از cgroup و Namespace می‌توان روی هر پراسس اجرایی در سیستم عامل لینوکس محدودیت‌های بسیاری را اعمال نمود و این محدودیت‌ها می‌تواند با جزئیات زیادی اعمال شود. cgroup به شما قابلیت‌های خیلی بیشتری از دستور nice برای تعیین محدودیت استفاده از CPU روی یک پراسس می‌دهد.

زمانی که از cgroupها و Namespaceها در کنار یکدیگر استفاده کنیم، در نهایت گروهی از پراسس‌های لینوکس را در یک محیط کاملاً ایزوله شده خواهیم داشت. این دقیقاً همان چیزی است که به آن یک کانتینر لینوکسی می‌گوییم. کانتینرها در لینوکس با مجموعه کاملی از Namespaceها محدود شده‌اند به طوری که آنها فقط می‌توانند دایرکتوری را که از آن بوت شده‌اند، پراسس‌های مربوط به خود، User IDهای خود و هرگونه رابط شبکه ای که اجازه دسترسی به آنها را دارند، مشاهده کنند. به همین ترتیب، کانتینرها برای کنترل استفاده از RAM ،CPU  پهنای باند و IO با مجموعه کاملی از cgroupها محدود هستند. بنابراین، با اعمال تمام این محدودیت‌ها پراسس‌های در حال اجرا در یک کانتینر لینوکسی نمی‌توانند هیچ کدام از بخش‌های دیگر سیستم را ببینند و بنابراین طوری رفتار می‌کنند که انگار در یک کامپیوتر یا سرور جداگانه در حال اجرا هستند.

چطور می‌توانیم از cgroup و Namespace استفاده کنیم؟

در اکثر اوقات منطقی نیست که یک مدیر سیستم مستقیماً از cgroup و Namespace استفاده کند، چرا که ابزارهای ایجاد و مدیریت کانتینرها مثل Kubernetes ،Docker یا LXC خودشان این کار را برای شما انجام می‌دهند. با این حال، می‌توانید برای درک بهتر آنچه که در پشت پرده اتفاق می‌افتد، این کار را به صورت دستی انجام دهید.

کنترل cgroupها در فایل سیستم  /sys/fs/cgroup/  صورت می‌گیرد.

در مثال زیر tar در یک cgroup با محدودیت RAM اجرا شده است:

# mkdir -p /sys/fs/cgroup/test/
# cat /sys/fs/cgroup/cpuset.cpus > /sys/fs/cgroup/test/cpuset.cpus
# cat /sys/fs/cgroup/cpuset.mems > /sys/fs/cgroup/test/cpuset.mems
# echo $((1<<26)) >/sys/fs/cgroup/test/memory.kmem.limit_in_bytes
# echo $ > /sys/fs/cgroup/test/tasks
# tar xfz linux-3.14.1.tar.gz

در چهار خط اول یک cgroup با نام test ایجاد شده که امکان دسترسی به تمام سخت افزارهای فیزیکی را دارد اما استفاده از RAM در آن محدود شده است. در خط پنجم، bash و هر پراسسی که بعداْ در آن اجرا می‌شود در cgroup که قبلاً ایجاد کردیم، قرار گرفته است و در خط ششم، tar در حالت عادی اجرا می‌شود اما چون در cgroup قرار گرفته بنابراین محدودیت RAM روی آن اعمال خواهد شد.

محدودیت‌های متعددی وجود دارد که می‌توانید با cgroup اعمال کنید. داکیومنت مربوط به آن‌ها را می‌توانید در لینک‌های زیر مشاهده کنید:

https://www.kernel.org/doc/Documentation/cgroup-v1

https://www.kernel.org/doc/Documentation/cgroup-v2.txt

از طرف دیگر، Namespaceها با دستور unshare کنترل می شوند. دستور unshare برای مدیریت و دستکاری Namespaceها در util-linux-ng 2.17 و نسخه‌های بعدی موجود است. به عنوان مثال اگر دستورهای زیر را اجرا کنید:

# unshare --mount /bin/bash
# mount /dev/sda2 /mnt

bash را در یک Mount Namespace ایزوله شده اجرا می‌کند. این بدین معناست که فایل سیستمی که در خط بعدی مانت شده تنها از طریق همان bash (و پراسس‌هایی که در آن اجرا می‌شوند) که در حال استفاده از آن هستید قابل مشاهده است، و بقیه سیستم امکان مشاهده این فایل سیستم را در محلی که مانت شده است ندارند. پیشنهاد می‌شود برای مشاهده بقیه Namespaceهایی که می‌توان از آن‌ها استفاده کرد به صفحه man دستور unshare مراجعه کنید.

جمع‌بندی

همانطور که می‌توانید تصور کنید، در صورتی که از مجموعه کاملی از cgroupها و Namespaceها با یکدیگر استفاده کنیم، نتیجه آن ایزوله شدن نرم‌افزار محدود شده از بقیه سیستم خواهد بود که در‌واقع به آن یک کانتینر لینوکسی می‌گوییم. فهمیدن نحوه عملکرد cgroup و Namespace می‌تواند در درک اینکه برنامه‌های کانتینر شده چطور و چرا چنین رفتاری دارند، کمک کند. کانتینرها یکی از ویژگی‌های قدرتمند سیستم عامل لینوکس هستند و هرروز محبوبیت بیشتری پیدا می کنند. حال که نسبتاْ با نحوه عملکرد آن‌ها آشنا شدید، پیشنهاد می‌شود در تکنولوژی‌های دیگر مثل Silverblue ،LXC ،Docker ،Kubernetes یا Flatpak کاوش بیشتری انجام دهید تا ببینید که با نرم‌افزارهای کانتینر شده می‌توانید چه کارهایی انجام دهید.