اولین باری که با مفهوم container آشنا شدم، اولین چیزی که گفتم این بود که چی شده؟ چطوری شد این طوری شد؟ یعنی چی بدون vm محیط ایزوله(isolate) داریم؟ داکر کیه( من با lxc بعدا آشنا شدم و هنوزم پشت صحنه چیکار میکنه رو نمیدونم)؟ این کار رو چطوری انجام میده؟ چطوری ممکنه؟ اصلا ممکنه یا ایستگاهمون کردی؟
جواب کوتاه این سوال(سوال مطرح شده تو عنوان مطلب) استفاده از cgroup و namespaceهاست، که البته به داکر هم ربطی ندارد و از ویژگیهای هسته لینوکساند، که داکر ازشون برای ایزولهسازی استفاده میکند. اما اینکه این دو ویژگی چی هستند رو در ادامه با هم بررسی میکنیم.
یکی از نیازمندیهای ایزولهسازی، جدا بودن پردازشهای(process) مرتبط با اون از دیگر پردازشهاست به گونهای که انگار این پردازشها در یک دنیای خودشون هستند و دیگر پردازشها هم براشون قابل رویت(visibility) نباشد. این عمل توسط Namespace انجام میشود. در واقع Namespace امکان حداسازی چندتا بخش رو برای ما فراهم میکند. چیزهایی مثل رابطهای(interface) شبکه، نصب(mount) فایل سیستمها، pidها و حتی user idها، که داکر با استفاده از این قابلیتها جداسازی موارد مختلف رو انجام میده.
خب اگر این ویژگی جزء هسته لینوکس به حساب میاد، پس قاعدتا ما هم باید بتونیم ازش استفاده کنیم. به عنوان مثال میتونیم به کمک دستور زیر یک namepace از نوع pid بسازیم و داخلش bash رو اجرا کنیم.
$ sudo unshare --fork --pid --mount-proc bash
نتیجه خروجی:
root@test:~# ps aux USER PID | %CPU | %MEM | VSZ | RSS | TTY | STAT | START| TIME | COMMAND root 1 | 0.0 | 0.0 | 12932 | 4216 | pts/0 | S | 19:54 | 0:00 | bash root 8 | 0.0 | 0.0 | 14588 | 3520 | pts/0 | R+ | 19:54 | 0:00 | ps aux
جالب بود نه؟
حواستون باشه که pid اصلی این bash تو namespace عمومی یک مقدار دیگهست.
برای وارد شدن به namespaceهای مختلف هم میتونیم از دستور nsenter استفاده میکنیم. به نظرتون آیا docker exec معادل همین دستوره؟ نه نیست!! چرا؟ اینجا رو بخونید.
عبارت cgroup مخفف Control Group هستش و برای کنترل منابع، طراحی و استفاده میشود. منظور از منابع هم چیزهایی مثل CPU, RAM, Network, IO هستش. کارش هم اینه که تعیین کند یک پردازش(یا یک گروه از پردازشها) چه میزان از منبع(یا منابع) رو میتونه دسترسی داشته باشد.
اگر هم بخوایم که یک cgroup ایجاد کنیم، خواهیم داشت:
# mkdir /sys/fs/cgroup/memory/vahid/
خب الان ما یک محدودیت برای حافظه داریم با نام vahid که داخلش یک سری فایل دارد که هر کدوم استفاده خاص خودش رو دارد. یک فایل به اسم memory.limit_in_bytes وحود دارد که میزان محدودیت استفاده از حافظه رو به بایت مشخص میکند و میتونیم مقدار اون رو با مقدار دلخواه خودمون عوضش کنیم.
مرحله بعد اینکه این محدودیت رو به پردازش مدنظرمون اعمال کنیم. بعد از اعمال اون اگر این پردازش ما از اون حد تعیین شده مقدار حافظه بیشتری رو درخواست کنه با خطای memory allocation مواجه خواهد شد.
(عکس پایین رو روش کلیک کنید و تو حالت بزرگ تر ببنین اگر باز مشخص نبود از این لینک کمک بگیرید.)
به عنوان مثال میتونیم مقدار محدودیت رو ۱۰ مگ بزاریم و اون رو روی یک bash اعمال کنیم. بعدش بیایم و پایتون رو با این bash اجرا کنیم، اینجاست که یک خطا دریافت میکنیم.
$ sudo echo 10000000 > /sys/fs/cgroup/memory/vahid/memory.limit_in_bytes $ echo $$ > /sys/fs/cgroup/memory/vahid/cgroup.proc $ python
تو خط اول حد حافظه رو مشخص کردیم. خط دوم pid مربوط به bash فعلی رو گرفتیم و اون رو به این cgroup ایجاد شده اضافه کردیم(در واقع از cgroup فعلی جدا کردیم و بردیم داخل vahid). یعنی از این به بعد این پردازش با محدودیتهای تعریف شده برای vahid اجرا بشه. اخر سر هم اومدیم داخل این bash با محدودیت حافظه پایتون رو اجرا کردیم که با خطا مواجه میشیم و از bash میاد بیرون.
به همین راحتی! :)
ما ایزولهسازی با دو ویژگی قبلی انجام دادیم، اما چی میشد اگر میخواستیم، که system callهارو هم محدود کنیم؟ اینجاست که باید از seccomp-bpf باید استفاده کنیم که مشخص میکنه هر پردازش چه system callهایی رو میتونه انجام بده.
همین! لبخند بزنین لطفا :)
منابع: