توی این درس یاد میگیری کانتینر (container) و ماشین مجازی (virtual machine یا VM) دقیقاً چه فرقی دارند: چرا VM به یک سیستمعامل کامل و hypervisor نیاز دارد ولی کانتینر هستهی (kernel) میزبان را با بقیه به اشتراک میگذارد، namespace و cgroup چه نقشی دارند، و در عمل کی کدام را انتخاب میکنی. همهچیز را با دستور واقعی خودت میسنجی، از جمله اینکه کرنل داخل کانتینر با کرنل سیستمت یکی است یا نه.
تشبیه: خانهی مستقل در برابر آپارتمان
Section titled “تشبیه: خانهی مستقل در برابر آپارتمان”ماشین مجازی مثل خانهی ویلایی مستقل است: هر خانه فونداسیون، لولهکشی، برق و سقف خودش را دارد. امن و کاملاً جداست، ولی ساختنش گران است و حجم زیادی زمین و مصالح میخواهد.
کانتینر مثل آپارتمان در یک ساختمان است: همه فونداسیون، لولهکشی اصلی و برق ساختمان (هستهی سیستمعامل) را به اشتراک میگذارند، ولی هر واحد در بسته است و همسایهها داخلش را نمیبینند. ساختنش ارزان و سریع است. بهایش این است که همه به یک ساختمان وابستهاند: اگر لولهکشی اصلی مشکل داشته باشد، همه را درگیر میکند.
دو معماری کنار هم
Section titled “دو معماری کنار هم”- Hypervisor نرمافزاری است که سختافزار را «مجازی» میکند تا چند سیستمعامل همزمان روی یک دستگاه اجرا شوند (مثل VMware، VirtualBox، Hyper-V، KVM).
- کانتینر هیچ سیستمعامل مهمانی ندارد. فقط یک پروسه روی هستهی میزبان است که با ویژگیهای هستهی لینوکس (namespace و cgroup) جدا شده است.
مثالهای عملی
Section titled “مثالهای عملی”مثال ۱: کرنل کانتینر کدام است؟
Section titled “مثال ۱: کرنل کانتینر کدام است؟”docker run --rm alpine uname -auname -srdocker info --format 'کرنل موتور داکر: {{.KernelVersion}}'Linux 82dc7603ca7b 7.0.14-linuxkit #1 SMP PREEMPT Fri Sep 18 10:19:32 UTC 2026 aarch64 LinuxDarwin 27.0.0کرنل موتور داکر: 7.0.14-linuxkitدستور اول uname -a را داخل یک کانتینر Alpine اجرا کرد؛ دستور دوم همان را روی سیستم میزبان خودت. دستور سوم کرنل موتور داکر را میپرسد. دقت کن: کانتینر Linux میگوید حتی اگر سیستم تو مک (Darwin) باشد، و شمارهی کرنلش دقیقاً همان کرنل موتور داکر است. کانتینر کرنل خودش را ندارد و از همان کرنل لینوکسی استفاده میکند که موتور داکر روی آن اجرا میشود (روی مک و ویندوز، یک VM سبک لینوکسی؛ روی خود لینوکس، خود سیستم تو).
مثال ۲: حجم و سرعت راهاندازی
Section titled “مثال ۲: حجم و سرعت راهاندازی”docker images --format 'table {{.Repository}}\t{{.Tag}}\t{{.Size}}' alpinedocker images --format 'table {{.Repository}}\t{{.Tag}}\t{{.Size}}' ubuntuREPOSITORY TAG SIZEalpine latest 26.6MBalpine 3.12 8.7MBREPOSITORY TAG SIZEubuntu 24.04 141MBTIMEFORMAT='زمان کل: %Rs'time docker run --rm alpine echo "hello"helloزمان کل: 0.287simage کامل یک سیستمعامل مینیمال (Alpine) فقط چند مگابایت است و روشن کردنش کسری از ثانیه طول میکشد، چون هیچ سیستمعاملی بوت نمیشود؛ فقط یک پروسه شروع میشود. (اعداد روی سیستم تو فرق میکنند؛ مهم مقیاس آنهاست: مگابایت و کسری از ثانیه، نه گیگابایت و دقیقه.)
مثال ۳: جداسازی با namespace
Section titled “مثال ۳: جداسازی با namespace”docker run --rm alpine sh -c 'echo "PID من: $$"; echo "hostname: $(hostname)"; ps'PID من: 1hostname: 0dbb9e396c8cPID USER TIME COMMAND 1 root 0:00 psداخل کانتینر پروسهی اصلی شمارهی ۱ دارد و فقط پروسههای خودش را میبیند؛ دهها پروسهی دیگر سیستم میزبان دیده نمیشوند. hostname هم مخصوص خود کانتینر است. این همان جداسازی PID namespace و UTS namespace است.
کانتینری که روی یک مک اجرا میشود، از چه هستهای استفاده میکند؟
کانتینرها به هستهی لینوکس نیاز دارند؛ Docker Desktop برای مک و ویندوز یک VM لینوکسی کوچک اجرا میکند و کانتینرها از کرنل آن استفاده میکنند.
مثال ۴: محدودیت منابع با cgroup
Section titled “مثال ۴: محدودیت منابع با cgroup”docker run --rm --cpus 0.5 alpine cat /sys/fs/cgroup/cpu.maxdocker run --rm --memory 100m alpine cat /sys/fs/cgroup/memory.maxdocker run --rm alpine cat /sys/fs/cgroup/memory.max50000 100000104857600maxعدد 50000 100000 یعنی «در هر ۱۰۰۰۰۰ میکروثانیه فقط ۵۰۰۰۰ میکروثانیه CPU» که معادل نصف یک هسته است. عدد 104857600 همان ۱۰۰ مگابایت است و max یعنی بدون سقف. این محدودیتها را cgroup هسته اعمال میکند، نه یک نرمافزار جداگانه.
مثال ۵: کانتینر از بیرون چه شکلی است؟
Section titled “مثال ۵: کانتینر از بیرون چه شکلی است؟”docker run -d --name lx-sleeper alpine sleep 300docker top lx-sleeperdocker inspect --format 'PID در کرنل میزبان (داخل VM داکر): {{.State.Pid}}' lx-sleeperdocker rm -f lx-sleeper095d727d0680efad9140267266771565731103f87140e43c99a4a1ad858986f3UID PID PPID C STIME TTY TIME CMDroot 66250 66227 4 13:43 ? 00:00:00 sleep 300PID در کرنل میزبان (داخل VM داکر): 66250lx-sleeperاز بیرون، کانتینر فقط یک پروسهی sleep است. docker top پروسههای داخل کانتینر را از دید میزبان نشان میدهد و docker inspect شمارهی واقعی آن را در کرنل. (این شماره با «PID ۱ داخل کانتینر» فرق دارد؛ یک پروسه، دو شماره، چون هر namespace شمارهگذاری خودش را دارد.)
مثال ۶: حداقل منابع قابل قبول
Section titled “مثال ۶: حداقل منابع قابل قبول”docker run --rm --memory 1m alpine truedocker: Error response from daemon: Minimum memory limit allowed is 6MB
Run 'docker run --help' for more informationداکر برای سقف حافظه حداقلی دارد (در خطای بالا میبینی). این یعنی حتی کانتینر هم نمیتواند با هر منبع ناچیزی اجرا شود.
پشت پرده: دیوارهای کانتینر از چه ساخته شدهاند؟
Section titled “پشت پرده: دیوارهای کانتینر از چه ساخته شدهاند؟”کانتینر یک «چیز» واحد در هستهی لینوکس نیست؛ ترکیبی از چند ویژگی است:
| ویژگی هسته | چه چیزی را جدا یا محدود میکند؟ |
|---|---|
| PID namespace | فهرست پروسهها؛ کانتینر فقط پروسههای خودش را میبیند |
| Network namespace | کارت شبکه، IP، جدول مسیریابی و پورتها |
| Mount namespace | فایلسیستم؛ کانتینر ریشهی خودش را دارد |
| UTS namespace | hostname |
| IPC namespace | ارتباط بین پروسهها (حافظهی مشترک و صف پیام) |
| User namespace | نگاشت شمارهی کاربرها (اختیاری) |
| cgroup | محدودیت و حسابداری منابع (CPU، حافظه، I/O) |
namespace مشخص میکند پروسه چه چیزی را میبیند؛ cgroup مشخص میکند چقدر میتواند مصرف کند. VM بهجای اینها، سختافزار را شبیهسازی میکند و یک سیستمعامل کامل روی آن بوت میشود؛ جداسازیاش قویتر است چون هستهی جدا دارد، ولی بهایش مصرف بیشتر و شروع کندتر است.
جداسازی ضعیفتر است؟
Section titled “جداسازی ضعیفتر است؟”چون همهی کانتینرها یک هسته دارند، یک باگ امنیتی در هسته میتواند همه را درگیر کند. به همین دلیل در محیطهایی که به جداسازی بسیار قوی نیاز است (مثلاً اجرای کد ناشناس مشتریهای مختلف)، کانتینر را معمولاً داخل یک VM اجرا میکنند. این دو رقیب نیستند؛ اغلب مکمل هماند. (درس «امنیت پایه» همین بخش را بیشتر باز میکند.)
کی کدام؟
Section titled “کی کدام؟”| معیار | کانتینر | ماشین مجازی |
|---|---|---|
| زمان شروع | ثانیه یا کمتر | معمولاً دقیقه |
| حجم | مگابایت تا صدها مگابایت | چند گیگابایت |
| هسته | مشترک با میزبان | مخصوص خود VM |
| سیستمعامل مهمان متفاوت (مثلاً ویندوز روی لینوکس) | نه | بله |
| جداسازی | خوب (در سطح پروسه) | قویتر (هستهی جدا) |
| تراکم روی یک سرور | بسیار زیاد | کمتر |
| مناسب برای | میکروسرویس، CI، محیط توسعهی یکسان | سیستمعاملهای متفاوت، جداسازی شدید، برنامههای قدیمی |
اشتباهات رایج
Section titled “اشتباهات رایج”۱) «داخل کانتینر bash نیست»
Section titled “۱) «داخل کانتینر bash نیست»”docker run --rm alpine bashdocker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: exec: "bash": executable file not found in $PATH
Run 'docker run --help' for more informationimage های کوچک مثل Alpine ابزارهای کمی دارند. راهحل: بهجای bash از sh استفاده کن، یا image ای مثل ubuntu که bash دارد.
۲) انتظار «سیستمعامل کامل»
Section titled “۲) انتظار «سیستمعامل کامل»”docker run --rm alpine systemctl statusdocker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: exec: "systemctl": executable file not found in $PATH
Run 'docker run --help' for more informationداخل کانتینر معمولاً سیستم init و سرویسهای سیستمی (systemd) وجود ندارد؛ کانتینر برای اجرای یک برنامهی اصلی ساخته شده، نه یک سرور کامل. راهحل: هر سرویس را در کانتینر جدا بگذار.
۳) فکر کردن «کرنل را میتوانم داخل کانتینر عوض کنم»
Section titled “۳) فکر کردن «کرنل را میتوانم داخل کانتینر عوض کنم»”کرنل مال میزبان است و از داخل کانتینر عوض نمیشود (مثال ۱: همان کرنل میزبان بود). اگر کرنل دیگری لازم داری، به VM نیاز داری.
نسخهی کرنل را یک بار داخل یک کانتینر Alpine و یک بار داخل یک کانتینر Ubuntu چاپ کن (uname -r). چه نتیجهای میگیری؟
دیدن جواب
docker run --rm alpine uname -rdocker run --rm ubuntu:24.04 uname -r7.0.14-linuxkit7.0.14-linuxkitهر دو عدد یکی است، چون هر دو کانتینر از کرنل مشترک موتور داکر استفاده میکنند؛ فقط فایلهای سطح کاربر (Alpine یا Ubuntu) فرق دارند.
یک کانتینر با hostname دلخواه my-box اجرا کن که hostname خودش را چاپ کند. سپس بدون آن گزینه اجرا کن و hostname پیشفرض را ببین. نتیجه چه میگوید؟
دیدن جواب
docker run --rm --hostname my-box alpine hostnamedocker run --rm alpine hostnamemy-box9e302545810fhostname هر کانتینر مال خودش است (UTS namespace)؛ پیشفرضش شناسهی کوتاه کانتینر است و با --hostname عوض میشود.
یک کانتینر با سقف CPU برابر 0.25 هسته بساز و با خواندن فایل cgroup ثابت کن اعمال شد. بعد حساب کن عدد اول cpu.max چرا چنین است. در آخر یک سقف حافظهی نامعتبر (512k) بده و خطا را ببین.
دیدن جواب
docker run --rm --cpus 0.25 alpine cat /sys/fs/cgroup/cpu.maxdocker run --rm --memory 512k alpine true25000 100000docker: Error response from daemon: Minimum memory limit allowed is 6MB
Run 'docker run --help' for more informationنصف هسته 50000 از 100000 بود؛ پس 0.25 برابر 25000 از 100000 میشود. عدد دوم دورهی زمانی (۱۰۰ میلیثانیه) و عدد اول سهمیهی CPU در آن دوره است. سقف حافظهی خیلی کم هم توسط داکر رد میشود.
آزمونک
Section titled “آزمونک”مهمترین تفاوت کانتینر با VM چیست؟
VM سیستمعامل مهمان و هستهی جدا دارد؛ کانتینر یک پروسهی جداشده روی هستهی مشترک است.
namespace چه چیزی را مشخص میکند؟
namespace دید پروسه را محدود میکند؛ cgroup میزان مصرف را.
خروجی cpu.max برابر «50000 100000» یعنی…
در هر ۱۰۰۰۰۰ میکروثانیه فقط ۵۰۰۰۰ میکروثانیه CPU مجاز است.
برای اجرای یک برنامهی ویندوزی روی سرور لینوکس کدام لازم است؟
کانتینرهای لینوکسی از هستهی لینوکس استفاده میکنند و ویندوز اجرا نمیشود.
چرا کانتینر در حد ثانیه بالا میآید؟
هستهی میزبان از قبل روشن است؛ فقط پروسهی برنامه شروع میشود.
جمعبندی
Section titled “جمعبندی”- VM = سختافزار مجازی + سیستمعامل مهمان کامل + هستهی جدا؛ کانتینر = یک پروسهی جداشده روی هستهی مشترک.
- جداسازی کانتینر با namespace (چه چیزی را میبیند) و cgroup (چقدر میتواند مصرف کند) انجام میشود.
- کانتینر سبک، سریع و متراکم است؛ VM جداسازی قویتر و امکان سیستمعامل متفاوت دارد.
- روی مک و ویندوز، کانتینرها داخل یک VM سبک لینوکسی اجرا میشوند.
- این دو مکملاند: کانتینر را اغلب داخل VM اجرا میکنند.
| دستور | کاری که میکند |
|---|---|
docker run --rm alpine uname -a | اجرای uname داخل کانتینر Alpine (و پاک شدن) |
uname -sr | سیستمعامل و کرنل میزبان |
docker info --format "{{.KernelVersion}}" | کرنل موتور داکر |
docker run --rm --cpus 0.5 IMAGE | سقف CPU نصف هسته |
docker run --rm --memory 100m IMAGE | سقف حافظه ۱۰۰ مگابایت |
docker run --rm --hostname NAME IMAGE | hostname دلخواه برای کانتینر |
docker top NAME | پروسههای داخل کانتینر از دید میزبان |
docker inspect --format "{{.State.Pid}}" NAME | PID واقعی کانتینر در میزبان |