همیشه وقتی یه چیز جدید می خونم تا نفهمم اون پشت مشتا چه اتفاقی داره میافته احساس می کنم چیزی یادم نگرفتم، یا یه چیزی هست هنوز نمیدونم! خلاصه اینقدر قلقلکم می ده که برم ببینم واقعا چه اتفاقی داره پشت پرده میافته.
مدتی بود که از داکر برای کانتینرایز (containerize) کردن اپلیکشن ها استفاده می کردم و درک کلی و کافی ازش داشتم ولی خب این حس که یه چیزی هست که نمی دونم وجود داشت! برای همین رفتم سراغ اینکه یکم عمیق تر شم تا ببینم پشت پرده چه اتفاقی داره میافته!
تو این مقاله هم می خوام یه خلاصه ای از اتفاقاتی که داره میافته رو براتون بگم، بدون استفاده از مفاهیم قلمبه سلمبه، چون هم میخوام برای همه قابل درک باشه با هر سطحی از دانش و هم اینکه واقعا ته نداره اگه بخوای تو دیتیل هر چیزی بری!
خب بیایین با این سوال که کانتینر چیه (که هر کسی که با داکر کار کرده یه جوابی براش داره) شروع کنیم. بطور خیلی کلی کانتینر یه نمونه اجرا شده از امیج داکر (Docker Image) یا حتی می تونیم بگیم یه process در حال اجراس که ویژگی منحصربهفردش اینه که کاملا ایزوله شدس و این یکی از دلایل ایجاد و استفاده از Docker هست که بتونیم اپلیکشن هامون رو بصورت ایزوله به همراه دپندنسی هاش (Dependency) روی ماشین مقصد بدون مشکل اجرا کنیم.
بیاییم نریم تو جزییات و پیچیدش نکنیم و اینطوری درنظر بگیریم که وقتی میگیم کانتینر یعنی داریم راجع به یه پروسس در حال اجرا که ایزوله شدس صحبت می کنیم (جلو تر وقتی با یه سری از مفاهیم آشنا شدیم بهتر می تونیم کانتینر رو تعریف کنیم)، خب چیزی که من میخواستم برم دنبال جوابش هم همین نکتهس! یعنی چی ایزوله؟ چطوری ایزوله میشه؟ کی این کارو می کنه؟ داکر؟
اینا سوالایی هستن که میخوام راجع بهشون حرف بزنم.
خب بیاییم اول تکلیف اینو مشخص کنیم که پروسس ایزوله شده ینی چی؟ برای اینکه درک بهتری داشته باشیم بیاییم به این نمودار نگاه کنیم:
توی این نمودار ۳تا process رو داریم که از طریق کرنل(Kernel) سیستم عامل به منابع سخت افزاری سیستم دسترسی پیدا می کنن، فرض کنیم قراره اینا توی یه فایلی توی هارددیسک چیزی بنویسن یا بخونن و این فایل هم تو مسیر tmp/file/ ذخیره شده.
توی این حالت هر ۳ پروسس دارن یک فایل رو تغییر میدن که اگه هرکدوم از این پروسس ها بخوان مقدار این فایل رو بخونن نمی تونن انتظار داشته باشن که مقدارش همون چیزی هست که خودشون توش نوشتن، که یه چیز خارج از انتظاره براشون و ممکنه تو روند کارشون مشکلی ایجاد کنه، معنیش اینه که پروسس ها ایزوله نیستن.
خب حالا بیاییم به این سوال که پروسس ایزوله شده یعنی چی جواب بدیم، پروسس ایزوله شده (Isolated process) به یک پروسس یا برنامه اشاره داره که در یک محیط عملیاتی جداگونه اجرا میشه و از سایر پروسسها یا برنامهها در سیستم عامل جداست. این مفهوم ایزوله سازی برای تفکیک و محافظت از منابع و فعالیتهای یک پروسس مشخص استفاده میشه.
وقتی یک پروسس در یک محیط ایزوله اجرا میشه، این یعنی که این پروسس مستقل از سایر پروسسها عمل میکنه و تأثیر کوچکی روشون داره. تو محیط ایزوله شده، پروسس ممکنه دسترسی محدودتری به منابع سیستمی مثل حافظه، فایلها، شبکه و دستگاههای ورودی/خروجی داشته باشه.
خب حالا که فهمیدیم پروسس ایزوله شده یعنی چی بریم ببینیم این اتفاق چطوری داره تو لینوکس میافته.
یه بار دیگه به این نمودار دقت کنین، یه مفهومی توش بود که راجع بهش حرف نزدیم default namespace.
تو لینوکس هر پروسس بدون استثنا داره توی یه namespace مشخص اجرا میشه، که این namespace مشخص میکنه پروسس ما به چه منابعی از سیستم دسترسی داره، که همین موضوع نشون میده چرا پروسس های ما نسبت به هم ایزوله نیستن و از منابع مشترک استفاده می کنن (چون تو یک namespace مشترک به اسم default namespace دارن اجرا میشن).
خب حالا با این مقدمه بریم سراغ اینکه لینوس چطوری پروسس ها رو ایزوله میکنه؟ برای این منظور دو feature تو لینوکس پیاده شده که مسئول این ایزوله سازی هستن:
با این ویژگی تا یه حدی آشنا شدیم، namespaceها خیلی خلاصه مسئول این هستن که مشخص کنن هر پروسس یا یه گروه از پروسسها به چه منابعی می تونن دسترسی داشته باشن! البته مهم تر از اون، پروسسی که در یک namespace قرار می گیره اینطور براش شبیه سازی می شه که فک می کنه که به تمام منابع سیستم دسترسی داره و خودش تنها پروسسی هست که داره اجرا میشه (مثل دید ما انسان ها به دنیا ?). به نمودار زیر نگاه کنیم تا بهتر این مفهوم رو درک کنیم:
تو این نمودار می تونیم ببینیم که پروسسی که تو یک namespace قرار گرفته به یک نسخه انتزایی از منابع سیستم مثل RAM, CPU, Network و Hard Drive دسترسی داره و به همین ترتیب پروسس های دیگه در namespace های دیگه هم فقط به نسخهای از منابع که بهشون تخصیص داده شده دسترسی دارن و نمی تونن دخل و تصرفی به منابع پروسس های دیگه داشته باشن. به همین دلیل میگیم پروسس ها دارن تو یک محیط ایزوله شده اجرا میشن. نمودار پایین نشون میده که چطور دو تا پروسس نسبت به هم ایزوله هستن:
نکته مهم اینکه تنها فقط یک نوع namespace برای این منظور وجود نداره، namespaceهای مختلفی تو لینوکس وجود داره که هر کدومشون مسئول مدیریت منابعی از سیستم هستن و اینجا می خوایم با چندتا از پر استفاده ترینشون آشنا بشیم که مخصوصا تو تکنولوژی های مثل داکر برای ایجاد یک کانتینر استفاده میشن:
این namespace برای ایزوله کردن ID پروسسها ایجاد شده، به این معنی که پروسسهایی که در PID namespace قرار میگیرن IDهاشون طوری تعریف میشه که تداخلی با ID پروسسهای دیگه در namespaceهای دیگه نداشته باشن.
این نوع از namespace همونطور که از اسمش مشخصه، مسئول ایزوله سازی منابع مرتبط با شبکه هست مثل network interfaces, IP addresses, routing tables و firewall rules. هر network namespace می تونه کانفیگ مربوط به شبکه خودش رو داشته باشه که اینطور براش بنظر میاد که یه سخت افزار شبکه جداگونه بهش اختصاص دادن که هر طور نیاز داره می تونه کانفیکش کنه بدون اینکه نگران این باشه که مشکلی برای پروسس های دیگه بوجود بیاره. این یعنی میتونیم مثلا تو namespaceهای مختلف رنج IP های یکسان داشته باشیم بدون اینکه مشکلی ایجاد بشه.
برای ایزوله کردن mount pointهای فایل سیستم استفاده میشه. پروسس هایی که تو mount namespaceهای مختلف اجرا میشن، دیدی که نسبت درخت فایل سیستمشون دارن متفاوت از هم دیگهست، که باعث میشه پروسسها فایل سیستم ایزوله شده خودشون رو داشته باشن و نتونن دخل و تصرفی روی فایل های پروسس دیگه داشته باشن.
برای ایزوله کردن تو سطح user و group استفاده میشه که این اجازه رو به پروسسها میده که با دسترسی های مختلف (privileges) در namespace اجرا بشن.
خب تا اینجا متوجه شدیم که namespace برای تخصیص منابع به پروسسها به کار میرن که انواع مختلفی هم داشت که با بعضیهاشون آشنا شدیم و گفتیم با namespaceها یه کپی از منابع سیستم رو اختصاص میدیم به یه پروسس ولی سوال اینجاست که خب چقدر از منابع؟ یا هر پروسس چقدر از این منابع رو در اختیار داره؟
جواب این سوال cgroups هست، cgroups یه فیچر تو کرنل لینوکس هست که مسئول تخصیص و مدیریت میزان مصرف منابع برای هر پروسس هست. پس ما می تونیم با استفاده از namespaceها منابعی رو که پروسس ها نیاز دارن بهشون تخصیص بدیم و به وسیلهی cgroups مشخص کنیم که به چه میزان از این منابع می تونن استفاده کنن.
خب حالا بیاییم یک بار دیگه کانتینر رو معرفی کنیم، کانتینر یک محیط ایزوله شدهست (Isolated environment) که با تکنولوژی هایی مثل داکر با استفاده از ابزارهایی هایی مانند namespace و cgroups ایجاد میشود و به برنامهها منابع مورد نیازشان (نرم افزاری و سختافزاری) را برای اجرا در اختیارشون میگذارن.
با استفاده از کانتینرها، برنامهها بهراحتی قابل بستهبندی، انتقال و اجرا در محیطهای مختلف دیگه هستن. همچنین کانتینرها میتونن برنامهها را در محیطهای متفاوت به صورت یکسان و پیشبینیپذیر اجرا کنند.
نکته: کانتینرها ویژگی های مهم دیگه علاوه بر Isolation، مانند Encapsulation، Portability و Consistency در اختیار ما قرار میده که مورد بحث ما نیستن.
در پایان خواستم به این نکته هم اشاره کنم که سعی کردم مفاهیم رو تا جای ممکنه ساده سازی کنم که با هر سطح از دانشی قابل درک باشه، و معمولا ساده سازی مفاهیم یه سری چیزا ممکنه جا بمونه یا اونطوری که هست کامل بیان نشه. هدفم از نوشتن این نوشته ایجاد یه کنجکاوی بود که برید ببینین اون پشت مشتآ چه اتفاقی داره میافته که عمیق تر مسائل رو یاد بگیرید :)