داکر ابزاری محبوب برای کانتینرسازی است که به نرمافزارها، سیستم فایلی شامل تمام نیازمندیهایشان برای اجرا را ارائه میدهد. استفاده از کانتینرهای داکر تضمین میکند که نرمافزار در هر محیط استقراری دقیقاً به یک شکل عمل کند، چراکه محیط اجرایی آن بهشدت یکپارچه و ثابت است. در این آموزش به نحوه کار با کانتینر در داکر میپردازیم و مروری کوتاه بر رابطه بین Docker imageها و Docker containerها خواهیم داشت.
کانتینر چیست؟
برای درک بهتر کانتینرها، ابتدا نگاهی به ماشینهای مجازی (VM) میاندازیم. ماشین مجازی بهگونهای عمل میکند که اپلیکیشنهای کاربردی را درون یک سیستمعامل مهمان اجرا میکند؛ این سیستمعامل مهمان خود بر بستر سختافزار مجازیشدهای قرار دارد که توسط سیستمعامل میزبان سرور فراهم شده است. ماشینهای مجازی در ایجاد ایزولۀ کامل برای فرایندهای اپلیکیشن بسیار کارآمد هستند؛ به این معنا که بروز مشکل در سیستمعامل میزبان، بهسختی میتواند تأثیر نامطلوبی بر نرمافزار مهمان بگذارد (و برعکس). بااینحال، این سطح از ایزولهسازی هزینه زیادی دارد، زیرا منابع محاسباتی قابل توجهی صرف شبیهسازی سختافزار و راهاندازی سیستمعامل مهمان میشود. اگر تمایل دارید از کانتینرها بهطور کامل بهره ببرید، توصیه میکنیم ابتدا نصب داکر را بهدرستی انجام دهید تا بتوانید برنامههای خود را در این محیط سبک و منعطف پیادهسازی و اجرا کنید.
دلایل استفاده از داکر و اهمیت مدیریت آن!
کانتینرها با فراهمکردن مکانیزم «بستهبندی»، اپلیکیشنها را از محیط اجرایشان جدا میکنند. در نتیجه، نرمافزارهایی که در قالب کانتینر اجرا میشوند، بدون توجه به نوع محیط، چه یک سرور مجازی، چه ابر عمومی یا حتی لپتاپ شخصی، بهصورت پیوسته و بدون توقف قابل راهاندازیاند. از این طریق، توسعهدهندگان میتوانند محیطهای قابل پیشبینی و ایزولهای را پیکربندی کنند که در آن هر اپلیکیشن جدا از بقیه کار میکند و کمترین تداخل را با سایر نرمافزارها دارد.
کار با کانتینر در داکر
هر بار که دستور docker run را اجرا میکنید، یک کانتینر جدید از ایمیجی که مشخص کردهاید ساخته میشود. این موضوع ممکن است تا حدی گیجکننده باشد؛ بنابراین در ادامه با چند مثال به نحوه کار با کانتینر در داکر میپردازیم.
مرحله ۱: ایجاد دو کانتینر
فرمان زیر با استفاده از ایمیج پایهی اوبونتو یک کانتینر جدید میسازد. گزینهی -t یک ترمینال در اختیار ما قرار میدهد و -i هم امکان تعامل با آن ترمینال را فراهم میکند. ما به فرمان پیشفرضی که در Dockerfile ایمیج پایهی Ubuntu تعریف شده (یعنی bash) تکیه میکنیم تا وارد شل شویم:
docker run -ti ubuntu
بعد از اجرای این دستور، اعلان خط فرمان تغییر میکند تا نشان دهد درون کانتینر هستیم. این اعلان بهصورت کاربر root و پس از آن شناسهی ۱۲ کاراکتری کانتینر را نشان میدهد.
http://root@11cc47339ee1/#
حالا یک تغییر ساده در کانتینر ایجاد میکنیم. مقداری متن در فایل /tmp/Example1.txt مینویسیم و سپس با cat محتوای آن را بررسی میکنیم:
http://root@11cc47339ee1/# echo "Example1" > /tmp/Example1.txt
http://root@11cc47339ee1/# cat /tmp/Example1.txt
خروجی:
Example1
اکنون از کانتینر خارج میشویم:
http://root@11cc47339ee1/# exit
کانتینرهای Docker به محض اتمام فرمانی که با آنها صادر شده، متوقف میشوند. بنابراین، کانتینر ما با خروج از شل bash متوقف شد. اگر دستور زیر را برای نمایش کانتینرهای در حال اجرا وارد کنیم، کانتینرمان را نخواهیم دید:
docker ps
خروجی:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
اگر از سوییچ -a- استفاده کنیم (که همه کانتینرها، چه در حال اجرا و چه متوقفشده را نشان میدهد) کانتینر ما در فهرست ظاهر میشود:
docker ps -a
خروجی:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
11cc47339ee1 ubuntu "/bin/bash" 6 minutes ago Exited (127) 8 seconds ago small_sinoussi
هنگام ساخت کانتینر، یک شناسه (ID) و یک نام تصادفی به آن اختصاص داده میشود. در این مثال، 11cc47339ee1 شناسهی کانتینر است و small_sinoussi نام تصادفی آن. خروجی دستور ps -a علاوه بر این دو مقدار، نشان میدهد این کانتینر از چه ایمیجی ساخته شده (ubuntu) و چه زمانی ایجاد شده (۶ دقیقه پیش) و همچنین چه فرمانی در آن اجرا شده (/bin/bash). در بخش وضعیت (STATUS) هم مشخص است که کانتینر متوقف شده (Exited) و چند ثانیه پیش این اتفاق افتاده. اگر کانتینر همچنان در حال اجرا بود، وضعیت آن بهصورت Up نمایش داده میشد و مدت زمان اجرای آن هم ذکر میشد.
اگر همین دستور docker run -ti ubuntu را دوباره اجرا کنیم، یک کانتینر کاملاً جدید ساخته میشود:
docker run -ti ubuntu
از آنجا که شناسهی جدید در اعلان خط فرمان نمایش داده میشود، میفهمیم این یک کانتینر متفاوت است. همچنین اگر به دنبال فایل Example1 بگردیم، وجود ندارد:
http://root@6e4341887b69/# cat /tmp/Example1
خروجی:
cat: /tmp/Example1: No such file or directory
شاید تصور کنیم دادهها از بین رفتهاند، اما اینطور نیست. فقط در کانتینر دیگری هستیم. از کانتینر دوم نیز خارج میشویم:
http://root@6e4341887b69/# exit
اکنون اگر کانتینرها را فهرست کنیم، میبینیم هر دو وجود دارند:
docker ps -a
خروجی:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6e4341887b69 ubuntu "/bin/bash" About a minute ago Exited (1) 6 seconds ago kickass_borg
11cc47339ee1 ubuntu "/bin/bash" 13 minutes ago Exited (127) 6 minutes ago small_sinoussi
مرحله ۲: راهاندازی مجدد کانتینر اول
برای راهاندازی مجدد یک کانتینر موجود، از دستور start به همراه سوییچ -a (جهت اتصال به ترمینال کانتینر) و -i (جهت تعاملیکردن آن) استفاده میکنیم. در ادامه، باید شناسه یا نام کانتینر را وارد کنیم. حتماً شناسهی کانتینر خودتان را جایگزین کنید:
docker start -ai 11cc47339ee1
با این کار، دوباره در اعلان bash کانتینر قبلی قرار میگیریم. اگر فایل پیشتر ایجادشده را با دستور cat بررسی کنیم، همچنان وجود دارد:
http://root@11cc47339ee1/# cat /tmp/Example1.txt
خروجی:
Example1
حالا میتوانیم از کانتینر خارج شویم:
http://root@11cc47339ee1/# exit
این خروجی نشان میدهد که تغییراتی که درون کانتینر اعمال میکنیم با توقف و راهاندازی مجدد آن از بین نمیروند. فقط زمانی که کانتینر را حذف کنیم، محتوای درونش پاک میشود. همچنین باید توجه کنیم که این تغییرات فقط مربوط به همان کانتینر هستند؛ وقتی کانتینر دوم را ساختیم، از ایمیج اصلی استفاده شد و تغییری را که در کانتینر اول انجام دادیم نداشت.
مرحله ۳: حذف هر دو کانتینر
ما تاکنون دو کانتینر ساختهایم و در این مرحله قصد داریم هر دو را حذف کنیم. دستور docker rm فقط روی کانتینرهای متوقفشده کار میکند و میتواند با استفاده از نام یا شناسه یک یا چند کانتینر، آنها را حذف کند. بنابراین میتوانیم هر دو کانتینر را با دستور زیر حذف کنیم:
docker rm 11cc47339ee1 kickass_borg
خروجی:
11cc47339ee1
kickass_borg
هر دوی این کانتینرها و تمام تغییراتی که در آنها ایجاد کردیم، اکنون حذف شدهاند.
سخن پایانی
ما در این مطلب با جزئیات به نحوه کار با کانتینر در داکر و دستور docker run پرداختیم تا ببینیم چطور هر بار که این دستور اجرا میشود، بهصورت خودکار یک کانتینر جدید ایجاد میکند. همچنین یاد گرفتیم چگونه میتوان یک کانتینر متوقفشده را پیدا کرد، آن را دوباره راهاندازی کرد و به آن متصل شد.