راهنمای آموزش Docker

مفهوم کانتینر سازی خود کاملاً قدیمی است، اما ظهور Docker Engine در سال 2013، تهیه کانتینر برنامه های شما را بسیار آسان کرده است.

بر اساس نظرسنجی Stack Overflow Developer Survey - 2020 ، داکر محبوب ترین پلتفرم است.

شروع کار در ابتدا می تواند کمی ترسناک باشد. بنابراین در این مقاله، ما همه چیز را از سطح پایه تا سطح متوسط containerization یاد خواهیم گرفت. پس از مرور کل مقاله ، باید بتوانید:

  • Containerize هر برنامه ای
  • آپلود Docker Image ها در Docker Hub
  • کار با چندین container با استفاده از Docker Compose

پیش نیازها

  • آشنایی با ترمینال لینوکس
  • آشنایی با JavaScript (برخی از پروژه های بعدی از JavaScript استفاده می کنند)

کد پروژه

کد پروژه های نمونه را می توانید در repository زیر پیدا کنید:

کد کامل را می توانید در برنچ containerized پیدا کنید.

مقدمه ای بر کانتینر سازی و داکر

طبق IBM,

"کانتینر سازی، فرآیند محصور سازی کد نرم افزار به همراه تمام وابستگی های آن در داخل یک پکیج واحد است تا بتوان آن را به طور مداوم در هر مکانی اجرا کرد."

داکر یک containerization platform منبع باز است. این قابلیت را برای اجرای برنامه ها در یک محیط ایزوله شناخته شده به عنوان container فراهم می کند.

کانتینرها  مانند ماشین های مجازی بسیار سبک وزن هستند که می توانند مستقیماً روی کرنل سیستم عامل بدون نیاز به hypervisor کار کنند. در نتیجه ما می توانیم چندین کانتینر را به طور همزمان اجرا کنیم.

در حال اجرا همزمان کانتینرها

هر container شامل یک برنامه کاربردی همراه با همه وابستگی های آن است و از موارد دیگر جدا شده است. توسعه دهندگان می توانند این کانتینر ها را به عنوان image هایی از طریق رجیستری عوض کنند و همچنین می توانند مستقیماً روی سرورها deploy کنند.

ماشین های مجازی در مقابل کانتینر

طبق IBM,

"ماشین مجازی معادل شبیه سازی شده یک سیستم رایانه ای فیزیکی با پردازنده، حافظه،  سیستم عامل مجازی است."

برنامه ای که تحت عنوان hypervisor شناخته می شود، ماشین های مجازی را ایجاد و اجرا می کند. کامپیوتر فیزیکی که دارای یک hypervisor است، host نامیده می شود، در حالی که ماشین های مجازی سیستم guest نامیده می شوند.

ماشینهای مجازی

Hypervisor با منابعی مانند CPU، حافظه و فضای ذخیره سازی به عنوان یک استخر که می تواند به راحتی بین ماشین های مجازی مهمان موجود تخصیص یابد، رفتار می کند.

Hypervisors دو نوع هستند:

  • Hypervisor نوع 1 (VMware vSphere، KVM، Microsoft Hyper-V).
  • Hypervisor نوع 2 (Oracle VM VirtualBox ، VMware Workstation Pro / VMware Fusion).

سایت رسمی Docker resources می گوید ،

"container یک مفهوم انتزاعی در لایه برنامه است که کد و وابستگی ها را با هم پکیج می کند. به جای مجازی سازی کل ماشین فیزیکی، کانتینرها فقط سیستم عامل میزبان را مجازی می کنند. "

 

Container ها

کانتینرها بالای ماشین فیزیکی و سیستم عامل آن قرار دارند. هر کانتینر کرنل سیستم عامل میزبان و معمولاً باینری ها و کتابخانه ها را نیز به اشتراک می گذارد.

نصب داکر

به صفحه دانلود Docker Desktop بروید و سیستم عامل خود را از انتخاب کنید:

سیستم عامل خود را انتخاب کنید

من مراحل نصب نسخه Mac را نشان خواهم داد اما نصب سایر سیستم عامل ها نیز باید به همین سادگی انجام می شود.

مراحل نصب Mac دو مرحله دارد:

  1. نصب فایل Docker.dmg دانلود شده.
  2. Docker را در دایرکتوری Application خود درگ کنید.

اکنون به دایرکتوری Application خود بروید و با دوبار کلیک کردن، Docker را باز کنید. daemon باید اجرا شود و یک آیکن در نوار منوی شما ظاهر شود (نوار وظیفه در ویندوز):

آیکن داکر

برای دسترسی به داشبورد Docker می توانید از این آیکن استفاده کنید:

داشبورد داکر

در حال حاضر ممکن است کمی خسته کننده به نظر برسد، اما همین که چند کانتینر اجرا کنید، این موضوع بسیار جالب تر خواهد شد.

Hello World در Docker

اکنون که داکر آماده کار روی ماشین های شما هست، وقت آن است که اولین کانتینر خود را اجرا کنیم. ترمینال را باز کنید (خط فرمان در ویندوز) و دستور زیر را اجرا کنید:

docker run hello-world

اگر همه چیز خوب پیش رفت، باید خروجی مانند موارد زیر را ببینید:

خروجی از دستور docker run hello-world

hello-world image نمونه ای از حداقل کانتینرسازی با داکر است. یک فایل hello.c دارد که وظیفه چاپ پیامی را که روی ترمینال خود مشاهده می کنید، دارد.

تقریباً هر image شامل یک دستور پیش فرض است. در مورد hello-world image، دستور پیش فرض اجرای باینری hello است که از کد C قبلاً ذکر شده کامپایل شده است.

اگر داشبورد را دوباره باز کنید، باید کانتینر hello-world را در آنجا پیدا کنید:

Container Log ها

وضعیت EXITED(0) است که نشان می دهد کانتینر با موفقیت اجرا و خارج شده است. می توانید لاگ ها ، آمار (CPU / حافظه / دیسک / شبکه استفاده) یا بررسی (environment/port mappings) را مشاهده کنید.

برای درک اینکه چه اتفاقی افتاده است ، باید با Docker Architecture،کانتینرها، image ها و Registry ها آشنا شوید.

معماری داکر 

Docker از معماری client-server استفاده می کند. این انجین از سه کامپوننت اصلی تشکیل شده است:

  1. daemon (dockerd): Docker Daemon فرایندی است که مدام در پس زمینه اجرا می شود و منتظر دستورات کلاینت است. daemon توانایی مدیریت آبجکت های مختلف Docker را دارد.
  2. Docker Client: سرویس client  (docker) یک برنامه واسط command-line است که بیشتر مسئولیت انتقال دستورات صادر شده توسط کاربران را دارد.
  3. REST API: REST API به عنوان پلی بین daemon و client عمل می کند. هر دستوری که با استفاده از client صادر می شود از API عبور می کند تا سرانجام به daemon برسد.

در داکیومنت های رسمی داکر یک نمایش گرافیکی زیبا از معماری وجود دارد:

https://docs.docker.com/get-started/overview/#docker-architecture

اگر در حال حاضر گیج کننده به نظر می رسد نگران نباشید. همه چیز در بخش های آینده بسیار واضح تر می شود.

Image ها و Container ها

image ها یک فایل چند لایه هستند که به عنوان template برای ایجاد کانتینرهای Docker عمل می کنند. image ها را می توان از طریق registry ها عوض کرد. ما می توانیم از هر image یی که دیگران ساخته اند استفاده کنیم یا می توانیم آنها را با افزودن دستورهای جدید اصلاح کنیم.

image ها را می توان از scratch نیز ایجاد کرد. لایه پایه یک image فقط خواندنی است. وقتی یک Dockerfile را ویرایش می کنیم و آن را از نو می سازیم، فقط قسمت اصلاح شده در لایه بالایی دوباره ساخته می شود.

کانتینرها image های در حالت اجرا هستند. هنگامی که ما image یی مانند hello-world را اجرا می کنیم، آنها یک محیط جداگانه و مناسب برای اجرای برنامه موجود در image ایجاد می کنند. این محیط ایزوله یک container است.

Registry ها

رجیستر ها برای ذخیره سازی Docker image ها هستند. Docker Hub رجیستری public پیش فرض برای ذخیره image ها است.

هر زمان که دستوراتی مانند docker run یا docker pull را اجرا کنیم، daemon معمولاً image ها را از hub دریافت می کند. هر کسی می تواند image ها را با استفاده از دستور docker push در hub آپلود کند. می توانید مانند هر وب سایت دیگری به hub بروید و image ها را جستجو کنید.

Docker Hub

اگر یک اکانت ایجاد کنید، می توانید image های سفارشی را نیز آپلود کنید. image هایی که آپلود  کرده ام برای همه در صفحه https://hub.docker.com/u/fhsinchy در دسترس است.

تصویر کامل

اکنون که با معماری، image ها، کانتینرها و رجیسترها آشنا شدید، آماده درک این مسئله هستید که هنگام اجرای دستور docker run hello-world چه اتفاقی افتاده است. نمایش گرافیکی فرآیند به شرح زیر است:

docker run hello-world

کل فرآیند در پنج مرحله اتفاق می افتد:

  1. ما دستور docker run hello-world را اجرا می کنیم.
  2. Docker client به daemon می گوید که ما می خواهیم یک کانتینر را با استفاده از hello-world image اجرا کنیم.
  3. Docker daemon آخرین نسخه image را از رجیستری بیرون می کشد.
  4. کانتینری از image ایجاد می کند.
  5. container تازه ایجاد شده را اجرا می کند.

این به عنوان پیش فرض Daemon Docker است که به دنبال image هایی در hub است که به صورت لوکال وجود ندارند. اما پس از واکشی یک image، در کش باقی می ماند. بنابراین اگر دستور را دوباره اجرا کنید، خطوط زیر را در خروجی نمی بینید:

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
0e03bdcc26d7: Pull complete
Digest: sha256:d58e752213a51785838f9eed2b7a498ffa1cb3aa7f946dda11af39286c3db9a9
Status: Downloaded newer image for hello-world:latest

اگر نسخه جدیدتری از image در دسترس باشد، daemon دوباره image را واکشی می کند. که:latest یک برچسب است. image ها معمولاً دارای برچسب های معنی دار برای نشان دادن نسخه ها یا ساختارها هستند. در بخش بعدی با جزئیات بیشتر در این باره می آموزید.

دستکاری کانتینرها

در بخش قبلی ، مختصری در مورد Docker client بحث داشته ایم. این برنامه رابط خط فرمان است که دستورات ما را به Docker daemon می رساند. در این بخش، با روشهای پیشرفته تری برای دستکاری container در Docker آشنا خواهید شد.

Running Container ها

در بخش قبلی، ما از docker run برای ایجاد و اجرای کانتینر با استفاده از hello-world image استفاده کرده ایم. syntax عمومی این دستور به شرح زیر است:

docker run 

در اینجا image name می تواند هر image یی از Docker Hub یا ماشین لوکال ما باشد. امیدوارم که متوجه شده باشید که ما گفته ایم create و run و فقط run نکنید، دلیلش هم این است که دستور docker run در واقع جاب از دو دستور جداگانه docker را انجام می دهد:

  1. docker create - کانتینر را از image داده شده ایجاد می کند و container id را برمی گرداند.
  2. docker start - یک کانتینر را با id داده شده از یک دستور ایجاد شده شروع می کند.

برای ایجاد یک کانتینر از hello-world image دستور زیر را اجرا کنید:

docker create hello-world

این دستور باید یک رشته طولانی مانند cb2d384726da40545d5a203bdb25db1a8c6e6722e5ae03a573d717cd93342f61 تولید کند - این container id است. از این id می توان برای راه اندازی کانتینر ساخته شده استفاده کرد.

12 کارکتر اول container id برای شناسایی کانتینرکافی است. به جای استفاده از کل رشته، استفاده از cb2d384726da باید کفایت کند.

برای شروع این container دستور زیر را اجرا کنید:

docker start cb2d384726da

شما باید container id را به عنوان خروجی دریافت کنید و نه چیز دیگری. ممکن است فکر کنید کانتینر به درستی کار نکرده است. اما اگر داشبورد را بررسی کنید، می بینید که کانتینر با موفقیت اجرا و خارج شده است.

خروجی از دستورات docker start cb2d384726da و docker ps -a :

آنچه در اینجا رخ داده این است که ما ترمینال خود را به output stream کانتینر اتچ نکردیم. دستورات UNIX و LINUX معمولاً هنگام اجرا سه تا  I/O stream را باز می کنند، یعنیSTDIN ، STDOUT و STDERR.

اگر می خواهید بیشتر بدانید، یک مقاله شگفت انگیز در مورد این موضوع وجود دارد.

برای اتچ ترمینال خود به output stream کانتینر باید از گزینه-a یا --attach استفاده کنید:

docker start -a cb2d384726da

اگر همه چیز درست پیش برود ، باید خروجی زیر را ببینید:

خروجی از دستور docker start -a cb2d384726da

ما می توانیم از دستور start برای اجرای هر کانتینر که از قبل اجرا نشده استفاده کنیم. با استفاده از دستور run هر بار یک کانتینر جدید ایجاد می شود.

کانتینرها را لیست کنید

ممکن است از قسمت قبلی بخاطر داشته باشید که از داشبورد می توان به راحتی برای بررسی container ها استفاده کرد.

لیست Container 

این یک ابزار بسیار مفید برای بررسی container ها است، اما برای مشاهده یک لیست ساده از کانتینر ها بسیار زیاد است. به همین دلیل روش ساده تری برای این کار وجود دارد. دستور زیر را در ترمینال خود اجرا کنید:

docker ps -a

و باید لیستی از تمام کانتینرها را در ترمینال خود مشاهده کنید.

خروجی از دستور docker ps -a

گزینه -a یا --all نشان می دهد که ما می خواهیم نه تنها کانتینرهای در حال اجرا بلکه موارد متوقف شده را نیز ببینیم. با اجرای ps بدون گزینه -a فقط کانتینرهای در حال اجرا لیست می شوند.

راه اندازی مجدد کانتینرها

ما قبلاً برای اجرای یک کانتینر از دستور start استفاده کرده ایم. دستور دیگری برای شروع کانتینرها وجود دارد به نام restart . اگرچه به نظر می رسد دستورات همان هدف را انجام می دهند، اما تفاوت کمی دارند.

دستور start کانتینرهایی که در حال اجرا نباشند را اجرا می کند. ولی، دستور restart یک کانتینر در حال اجرا را از kill می کند و دوباره آن را اجرا می کند. اگر از یک restart برای کانتینر استاپ شده استفاده کنیم، عملکرد آن دقیقاً مانند دستور start خواهد بود.

حذف کانتینرهای معلق

کانتینرهایی که قبلا خارج شده اند در سیستم باقی مانده اند. این کانتینرهای  معلق یا غیرضروری فضا اشغال می کنند و حتی می توانند در زمان های بعدی مشکلاتی را ایجاد کنند.

چند روش برای حذف کردن container ها وجود دارد. اگر می خواهیم آنها را به طور خاص حذف کنیم، می توانیم از دستور rm استفاده کنیم. سینتکس عمومی این دستور به شرح زیر است:

docker rm 

برای حذف کانتینر با شناسه e210d4695c51، دستور زیر را اجرا کنید:

docker rm e210d4695c51

و باید شناسه کانتینرحذف شده را به عنوان خروجی دریافت کنید. اگر می خواهیم همه آبجکت های داکر(images، containers، networks، cache) را پاک کنیم می توانیم از دستور زیر استفاده کنیم:

docker system prune

داکر درخواست تأیید خواهد کرد. برای رد شدن از این مرحله تأیید می توانیم از گزینه -f یا --force استفاده کنیم. این دستور در پایان اجرای موفقیت آمیز مقدار فضای بازیابی شده را نشان می دهد.

Running Containers در Interactive Mode

تاکنون ما فقط کانتینرهای ساخته شده از hello-world image را اجرا کرده ایم. دستور پیش فرض برای hello-world image اجرای تنها برنامه hello.c همراه با image است.

همه image ها به همین سادگی نیستند. image ها می توانند کل سیستم عامل را درون خود محصور کنند. توزیع های لینوکس مانند Ubuntu, Fedora, Debian همه دارای image های رسمی داکر در hub هستند.

ما می توانیم با استفاده از image رسمی اوبونتو، ubuntu را درون یک کانتینر اجرا کنیم. اگر بخواهیم با اجرای دستور docker run ubuntu یک کانتینر اوبونتو اجرا کنیم، می بینیم که هیچ اتفاقی نمی افتد. اما اگر دستور را با گزینه -it به صورت زیر اجرا کنیم:

docker run -it ubuntu

باید مستقیماً روی bash داخل Ubuntu container قرار بگیریم. در این پنجره bash، قادر به انجام تسک هایی خواهیم بود که معمولاً در ترمینال معمولی اوبونتو انجام می دهیم. ما با اجرای دستور استاندارد cat /etc/os-release جزئیات OS را چاپ کرده ایم:

خروجی از دستور docker run -it ubuntu

دلیل وجود گزینه -it این است که Ubuntu image برای شروع bash هنگام راه اندازی پیکربندی شده است. Bash یک برنامه تعاملی است. به این معنی که اگر ما هیچ دستوری را تایپ نکنیم، bash هیچ کاری نمی کند.

برای تعامل با برنامه ای که درون container است، باید به صراحت به container اطلاع دهیم که می خواهیم یک interactive session داشته باشیم.

گزینه -it زمینه را برای تعامل ما با هر برنامه تعاملی درون container فراهم می کند. این گزینه در واقع دو گزینه جداگانه است که با هم ادغام شده اند.

  • گزینه -i ما را به جریان ورودی container متصل می کند، بنابراین می توانیم ورودی ها را به bash ارسال کنیم.
  • گزینه -t اطمینان حاصل می کند که چند فرمت مناسب و یک native terminal  به دست می آوریم.

هر زمان که می خواهیم container را در حالت تعاملی اجرا کنیم، باید از گزینه -it استفاده کنیم. اجرای docker run -it node یا docker run -it python باید ما را مستقیماً روی برنامه python REPL یا node قرار دهد.

اجرای کد JavaScript در داخل node REPL

ما نمی توانیم هیچ کانتینر تصادفی را در حالت تعاملی اجرا کنیم. برای اجرای در حالت تعاملی، کانتینر باید موقع شروع یک برنامه تعاملی در هنگام راه اندازی پیکربندی شود. Shell ها ، REPL ها ، CLI ها و ... نمونه هایی از برخی برنامه های تعاملی هستند.

ایجاد کانتینر با استفاده از image های قابل اجرا

تاکنون گفته ام که Docker image ها یک دستور پیش فرض دارند که به صورت خودکار اجرا می شوند. این برای هر image یی درست نیست. برخی از image ها به جای دستور (CMD) با یک (ENTRYPOINT) پیکربندی می شوند.

یک entry-point به ما امکان می دهد کانتینر را که به عنوان یک executable اجرا می شود پیکربندی کنیم. مانند هر executable منظم دیگر، ما می توانیم آرگومان هایی را به این کانتینر ارسال کنیم. سینتکس عمومی برای ارسال آرگومان ها به یک executable container به شرح زیر است:

docker run

Ubuntu image قابل اجرا است و نقطه ورود به ایمیج bash است. آرگومان های ارسال شده به یک کانتینر اجرایی مستقیماً به برنامه entry-point منتقل می شوند. این بدان معنی است که هر آرگومانی که به Ubuntu image ارسال می کنیم مستقیماً به bash منتقل می شود.

برای دیدن لیستی از تمام دایرکتوری های داخل Ubuntu container، می توانید دستور ls را به عنوان آرگومان منتقل کنید.

docker run ubuntu ls

شما باید لیستی از دایرکتوری ها مانند موارد زیر را دریافت کنید:

خروجی دستور  docker run ubuntu ls

توجه داشته باشید که ما از گزینه -it استفاده نمی کنیم، زیرا نمی خواهیم با bash تعامل داشته باشیم، بلکه فقط خروجی می خواهیم. ما می توانیم هر دستور bash معتبری را به عنوان آرگومان ارسال کنیم. مانند ارسال دستور pwd به عنوان آرگومان، دایرکتوری کار موجود را برمی گرداند.

توجه داشته باشید که ما از گزینه -it استفاده نمی کنیم، زیرا نمی خواهیم با bash تعامل داشته باشیم، بلکه فقط خروجی می خواهیم. ما می توانیم هر دستور bash معتبری را به عنوان آرگومان ارسال کنیم. مانند ارسال دستور pwd به عنوان آرگومان، دایرکتوری کار موجود را برمی گرداند.

لیست آرگومانهای معتبر معمولاً به خود برنامه entry-point بستگی دارد. اگر کانتینر از shell به عنوان entry-point استفاده کند، هر دستور shell معتبری می تواند به عنوان آرگومان ارسال شود. اگر کانتینر از برنامه دیگری به عنوان entry-point استفاده کند، آرگومان های معتبر برای آن برنامه خاص را می توان به container ارسال کرد.

اجرای Containerها در مد Detached

فرض کنید که می خواهید یک سرور Redis در سیستم خود اجرا کنید. Redis یک سیستم پایگاه داده در حافظه بسیار سریع است که اغلب به عنوان کش در برنامه های مختلف استفاده می شود. ما می توانیم یک سرور Redis را با استفاده از redis image اجرا کنیم. برای انجام این کار از دستور زیر استفاده می کنیم:

docker run redis

ممکن است چند لحظه طول بکشد تا image را از hub بگیرید و سپس باید ببینید که متن طولانی در ترمینال شما ظاهر می شود.

خروجی از دستور  docker run redis

همانطور که مشاهده می کنید، سرور Redis در حال اجرا است و آماده پذیرش کانکشن ها است. برای روشن نگه داشتن سرور، باید این پنجره ترمینال را باز نگه دارید (که از نظر من دردسر ساز است).

می توانید این نوع کانتینرها را در حالت detached اجرا کنید. کانتینرهایی که در حالت detached اجرا می شوند مانند یک سرویس در پس زمینه اجرا می شوند. برای detached یک کانتینر، می توانیم از گزینه -d یا --detach استفاده کنیم. برای اجرای کانتینر در حالت detached، دستور زیر را اجرا کنید:

docker run -d redis

باید container id را به عنوان خروجی دریافت کنید.

خروجی از دستور docker run -d redis

سرور Redis اکنون در پس زمینه اجرا می شود. با استفاده از داشبورد یا با استفاده از دستور ps می توانید آن را بررسی کنید.

اجرای دستورات درون یک Container در حال اجرا

اکنون که یک سرور Redis در پس زمینه در حال اجرا است، فرض کنید که می خواهید برخی عملیات را با استفاده از ابزار redis-cli انجام دهید. شما نمی توانید فقط این دستور  docker run redis redis-cli را اجرا کنید و جلو بروید. کانتینر در حال اجرا است.

برای موقعیت هایی از این دست، یک دستور برای اجرای سایر دستورات درون یک container در حال اجرا به نام exec وجود دارد، و syntax این دستور به شرح زیر است:

docker exec 

اگر شناسه Redis کانتینر 5531133af6a1 باشد، دستور باید به صورت زیر باشد:

docker exec -it 5531133af6a1 redis-cli

و باید دقیقاً وارد برنامه redis-cli شوید:

خروجی از دستور docker exec -it 5531133af6a1 redis-cli

توجه داشته باشید که ما از گزینه -it استفاده می کنیم زیرا این یک interactive session خواهد بود. اکنون می توانید هر دستور Redis معتبری را در این پنجره اجرا کنید و داده ها در سرور باقی می مانند.

با فشار دادن کلید ترکیبی ctrl + c یا بستن پنجره ترمینال می توانید از آن خارج شوید. به خاطر داشته باشید، حتی اگر از برنامه CLI خارج شوید، سرور همچنان در پس زمینه کار می کند.

شروع Shell داخل یک Container در حال اجرا

فرض کنید به دلایلی می خواهید از shell داخل کانتینر در حال اجرا استفاده کنید. می توانید این کار را با استفاده از دستور exec همراه با sh انجام دهید. مانند دستور زیر قابل اجرا است:

docker exec -it  sh

اگر شناسه redis کانتینر 5531133af6a1 است، دستور زیر را برای شروع پوسته داخل کانتینر اجرا کنید:

docker run exec -it 5531133af6a1 sh

شما باید مستقیماً روی shell داخل container قرار بگیرید.

خروجی از دستور docker run exec -it 5531133af6a1 sh

می توانید هر دستور پوسته معتبری را در اینجا اجرا کنید.

دسترسی به لاگ های مربوط به یک Container در حال اجرا

اگر می خواهیم لاگ های مربوط به یک container را مشاهده کنیم، داشبورد می تواند واقعاً مفید باشد.

لاگ ها در داشبورد Docker

همچنین می توانیم از دستور logs برای بازیابی لاگ های مربوط به یک کانتینر در حال اجرا استفاده کنیم. سینتکس عمومی این دستور به شرح زیر است:

docker logs 

اگر شناسه Redis کانتینر 5531133af6a1 است، دستور زیر را برای دسترسی به لاگ های مربوط به container اجرا کنید:

docker logs 5531133af6a1

باید متن طولانی در پنجره ترمینال شما ظاهر شود.

خروجی از دستور docker logs 5531133af6a1

جریان خروجی container دست پیدا کرده و گزارش ها را به صورت بلادرنگ دریافت کنید.

هرگونه ثبت بعدی بلافاصله در ترمینال نشان داده می شود، به شرطی که با فشار دادن کلید ترکیبی ctrl + c یا بستن پنجره از آن خارج نشوید. container حتی اگر از پنجره log خارج شوید، ادامه خواهد یافت.

متوقف کردن یا kill کردن یک کانتینر در حال اجرا

با بستن پنجره ترمینال یا زدن کلید ترکیبی ctrl + c می توان کانتینر در حال اجرا را در پیش زمینه متوقف کرد. کانتینر در حال اجرا در پس زمینه را نمی توان به همین ترتیب متوقف کرد.

دو دستور برای متوقف کردن یک کانتینر در حال اجرا وجود دارد:

  • docker stop - سعی دارد با ارسال سیگنال SIGTERM به کانتینر، آن را متوقف کند. اگر متوقف نشود، سیگنال SIGKILL ارسال می شود.
  • docker kill - بلافاصله با ارسال سیگنال SIGKILL کانتینر را متوقف می کند. گیرنده نمی تواند سیگنال SIGKILL را نادیده بگیرد.

برای متوقف کردن یک کانتینر با شناسه bb7fadc33178  دستور docker stop bb7fadc33178 را اجرا کنید. با استفاده از docker kill bb7fadc33178 بدون اینکه فرصتی برای پاکسازی ایجاد شود، بلافاصله کانتینر خاتمه می یابد.

Mapping Port ها

فرض کنید که می خواهید نمونه ای از وب سرور محبوب Nginx را اجرا کنید. با استفاده از nginx image رسمی می توانید این کار را انجام دهید. برای اجرای کانتینر دستور زیر را اجرا کنید:

docker run nginx

Nginx قرار است همچنان ادامه داشته باشد، بنابراین شما همچنین می توانید از گزینه -d یا --detach استفاده کنید. به طور پیش فرض Nginx روی پورت 80 اجرا می شود. اما اگر می خواهید به http://localhost:80 دسترسی پیدا کنید باید چیزی مانند موارد زیر را ببینید:

http://localhost:80

این به این دلیل است که Nginx روی پورت 80 داخل container کار می کند. کانتینرها محیط های جداگانه ای هستند و سیستم میزبان شما از آنچه در داخل container می گذرد چیزی نمی داند.

برای دسترسی به پورتی که درون container است، باید آن پورت را به پورتی روی سیستم میزبان map کنید. این کار را می توانید با استفاده از گزینه -p یا --port با دستور docker run انجام دهید. syntax عمومی این گزینه به شرح زیر است:

docker run -p  nginx

با اجرای docker run -p 80:80 nginx پورت 80 ماشین میزبان را به پورت 80 کانتینر map می کند. اکنون سعی کنید به آدرس http://localhost:80 دسترسی پیدا کنید:

http://localhost:80

اگر docker run -p 8080:80 nginx را به جای 80:80 اجرا کنید، سرور Nginx در پورت 8080 ماشین میزبان در دسترس خواهد بود. اگر بعد از مدتی شماره پورت را فراموش کردید، می توانید به داشبورد نگاهی بیندازید:

Port mapping در داشبورد داکر

تب Inspect شامل اطلاعاتی در مورد port mapping است. همانطور که مشاهده می کنید، ما پورت 80 را از کانتینر به پورت 8080 سیستم میزبان map کرده ایم.

جداسازی کانتینر

از همان لحظه ای که مفهوم container را به شما معرفی کردیم، گفتیم که container ها محیطی جدا هستند. وقتی می گویم ایزوله، منظور فقط از سیستم میزبان نیست بلکه از کانتینرهای دیگر نیز هست.

در این بخش، ما یک آزمایش کوچک برای درک این موارد جداگانه انجام خواهیم داد. دو پنجره ترمینال را باز کنید و با استفاده از دستور زیر دو نمونه کانتینر اوبونتو را اجرا کنید:

docker run -it ubuntu

اگر داشبورد را باز کنید باید دو container اوبونتو را ببینید که در حال اجرا هستند:

اجرای دو کانتینر اوبونتو

اکنون در پنجره بالا ، دستور زیر را اجرا کنید:

mkdir hello-world

دستور mkdir یک دایرکتوری جدید ایجاد می کند. اکنون برای دیدن لیست دایرکتوری ها در هر دو کانتینر، دستور ls را در داخل هر دو اجرا می کنید:

خروجی دستور ls  داخل هر دو کانتینر

همانطور که می بینید، دایرکتوری hello-world  درون کانتینری است که در پنجره ترمینال بالایی باز است و نه در قسمت پایین. این ثابت می کند که کانتینرها اگرچه از یک image ایجاد شده اند اما از یکدیگر جدا شده اند.

این نکته مهمی است که باید درک کرد. سناریویی را فرض کنید که مدتی داخل container کار می کنید. سپس کانتینر را stop کرده و روز بعد بار دیگر docker run -it ubuntu را اجرا می کنید. خواهید دید که همه کارهای شما ناپدید شده اند.

امیدواریم که از بخش قبلی به یاد داشته باشید، دستور run هر بار یک container جدید ایجاد و راه اندازی می کند. بنابراین یادتان باشد که container های ایجاد شده قبلی را با استفاده از دستور start و نه دستور run شروع کنید.

ایجاد Image های سفارشی

اکنون که شما درک کاملی از بسیاری از روش های دستکاری container با استفاده از سرویس Docker client دارید، وقت آن است که یاد بگیرید چگونه image های سفارشی بسازید.

در این بخش، بسیاری از مفاهیم مهم در مورد ساخت image، ایجاد کانتینر از آنها و به اشتراک گذاری آنها با دیگران را خواهید آموخت.

من پیشنهاد می کنم قبل از رفتن به بخشهای بعدی، Visual Studio Code را با Docker Extension نصب کنید.

اصول ایجاد image ها

در این بخش ما بر روی ساختار یک Dockerfile و دستورهای معمول تمرکز خواهیم کرد. Dockerfile یک داکیومنت متنی است که شامل مجموعه ای از دستورها برای دنبال کردن و ساختن image توسط Docker daemon است.

برای درک اصول ساخت image ها، یک Node image سفارشی بسیار ساده ایجاد خواهیم کرد. قبل از شروع، می خواهیم نحوه عملکرد node image را به شما نشان دهیم. برای اجرای یک کانتینر دستور زیر را اجرا کنید:

docker run -it node

Node image برای شروع Node REPL هنگام راه اندازی پیکربندی شده است. REPL یک برنامه تعاملی است بنابراین از فلگ -it استفاده می کند.

خروجی از دستور  docker run -it node

می توانید هر کد معتبر JavaScript را در اینجا اجرا کنید. ما یک node image سفارشی ایجاد خواهیم کرد که دقیقاً همان عملکرد را دارد.

برای شروع، یک دایرکتوری جدید در هر نقطه از سیستم خود ایجاد کنید و یک فایل جدید به نام Dockerfile درون آن ایجاد کنید. فولدر project را در داخل ادیتور کد باز کنید و کد زیر را در Dockerfile قرار دهید:

FROM ubuntu

RUN apt-get update
RUN apt-get install nodejs -y

CMD [ "node" ]

امیدوارم از بخش قبلی به یاد داشته باشید که image ها دارای چندین لایه هستند. هر خط در Dockerfile یک دستورالعمل است و هر دستورالعمل یک لایه جدید ایجاد می کند.

بگذارید Dockerfile را خط به خط برای شما توضیح دهیم:

FROM ubuntu

هر Dockerfile معتبری باید با دستور FROM شروع شود. این دستور یک مرحله ساخت جدید را شروع می کند و تصویر پایه را تنظیم می کند. با تنظیم ubuntu به عنوان image پایه، می گوییم که می خواهیم همه ویژگی های موجود در Ubuntu image داخل image ما موجود باشد.

اکنون که ویژگی های اوبونتو در image ما موجود است، می توانیم از مدیر بسته اوبونتو (apt-get) برای نصب Node استفاده کنیم.

RUN apt-get update
RUN apt-get install nodejs -y

دستور RUN دستورات موجود در یک لایه جدید بالای image فعلی را اجرا می کند. بنابراین در دستورهای آینده می توانیم به Node مراجعه کنیم، زیرا در این مرحله آن را نصب کرده ایم.

CMD [ "node" ]

هدف از دستور CMD ارائه پیش فرض ها برای یک executing container است. این پیش فرض ها می توانند شامل یک executable باشند، یا می توانید executable را حذف کنید، در این صورت باید یک دستور ENTRYPOINT را مشخص کنید. فقط یک دستور CMD در Dockerfile وجود دارد.

اکنون برای ساختن یک image از این Dockerfile، از دستور build استفاده خواهیم کرد. syntax این دستور به شرح زیر است:

docker build 

دستور build به یک Dockerfile و context نیاز دارد. context مجموعه ای از فایل ها و دایرکتوری ها است که در مکان مشخص شده قرار دارند. Docker به دنبال Dockerfile در context می رود و از آن برای ساختن image استفاده می کند.

یک پنجره ترمینال داخل آن دایرکتوری باز کنید و دستور زیر را اجرا کنید:

docker build 

اگر Dockerfile را درون دایرکتوری دیگری مانند /src/Dockerfile قرار دهید، پس کانتکس /src خواهد بود.

روند build ممکن است مدتی طول بکشد تا به پایان برسد. پس از پایان، باید این تصویر از متن را در ترمینال خود مشاهده کنید:

خروجی از دستور docker build .

اگر همه چیز خوب پیش برود، باید در پایان چیزی شبیه به Successfully built d901e4d15263 را ببینید. این رشته تصادفی image id است و نه container id. برای ایجاد و راه اندازی یک container جدید می توانید دستور run را با استفاده از این image id اجرا کنید.

docker run -it d901e4d15263

به یاد داشته باشید، Node REPL یک برنامه تعاملی است، بنابراین گزینه -it لازم است. پس از اجرای دستور باید روی Node REPL قرار بگیرید:

Node REPL را در داخل custom image خود قرار دهید

می توانید مانند تصویر رسمی Node ، هر کد معتبر JavaScript را در اینجا اجرا کنید.

ایجاد یک Executable Image

امیدوارم مفهوم executable image را از بخش قبلی به یاد بیاورید. image هایی که می توانند آرگومان های اضافی درست مانند یک regular executable را بگیرند. در این بخش، ایجاد یک نمونه را یاد میگیریم.

ما یک bash image سفارشی ایجاد خواهیم کرد و مانند آنچه با Ubuntu image در بخش قبلی انجام دادیم، آرگومان هایی را ارسال خواهیم کرد. با ایجاد یک Dockerfile در داخل یک دایرکتوری خالی شروع کنید و کد زیر را در آن قرار دهید:

FROM alpine

RUN apk add --update bash

ENTRYPOINT [ "bash" ]

ما از alpine image به عنوان پایه استفاده می کنیم. Alpine Linux یک توزیع لینوکس سبک وزن و امنیت محور است.

Alpine به طور پیش فرض با bash همراه نیست. بنابراین در خط دوم ما bash را با استفاده از Alpine package manager, apk نصب می کنیم. apk برای Alpine همان چیزی است که apt-get برای Ubuntu است. آخرین دستور bash را به عنوان entry-point این image تعیین می کند. همانطور که مشاهده می کنید، دستور ENTRYPOINT برابر با دستورالعمل CMD است.

برای ساخت image دستور زیر را اجرا کنید:

docker build .

فرآیند ساخت ممکن است مدتی طول بکشد. پس از پایان، باید image id ایجاد شده جدید را دریافت کنید:

خروجی از دستور docker build .

ادامه دارد...

× در حال پاسخ به:
محمد آقایی
محمد آقایی
30 بهمن 1399 / ساعت 16:52 پاسخ دهید

این آموزش تا چه زمانی تکمیل میشه؟