تفاوت FreeBSD Jails و Docker در چیست؟
تفاوت اصلی در ایزولهسازی است. داکر بر ایمیجهای لایهبندی شده و اجرای تکپردازش متکی است، اما FreeBSD Jails یک محیط کامل سیستمعامل با مدیریت مستقل منابع ارائه میدهد.
مقایسه FreeBSD jails و Docker containers در یک پاراگراف
FreeBSD jails و Docker containers مسئله یکسانی را با دو ساختار متفاوت حل میکنند. هر دو، محیطهای کاربری ایزولهشدهای را روی یک هسته مشترک اجرا میکنند، بنابراین هیچکدام ماشین مجازی نیستند. تفاوت در محتوای آنهاست. یک Docker container تنها یک پردازش را از یک ایمیج لایهبندیشده که از یک رجیستری دریافت کردهاید، اجرا میکند. یک jail یک محیط کاربری کامل FreeBSD را اجرا میکند: /etc اختصاصی خود، اسکریپتهای راهاندازی rc مخصوص به خود، دیتابیس pkg مجزا و هر تعداد پردازشی که بخواهید. تقریباً تمام تفاوتهای دیگر در این صفحه، ناشی از همین یک تفاوت بنیادین است.
پلتفرم SSD Nodes ایمیجهای FreeBSD ارائه نمیدهد. شما نمیتوانید سرور FreeBSD روی این پلتفرم اجاره کنید و مطالب زیر به هیچ عنوان راهنمای نصب برای ماشینی که میتوانید از اینجا تهیه کنید، نیست. این صرفاً مقایسهای بین دو مدل ایزولهسازی است که نوشته شده تا بتوانید تشخیص دهید یک workload واقعاً به کدامیک نیاز دارد و بتوانید پیکربندی تیم FreeBSD را بدون حدس و گمان مطالعه کنید.
Jail دقیقاً چیست
Jailها در مارس 2000 با FreeBSD 4.0 معرفی شدند؛ یعنی قدیمیتر از cgroups و حدود یک دهه قدیمیتر از Docker هستند. این مکانیزم بر پایه یک فراخوانی هسته (kernel call) استوار است. jail(8) یک درخت دایرکتوری را میگیرد و پردازشهایی را درون آن با یک jail ID اختصاصی اجرا میکند؛ سپس هسته سیستمعامل، مجموعهای ثابت از عملیات را برای هر پردازشی که آن ID را داشته باشد، مسدود میکند. یک پردازش در Jail نمیتواند پردازشهای خارج از Jail خود را ببیند، نمیتواند فایلسیستمها را mount یا unmount کند، قادر به بارگذاری ماژولهای هسته نیست و نمیتواند به آدرسهای شبکهای که به آن Jail اختصاص داده نشده، bind شود. هیچ نوع namespace مجزایی برای یادگیری وجود ندارد و نیازی به فعالسازی ویژگیها به صورت تکبهتک نیست: محدودیتها به صورت یکپارچه اعمال میشوند و از طریق پارامترهای موجود در فایل پیکربندی Jail تنظیم میگردند.
در سیستم میزبان، jls لیست Jailهای در حال اجرا را نمایش میدهد و jexec web sh شما را به یک shell در داخل Jail با نام web وارد میکند.
شما یک Jail را با قرار دادن userland سیستمعامل FreeBSD در یک دایرکتوری میسازید. سیستم پایه این کار را برای شما انجام میدهد:
sudo bsdinstall jail /usr/local/jails/containers/webاین دستور مجموعه توزیع پایه را برای نسخه شما دریافت کرده و مراحل معمول پس از نصب را اجرا میکند؛ بنابراین شما رمز عبور root را تعیین کرده و منطقه زمانی را دقیقاً همانطور که در یک سرور جدید انجام میدهید، انتخاب میکنید. نتیجه، یک نصب کامل FreeBSD است که در یک پوشه قرار دارد. سپس آن را در /etc/jail.conf توصیف میکنید:
web {
host.hostname = "web.example.internal";
path = "/usr/local/jails/containers/web";
ip4.addr = "10.0.0.10";
exec.start = "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown";
mount.devfs;
}آن را استارت بزنید و سپس وضعیتش را بررسی کنید:
sudo service jail start web
jlsjls اکنون باید web را به همراه JID، نام میزبان و آدرس IP آن لیست کند. اگر Jail ظاهر نشد، sudo jail -c web را مستقیماً اجرا کنید. این دستور همان پیکربندی را در پیشزمینه (foreground) اعمال میکند و پارامتری که قادر به پذیرش آن نبوده را چاپ میکند، به جای اینکه خطا را در خروجی سرویس رها کند.
خطی که ارزش دو بار خواندن دارد exec.start = "/bin/sh /etc/rc" است. استارت زدن یک Jail، اسکریپت بوت معمول FreeBSD را در داخل آن اجرا میکند؛ بنابراین Jail تمام سرویسهایی که در /etc/rc.conf خودش فعال شدهاند را بالا میآورد. یک کانتینر Docker هیچ مرحله معادل این ندارد، زیرا فقط پردازش entrypoint تصویر را اجرا میکند و با توقف آن پردازش، کانتینر نیز متوقف میشود.
نحوه دریافت نرمافزار: ایمیجها و رجیستریها در مقابل فضای کاربری که خودتان پر میکنید
این تفاوتی است که در همان روز اول احساس میکنید.
با Docker شما نام نرمافزار را مشخص میکنید و آن را دریافت میکنید. docker pull nginx یک ایمیج لایهبندیشده و مبتنی بر محتوا را دریافت میکند که توسط شخص دیگری ساخته و تست شده است و docker compose up -d آن را به همراه Volumeها و شبکه متصل به آن اجرا میکند. رجیستری در واقع همان محصول است. بخش بزرگی از ارزش گردشکار Docker در این است که هزاران پروژه، یک ایمیج عملیاتی منتشر میکنند؛ همین موضوع باعث میشود اجرای Docker روی یک VPS یک کار کوتاه باشد تا یک پروژه زمانبر.
FreeBSD هیچ رجیستری عمومی پیشفرضی برای ایمیجهای jail ندارد. شما یک فضای کاربری خالی ایجاد میکنید و نرمافزار را درون آن نصب میکنید، درست همانطور که یک سرور خام را راهاندازی میکنید. این کار نیاز به تایپ بیشتری دارد. اما شفافتر هم هست، زیرا آنچه در jail اجرا میشود همان چیزی است که pkg در آن قرار داده است، از همان مجموعه بستههایی که میزبان (host) استفاده میکند.
ابزارها این فرآیند را کوتاه میکنند. BastilleBSD مدیر رایج jail است و خود یک بسته محسوب میشود:
sudo pkg install bastille
sudo sysrc bastille_enable=YES
sudo bastille setup
sudo bastille bootstrap 15.1-RELEASEbastille setup شبکه، فضای ذخیرهسازی و فایروال را برای شما پیکربندی میکند. bastille bootstrap یک نسخه (release) را یکبار دانلود میکند و هر jail که پس از آن ایجاد کنید، از همان استفاده مجدد میکند. FreeBSD 15.1 نسخه عملیاتی فعلی است که در ژوئن 2026 منتشر شده است؛ هر نسخهای که اجرا میکنید را جایگزین کنید.
ایجاد یک jail در این حالت تنها یک دستور است و پر کردن آن نیز یک دستور دیگر:
sudo bastille create web 15.1-RELEASE 10.17.89.10/24
sudo bastille pkg web install nginx
sudo bastille service web nginx start
sudo bastille console webbastille console web به شما یک shell ورود به سیستم در داخل jail میدهد و bastille list نشان میدهد که چه چیزی روی میزبان وجود دارد. برای تکرار یک build، قالبهای (template) Bastille مراحل را در یک فایل نگه میدارند و آنها را روی یک jail اعمال میکنند، که نزدیکترین مفهوم در این دنیا به Dockerfile است. یک قالب روی هر jail بازپخش میشود. هیچچیز از پیش ساختهشدهای وارد نمیشود.
بنابراین خلاصه صادقانه کوتاه است. Docker ساختههای دیگران را به شما میدهد. Jails به شما اجازه میدهد نصبهای خودتان را داشته باشید. اگر نرمافزار موجود در لیست کوتاه شما فقط به صورت container image عرضه میشود، این موضوع پیش از آنکه هر معیار دیگری مورد بررسی قرار گیرد، تکلیف انتخاب را روشن میکند.
وضعیت و ارتقا: بخشی که ZFS تغییر میدهد
Docker وضعیت (state) را بهصورت هدفمند تفکیک میکند. فایلسیستم کانتینر دورریختنی است، دادههای شما در یک named volume یا bind mount قرار دارند و ارتقا شامل docker compose pull و سپس docker compose up -d است. کانتینر جایگزین میشود و هر چیزی که در volume قرار نداده باشید، از بین میرود. اگر از این قاعده پیروی کنید، این یک قابلیت است و اگر آن را فراموش کنید، منجر به حادثه از دست رفتن داده میشود؛ به همین دلیل است که انتخاب بین bind mount و named volume در یک Compose stack اهمیت بسیار زیادی دارد.
یک jail وضعیت را تفکیک نمیکند و ZFS دلیلی است که این مدل کار میکند. کل jail یک dataset واحد است:
sudo zfs snapshot zroot/jails/containers/web@pre-upgrade
sudo pkg -j web upgrade
sudo zfs rollback zroot/jails/containers/web@pre-upgradeپیش از اجرای این دستور، نام واقعی dataset را با zfs list بررسی کنید؛ مسیر بالا چیدمانی است که در کتاب راهنما استفاده شده است. گرفتن snapshot حدود یک ثانیه زمان میبرد و تا زمانی که محتویات jail تغییر نکند، تقریباً هیچ فضایی اشغال نمیکند. اگر ارتقا باعث خرابی سرویس شود، rollback کل userland را به وضعیت قبلی بازمیگرداند، از جمله دیتابیس بستهها و فایلهای پیکربندی که بهصورت دستی در ساعت 2 بامداد ویرایش کردهاید. Docker هیچ معادل داخلی برای این کار ندارد، زیرا مدل آن فرض را بر این میگذارد که شما هرگز به چنین چیزی نیاز ندارید.
zfs clone نیمه دیگر ماجراست. یک clone از یک snapshot، یک jail جدید و قابلنوشتن است که بلوکهای تغییرنیافته را با والد خود به اشتراک میگذارد؛ بنابراین یک کپی staging از یک jail با حجم 3 GB تا زمانی که شروع به تغییر آن نکنید، تقریباً هیچ هزینهای روی دیسک ندارد. این همان روشی است که یک مدیر سیستم FreeBSD برای ساخت یک jail "مشابه محیط عملیاتی" جهت تمرین ارتقا استفاده میکند.
ارتقای سیستم پایه از بستهها جداست. برای jailای که کپی مخصوص به خود از userland را نگه میدارد:
sudo freebsd-update -b /usr/local/jails/containers/web fetch installThin jailها از تکرار این کار جلوگیری میکنند. آنها یک base مشترک و فقطخواندنی را از طریق nullfs mount میکنند و به هر jail یک لایه کوچک قابلنوشتن اختصاص میدهند؛ بنابراین شما base را یکبار وصله (patch) میکنید و همه jailها نتیجه را مشاهده میکنند. Bastille بهصورت پیشفرض thin jail ایجاد میکند.
شبکهسازی: پورتهای منتشرشده در برابر تصمیم آدرسدهی
Docker تصمیمگیری درباره شبکه را برای شما انجام میدهد و از شما میخواهد استثناها را منتشر کنید. کانتینرها روی یک bridge قرار میگیرند، از طریق نام سرویس در یک شبکه تعریفشده توسط کاربر به یکدیگر دسترسی پیدا میکنند و -p 8080:80 یکی از آنها را برای میزبان (host) در دسترس قرار میدهد. Docker برای تحقق این امر، قوانین فیلتر بسته (packet filter) خاص خود را مینویسد؛ این همان روشی است که باعث میشود پورت کانتینر منتشرشده مستقیماً از ufw عبور کند.
یک jail شما را ملزم میکند که مدل را از همان ابتدا انتخاب کنید و دو مدل وجود دارد.
IP اشتراکی (Shared IP). در این حالت ip4.addr = "10.0.0.10" آن آدرس را به یک اینترفیس موجود در میزبان اضافه کرده و jail را به آن محدود میکند. jail پشته شبکه (network stack) اختصاصی خود را ندارد، بنابراین نمیتواند فایروال خود را اجرا کند. همچنین نمیتواند واقعاً به تمام آدرسها bind شود: یک سوکت درون jail که درخواست 0.0.0.0 میکند، توسط هسته (kernel) به آدرس اختصاصی همان jail بازنویسی میشود. دو jail نمیتوانند همزمان روی پورت 80 از یک آدرس گوش دهند، بنابراین باید به هر کدام یک آدرس اختصاص دهید یا یک reverse proxy در مقابل آنها قرار دهید.
VNET. با افزودن vnet; به jail، آن jail یک پشته شبکه کامل دریافت میکند: اینترفیسهای اختصاصی، جدول مسیریابی (routing table) اختصاصی و قوانین فایروال اختصاصی خود. شما آن را با یک epair به میزبان متصل میکنید؛ یک کابل مجازی که یک سر آن در هر طرف قرار دارد و سرِ سمت میزبان را روی یک bridge میگذارید. این مدل بیشترین شباهت را به آنچه Docker ارائه میدهد دارد و همان حالتی است که در پسزمینه انواع jailهای -V و -B در Bastille استفاده میشود.
فوروارد کردن یک پورت میزبان به داخل یک jail، یک قانون تغییر مسیر (redirect) از نوع pf است. Bastille این فرآیند را بستهبندی میکند:
sudo bastille rdr web tcp 80 80در اینجا هیچ EXPOSE و انتشار خودکاری وجود ندارد. هیچچیز به یک jail نمیرسد مگر اینکه آدرس آن یا یک قانون تغییر مسیر، اجازه آن را بدهد. این شروعی کندتر و فایروالی بسیار ساکتتر را به همراه دارد.
محدودیتهای منابع: cgroups در مقابل rctl
Docker یک کانتینر را با استفاده از cgroups محدود میکند و این محدودیتها در همانجایی تعریف میشوند که کانتینر تعریف شده است: --memory=1g --cpus=1.5 در خط فرمان، یا کلیدهای متناظر در یک فایل Compose. اگر پشتهٔ خود را در یک فایل Docker Compose روی یک VPS نگهداری میکنید، محدودیت در کنار سرویسی که بر آن اعمال میشود قرار میگیرد و همراه با آن در git جابهجا میشود.
FreeBSD از rctl استفاده میکند که یک زیرسیستم است و باید آن را فعال کنید. حسابرسی منابع بهصورت پیشفرض غیرفعال است، زیرا در هر تخصیص منابع، هزینهٔ اندکی دارد. این tunable را به /boot/loader.conf اضافه کرده و سیستم را reboot کنید:
kern.racct.enable=1سپس یک قانون تنظیم کرده و آن را پایش کنید:
sudo rctl -a jail:web:vmemoryuse:deny=1g
rctl -hu jail:webrctl -hu jail:web میزان استفادهٔ فعلی jail را با واحدهای قابلفهم برای انسان چاپ میکند، بنابراین میتوانید ببینید پیش از بروز هرگونه اختلال، چقدر تا رسیدن به حد مجاز فاصله دارید. عمل deny باعث میشود تخصیص منابع بیش از حد مجاز در داخل jail با شکست مواجه شود، بنابراین بهجای مشاهدهٔ پیام kill در میزبان، خطای تخصیص خودِ برنامه را خواهید دید.
قوانینی که با rctl -a اضافه میشوند، با reboot بعدی از بین میروند. سرویس rctl در FreeBSD آنها را از /etc/rctl.conf بارگذاری مجدد میکند، بنابراین قانون را در آن فایل بنویسید و سرویس را فعال کنید:
sudo sysrc rctl_enable=YESاین همان نقطهای است که Docker بهوضوح راحتتر است. محدودیت در یک فایل Compose همراه با سرویسی که آن را محدود میکند بازبینی میشود. یک قانون rctl، خطی در یک فایل مجزا است که به یک jail تعریفشده در جای دیگری اشاره دارد.
هنگامی که پاسخ یک ماشین مجازی است: bhyve
یک jail از هسته (kernel) میزبان استفاده میکند، بنابراین برخی موارد برای همیشه خارج از دسترس هستند. این محیط نمیتواند نسخهٔ متفاوتی از هسته را اجرا کند، نمیتواند ماژول هسته بارگذاری کند و نمیتواند باینریهای Linux را به شکلی که یک کانتینر Linux اجرا میکند، به کار بگیرد. سیستمعامل FreeBSD یک لایهٔ سازگاری با Linux به نام linuxulator دارد، اما این لایه تنها زیرمجموعهای از فراخوانهای سیستمی (system calls) لینوکس را پیادهسازی میکند و پاسخ عمومی برای تصاویر (images) دلخواه لینوکس نیست.
bhyve هایپروایزر FreeBSD است و زمانی که به یک مرز واقعی ماشین نیاز دارید، ابزار مناسبی محسوب میشود: برای اجرای یک سیستمعامل متفاوت، یک هستهٔ متفاوت، یا زمانی که نمیخواهید هستهٔ سیستم را با یک مستأجر (tenant) به اشتراک بگذارید. هزینهٔ این کار، حافظهای است که بهجای اشتراکگذاری، رزرو میشود و همچنین مدیریت یک هستهٔ دوم برای اعمال وصلههای امنیتی. این همان تصمیمی است که در لینوکس میان کانتینرها و ماشینهای مجازی کامل میگیرید و همین تصمیم تعیین میکند که آیا به یک VPS که از مجازیسازی تو در تو (nested virtualization) پشتیبانی میکند در زیرساخت خود نیاز دارید یا خیر.
اکوسیستم؛ دلیل صادقانهای که اکثر تیمها از Docker استفاده میکنند
تمام مطالب بالا مربوط به مدل فنی است. آنچه انتخاب اکثر تیمها را تعیین میکند، وسعت دنیای پیرامون هر یک از این ابزارهاست.
Docker با خود Docker Hub و GHCR، docker compose، قابلیت استفاده از Kubernetes زمانی که یک سرور دیگر پاسخگو نیست، CI runnerهایی که از پیش با پشتیبانی از کانتینر یکپارچه شدهاند و دستورات راهاندازی سریع در فایل README تقریباً تمام پروژهها را به همراه میآورد. Jails از درخت پورتهای FreeBSD بهره میبرد که بزرگ و بهدقت نگهداری میشود، اما مجموعهای بسیار کوچکتر از بستههای آمادهٔ اجرا برای برنامهها دارد. وقتی پروژهای فقط یک image کانتینر منتشر میکند، در مسیر FreeBSD باید مستندات آن را بخوانید و اجزا را خودتان کنار هم قرار دهید.
Jails در سوی دیگر این معامله جایگاه خود را پیدا میکند. زمانی به سراغ آنها میروید که از ZFS استفاده میکنید و برایتان قابلیت snapshot و بازگردانی (rollback) کل یک سرویس اهمیت دارد، یا زمانی که سرویسهای شما بومی FreeBSD هستند، یا وقتی به جای یک پردازش واحد، به یک userland کامل برای هر tenant نیاز دارید، یا زمانی که میخواهید هسته سیستمعامل، فیلتر بستهها (packet filter)، سیستم فایل و مستندات همگی به عنوان یک سیستم واحد نگهداری شوند. نکته آخر همان چیزی است که وقتی مردم FreeBSD را منسجم (coherent) مینامند، به آن اشاره دارند. این موضوع در مقایسه جامعتر لینوکس و FreeBSD به عنوان پلتفرمهای سرور و در تغییرات FreeBSD 15 برای استفاده در سرور فضای بیشتری دارد.
یک قضاوت نهایی: اگر تیم شما از قبل با Docker آشناست، هزینه مهاجرت واقعی است و سود آن باید کاملاً مشخص باشد. به خاطر کیفیت ایزولهسازی تغییر مسیر ندهید؛ این دو مدل آنقدر به هم نزدیک هستند که پیکربندی شما اهمیت بیشتری دارد. به این دلیل تغییر دهید که خواهان بازگردانی کل سرویسها با پشتیبانی ZFS هستید، یا اینکه از قبل در حال استفاده از FreeBSD هستید.
FAQ
آیا میتوانم ایمیجهای Docker را روی FreeBSD اجرا کنم؟
خیر، ایمیجهای لینوکسی را نمیتوان اجرا کرد و این مسیر پشتیبانیشدهای نیست. FreeBSD از کانتینرهای OCI پشتیبانی میکند: sudo pkg install -y podman-suite ابزار Podman را نصب میکند که کانتینرها را از طریق ocijail اجرا میکند؛ یک runtime که در لایه زیرین، jailهای واقعی میسازد. این ابزار به fdescfs نیاز دارد که روی /dev/fd برای مانیتور کانتینر mount شده باشد و همچنین pf برای NAT (ترجمه آدرس شبکه) کانتینر. ایمیجهای OCI که بهصورت بومی برای FreeBSD ساخته شدهاند، بهترین عملکرد را دارند. ایمیجهای لینوکسی علاوه بر این به لایه سازگاری لینوکس نیاز دارند و تا آگوست 2026، پورت Podman در FreeBSD همچنان به عنوان آزمایشی توصیف میشود. اگر استقرار شما مجموعهای از ایمیجهای لینوکسی است، آن را روی لینوکس اجرا کنید. در خانواده RHEL، این یعنی نصب Docker روی Rocky Linux یا AlmaLinux، جایی که Podman به عنوان بستهای که پیش از نصب هر چیزی، دستور docker را در اختیار دارد، ظاهر میشود.
آیا jailهای FreeBSD امنتر از کانتینرهای Docker هستند؟
هر دو از یک هسته میزبان مشترک استفاده میکنند، بنابراین یک باگ در هسته برای هر دو ریسک محسوب میشود و هیچکدام مرز امنیتی مناسبی برای کدهای کاملاً غیرقابلاعتماد نیستند. تفاوت در نقطه شروع است. یک jail با مجموعهای گسترده از عملیاتِ مسدودشده شروع میشود و شما آنها را یکبهیک فعال میکنید. یک کانتینر Docker به عنوان root در مجموعهای از namespaceها با برخی قابلیتهای حذفشده شروع میشود و سختسازی بیشتر، اختیاری است. در عمل، پیکربندی بیش از مدلِ انتخابی تعیینکننده است: یک jail که با allow.mount و allow.raw_sockets فعال اجرا شود، امنتر از یک کانتینر با پیکربندی دقیق نیست.
چگونه از یک jail نسخه پشتیبان تهیه کنم؟
از dataset اسنپشات بگیرید و آن را ارسال کنید. sudo zfs snapshot zroot/jails/containers/web@backup، سپس آن اسنپشات را با zfs send به یک pool دیگر یا فایلی که از سیستم خارج میکنید، بفرستید. از آنجا که یک jail تمام userland خود را در یک dataset نگه میدارد، اسنپشات بستههای نصبشده و دادهها را در یک نقطه زمانی منسجم، به همراه تمام فایلهای پیکربندی که دستی ویرایش کردهاید، ثبت میکند. این دقیقاً برعکس عادت Docker است که در آن از volumeهای نامگذاریشده و فایل Compose نسخه پشتیبان میگیرید و بقیه را از روی ایمیج بازسازی میکنید.
آیا به BastilleBSD نیاز دارم یا سیستم پایه کافی است؟
سیستم پایه کافی است و نقطه شروع بهتری است. jail.conf، jls، jexec و service jail start کل مدل را پوشش میدهند و وقتی آنها را بشناسید، میتوانید بدون نیاز به یادگیری ابزارهای خاص هر میزبان، هر سیستم FreeBSD را مدیریت کنید. Bastille یک لایه راحتی روی آن است: نسخهها را bootstrap میکند، jailهای سبک میسازد، قالبها را اعمال میکند و قوانین redirect مربوط به pf را برای شما مینویسد. ابتدا دستورات پایه را یاد بگیرید، سپس زمانی که تعداد jailها تایپ کردن را خستهکننده کرد، از Bastille استفاده کنید.