مقدمه
Docker Engine به اجزای سمت سرور داکر اشاره دارد که مسئول ساخت، اشتراکگذاری و اجرای کانتینرها هستند. برخلاف تصور رایج، این موتور یک برنامه یکپارچه نیست، بلکه سیستمی ماژولار متشکل از ابزارهای کوچک و تخصصی است که از پروژههایی مانند OCI، CNCF و پروژه Moby گرفته شدهاند.
از یکپارچگی تا طراحی ماژولار
در ابتدای عرضه داکر، موتور از دو بخش اصلی تشکیل میشد: daemon داکر، یک باینری یکپارچه که مسئولیت API، ساخت ایمیج و اجرای کانتینر را بر عهده داشت، و LXC که مستقیماً با kernel لینوکس تعامل میکرد.
وابستگی به LXC مشکلاتی ایجاد کرد، زیرا این ابزار مخصوص لینوکس بود و مستقل از نیازهای داکر توسعه مییافت. داکر در نهایت آن را با ابزار مستقل از پلتفرم خود به نام libcontainer جایگزین کرد.
با گذر زمان، خود daemon یکپارچه به مانعی برای نوآوری تبدیل شد، به همین دلیل داکر بهتدریج آن را به ابزارهای کوچکتر و قابل استفاده مجدد تقسیم کرد. مهمترین نتایج این تلاش، containerd و runc بودند که اکنون فراتر از خود داکر، در پروژههایی مانند Kubernetes، Firecracker و Fargate نیز استفاده میشوند.
تأثیر OCI
همزمان با این تحولات، Open Container Initiative (OCI) در حال تعریف دو استاندارد کلیدی بود: image-spec و runtime-spec، که هر دو در سال ۲۰۱۷ بهعنوان نسخه ۱.۰ منتشر شدند. استاندارد سومی به نام distribution-spec نیز نحوه توزیع ایمیجها از طریق رجیستریها را تعیین میکند.
شرکت Docker, Inc. از اعضای بنیانگذار OCI بوده و همچنان بهطور فعال در آن مشارکت دارد. تمام نسخههای مدرن داکر این مشخصات را از طریق ابزارهایی مانند runc، BuildKit و Docker Hub پیادهسازی میکنند.
runc: رانتایم سطح پایین
runc پیادهسازی مرجع runtime-spec در OCI است. این ابزار یک لایه سبک CLI روی libcontainer است که مسئولیت تعامل با kernel برای ساخت واقعی namespaces و cgroups کانتینر را بر عهده دارد. بهمحض شروع کانتینر، فرآیند runc که آن را ایجاد کرده خاتمه مییابد.
containerd: رانتایم سطح بالا
containerd چرخه حیات کانتینرها را مدیریت میکند — شامل شروع، توقف و حذف آنها — در حالی که کار سطح پایین kernel را به runc واگذار میکند. این ابزار در ابتدا قرار بود یک ابزار کوچک و متمرکز باشد، اما اکنون مدیریت ایمیج، شبکه و volume را نیز شامل میشود که پروژههایی مانند Kubernetes میتوانند بهصورت انتخابی از آن استفاده کنند.
containerd توسط Docker, Inc. ساخته شد و بعداً به CNCF اهدا شد، جایی که اکنون یک پروژه graduated و آماده تولید محسوب میشود.
نحوه راهاندازی یک کانتینر
وقتی دستوری مانند زیر اجرا میشود:
docker run -d --name ctr1 nginxCLI آن را به یک درخواست API تبدیل کرده و به daemon ارسال میکند. daemon این درخواست را از طریق gRPC به containerd منتقل میکند، containerd ایمیج را به یک بسته OCI تبدیل کرده و به runc دستور میدهد کانتینر را بسازد. runc با kernel تعامل کرده و namespaces و cgroups مورد نیاز را میسازد، سپس پس از اجرای فرآیند کانتینر خاتمه مییابد.
این جداسازی باعث میشود daemon بتواند بدون تأثیر بر کانتینرهای در حال اجرا متوقف، ریاستارت یا بهروزرسانی شود — مفهومی که کانتینرهای بدون daemon نامیده میشود.
Shim چیست؟
بین containerd و لایه OCI، یک فرآیند shim قرار دارد. برای هر کانتینر جدید، containerd یک shim و یک فرآیند runc ایجاد میکند؛ پس از خروج runc، shim به فرآیند والد کانتینر تبدیل میشود. shim سه مزیت اصلی فراهم میکند: امکان کانتینرهای بدون daemon، بهبود کارایی با سبک نگهداشتن فرآیندها، و امکان جایگزینی runc با رانتایمهای سطح پایین دیگر.
پیادهسازی روی لینوکس
روی یک میزبان لینوکسی، این اجزا بهصورت باینریهای جداگانه وجود دارند:
/usr/bin/dockerd/usr/bin/containerd/usr/bin/containerd-shim-runc-v2/usr/bin/runc
نتیجهگیری
طراحی ماژولار Docker Engine — بر پایه مشخصات OCI، مدیریت چرخه حیات توسط containerd و اجرای سطح پایین توسط runc — این موتور را انعطافپذیرتر، قابلحملتر و قابلاستفاده مجدد در سراسر اکوسیستم کانتینر کرده است. درک این لایهها کلید تسلط واقعی بر نحوه کارکرد داکر در پشت صحنه است.