FreeBSD jails أم حاويات Docker: ما الفرق؟
تعرّف إلى الفرق العملي بين FreeBSD jail وحاوية Docker: userland كاملة مقابل image متعددة الطبقات، مع مقارنة البرامج والحالة والشبكات والحدود.
FreeBSD jails مقابل حاويات Docker، في فقرة واحدة
تحل FreeBSD jails وحاويات Docker المشكلة نفسها بطريقتين مختلفتين. يشغّل كلاهما userlands معزولة على kernel مشترك واحد، ولذلك لا يُعد أيٌّ منهما آلة افتراضية. يكمن الاختلاف في المحتوى الذي يعمل داخل كل منهما. تشغّل حاوية Docker عملية واحدة من image ذات طبقات سحبتها من registry. أما jail فتشغّل FreeBSD userland كاملة: ملف /etc الخاص بها، وrc startup scripts الخاصة بها، وقاعدة بيانات pkg الخاصة بها، وأي عدد تريده من العمليات. وينتج كل اختلاف آخر تقريباً في هذه الصفحة عن هذا الاختلاف الأساسي.
لا توفّر SSD Nodes صور FreeBSD. لا يمكنك استئجار خادم FreeBSD على هذه المنصة، وليس أي مما يلي دليل تثبيت لجهاز يمكنك شراؤه هنا. هذه مقارنة بين نموذجي عزل، كُتبت لتتمكن من تحديد النموذج الذي يحتاجه workload فعلياً، ولقراءة إعداد فريق FreeBSD دون تخمين.
ماهيّة الـjail فعلياً
ظهرت الـjails في FreeBSD 4.0 في مارس 2000، ولذلك فهي أقدم من cgroups بنحو عقد من الزمن تقريباً، وأقدم من Docker. تعتمد الآلية على استدعاء واحد للنواة. يأخذ jail(8) شجرة أدلة ويبدأ العمليات داخلها مع إرفاق معرّف jail بها، ثم ترفض النواة مجموعة محددة من العمليات لأي عملية تحمل ذلك المعرّف. لا تستطيع العملية داخل الـjail رؤية العمليات الموجودة خارجها، ولا تركيب أنظمة الملفات أو إلغاء تركيبها، ولا تحميل وحدات النواة، ولا الربط بعناوين الشبكة التي لم تُمنح للـjail. لا يوجد نوع namespace منفصل يجب تعلّمه، ولا تفعيل اختياري لكل ميزة. تُطبَّق القيود كوحدة واحدة، وتُعدَّل عبر المعلمات الموجودة في إعدادات الـjail.
على المضيف، يسرد jls الـjails قيد التشغيل، بينما يفتح لك 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
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 بيانات الحالة عن قصد. نظام ملفات الحاوية قابل للتخلص منه، بينما توجد بياناتك في volume مُسمّى أو bind mount، وتكون الترقية عبارة عن docker compose pull يتبعها docker compose up -d. تُستبدل الحاوية، ويضيع كل ما لم تضعه في volume. يُعد ذلك ميزة عندما تلتزم بالقاعدة، وحادثة فقدان بيانات عندما تنساها. لذلك يحمل الاختيار بين bind mounts وvolumes المُسمّاة أهمية كبيرة في مكدس Compose.
لا يفصل 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 قبل تشغيل هذا الأمر؛ فالمسار أعلاه هو التخطيط المستخدم في الدليل. تستغرق اللقطة نحو ثانية واحدة، ولا تستهلك مساحة تُذكر إلى أن تتغير محتويات jail. إذا عطّلت الترقية الخدمة، تعيد عملية rollback كامل userland إلى حالته السابقة، بما في ذلك قاعدة بيانات الحزم وملفات الإعداد التي عدّلتها يدوياً في الساعة 2 صباحاً. لا يملك Docker بديلاً مضمّناً لذلك، لأن نموذجه يفترض أنك لم تكن تريد هذه الإمكانية أصلاً.
يمثل zfs clone النصف الآخر. استنساخ snapshot هو jail جديد قابل للكتابة، ويشارك الكتل التي لم تتغير مع الأصل. لذلك لا تكلف نسخة staging من jail حجمه 3 GB مساحة تُذكر على القرص إلى أن تبدأ بتغييرها. هكذا ينشئ مسؤول FreeBSD jail مطابقاً للإنتاج للتدرّب على الترقية.
تُجرى ترقية النظام الأساسي بشكل منفصل عن الحزم. بالنسبة إلى jail الذي يحتوي على نسخته الخاصة من userland:
sudo freebsd-update -b /usr/local/jails/containers/web fetch installتتجنب thin jails تكرار هذا العمل. فهي تثبّت base مشتركاً للقراءة فقط عبر nullfs، وتمنح كل jail طبقة صغيرة خاصة به قابلة للكتابة. لذلك تصحح base مرة واحدة، وترى جميع jail النتيجة. ينشئ Bastille thin jails افتراضياً.
الشبكات: المنافذ المنشورة مقابل قرار العنونة
يتولى Docker اختيار نموذج الشبكات، ويطلب منك نشر الاستثناءات. توضع الحاويات على bridge، ويمكنها الوصول إلى بعضها باستخدام اسم الخدمة على شبكة يعرّفها المستخدم، بينما يعرّض -p 8080:80 إحداها للمضيف. ينشئ Docker قواعد تصفية حزم خاصة به لتحقيق ذلك، وهذا هو أيضاً سبب تجاوز منفذ حاوية منشور لجدار ufw مباشرة.
يفرض عليك jail اختيار النموذج مسبقاً، وهناك نموذجان.
Shared IP. يضيف ip4.addr = "10.0.0.10" ذلك العنوان إلى واجهة موجودة على المضيف، ويقيّد jail باستخدامه. لا يملك jail حزمة شبكات خاصة به، لذلك لا يمكنه تشغيل جدار ناري خاص به. ولا يمكنه فعلياً الارتباط بكل العناوين: إذ تعيد النواة كتابة مقبس داخل jail يطلب 0.0.0.0 ليستخدم عنوان jail نفسه. لا يمكن لـjailين الاستماع معاً على المنفذ 80 للعنوان نفسه، لذلك تمنح كل واحد منهما عنواناً، أو تضع reverse proxy أمامهما.
VNET. أضف vnet; إلى jail، فيحصل على حزمة شبكات كاملة: واجهاته الخاصة، وجدول التوجيه الخاص به، وقواعد الجدار الناري الخاصة به. صِله بالمضيف باستخدام epair، وهو كابل افتراضي له طرف على كل جانب، وضع طرف المضيف على bridge. هذا أقرب نموذج إلى ما يوفّره 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 عند إعادة التشغيل التالية. تعيد خدمة FreeBSD rctl تحميلها من /etc/rctl.conf، لذلك اكتب القاعدة في ذلك الملف ومكّن الخدمة:
sudo sysrc rctl_enable=YESهذا هو الجانب الذي يكون فيه Docker أكثر سهولة بوضوح. تُراجَع الحدود في ملف Compose مع الخدمة التي تقيّدها. أما قاعدة rctl فهي سطر في ملف منفصل يسمّي jail معرّفاً في مكان آخر.
عندما تكون الإجابة آلة افتراضية: bhyve
تشترك jail في نواة المضيف، لذلك تبقى بعض الأمور خارج نطاقها نهائياً. لا يمكنها تشغيل إصدار مختلف من النواة، أو تحميل وحدة نواة، أو تشغيل ملفات Linux الثنائية بالطريقة التي تعمل بها حاوية Linux. يوفّر FreeBSD طبقة توافق مع Linux تُسمّى linuxulator، لكنها تنفّذ مجموعة فرعية من استدعاءات نظام Linux، وليست حلاً عاماً لأي صور Linux عشوائية.
bhyve هو hypervisor الخاص بـFreeBSD، وهو الأداة المناسبة عندما تحتاج إلى حد فاصل حقيقي بين الآلات: نظام تشغيل مختلف، أو نواة مختلفة، أو مستأجر لا تريد أن تشاركه النواة. ستدفع تكلفة ذلك من الذاكرة المحجوزة بدلاً من الذاكرة المشتركة، ومن الحاجة إلى تصحيح نواة ثانية. هذا هو القرار نفسه الذي تتخذه على Linux بين الحاويات والآلات الافتراضية الكاملة، وهو القرار الذي يحدد ما إذا كنت تحتاج إلى VPS يدعم الافتراضية المتداخلة تحته.
المنظومة، وهي السبب العملي لاستخدام معظم الفرق لـDocker
يتناول ما سبق النموذج. لكن حجم المنظومة المحيطة بكل نموذج هو العامل الذي يحسم الاختيار لدى معظم الفرق.
يوفّر Docker كلاً من Docker Hub وGHCR وdocker compose وKubernetes عندما لا يعود خادم واحد كافياً، كما يوفّر مشغّلات CI تتضمن دعم الحاويات معدّاً مسبقاً، وتعليمات بدء سريعة بأمر واحد في ملف README لمعظم المشاريع تقريباً. توفّر Jails شجرة منافذ FreeBSD، وهي كبيرة وتحظى بصيانة دقيقة، إضافة إلى مجموعة أصغر بكثير من حزم التطبيقات الجاهزة للتشغيل. عندما ينشر مشروع صورة حاوية ولا ينشر أي شيء آخر، يكون المسار في FreeBSD هو قراءة وثائقه وتجميع المكوّنات بنفسك.
تثبت Jails جدواها في الجانب الآخر من هذه المفاضلة. استخدمها عندما تشغّل ZFS بالفعل وتقدّر إمكانية أخذ snapshot لخدمة كاملة وإجراء rollback لها، أو عندما تكون خدماتك أصلية في FreeBSD، أو عندما تريد userland كاملاً لكل مستأجر بدلاً من عملية واحدة، أو عندما تريد صيانة kernel وpacket filter وfilesystem والتوثيق معاً بوصفها نظاماً واحداً. وهذا هو المقصود عندما يصف الناس FreeBSD بأنه متماسك. يتوفر شرح أوسع لذلك في المقارنة الأوسع بين Linux وFreeBSD بوصفهما منصتين للخوادم وفي التغييرات التي أدخلها FreeBSD 15 على استخدام الخوادم.
حكم أخير. إذا كان فريقك يعرف Docker بالفعل، فتكلفة الانتقال حقيقية، ويجب أن تكون فائدته محددة. لا تنتقل بسبب جودة العزل؛ فالنموذجان متقاربان بما يكفي لجعل إعداداتك أهم من النموذج نفسه. انتقل لأنك تريد rollback لخدمات كاملة مدعومًا بـ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. في عائلة RHEL، يعني ذلك تثبيت Docker على Rocky Linux أو AlmaLinux، حيث يظهر Podman مرة ثانية باعتباره الحزمة التي تملك الأمر docker مسبقاً قبل أن تثبّت أي شيء.
هل FreeBSD jails أكثر أماناً من حاويات 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 كتابة الأوامر أمراً مرهقاً.