FreeBSD jails أم حاويات Docker: ما الفرق؟
تعرّف إلى الفرق العملي بين النموذجين: jail يشغّل userland كاملاً، بينما تستخدم Docker صوراً ذات طبقات، مع مقارنة البرامج والحالة والشبكات والحدود.
FreeBSD jails مقارنةً بحاويات Docker في فقرة واحدة
تحل FreeBSD jails وحاويات Docker المشكلة نفسها بطريقتين مختلفتين. يعمل كلاهما على تشغيل userlands معزولة فوق kernel مشترك واحد، ولذلك لا يُعد أيٌّ منهما جهازاً افتراضياً. يكمن الاختلاف في المحتوى الداخلي. تشغّل حاوية Docker عملية واحدة من image ذات طبقات جرى سحبها من registry. أما jail فتشغّل userland كاملاً لنظام FreeBSD: لديها /etc الخاص بها، ونصوص بدء التشغيل rc الخاصة بها، وقاعدة بيانات pkg الخاصة بها، ويمكنها تشغيل أي عدد تريده من العمليات. وينتج معظم الاختلافات الأخرى في هذه الصفحة عن هذا الاختلاف وحده.
لا يوفّر SSD Nodes صور FreeBSD. لا يمكنك استئجار خادم FreeBSD على هذه المنصة، وليس أي مما يلي دليل تثبيت لجهاز يمكنك شراؤه منها. هذه مقارنة بين نموذجي عزل، كُتبت لتتمكن من تحديد النموذج الذي يناسبه حمل العمل فعلياً، ولقراءة إعدادات فريق FreeBSD من دون تخمين.
ماهيّة الـjail فعلياً
ظهرت jails في FreeBSD 4.0 في مارس 2000، لذلك فهي أقدم من cgroups بنحو عقد، وأقدم من Docker بنحو عقد تقريباً. تعتمد الآلية على استدعاء واحد للنواة. يأخذ jail(8) شجرة مجلدات ويبدأ العمليات داخلها مع إرفاق معرّف jail بها، ثم ترفض النواة مجموعة محددة من العمليات لأي عملية تحمل ذلك المعرّف. لا تستطيع العملية داخل jail رؤية العمليات الموجودة خارج jail، ولا تركيب أنظمة الملفات أو إلغاء تركيبها، ولا تحميل وحدات النواة، ولا الارتباط بعناوين الشبكة التي لم تُمنح إلى jail. لا يوجد نوع namespace منفصل يجب تعلّمه، ولا إعداد اختياري لكل ميزة؛ تصل القيود كوحدة واحدة، وتُعدَّل بواسطة المعلمات الموجودة في إعدادات jail.
على المضيف، يسرد jls الـjails قيد التشغيل، بينما ينقلك jexec web sh إلى shell داخل jail المسماة web.
تنشئ jail بوضع userland الخاص بـFreeBSD داخل مجلد. ينفّذ النظام الأساسي ذلك نيابةً عنك:
sudo bsdinstall jail /usr/local/jails/containers/webيجلب هذا الأمر مجموعة base الخاصة بإصدارك، وينفّذ خطوات ما بعد التثبيت المعتادة. لذلك تعيّن كلمة مرور 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
jlsيجب أن يسرد jls الآن web مع JID واسم المضيف وعنوان IP الخاص به. إذا لم تظهر jail، شغّل sudo jail -c web مباشرةً. يطبّق الأمر الإعداد نفسه في الواجهة الأمامية، ويطبع المعلمة التي تعذّر قبولها، بدلاً من ترك الفشل في مخرجات الخدمة.
السطر الذي يستحق القراءة مرتين هو exec.start = "/bin/sh /etc/rc". يؤدي تشغيل jail إلى تشغيل نص إقلاع FreeBSD المعتاد داخلها، لذلك تشغّل jail كل خدمة مفعّلة في /etc/rc.conf الخاص بها. لا توجد خطوة مكافئة في حاوية Docker، لأنها تشغّل عملية entrypoint الخاصة بالصورة وتتوقف عندما تتوقف تلك العملية.
طريقة الحصول على البرمجيات: الصور والسجلات مقابل userland تملؤه بنفسك
هذا هو الفرق الذي ستلاحظه منذ اليوم الأول.
مع Docker، تحدد اسم البرمجية وتحصل عليها. يجلب docker pull nginx صورة متعددة الطبقات ومعنونة بالمحتوى، أنشأها واختبرها شخص آخر، ثم يشغّلها docker compose up -d مع إرفاق وحدات التخزين والشبكة الخاصة بها. السجل هو جوهر المنتج. تكمن معظم قيمة سير عمل Docker في أن آلاف المشاريع تنشر صورة عاملة، وهذا ما يجعل تشغيل Docker على VPS مهمة قصيرة بدلاً من أن يكون مشروعاً.
لا يوفّر FreeBSD سجلاً عاماً افتراضياً لصور jail. تنشئ userland فارغاً وتثبّت البرمجيات داخله، بالطريقة نفسها التي تُعدّ بها خادماً أساسياً. يتطلب ذلك كتابة أوامر أكثر. لكنه أكثر شفافية أيضاً، لأن ما يعمل داخل jail هو ما وضعه pkg هناك، من مجموعة الحزم نفسها التي يستخدمها المضيف.
تجعل الأدوات العملية أقصر. BastilleBSD هو مدير jail الشائع، وهو حزمة:
sudo pkg install bastille
sudo sysrc bastille_enable=YES
sudo bastille setup
sudo bastille bootstrap 15.1-RELEASEيُعدّ bastille setup إعدادات الشبكة والتخزين وجدار الحماية نيابةً عنك. ينزّل bastille bootstrap إصداراً مرة واحدة، ثم تعيد كل jail تنشئها بعد ذلك استخدامه. FreeBSD 15.1 هو إصدار الإنتاج الحالي، وقد صدر في June 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 webيوفّر bastille console web لك shell لتسجيل الدخول داخل jail، بينما يعرض bastille list ما هو موجود على المضيف. لإعادة تنفيذ عملية بناء، تحتوي قوالب Bastille على الخطوات في ملف وتطبّقها على jail، وهذا أقرب ما يوفّره هذا العالم إلى Dockerfile. يُعاد تطبيق القالب على كل jail. لا يصل أي شيء مبنياً مسبقاً.
إذن، الخلاصة الصريحة قصيرة. يسلّمك Docker عمليات بناء نفّذها الآخرون. أما jail فتسلّمك عمليات التثبيت التي تنفّذها بنفسك. إذا كانت البرمجية الموجودة في قائمتك لا تُوزَّع إلا على شكل صورة حاوية، فذلك يحسم السؤال قبل أن يكون لأي عامل آخر تأثير.
الحالة والترقيات: الجزء الذي يغيّره ZFS
يفصل Docker الحالة عمداً. نظام ملفات الحاوية قابل للتخلص منه، بينما تُخزَّن بياناتك في وحدة تخزين مسمّاة أو في bind mount، وتكون الترقية docker compose pull متبوعةً بـ docker compose up -d. تُستبدل الحاوية، وتُفقد كل البيانات التي لم تضعها في وحدة تخزين. يكون ذلك ميزة عندما تلتزم بالقاعدة، وحادثة فقدان بيانات عندما تنساها. لذلك يحمل الاختيار بين bind mounts ووحدات التخزين المسمّاة أهمية كبيرة في مكدس Compose.
لا يفصل jail الحالة، وZFS هو سبب نجاح ذلك. يمثّل jail مجموعة بيانات واحدة:
sudo zfs snapshot zroot/jails/containers/web@pre-upgrade
sudo pkg -j web upgrade
sudo zfs rollback zroot/jails/containers/web@pre-upgradeتحقق من اسم مجموعة البيانات الفعلي باستخدام zfs list قبل تشغيل هذا الأمر؛ فالمسار أعلاه هو التخطيط الذي يستخدمه الدليل. تستغرق اللقطة نحو ثانية واحدة، ولا تشغل مساحة تُذكر حتى تتغير محتويات jail. إذا عطّلت الترقية الخدمة، تعيد عملية rollback بيئة المستخدم كاملة إلى حالتها السابقة، بما في ذلك قاعدة بيانات الحزم وملفات الإعداد التي عدّلتها يدوياً في الساعة 2 صباحاً. لا يوفّر Docker بديلاً مضمّناً مماثلاً، لأن نموذجه يفترض أنك لم تكن تريد ذلك أصلاً.
يمثّل zfs clone النصف الآخر. إن نسخة clone من لقطة هي jail جديدة قابلة للكتابة، وتشترك مع الأصل في الكتل التي لم تتغير. لذلك لا تكلّف نسخة staging من jail بحجم 3 GB شيئاً يُذكر على القرص حتى تبدأ بتغييرها. بهذه الطريقة ينشئ مسؤول FreeBSD jail «مطابقاً للإنتاج» للتدرّب على الترقية.
تكون ترقية النظام الأساسي منفصلة عن الحزم. بالنسبة إلى jail التي تحتوي على نسختها الخاصة من بيئة المستخدم:
sudo freebsd-update -b /usr/local/jails/containers/web fetch installتتجنب thin jails تكرار هذا العمل. فهي توصل قاعدة أساسية مشتركة للقراءة فقط عبر nullfs، وتمنح كل jail طبقة صغيرة قابلة للكتابة خاصة بها. لذلك تصحح القاعدة مرة واحدة، وترى كل jail النتيجة. ينشئ Bastille thin jails افتراضياً.
الشبكات: المنافذ المنشورة مقابل قرار العنونة
يحدّد Docker إعداد الشبكة نيابةً عنك، ويطلب منك نشر الاستثناءات. تتصل الحاويات بجسر، وتتواصل مع بعضها باستخدام اسم الخدمة على شبكة يعرّفها المستخدم، ويكشف -p 8080:80 إحداها للمضيف. يكتب Docker قواعد ترشيح الحزم الخاصة به لتحقيق ذلك، وهذا هو أيضاً سبب تجاوز منفذ حاوية منشور لجدار ufw مباشرة.
تفرض عليك jail اختيار النموذج مسبقاً، وهناك نموذجان.
عنوان IP مشترك. يضيف ip4.addr = "10.0.0.10" ذلك العنوان إلى واجهة موجودة على المضيف، ويقيّد jail باستخدامه. لا تملك jail مكدس شبكة خاصاً بها، لذلك لا يمكنها تشغيل جدار ناري خاص بها. كما لا يمكنها فعلياً الارتباط بكل العناوين: فعندما يطلب مقبس داخل jail 0.0.0.0، تعيد النواة كتابة الطلب باستخدام عنوان jail نفسها. لا يمكن لـjailين الاستماع معاً على المنفذ 80 للعنوان نفسه، لذلك تمنح كل واحدة منهما عنواناً، أو تضع reverse proxy أمامهما.
VNET. أضف vnet; إلى jail، فتحصل على مكدس شبكة كامل: واجهاتها الخاصة، وجدول التوجيه الخاص بها، وقواعد الجدار الناري الخاصة بها. صِلها بالمضيف باستخدام epair، وهو كابل افتراضي له طرف في كل جانب، وضع طرف المضيف على جسر. هذا أقرب نموذج إلى ما يوفّره Docker، وهو الوضع الذي تستند إليه أنواع jail -V و-B في Bastille.
يُنفَّذ تمرير منفذ من المضيف إلى jail باستخدام قاعدة إعادة توجيه 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، وهو نظام فرعي يجب تفعيله. يكون احتساب الموارد معطلاً افتراضياً لأنه يضيف تكلفة صغيرة إلى كل عملية تخصيص. أضف المتغير القابل للضبط إلى /boot/loader.conf ثم أعد التشغيل:
kern.racct.enable=1بعد ذلك، عيّن قاعدة وراقبها:
sudo rctl -a jail:web:vmemoryuse:deny=1g
rctl -hu jail:webيعرض rctl -hu jail:web الاستخدام الحالي للـjail بوحدات مقروءة، لكي تعرف مدى اقترابه من الحد قبل حدوث أي عطل. يجعل الإجراء deny عملية التخصيص التي تتجاوز الحد تفشل داخل الـjail، وبذلك ترى خطأ التخصيص الصادر عن التطبيق نفسه بدلاً من رسالة قتل على المضيف.
تختفي القواعد التي تضيفها باستخدام rctl -a عند إعادة التشغيل التالية. تعيد خدمة rctl تحميلها من /etc/rctl.conf، لذلك اكتب القاعدة في ذلك الملف وفعّل الخدمة:
sudo sysrc rctl_enable=YESهذا هو الجانب الذي يكون فيه Docker أكثر سهولة بوضوح. تُراجَع الحدود في ملف Compose مع الخدمة التي تفرضها. أما قاعدة rctl فهي سطر في ملف منفصل، ويشير هذا السطر إلى jail معرّف في مكان آخر.
عندما تكون الإجابة جهازاً افتراضياً: bhyve
تشترك jail في نواة المضيف، لذلك تبقى بعض الأمور خارج نطاقها نهائياً. لا يمكنها تشغيل إصدار مختلف من النواة، أو تحميل وحدة نواة، أو تشغيل ملفات Linux الثنائية بالطريقة التي تعمل بها حاوية Linux. يوفّر FreeBSD طبقة توافق مع Linux تُسمّى linuxulator، لكنها تنفّذ مجموعة فرعية من استدعاءات نظام Linux، وليست حلاً عاماً لصور Linux العشوائية.
bhyve هو برنامج مراقبة الأجهزة الافتراضية في FreeBSD، وهو الأداة المناسبة عندما تحتاج إلى حد فاصل حقيقي بين الأجهزة: نظام تشغيل مختلف، أو نواة مختلفة، أو مستأجر لا تريد مشاركته النواة. ستدفع مقابل ذلك ذاكرة محجوزة بدلاً من الذاكرة المشتركة، ونواة ثانية يجب تصحيحها. هذا هو القرار نفسه الذي تتخذه في Linux بين الحاويات والأجهزة الافتراضية الكاملة، وهو ما يحدد ما إذا كنت تحتاج إلى VPS يدعم الافتراضية المتداخلة تحته.
النظام البيئي، وهو السبب العملي لاستخدام معظم الفرق لـDocker
يتعلق كل ما سبق بالنموذج. لكن ما يحسم الاختيار لدى معظم الفرق هو حجم النظام المحيط بكل نموذج.
يوفّر Docker كلاً من Docker Hub وGHCR، وdocker compose، وKubernetes عندما لا يعود خادم واحد كافياً، وعوامل CI التي يتوفر فيها دعم الحاويات مسبقاً، وتعليمات بدء سريع بأمر واحد في README لمعظم المشاريع تقريباً. توفّر Jails شجرة منافذ FreeBSD، وهي كبيرة وتخضع لصيانة دقيقة، إضافة إلى مجموعة أصغر بكثير من حزم التطبيقات الجاهزة للتشغيل. عندما ينشر مشروع ما صورة حاوية ولا ينشر أي شيء آخر، يكون المسار في FreeBSD هو قراءة وثائقه وتجميع الأجزاء بنفسك.
تثبت Jails جدواها في الجانب الآخر من هذه المفاضلة. اخترها عندما تستخدم ZFS بالفعل وتقدّر إنشاء لقطة كاملة لخدمة واستعادتها، أو عندما تكون خدماتك أصلية في FreeBSD، أو عندما تريد userland كاملاً لكل مستأجر بدلاً من عملية واحدة، أو عندما تريد أن تتم صيانة النواة ومرشح الحزم ونظام الملفات والوثائق معاً باعتبارها نظاماً واحداً. هذه النقطة الأخيرة هي المقصودة عند وصف FreeBSD بأنه متماسك، وتحظى بمزيد من التفصيل في المقارنة الأوسع بين Linux وFreeBSD كمنصتين للخوادم وفي ما الذي غيّره FreeBSD 15 لاستخدام الخوادم.
حكم ختامي واحد: إذا كان فريقك يعرف Docker بالفعل، فتكلفة الانتقال حقيقية، ويجب أن تكون الفائدة محددة. لا تنتقل بسبب جودة العزل؛ فالنموذجان متقاربان بما يكفي لتكون إعداداتك أهم. انتقل لأنك تريد الاستعادة المدعومة بـZFS للخدمات كاملة، أو لأنك تستخدم FreeBSD بالفعل.
FAQ
هل يمكنني تشغيل صور Docker على FreeBSD؟
ليس صور Linux، وليس بطريقة مدعومة. يدعم FreeBSD حاويات OCI: يثبّت sudo pkg install -y podman-suite Podman، الذي يشغّل الحاويات عبر ocijail، وهو runtime ينشئ jails فعلية في الطبقة الأساسية. يحتاج إلى تركيب fdescfs على /dev/fd لمراقب الحاويات، وإلى pf لـ NAT الخاص بالحاويات (ترجمة عناوين الشبكة). تعمل صور OCI الأصلية لـFreeBSD بأفضل شكل. تتطلب صور Linux أيضاً طبقة توافق Linux، واعتباراً من August 2026 ما يزال منفذ Podman في FreeBSD موصوفاً بأنه تجريبي. إذا كان النشر لديك يتكون من مجموعة من صور Linux، فشغّله على Linux.
هل jails في FreeBSD أكثر أماناً من حاويات Docker؟
يشترك كلاهما في نواة المضيف نفسها، لذلك يمثل خطأ في النواة خطراً على كليهما، ولا يُعد أي منهما الحد الفاصل المناسب لتشغيل تعليمات برمجية غير موثوقة فعلياً. يكمن الاختلاف في نقطة البداية. تبدأ jail برفض مجموعة واسعة من العمليات، ثم تعيد تمكينها معلماً واحداً في كل مرة. تبدأ حاوية Docker بصلاحية root داخل مجموعة من namespaces مع إسقاط بعض capabilities، ويكون تطبيق إجراءات تقوية إضافية اختيارياً. عملياً، يحدد الإعداد الأمان أكثر من النموذج: jail تعمل مع تمكين allow.mount وallow.raw_sockets ليست أكثر أماناً من حاوية أُعدت بعناية.
كيف أنشئ نسخة احتياطية من jail؟
أنشئ snapshot لـdataset ثم انقله. sudo zfs snapshot zroot/jails/containers/web@backup، ثم zfs send ذلك snapshot إلى pool آخر أو إلى ملف تنقله خارج الخادم. بما أن jail تحتفظ بكامل userland الخاص بها في dataset واحد، يلتقط snapshot الحزم المثبتة والبيانات في نقطة زمنية متسقة، إلى جانب كل ملف إعداد عدّلته يدوياً. وهذا عكس أسلوب Docker، حيث تنشئ نسخة احتياطية من named volumes وملف Compose، ثم تعيد بناء بقية المكونات من الصورة.
هل أحتاج إلى BastilleBSD، أم يكفي النظام الأساسي؟
يكفي النظام الأساسي، وهو المكان الأفضل للبدء. تغطي jail.conf وjls وjexec وservice jail start النموذج بأكمله، وبعد معرفتها يمكنك قراءة إعدادات أي مضيف FreeBSD من دون تعلم أدوات ذلك المضيف أولاً. Bastille طبقة تسهّل الاستخدام فوقه: تجهّز الإصدارات، وتنشئ jails خفيفة، وتطبّق القوالب، وتكتب قواعد إعادة التوجيه في pf نيابةً عنك. تعلّم الأوامر الأساسية أولاً، ثم أضف Bastille عندما يجعل عدد jails كتابة الأوامر أمراً مرهقاً.