فرض کنید میخواهیم پکیج و سرویسهای مختلفی را روی یک سیستمعامل نصب کنیم که به آنها اختصارا app میگوییم. طبیعی است که هر App کتابخانهها و نیازمندیهای متفاوتی دارد که به آنها dependency گفته میشود. ممکن است app هایی داشته باشیم که به dependency با نام یکسان اما ورژنهای متفاوت نیازمند باشند. همچنین این dependency ها ممکن است خودشان نیز dependency داشته باشند. این مورد در شکل زیر نشان داده شده است.
مدیریت پکیجها در لینوکس نسبتا به خوبی صورت میگیرد اما dependency ها زمانی مشکلساز میشوند که دو پکیج conflict (تداخل) بخورند یا اینکه یک کتابخانه برای اجرا نیاز به محیط یا سیستمعاملی دیگر داشته باشد، عبارت
The following packages have unmet dependencies...
عبارت نسبتا آشنایی برای افرادی است که با دبیانبیسها کار کردهاند!
شاید بتوان برای برخی از کامپوننتها و سرویسها، ماشینهای مجازی (Virtual Machine یا VM) مجزایی را درنظر گرفت که تاحدی مشکلات Conflict و محیط اجرا را حل میکند، اما این سناریو یک ایراد بسیار بزرگ دارد:
زمانی که محصول رشد میکند یا تعداد سرویسها زیاد میشود، نیازمند VM های متعددی هستیم. طبیعی است که افزایش تعداد VM نه تنها منجر به افزایش هزینه و هدر-رفت منابع سختافزاری میشود که مدیریت خود VM ها و سیستمعاملهای نیز سخت خواهد شد.
جدای از بحث dependency و نگهداری (Maintenance) از سویی دیگر، در محیطهای عملیاتی (Production) چندمورد ضروری است:
مواردی که گفته شد، تقریباً بطور کامل با کانتینر حل میشود:
بجای استفاده از VM های متعدد برای حل این موارد، میتوان از Linux Container Technology (LXC) بعنوان یکی از راههای پیادهسازی و مدیریت کانتینرها نام برد. LXD نیز یکی دیگر از راههای پیادهسازی کانتینرها است. کانتینر سربار بسیار بسیار کمی نسبت به VM دارد.
کانتینر نسبت به VM بسیار سبک است (Lightweight) و اجرای تعداد زیادی از کامپوننتها روی یک سیستمعامل را امکانپذیر میکند. همچنین منابع سختافزاری بسیار کمتری نیاز دارد، چرا که هر VM نیازمند منابع، سیستمعامل، کرنل و ... اختصاصی مربوط به خودش است. بطور کلی اگر بخواهیم کانتینر را تعریف کنیم شاید عبارت زیر مناسب باشد:
کانتینر، یک یا چند پراسس ایزولهشده و بدون سربار اضافی است که منابع اختصاصداده شده به خودش را مصرف میکند. درواقع بجای ایزولهکردن در لایهی hypervisor ، ایزولهسازی در سطح کرنل انجام میشود. بعبارت دیگر در کانتینر هزینهی کرنل را نمیدهیم.
شکل زیر تفاوت کانتینر و VM را بخوبی نشان میدهد:
همانطور که گفته شد و در تصویر بالا نیز مشاهده میشود، کانتینر از کرنل هاست اصلی استفاده میکند، بنابراین خروجی دستور زیر برای مشاهده ورژن و نام کرنل در هر کانتینر و هاست اصلی یکسان است:
uname -srvm
نتیجه:
اما یک سوال را هنوز نمیتوانم پاسخ دهم و خوب است که در مورد آن فکر کنیم:
اصلا مقایسهی کانتینر و VM کار درستی است؟
کانتینر در باربری (از جمله باربری دریایی) تحولی را بوجود آورد. یعنی تولیدکننده بار خود را در یک ساختار مشخص (کانتینر) ریخته و ارسال میکند. داخل کانتینر هرچیزی میتواند باشد: عروسک، تلفن همراه، لپتاپ، کفش و ... که ساختار و نحوه ارسال آن کاملا مستقل از جنس ارسالی است.
یک. Linux Namespaces
دو. Linux Control Groups (cgroups)
در لینوکس، Namespace ها باعث میشوند که هر پراسس نگاه (View) خودش را به سیستم داشته باشد، این «زاویهی دید» شامل فایلها، پردازنده، Hostname، اینترفیس شبکه و مواردی از این قبیل است.
لینوکس بطور پیشفرض یک Namespace دارد که همه منابع سیستم مانند PID ها، UserID ها، اینترفیسهای شبکه و … به آن Namespace تعلق دارند.
میتوان Namespace های مختلفی را ایجاد و منابع سیستم را برای آنها مدیریت کرد. همچنین اجرای پراسس در آن Namespace ها امکانپذیر بوده، بگونهای که پراسس تنها منابع مربوط به آن Namespace را مشاهده میکند.
لازم به ذکر است انواع مختلفی از Namespace در لینوکس وجود داشته و پراسس لزوماً فقط به یک Namespace خاص تعلق ندارد. از انواع مختلف Namespace میتوان به موارد زیر اشاره کرد:
هر نوعی از Namespace برای ایزولهکردن گروهی از منابع بکار میرود. بعنوان مثال UTS مشخص میکند که پراسس کدام Hostname و دامنه (Domain Name) را ببیند؛ مثلا با دو Namespace متفاوت از نوع UTS میتوان پراسسهایی داشت که دو Hostname مختلف را میبینند و بعبارت دیگر دو پراسس داریم که در دو ماشین مجزا اجرا شدهاند.
گروههای کنترلی یا Linux Control Groups (cgroups) دومین ویژگیای است که لینوکس برای ایزولهکردن کانتینرها بکار میگیرد. Cgroup یکی از Feature های کرنل لینوکس بوده و محدودیت منابع مصرفی کانتینر و پراسس (یا گروهی از پراسسها) را مشخص میکند. پراسس نمیتواند بیش از آنچه که بدان اختصاص داده شده است را مصرف کند. این محدودیت منابع شامل پردازنده، رم، پهنایباند و مواردی از این دست است. همانطور که پراسسهای دو سیستم مجزا نمیتوانند منابع یکدیگر را مصرف کنند، این محدودیت منابع نیز باعث میشود که پراسس اجازه مصرف منابع سایر پراسسها را نداشته باشد.
تا اینجا توضیحاتی در مورد کانتینر داده شد، اما رابطه کانتینر و داکر چیست؟ میتوان گفت که داکر یک سرویس (یا پلتفرم) برای مدیریت کانتینرها است، درواقع داکر اولین سیستم مدیریت کانتینر بود که علاوه بر نقش مدیریت کانتینر، عملیات پکیجکردن و dependency ها را نیز به سادگی و در سیستمعاملهای مختلف مدیریت میکند. یکی از ویژگیهای بسیار عالی داکر، نیز همین مورد است؛ اجرای کانتینرهای مختلف روی سیستمعاملهای مختلف.
و اگر سادهتر بگوییم:
داکر یک پکیج کاملاً Portable میسازد که علاوه بر اجرا بصورت کانتینر و مستقل از سیستمعامل، امکان مدیریت منابع را هم فراهم میکند. این پکیجها همان Image های داکر هستند.
مثالی را برای تقریب به ذهن میزنم:
در مجازیسازی، ایمیج (مثلاً export فایل ovf) داریم که روی virtual-box (یا هر مجازیساز دیگر مثل ESX) اجرا شده و VM شروع بکار میکند. برای داکر هم Image داریم که سرویس داکر آن را اجرا کرده و کانتینر شروع بکار میکند، با این تفاوت که سربار اضافی نداریم و کرنل مشترک است.
برای این نوشته تا حد زیادی از کتاب Kubernetes in Action کمک گرفته شده است. همچنین تصاویر نیز از همین منبع است.