امروزه در هرجایی صحبت از داکر و کوبرنتیز درمیان است. بسیاری از شرکتها به استفاده از این ابزارها روی آوردهاند و نیازمندیبسیاری از آگهیهای استخدامی، تسلط بر این ابزارهاست. اما این ابزارها چیستند و چه کاری میکنند؟
قبل از شروع بهتر است تا مفهومی به نام کانتینر آشنا شویم. کانتینر یک فرآیند (process) ایزوله شدهاست که کد و تمام دیپندنسیهای کد راآن را ایزوله میکند تا برنامه به سرعت و با اطمینان از یک محیط اجرابه محیط دیگر اجرا، منتقلشود. برای درک کانتینرها بیایید آنها را با ماشینها مجازی مقایسه کنیم. امروزه برای توسعهی چابکتر سرویسها به استفاده از میکروسرویسها روی آوردهایم که در آن هر سرویس در یک بسته ایزولهشده از بقیه اجرا میشود. در قدیم هر میکروسرویس را در یک ماشین مجازی اجرا میکردیم. ماشین مجازی یک کامپیوتر مجازی است که باعث میشود در یک کامپیوتر، تعدادی ماشین کوچکتر اجرا کنیم به گونهای که تفاوت خاصی با کامپیوترهای فیزیکی نداشته باشد. استفاده از ماشینهای مجازی مزیتهایی مثل clone کردن سریع یک ماشین یا عدم نگرانی از خرابی ماشینهای مجازی (چون خرابی ماشینهای مجازی هزینه چندانی برای ما ندارد)، امکان بهینهسازی منابعو پایین آمدن هزینهنگهداری داشت، شرکتها میتوانندبا خرید سرور و نصب hypervisor بر روی آن، چندیدن ماشین همزمان برای خود داشته باشند. از آنجایی که این مرحله، ایجاد یک ماشین ارزانبود،هر سرویس در محیط مختص به خود اجرا میشدواز محیطی که توسط یک یا دو سرور تشکیل شده بود (همه در یک یا پایگاه داده + وب سرور) به معماری پیچیدهتریحرکت کردهایم. امروزه دسترسی به تعداد خدمات مرتبط در یک برنامه وب آسان است. پایگاه داده، API Backend، SPA Frontend، سرور ذخیرهسازی، و داشتن ده سرویس بسیار مرسوم است. پس اگرچه ماشین مجازی راه حل خوبی است، اما راه حل بهینهای نیست. اکنون ما به انعطاف بیشتر و مصرف کمتر منابع سختافزاری به همراه توزیع وسیعتر نیاز داریم تا هزینههای کمتری متحمل شویم! برای پاسخ به این مسئله، ایدهکانتیرها مطرح شد. مشکل این بود هر سرویس باید در سیستمعامل جدایی اجرا میشد که همین باعث میشد یک سرویس ساده فضای بسیاری را آشغال کند. همچنین گرفتن instanceها سریع نبود. ایده کانتیر این است که هر سرویس در یک یا چند پراسس ایزوله شده در سطح سیستم عامل اجرا شود تا هدر رفت منابع کاهش یابد و ساختن نمونه سریعتر صورت گیرد.
داکر مفهوم کانتیر را اینگونه تعریف میکند:
کانتینر یک واحد استاندارد نرم افزار است که کد و تمام وابستگیهای آن را بسته بندی می کند تا برنامه به سرعت و با اطمینان از یک محیط به محیط دیگر اجرا شود. یک ایمیج Docker یک بسته نرم افزاری سبک وزن، مستقل و قابل اجرا است که شامل همه چیزهایی است که برای اجرای یک برنامه لازم است: کد، رانتایم،،کتابخانه های سیستم و تنظیمات.
به عبارت سادهتر میتوان گفت:
کانتینر چیزی جز یک پراسس ایزوله شده نیست.
در واقع ما در مفهوم کانتیرها معتقدیم جداسازی در سطح سیستمعامل اتفاق بیافتد نه سختافزار.
از نظر شخصی من این مقایسه چندان مناسب نیست. زیرا scopeهای متفاوتی هستند و بر هر حال کانتینرها پراسسهای یک ماشین مجازی میتوانند باشند ولی از نظر امنیت و موارد دیگری، مقایسه را میتوان انجام داد:
+ سربار کمتر: از آنجایی که کانتینرها در یک سیستم عامل میزبان اجرا میشوند و از منابع و پراسسهای آن استفاده میکنند، بنابراین سربار حافظه و اجرای کمتری خواهند داشت.
+ زمان شروع کمتر: در کانتنرها زمان شروع در حد میلیثانیه است در حالیکه در ماشینهای مجازی این زمان شروع میتواند به دقیقه کشیده شود.
-امنیت کمتر: به دلیل اینکه جداسازی در سطح سیستمعامل اتفاق میافتد. پس یک پراسس مخرب میتواند پراسسهای درحال اجرا در سیستم عامل را ببیند و درصوت داشتن دسترسی آن پراسس را بکشد. یا اگر یک پراسس در حال اجرا باشد با آسیبپذیریهایی مثل فرار از کانتینر (Container Escape) از محیط ایزوله خود فرار کند (در مورد katacontainers و google gvisor بهتر است مطالعه کنید).
داکر پرتکرارترین واژهای است که امروزه در ارتباط با کانتینرها میشنویم. شرکت آمازون داکر را اینگونه تعریف میکند:
داکر یک پلتفرم نرمافزاری است که به شما امکان می دهد تا برنامه ها را به سرعت بسازید، آزمایش کنید و اجرا کنید. Docker نرمافزار را در واحدهای استانداردی به نام کانتینرها بستهبندی میکند که همه چیزهایی را که نرمافزار برای اجرا نیاز دارد از جمله کتابخانهها، ابزارهای سیستم، کد و زمان اجرا دارد. با استفاده از Docker، می توانید به سرعت برنامه ها را در هر محیطی استقرار و مقیاس بندی کنید و بدانید که کد شما اجرا خواهد شد.
بنابراین داکر نرمافزاری است که از مفهوم کانتینرها برای بالا بردن isolation ، قابلیت انعطاف و قابلیت حمل استفاده میکند.
همانگونه که در تصویر بالا میبینید، معماری داکر به صورت کلاینت-سرور است. که مشتری میتواند یک اپلیکیشن CLI باشد یا یک اپ تحت وب یا دسکتاپ که ارتباط از طریق REST API فراهم شده باشد. سرور داکر را اصطلاحا Docker Daemon نیز میگوییم. کلاینت و سرور میتوانند در یک سیستم اجرا شوند و هم میتوانند دو دوسرور متفاوت اجرا شود. در این مورد محدودیتی نداریم!
مشتریان متفاوتی برای daemon وجود دارد که از بین آنها میتوانیم به docker build، docker pull برای دانلود و ساخت یک کانتینر داکری یا docker compose که برای ساخت و مدیریت مجموعهای از کانتینرها در سیستم است، اشاره کنیم.
سرور (Daemon) داکر
به درخواستهای Docker API گوش میدهد و اشیایی که در داکر تعریف شده است مانند تصاویر، کانتینرها، شبکهها و حجمها را مدیریت میکند. یک دیمون همچنین می تواند برای مدیریت سرویس های Docker با دیگر دیمون ها ارتباط برقرار کند.
مشتری
مشتری داکر راه اصلی تعامل بسیاری از کاربران با سرور داکر است. وقتی از دستوراتی مانند docker run استفاده می کنید، کلاینت این دستورات را به dockerd می فرستد که آنها را اجرا می کند. دستور docker از Docker API استفاده می کند. مشتری داکر می تواند با سرورهای متفاوتی ارتباط برقرار کند.
رجیستری
رجیستری داکر ایمیجهای داکر را ذخیره می کند. داکر هاب یک رجیستری عمومی است که همه می توانند از آن استفاده کنند و داکر به طور پیش فرض برای جستجوی تصاویر در داکر هاب پیکربندی شده است. شما حتی می توانید رجیستری خصوصی خود را اجرا کنید. این رجیستری برای ایران تحریم شده است و اگر قصد استفاده از رجیستری را دارید باید از فیلترشکن یا dns serverهای عمومی مثل شکن استفاده کنید. :))
هنگامی که از دستورات docker pull یا docker run استفاده می کنیم، تصاویر مورد نیاز از رجیستری لوکال ما خارج می شوند. هنگامی که از دستور docker pull استفاده میکنیم، تصویر ما به رجیستری پیکربندی شده شما منتقل می شود و به صورت عمومی یا خصوصی در دسترس قرار خواهد گرفت.
ایمیجها
ایمیجها بلوک سازنده کانتینرهای داکری هستند. در واقع ایمیجها پراسسهای ایزولهشده ای هستند که در فضاینام خود میتوانند تعدادی فایل یا پوشه یا حتی روتفایلهای یک سیستمعامل را ببینند. راههایی برای استفاده از ایمجها است ولی در اینجا میخواهم به کاربرد Dockerfile اشاره کنم که برای ساخت یک ایمیج داکری به کار میرود. Dockerfile یک فایل متنی است که شامل تمام دستوراتی است که میتوانیم در خط فرمان برای ساخت یک تصویر فراخوانی کنیم. با استفاده از docker build، کاربران میتوانیم یک بیلد خودکار ایجاد کنند که چندین دستورالعمل خط فرمان را پشت سر هم اجرا می کند.
نکته مهم درباره ایمیجهای داکر این است که این ایمیجها فقط-خواندنی و به صورت لایهای هستند. یعنی هنگامی که یک وب سرور node را در کانتینر اجرا میکنید. در لایه زیرین rootfs سیستمعامل قرار دارد و در لایه بالاتر node runtime نصب میشود و سپس در لایههای بالاتر از آن نیز اپلیکیشن ساخته میشود.
کانتینرها
کانتینر یک نمونه قابل اجرا از یک ایمیج داکری است. میتوانیم با استفاده از Docker API یا CLI یک کانتیر ایجاد، شروع، توقف، حرکت یا حذف کنیم یا یک کانتینر را به یک یا چند شبکه متصل کنیم. به طور پیش فرض، یک کانتینر به از سایر کانتینرها ایزوله شده و توانایی ارتباط با آنها را ندارد برای ایجاد ارتباط لازم است یک شبکه مجازی ایجاد شود تا کانتینرها بتوانند یکدیگر را ببینند. نکتهای که درمورد کانتینرها وجود دارد این است که حافظهی تخصیص داده شده به آنها پایدار نیست و با حذف شدن حافظه از بین میرود و برای ذخیره در سیستمعامل یا استفاده از حافظه مشترک توسط کانتینرها باید volume ساخته شود.
کوبرنتیز اصطلاحا یک پلتفرم Container Orchestration است که وظیفهی مدیریت بارهای کاری را برعهده دارد. یک گروه ارکستر را در نظر بگیرید که وظایف مختلف و نقشهای متفاوتی وجود دارد. وظیفه رهبر ارکستر این است که این هماهنگی را بین نقشهای مختلف به وجود بیاورد تا در موقعیت هر نقش کار خودش را انجام دهد. بعضی وقتها برخی نقشها باید بیکار باشند یا با تمام قوا فعالیت کنند. کوبرنتیز برای مجموعهای از کانتینرها که در سیستم فعالیت میکنند چنین نقشی دارد. وبسایت کوبرنتیز خودش را اینگونه تعریف میکند.
کوبرنتیز یک پلتفرم قابل حمل، توسعه پذیر و منبع باز برای مدیریت بارهای کاری و خدمات کانتینری است که هم پیکربندی و هم اتوماسیون را تسهیل می کند.
کانتینرها راه خوبی برای ایزوله کردن و اجرای برنامه های ما هستند. در محیط پروداکشن، باید کانتینرهایی را که برنامه ها را اجرا می کنند مدیریت کنیم و اطمینان حاصل کنیم که هیچ خرابی وجود ندارد. برای مثال، اگر ظرفی اصطلاحا down شود، ظرف دیگری باید راهاندازی شود. آیا اگر این رفتار توسط یک سیستم مدیریت شود، آسانتر نخواهد بود؟ به این ترتیب کوبرنتیز به کمک می آید! کوبرنتیز چارچوبی برای اجرای انعطافپذیر سیستم های توزیع شده در اختیار ما قرار میدهد. از برنامه را به صورت خودکار scale میکند و مراقب خطاهایی که برنامه ما با آنها مواجه میشود نیز هست، همچنین میتوانیم برای استقرار اپها الگوهایی تعریف کنیم. به عنوان مثال، Kubernetes می تواند به راحتی یک سناریو استقرار به صورت rolling upgrade را برای سیستم ما مدیریت کند.
کوبرنتیز شامل یک یا چند نود مستر و تعدای نود کارگر است. نودهای مستر در واقع مغز متفکر سیستم و مسئول توزیعبار یا اجرای سایر سناریوها در سطح کلاستر ما هستند.
کنترلر یا Control Plane: نام دیگر مستر control plane است که به وظایف کنترلی آن اشاره دارد. control plane از کامپوننتهای متعددی تشکیل شده است که به شرح زیر است:
سرور API یا kube api server: همانگونه که از نامش مشخص است کامپوننتی برای برقراری نودهای کارگر با سرور مستر از طریق این درگاه(gateway) استفاده میکنند.
زمانبند (scheduler): زمانبند کوبرنتیز دادههایی منابع استفاده میکنند را ذخیره سازی میکند. سپس، تعیین می کند که آیا یک خوشه سالم است یا خیر. تعیین می کند که آیا کانتینرهای جدید باید مستقر شوند، و اگر چنین است، کجا باید قرار گیرند. زمانبند، سلامت نودهای خوشه و وضعیت منابع در هر یک را محاسبه میکند. در صورت نیاز به استقرار یا حذف یک نمونه، مورد مناسب را تعیین میکند.
مدیر کنترل: به طور خلاصه کنترولر سروی است که خوشه کوبرنتیز را اجرا میکند، آن را مانیتور میکند و از طریق سرور API وضعیت مورد نظر و وضعیت فعلی آنها را مشاهده می کند. اگر وضعیت فعلی و مورد نظر اشیاء مدیریت شده مطابقت نداشته باشند، کنترل کننده اقدامات اصلاحی را انجام می دهد تا وضعیت آبجکت را به سمت وضعیت مطلوب ببرد.
دیتابیس etcd :etcd یک پایگاهداده برای ذخیرهسازی کلید-مقداری است که دادههای پیکربندی و اطلاعات مربوط به وضعیت کلاستر را ذخیره میکند. etcd ممکن است به صورت خارجی پیکربندی شود.
نودها: یک خوشه کوبرنتیز باید حداقل یک نود داشته باشد. نودها هماهنگ و برنامهریزی شدهاند تا روی گرهها اجرا شوند. نودها میتوانند سرورهای فیزیکی یا ماشینهای مجازی در دیتاسنتر ما باشند.
رانتایمها: هر نودچرخه های عمر کانتینر را با استفاده از یک موتور زمان اجرا کانتینر اجرا و مدیریت می کند. از انواع رانتایم در کوبرنتیز استفاده میشود ولی معروفترین رانتایم های حوزه کوبرنتیز عبارتند از docker، contaienrd و crio.
کامپوننت kubelet: هر نود شامل یک Kubelet است، kubelet در واقع عاملی است که با کنترلر ارتباط برقرار میکند تا اطمینان حاصل شود که کانتینرهای یک pod در حال اجرا هستند. هنگامی که کنترلر نیاز به انجام یک عمل خاص در یک گره دارد، kubelet مشخصات pod را از طریق سرور API دریافت کرده و عمل را اجرا می کند. سپس تضمین می کند که کانتینرهای مرتبط سالم و در حال اجرا هستند.
سرویس kube-proxy: هر نود حاوی یک کامپوننت پروکسی شبکه به نام kube-proxy است که خدمات شبکه کوبرنتیز را تسهیل می کند. kubeproxy مسئول فروارد کردن ترافیک برای پادهای در حال اجرا در شبکه است.
پادها: پادها کوچکتر جز در معماری کوبرنتیز است. یک پاد یک نمونه از یک برنامه کاربردی و ساده ترین واحد را در کوبرنتیز نشان می دهد. با این حال، پادها برای کوبرنتیز مرکزی و حیاتی هستند. هر پاد از یک یا چند کانتینر تشکیل شده است که به طور منطقی با هم همراه هستند. در پادها قوانینی برای تعیین نحوه ارتباط و عملکرد کانتینرها در آن داریم.
این مطلب، بخشی از تمرینهای درس معماری نرمافزار در دانشگاه شهید بهشتی است.