مقدمه
کانتینر نمونهای در حال اجرا از یک ایمیج است و از یک ایمیج واحد میتوان چندین کانتینر راهاندازی کرد. در حالی که ایمیج مشترک فقطخواندنی است، هر کانتینر لایه نوشتنی مخصوص به خود را دارد که باعث میشود کانتینرها در مقایسه با ماشینهای مجازی سنتی، سریعتر، سبکتر و یکبارمصرفتر باشند.
کانتینر در برابر ماشین مجازی
کانتینرها و ماشینهای مجازی (VM) هر دو منابع را برای اجرای برنامهها مجازیسازی میکنند، اما به روشهایی بنیادین متفاوت. VM ها سختافزار را مجازیسازی میکنند: یک hypervisor منابع فیزیکی مانند CPU و RAM را در اختیار میگیرد و آنها را به ماشینهای مجازی تبدیل میکند که هرکدام به سیستمعامل کامل خود نیاز دارند. در مقابل، کانتینرها خود سیستمعامل را مجازیسازی میکنند، در حالی که kernel میزبان را به اشتراک میگذارند و کد برنامه را در واحدهای ایزوله و سبک بستهبندی میکنند.
از آنجا که هر VM به سیستمعامل مستقل خود نیاز دارد، VM ها منابع بیشتری مصرف کرده و دیرتر بالا میآیند. کانتینرها کاملاً از این سربار اجتناب میکنند، که به این معناست:
- کانتینرها کوچکتر و قابلحملترند
- روی یک زیرساخت یکسان، تعداد کانتینر بیشتری نسبت به VM قابل اجراست
- کانتینرها بسیار سریعتر راهاندازی میشوند
- تعداد سیستمعاملهایی که باید patch و مدیریت شوند کاهش مییابد
- کانتینرها سطح حمله کوچکتری دارند
یکی از نگرانیهای تاریخی درباره کانتینرها، مدل kernel مشترک است، زیرا یک آسیبپذیری در kernel میتواند بهطور نظری همه کانتینرهای یک میزبان را تحت تأثیر قرار دهد. پلتفرمهای مدرن کانتینری این ریسک را با فناوریهای امنیتی مانند SELinux، AppArmor، seccomp و اسکن آسیبپذیری ایمیج کاهش میدهند.
ایمیجها و کانتینرها
وقتی چندین کانتینر از یک ایمیج واحد راهاندازی میشوند، داکر به هر کانتینر یک لایه نوشتنی (R/W) نازک روی ایمیج مشترک و فقطخواندنی میدهد. تغییرات ایجادشده داخل کانتینر روی همین لایه نوشته میشوند. توقف یک کانتینر این لایه را حفظ میکند، اما حذف کانتینر آن را برای همیشه پاک میکند.
راهاندازی یک کانتینر
دستور docker run رایجترین روش راهاندازی یک کانتینر جدید است:
docker run -d --name webserver -p 5005:8080 nigelpoulton/ddd-book:web0.1در اینجا، -d کانتینر را در پسزمینه اجرا میکند، --name نامی به آن اختصاص میدهد و -p یک پورت هاست را به پورت کانتینر نگاشت میکند. اگر ایمیج بهصورت محلی موجود نباشد، داکر بهطور خودکار آن را از Docker Hub دریافت میکند.
نحوه شروع برنامهها توسط کانتینر
داکر یکی از سه روش را برای تعیین اینکه چه چیزی داخل کانتینر اجرا شود انتخاب میکند: دستورالعمل Entrypoint تعبیهشده در ایمیج، دستورالعمل Cmd در ایمیج، یا دستوری که مستقیماً روی CLI پاس داده شود. دستورالعملهای Entrypoint از خط فرمان قابل بازنویسی نیستند، در حالی که دستورالعملهای Cmd قابل بازنویسی هستند.
اتصال به یک کانتینر در حال اجرا
دستور docker exec امکان اجرای دستورات داخل یک کانتینر در حال اجرا را فراهم میکند، چه بهصورت تعاملی و چه بهصورت اجرای مستقیم یکباره:
docker exec -it webserver shاز آنجا که اکثر کانتینرها فقط شامل ابزارهای ضروری هستند، بسیاری از دستورات آشنا ممکن است داخل آنها در دسترس نباشند.
فرآیندهای کانتینر و بررسی آن
اکثر کانتینرها یک فرآیند اصلی اجرا میکنند که همیشه PID 1 دارد. اگر این فرآیند کشته شود، کل کانتینر متوقف میشود، زیرا کانتینرها فقط تا زمانی اجرا میشوند که فرآیند اصلی آنها در حال اجراست. دستور docker inspect اطلاعات پیکربندی و runtime دقیقی درباره یک کانتینر ارائه میدهد، از جمله وضعیت، نگاشت پورتها و entrypoint آن.
ماندگاری و از دست رفتن تغییرات
تغییرات ایجادشده در فایلسیستم یک کانتینر در حال اجرا در لایه نوشتنی آن ذخیره میشوند و در برابر توقف و راهاندازی مجدد باقی میمانند. اما پس از حذف یک کانتینر با docker rm، این تغییرات برای همیشه از بین میروند و یک کانتینر جدید از همان ایمیج این تغییرات را نخواهد داشت. تغییر مستقیم کانتینرهای زنده بهطور کلی یک anti-pattern محسوب میشود؛ روش توصیهشده ساخت یک ایمیج جدید با تغییرات لازم و جایگزینی کانتینر است.
دیباگ کانتینرهای سبک با Docker Debug
Docker Debug (که با اشتراک Pro، Team یا Business در دسترس است) مشکل رایج دیباگ ایمیجهای حداقلی فاقد شل یا ابزار عیبیابی را حل میکند. این ابزار یک جعبهابزار موقت به کانتینر یا ایمیج متصل میکند و امکان استفاده از ابزارهایی مانند ping، vim یا nslookup را فراهم میکند، حتی اگر نصب نباشند. تغییرات ایجادشده هنگام دیباگ یک کانتینر در حال اجرا باقی میمانند، اما تغییرات هنگام دیباگ کانتینر متوقفشده یا ایمیج، پس از پایان نشست حذف میشوند.
خودترمیمی با سیاستهای ریستارت
داکر از چهار سیاست ریستارت (Restart Policy) پشتیبانی میکند که نحوه بازیابی کانتینرها از خرابی را کنترل میکنند:
no(پیشفرض) — هرگز بهطور خودکار ریستارت نمیشودon-failure— فقط در صورت کد خروج غیرصفر ریستارت میشودalways— همیشه ریستارت میشود، حتی پس از ریستارت daemonunless-stopped— همیشه ریستارت میشود، مگر اینکه بهصورت دستی متوقف شده باشد
یک سیاست ریستارت هنگام راهاندازی کانتینر اعمال میشود، برای مثال:
docker run --name neversaydie -it --restart always alpine shنتیجهگیری
کانتینرها روشی بسیار کارآمدتر برای مجازیسازی برنامهها نسبت به VM ارائه میدهند و بهجای سیستمعاملهای اختصاصی، از ایزولهسازی مبتنی بر kernel مشترک استفاده میکنند. درک نحوه راهاندازی کانتینرها، ماندگاری تغییرات، امکان دیباگ آنها و بازیابی از خرابی از طریق سیاستهای ریستارت، برای کار قابلاعتماد با آنها در محیطهای واقعی ضروری است.