نکته: این مقاله تنها معماری و ساختار docker را بررسی می کند و آموزش داکر و نحوهی ساخت container ها نیست. همچنین در یک مقالهی جداگانه در سایت به آموزش مقدماتی نصب و پیکربندی Docker پرداختهایم که پیشنهاد می کنیم در صورت تمایل آن را مطالعه کنید.
اگر برنامهنویس باشید یا حداقل با تکنولوژیهای حوزهی وب سروکار داشته باشید حتما اسم docker به گوشتان خورده است. داکر از چیزی به نام container استفاده میکند اما ازآنجاییکه هم docker و هم container ها قبل از این شهرت زیادی نداشتند، احتمالاً از هیچکدام از آنها سر درنمیآورید. به نظر من حتی اگر قصد استفاده از Docker را ندارید بهتر است با مفاهیم پایهی آن و موضوعات مربوط به این مفاهیم مانند Virtual Machine آشنا شوید تا از دنیای تکنولوژی عقب نمانید.
توجه داشته باشید که این مقاله فقط برای معرفی و آشنایی با مفاهیم اصلی docker و موضوعات این حوزه است و اصلاً وارد کدنویسی آن نمیشود. مخاطبین هدف این مقاله افرادی هستند که از این حوزه چیزی نمیدانند بنابراین مطالعهی این مقاله پیشنیازی ندارد.
Container ها و VM ها (مخفف Virtual Machine یا ماشین مجازی) اهداف مشابهی دارند؛ ایزوله کردن یک برنامه و وابستگیهای آن به یک محیط خاص تا در هرجایی اجرا شود. هر دو نیز به ما اجازه میدهند که محیطی مجازی را برای خودمان ایجاد کنیم بدون اینکه نیازی به سختافزار جداگانه داشته باشیم. با این حساب شاید بپرسید تفاوت این دو در چیست؟ تفاوت آنها در معماری و رویکرد آنها به این مسئله است. بیایید به هرکدام بهصورت جداگانه نگاهی بیندازیم.
به زبان ساده Virtual machine ها یک کامپیوتر جدا را در سیستم شما شبیهسازی میکنند و این کامپیوتر شبیهسازیشده میتواند برنامهها را مثل یک کامپیوتر واقعی و مستقل اجرا کند. بهطور مثال شما ویندوز 10 را دارید و میتوانید در یک VM ویندوز 7 را نیز نصب کنید بنابراین ویندوز 7 را درون ویندوز 10 خواهید داشت! چرا کسی باید چنین کاری را انجام بدهد؟ فرض کنید شما از برنامهی خاصی استفاده میکنید که فقط روی ویندوز 7 اجرا میشود. در چنین حالتی بهجای عوض کردن کل سیستمعامل خود میتوانید ویندوز 7 را شبیهسازی کنید. یکی دیگر از استفادههای VM ها تست کردن نرمافزار است؛ مثلاً شما برنامهنویس ویندوز هستید و میخواهید نرمافزاری را که نوشتهاید در سیستمهای عامل مختلف تست نمایید. طبیعتاً نصب ویندوز 7 و سپس 8.1 و سپس 10 بسیار آزاردهنده است و راه سریعتر استفاده از VM ها است. در ضمن ازآنجاییکه محیط VM تا حدی ایزوله است از آن برای تست ویروسها و فایلهای مخرب نیز استفاده میکنند.
VM ها روی hypervisor اجرا میشوند و خود hypervisor روی ماشین میزبان و یا bare-metal اجرا میشود، بنابراین دو نوع hosted hypervisor و bare-metal hypervisor را داریم. چقدر از این جمله را فهمیدید؟ بگذارید توضیح بدهم. Hypervisor یک نرمافزار یا سختافزار است که VM ها میتوانند روی آن اجرا شوند. خود hypervisor ها نیز روی سیستمی فیزیکی (مانند کامپیوتر شما) اجرا میشوند که به آن ماشینِ میزبان یا host machine میگوییم. کار ماشین میزبان این است که RAM و CPU و چنین منابعی را در اختیار VM ها قرار بدهد. سیستم شما میتواند چندین VM را همزمان اجرا کند.
بنابراین تا اینجا متوجه شدیم که یک VM با استفاده از hypervisor روی ماشین میزبان اجرا میشود. در این حالت به آن VM ماشین مهمان یا guest machine نیز میگوییم. این ماشین میزبان یا همان VM سیستمعامل و هر چیزی که به آن نیاز داشته باشد را در خودش دارد (باینریهای سیستم و کتابخانهها و غیره) و همچنین سختافزار مجازی شدهی خود را نیز دارد بنابراین اگر از درون به آن نگاه کنیم network adapter و CPU و RAM و غیره خودش را دارد اما اگر از بیرون به آن نگاه کنیم میدانیم که این منابع را با ماشین میزبان تقسیم کرده است.
من بالاتر توضیح دادم که hypervisor ها یکی از دو نوع hosted hypervisor یا bare-metal hypervisor هستند. hosted hypervisor روی سیستمعامل اصلی ماشین میزبان سوار میشود. یعنی چه؟ یعنی بهصورت مستقیم به سختافزار ماشین میزبان دسترسی ندارد و باید از طریق سیستمعامل اصلی ماشین میزبان با سختافزار ارتباط برقرار کند. مزیت این روش این است که نیازی به نگرانی در رابطه با سازگاری سختافزار نداریم. ازآنجاییکه سیستمعامل اصلی مسئولیت برقراری ارتباط با سختافزار را دارد hosted hypervisor ها معمولاً ازنظر سختافزاری سازگارتر هستند. البته عیب این روش نیز این است که سیستمعامل اصلی ماشین میزبان بهعنوان یکلایهی ارتباطی اضافی قرار میگیرد و باعث کم شدن سرعت و مصرف بیشتر منابع سیستم میشود.
نوع دوم hypervisor ها نیز bare metal hypervisor است و احتمالا حدس زدهاید که بهجای ارتباط با سیستمعامل اصلی ماشین میزبان، بهصورت مستقیم روی سختافزار این ماشین نصب میشود. ازآنجاییکه این دسته از hypervisor ها بهصورت مستقیم با سختافزار تعامل دارند هیچ نیازی به نصب سیستمعامل روی ماشین میزبان نداریم. در چنین حالتی اولین چیزی که بهعنوان سیستمعامل روی ماشین میزبان نصب شود همان hypervisor خواهد بود. طبیعتا چنین روشی باعث بالا رفتن سرعت، پایداری و مقیاسپذیری سیستم میشود اما سازگاری سختافزاری در اینجا بسیار مهم خواهد بود.
تصویر بالا ساختار یک virtual machine را به خوبی برایتان توضیح میدهد.
برخلاف VM ها که شبیهسازی سختافزاری انجام میدهند، container ها شبیهسازی را در حد سیستمعامل انجام میدهند که در سطح user space است. زمانی که تا انتهای مقاله را بخوانید متوجه معنی این جمله خواهید شد.
ازنظر کاربردی و عملی container ها عینا شبیه به VM ها هستند؛ یعنی محیط خصوصی خودشان را برای پردازش دارند، دستورات root را اجرا میکنند، network interface خصوصی خودشان را دارند بنابراین میتوانید IP Table های خودتان را تعیین کنید، میتوانند فایلهای سیستمی را mount کنند و الیآخر. اما ازنظر ساختاری تفاوت بزرگ میان VM ها و container ها این است که container ها kernel سیستم میزبان را با دیگر Container را به اشتراک میگذارند. تصویر زیر میتواند مفهوم این جمله را بهخوبی نشان بدهد:
با مقایسهی این تصویر با ساختار VM ها کاملا متوجه این تفاوت ساختاری خواهید شد. زمانی که شما میخواهید Container ای را روی سیستم خود داشته باشید درواقع یک موتور (engine) برای آن نصب میشود و تکتک Container ها روی آن سوار میشوند. با این حساب container ها بهجای آنکه سختافزار یا kernel را در یک پکیج قرار بدهند user space یا محیط کاربری را بستهبندی میکنند. با این حساب معماری آنها از پایه به اشتراک گذاشتهشده است و تنها بخشیهایی که از صفر ساخته و شبیهسازی میشوند bin ها و lib ها (باینریها و کتابخانهها) هستند. به همین خاطر است که container ها نسبت به یک VM بسیار سبکتر و سریعتر هستند.
داکر یک پروژهی متنباز بر اساس Container های لینوکس است. Docker از ویژگیهای کرنل لینوکس مانند namespace ها و گروههای کنترل (control groups) استفاده میکند تا Container هایی را بر روی سیستمعامل سوار کند. توجه داشته باشید که اکثر سرورهای میزبانی در جهان با اختلاف بسیار زیادی از لینوکس استفاده میکنند و در مقایسهی کلی سرورهای بسیار کمی برای ویندوز سرور باقی میماند.
باید در نظر داشته باشید که Container ها اصلا تکنولوژی جدیدی نیستند. گوگل Container های خودش را دارد و سالها است که از آن استفاده میکند. همچنین container های لینوکسی دیگری مانند Solaris Zones یا BSD jails یا LXC نیز از قدیم وجود داشتهاند. با این حساب شاید بپرسید که چرا داکر ناگهانی اینقدر محبوب شده است؟ من ویژگیهای مهم docker را بهصورت خلاصه برایتان توضیح میدهم.
سهولت استفاده: Docker باعث شده است که همه (توسعهدهندگان، ادمین های سیستم، معماران و غیره) بتوانند بهسادگی از آن استفاده کنند. تست کردن و راهاندازی یک برنامه در Docker بسیار ساده و سریع است. شعار داکر جملهی build once, run anywhere است که یعنی «یکبار بساز و همهجا اجرا کن». چرا؟ به دلیل اینکه سهولت استفاده از docker به حدی است که هرکسی میتواند یک سیستم کامل را در لپتاپ خودش ساخته و سپس آن را در یک docker container قرار بدهد و از آن به بعد میتواند آن سیستم را در هر سروری اجرا کند.
سرعت بالا: docker container ها (یعنی Container هایی که با Docker ساخته میشوند) بسیار سبک و سریع هستند. بخشی از این سرعت بالا مربوط به طبیعت container ها است. همانطور که گفتم container ها محیطهای ایزولهای هستند که روی کرنل اجرا میشوند بنابراین نیازی به منابع فراوان و قوی ندارند. در مقایسه، VM ها برای اجرا باید یک سیستمعامل کامل را راهاندازی کنند بنابراین بسیار سنگین هستند اما راهاندازی یک docker container فقط در چند ثانیه انجام میشود.
Docker Hub: اگر با Docker Hub آشنا نیستید بهطور خلاصه میگویم که Docker Hub شبیه یک app store برای docker container ها است (مثل Google Play Store برای گوشی اندرویدی شما). در این Hub کاربران مختلف image های مختلفی از برنامههای مختلف را ساختهاند و شما با یک دستور ساده آن را درون container خود دانلود میکنید و معمولاً هیچ نیازی به ویرایش آن نیز نمیباشد. با این حساب عملیات پیکربندی و نصب دستی برنامهها تا حد زیادی کاهش پیدا میکند.
طراحی ماژولار و مقیاسپذیر: با استفاده از داکر تقسیم کردن قسمتهای مختلف برنامه و قرار دادن آنها در Container های مختلف بسیار آسان است. یعنی چه؟ بهطور مثال برنامهی Node.js شما در یک Container قرار دارد و سپس پایگاه دادهی redis شما در یک پایگاه دادهی دیگر قرار دارد. حالا که قسمتهای مختلف برنامهی شما در container های مختلفی قرار دارند میتوانید این قسمتهای مختلف را تغییر داده و یا کاملا عوض کنید بدون اینکه به ساختار اصلی برنامه صدمه بزنید. همچنین بهروزرسانی و مقیاس دهی این Container ها بسیار راحت است بنابراین مقیاسپذیری کل برنامهی شما بسیار بالا خواهد بود.
حالا که با container ها و تفاوتشان با VM ها آشنا شدهایم باید به سراغ مفاهیم مرتبط با داکر برویم و با اصطلاحات آن آشنا شویم. قبل از شروع توضیحات باید به تصویر زیر نگاهی بیندازید:
docker engine: موتور docker یا همان docker engine لایهای است که docker روی آن اجرا میشود (تصویر آن را در قسمت توضیح container ها مشاهده کردید). درواقع docker engine یک runtime است که container ها، تصاویر، image ها، build ها و بقیهی مسائل docker را مدیریت کرده و روی سیستمعامل لینوکس اجرا میشود. هر docker engine به سه قسمت تقسیم میشود:
بیایید با دو قسمت اول آشنا شویم (REST API ساده است و حتما همهی شما با آن آشنا هستید).
docker client: این قسمت همان قسمتی است که شما (توسعهدهنده یا هرکسی که به docker دسترسی دارد) با آن تعامل میکند. مثلا زمانی که شما دستور توقف یا ریستارت شدن را میدهید، در حال تعامل با داکر هستید. این client دستورات شما را گرفته و به daemon پاس میدهد.
docker daemon: این قسمت مسئول اجرای دستورات ارسالشده به docker است. یادتان باشد که docker client نمیتواند دستورات را اجرا کند بلکه آن را به daemon پاس میدهد. بهطور مثال دستوراتی مانند build یا run توسط این قسمت اجرا میشوند بنابراین شما هیچوقت بهصورت مستقیم با docker daemon تعامل نخواهید داشت. docker client میتواند در سیستم میزبان اجرا شود اما لزومی به انجام این کار ندارد، مثلا میتواند در یک سیستم دیگر اجراشده و سپس با docker daemon در سیستم میزبان تعامل داشته باشد اما docker daemon حتما در سیستم میزبان اجرا خواهد شد.
Dockerfile جایی است که شما دستورات مختلف برای ساخت یک docker image را مینویسید. سه مثال ساده از این دستورات به شکل زیر است:
زمانی که تمام دستورات خود را نوشتید باید دستور docker build را اجرا کنید تا از این دستورات، یک image ساخته شود. بهطور مثال فایل زیر، فایل رسمی داکر برای wordpress است:
FROM php:5.6-apache RUN a2enmod rewrite # install the PHP extensions we need RUN apt-get update && apt-get install -y libpng12-dev libjpeg-dev && rm -rf /var/lib/apt/lists/* \ && docker-php-ext-configure gd --with-png-dir=/usr --with-jpeg-dir=/usr \ && docker-php-ext-install gd RUN docker-php-ext-install mysqli VOLUME /var/www/html ENV WORDPRESS_VERSION 4.2.2 ENV WORDPRESS_UPSTREAM_VERSION 4.2.2 ENV WORDPRESS_SHA1 d3a70d0f116e6afea5b850f793a81a97d2115039 # upstream tarballs include ./wordpress/ so this gives us /usr/src/wordpress RUN curl -o wordpress.tar.gz -SL https://wordpress.org/wordpress-${WORDPRESS_UPSTREAM_VERSION}.tar.gz \ && echo "$WORDPRESS_SHA1 *wordpress.tar.gz" | sha1sum -c - \ && tar -xzf wordpress.tar.gz -C /usr/src/ \ && rm wordpress.tar.gz \ && chown -R www-data:www-data /usr/src/wordpress COPY docker-entrypoint.sh /entrypoint.sh # grr, ENTRYPOINT resets CMD now ENTRYPOINT ["/entrypoint.sh"] CMD ["apache2-foreground"]
همانطور که در قسمت قبل توضیح دادم image ها قالبهایی read-only هستند که بر اساس دستورات موجود در فایل dockerfile ساخته میشوند. با این حساب فایل image علاوه بر اینکه برنامهی پکیج شدهی شما و وابستگیهایش را توصیف میکند، میداند که چه process هایی را پس از اجرای برنامه، اجرا کند. طبیعتا docker image ها با استفاده از dockerfile ساخته میشوند و هر دستور درون dockerfile یکلایهی جدید را به image اضافه میکند. هر لایه نیز نمایندهی قسمتی از image است که یا جایگزین لایهی قبل از خود شده و یا به آن اضافه میشود. این لایهها یکی از دلایل اصلی سبک بودن و سرعت docker است. داکر برای دستیابی به این ویژگی از یک Union File System استفاده میکند.
داکر برای ساخت image ها از یک Union File System استفاده میکند که درواقع یک stackable file system است. یعنی چه؟ یعنی فایلها و پوشههای فایل سیستمهای مختلف (که به آنها شاخه میگوییم) روی یکدیگر قرار میگیرند تا یک فایل سیستم واحد را تشکیل بدهند. محتوای پوشههایی که در شاخههای رویهم، یک مسیر را دارند بهعنوان یک مسیر واحد تلقی میشوند. این کار باعث میشود که نیازی به ساخت کپیهای مختلف از هر لایه نداشته باشیم. زمانی که قرار است لایهی خاصی ویرایش شود، ابتدا یک کپی از آن گرفته میشود و سپس تغییرات روی آن اعمال خواهد شد بنابراین لایهی اصلی دستنخورده باقی میماند. چنین سیستمهای لایهای دو مزیت اصلی دارند:
volume ها درواقع دادههای یک container هستند و زمانی ساخته میشوند که cotainer ایجاد میشود. volume ها به شما اجازه میدهند که دادههای هر container را ذخیرهسازی کنید. توجه داشته باشید که این volume ها کاملا از Union File System جدا هستند و به شکل فایلهای عادی روی ماشین میزبان ذخیره میشوند بنابراین حتی اگر container خود را rebuild (بازسازی) یا update (بهروزرسانی) یا destroy (حذف کامل) کنید، بازهم دادهها دستنخورده برایتان باقی میماند. با این حساب اگر بخواهیم تغییری در volume ها ایجاد کنیم باید مستقیماً به سراغ خودشان برویم. همچنین در نظر داشته باشید که استقلال volume ها بدین معنی است که میتوانند در container های مختلف به اشتراک گذاشته شوند و به container خاصی وابسته نیستند.
ما تا این قسمت در مورد جزئیات هر docker container صحبت کردیم اما حالا نوبت به نگاهی کلی به آنها است. docker container ها یک نرمافزار را به همراه تمام وابستگیهایش (سیستمعامل، کدهای برنامه، ابزار سیستم، کتابخانهها و غیره) در یک جعبهی نامرئی قرار میدهند. docker container ها از docker image ها ساخته میشوند. ازآنجاییکه image ها read-only هستند داکر یک فایل سیستم read-write را به این image اضافه میکند تا یک container داشته باشیم.
در مرحلهی بعدی یک network interface را برای container میسازد و یک IP را نیز به آن میدهد تا بتواند با ماشین میزبان تعامل داشته باشد. درنهایت پروسههای مشخصشده توسط شما در هنگام ساخت image را اجرا میکند. زمانی که container خود را ساختید، میتوانید آن را در هر محیطی اجرا کنید، بدون اینکه نیازی به ایجاد تغییرات خاص داشته باشیم.
منبع: وبسایت freecodecamp
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.