ההבדלים בין FreeBSD jails לבין Docker containers
מה ההבדל בין סביבת משתמש מלאה ב-FreeBSD jails לבין מכולות Docker מבוססות images? השוואה טכנית על ניהול תהליכים, רשת, אחסון ומגבלות משאבים בין שני המודלים לבידוד תוכנה.
השוואה בין FreeBSD jails לבין Docker containers, בפסקה אחת
FreeBSD jails ו־Docker containers פותרים את אותה הבעיה בשתי צורות שונות. שניהם מריצים סביבות משתמש מבודדות על גבי ליבה משותפת, ולכן אף אחד מהם אינו מכונה וירטואלית. ההבדל טמון במה שנמצא בתוכם. מכולת Docker מריצה תהליך יחיד מתוך image שכבות שנמשך מתוך registry. לעומת זאת, jail מריץ סביבת משתמש מלאה של FreeBSD: יש לו /etc משלו, סקריפטים של rc להפעלה, מסד נתונים של pkg משלו, ומספר תהליכים כרצונכם. כמעט כל הבדל אחר המופיע בדף זה נובע מההבדל היסודי הזה.
SSD Nodes אינה מציעה images של FreeBSD. לא ניתן לשכור שרת FreeBSD בפלטפורמה זו, ושום דבר ממה שכתוב להלן אינו מהווה מדריך התקנה למכונה שניתן לרכוש כאן. זוהי השוואה בין שני מודלים של בידוד, שנכתבה כדי שתוכלו להחליט איזה מהם מתאים לעומס העבודה שלכם, וכדי שתוכלו לקרוא תצורה של צוות FreeBSD מבלי לנחש.
מהו למעשה Jail
מנגנון ה-Jails הגיע ל-FreeBSD בגרסה 4.0 במרץ 2000, מה שהופך אותו לוותיק יותר מ-cgroups ובוגר בכעשור מ-Docker. המנגנון מבוסס על קריאת kernel אחת. jail(8) לוקח עץ ספריות ומפעיל בתוכו תהליכים עם מזהה Jail צמוד, וה-kernel מסרב לבצע קבוצה קבועה של פעולות עבור כל תהליך הנושא מזהה זה. תהליך בתוך Jail אינו יכול לראות תהליכים מחוץ ל-Jail שלו, אינו יכול לבצע mount או unmount למערכות קבצים, אינו יכול לטעון מודולים של ה-kernel, ואינו יכול לבצע bind לכתובות רשת שלא הוקצו ל-Jail. אין כאן סוגי namespace נפרדים ללמידה ואין בחירה פרטנית לפי תכונה: ההגבלות מגיעות כיחידה אחת, המותאמת באמצעות פרמטרים בקובץ התצורה של ה-Jail.
במערכת המארחת, jls מציג את ה-Jails הפעילים ו-jexec web sh מעביר אתכם ל-shell בתוך ה-Jail ששמו web.
בונים Jail על ידי הצבת סביבת משתמש (userland) של FreeBSD בתוך ספרייה. מערכת הבסיס מבצעת זאת עבורכם:
sudo bsdinstall jail /usr/local/jails/containers/webפקודה זו מושכת את ערכת הפצת הבסיס עבור הגרסה שלכם ומריצה את שלבי ה-post-install הרגילים, כך שתגדירו סיסמת 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, שם המארח (hostname) וכתובת ה-IP שלו. אם ה-Jail אינו מופיע, הריצו את sudo jail -c web ישירות. הוא מחיל את אותה תצורה בחזית (foreground) ומדפיס את הפרמטר שלא התקבל, במקום להשאיר את הכשל בתוך פלט השירות.
השורה שראוי לקרוא פעמיים היא exec.start = "/bin/sh /etc/rc". הפעלת Jail מריצה את סקריפט ה-boot הרגיל של FreeBSD בתוכו, כך שה-Jail מעלה כל שירות שמוגדר כפעיל ב-/etc/rc.conf שלו. למכולת Docker אין שלב מקביל, כיוון שהיא מריצה את תהליך ה-entrypoint של ה-image ועוצרת כאשר תהליך זה מסתיים.
כיצד מתקינים תוכנה: תמונות ומרשמים לעומת סביבת משתמש שאתם בונים
זהו ההבדל המורגש כבר ביום הראשון.
ב-Docker אתם מציינים את שם התוכנה ומקבלים אותה. הפקודה docker pull nginx מושכת תמונה מבוססת שכבות וממוענת-תוכן שמישהו אחר בנה ובדק, והפקודה docker compose up -d מפעילה אותה עם ה-volumes והרשת המחוברים אליה. ה-registry הוא המוצר. רוב הערך בשימוש ב-Docker טמון בכך שאלפי פרויקטים מפרסמים תמונה תקינה, מה שהופך את הרצת Docker על גבי VPS למשימה קצרה במקום לפרויקט מורכב.
ב-FreeBSD אין registry ציבורי מובנה של תמונות jail. אתם יוצרים סביבת משתמש ריקה ומתקינים לתוכה, באותו אופן שבו הייתם מגדירים שרת חשוף. זה דורש יותר הקלדה. זה גם שקוף יותר, מכיוון שמה שרץ בתוך ה-jail הוא בדיוק מה שהפקודה pkg הציבה שם, מתוך אותה קבוצת חבילות שבה משתמש ה-host.
כלי עבודה מקצרים את התהליך. BastilleBSD הוא מנהל ה-jail הנפוץ והוא זמין כחבילה:
sudo pkg install bastille
sudo sysrc bastille_enable=YES
sudo bastille setup
sudo bastille bootstrap 15.1-RELEASEהפקודה bastille setup מגדירה עבורכם את הרשת, האחסון וה-firewall. הפקודה bastille bootstrap מורידה release פעם אחת, וכל jail שתיצרו לאחר מכן יעשה בו שימוש חוזר. FreeBSD 15.1 הוא ה-release הנוכחי בסביבת production, שיצא ביוני 2026; החליפו אותו ב-release שבו אתם משתמשים.
יצירת 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 מציגה מה קיים על ה-host. כדי לחזור על תהליך בנייה, תבניות (templates) של Bastille שומרות את השלבים בקובץ ומחילות אותם על ה-jail, וזה הדבר הקרוב ביותר שיש לעולם הזה ל-Dockerfile. תבנית מופעלת מחדש על כל jail. דבר אינו מגיע מוכן מראש.
לכן הסיכום הכנה הוא קצר. Docker מגיש לכם בנייה של אחרים. Jails מגישים לכם התקנות שלכם. אם התוכנה ברשימה שלכם מופצת כ-container image בלבד, זה מכריע את השאלה עוד לפני ששאר השיקולים נלקחים בחשבון.
מצב ושדרוגים: החלק של שינויי ZFS
Docker מפריד את המצב (state) באופן מכוון. מערכת הקבצים של המכולה היא זמנית, הנתונים שלכם נמצאים בתוך named volume או ב-bind mount, ושדרוג מתבצע באמצעות docker compose pull ולאחריו docker compose up -d. המכולה מוחלפת, וכל מה שלא שמרתם בתוך volume אובד. זוהי תכונה מועילה כאשר פועלים לפי הכלל, אך היא מובילה לאובדן נתונים כאשר שוכחים זאת; זו הסיבה לכך ש-הבחירה בין bind mounts לבין named volumes נושאת משקל כה רב ב-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 לפני הרצת הפקודה; הנתיב לעיל הוא המבנה שבו משתמש ה-handbook. ה-snapshot לוקח כשנייה אחת וכמעט אינו תופס מקום עד שתוכן ה-jail משתנה. אם השדרוג משבש את השירות, ה-rollback מחזיר את כל ה-userland למצבו הקודם, כולל מסד הנתונים של החבילות וקובצי התצורה שערכתם ידנית בשתיים לפנות בוקר. ל-Docker אין מקבילה מובנית, כיוון שהמודל שלו מניח שמעולם לא רציתם אחת כזו.
zfs clone הוא החצי השני. Clone של snapshot הוא jail חדש וניתן לכתיבה, החולק בלוקים שלא השתנו עם ה-parent שלו, כך שעותק staging של jail בנפח 3 GB כמעט אינו עולה דבר בדיסק עד שמתחילים לשנות אותו. כך מנהל מערכת FreeBSD בונה jail "זהה לייצור" כדי לתרגל שדרוג.
שדרוג מערכת הבסיס נפרד מהחבילות. עבור jail שמחזיק עותק משלו של ה-userland:
sudo freebsd-update -b /usr/local/jails/containers/web fetch installThin jails מונעים חזרה על עבודה זו. הם מעגנים (mount) בסיס משותף אחד לקריאה בלבד באמצעות nullfs ומעניקים לכל jail שכבה קטנה וניתנת לכתיבה משלו, כך שאתם מעדכנים את הבסיס פעם אחת וכל ה-jail רואים את התוצאה. Bastille יוצר thin jails כברירת מחדל.
רשת: פורטים מפורסמים לעומת החלטת כתובות
Docker מחליט עבורכם על הגדרות הרשת ומבקש מכם לפרסם את החריגים. מכולות ממוקמות על גשר (bridge), הן מגיעות זו לזו לפי שם השירות ברשת שהוגדרה על ידי המשתמש, ו־-p 8080:80 חושף אחת מהן למארח (host). Docker כותב חוקי סינון חבילות משלו כדי לאפשר זאת, וזו גם הדרך שבה פורט של מכולה מפורסמת עוקף ישירות את ufw.
בשימוש ב-jail עליכם לבחור את המודל מראש, וישנם שניים כאלה.
כתובת IP משותפת. ip4.addr = "10.0.0.10" מוסיף את הכתובת הזו לממשק מארח קיים ומגביל את ה-jail אליה. ל-jail אין מחסנית רשת (network stack) משלו, לכן הוא אינו יכול להריץ firewall משלו. הוא גם לא יכול באמת לבצע bind לכל כתובת: socket בתוך ה-jail שמבקש את 0.0.0.0 נכתב מחדש על ידי ה-kernel לכתובת של ה-jail עצמו. שני jail לא יכולים להאזין שניהם לפורט 80 באותה כתובת, לכן עליכם להקצות לכל אחד כתובת, או להציב reverse proxy בחזית.
VNET. הוספת vnet; ל-jail מעניקה לו מחסנית רשת מלאה: ממשקים משלו, טבלת ניתוב משלו וחוקי firewall משלו. אתם מחברים אותו למארח באמצעות epair, כבל וירטואלי עם קצה אחד בכל צד, ומציבים את קצה המארח על גשר. זהו המודל הקרוב ביותר למה ש-Docker מספק, וזהו המצב שמאחורי סוגי ה-jail של Bastille מסוג -V ו--B.
העברת פורט מארח לתוך jail היא חוק ניתוב מחדש מסוג pf. Bastille עוטף זאת:
sudo bastille rdr web tcp 80 80אין EXPOSE ואין פרסום אוטומטי. שום דבר לא מגיע ל-jail אלא אם הכתובת שלו או חוק ניתוב מחדש מאפשרים זאת. זו התחלה איטית יותר ו-firewall שקט הרבה יותר.
מגבלות משאבים: cgroups לעומת rctl
Docker מגביל מכולה באמצעות cgroups, והמגבלות מוגדרות במקום שבו המכולה מוגדרת: --memory=1g --cpus=1.5 בשורת הפקודה, או המפתחות התואמים בקובץ Compose. אם אתם כבר מנהלים את ה-stack שלכם בתוך קובץ 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:webהפקודה rctl -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 של המארח, לכן דברים מסוימים אינם נגישים עבורו באופן קבוע. הוא אינו יכול להריץ גרסת kernel שונה, אינו יכול לטעון מודול kernel, ואינו יכול להריץ קובצי binary של Linux כפי שעושה container של Linux. ב-FreeBSD קיימת שכבת תאימות ל-Linux, המכונה linuxulator, אך היא מיישמת רק תת-קבוצה של קריאות מערכת (system calls) של Linux ואינה מהווה פתרון כללי עבור תמונות (images) שרירותיות של Linux.
bhyve הוא ה-hypervisor של FreeBSD, והוא הכלי הנכון כאשר נדרש גבול הפרדה של מכונה אמיתית: מערכת הפעלה שונה, kernel שונה, או דייר שאינכם מעוניינים לחלוק איתו את ה-kernel. המחיר הוא זיכרון שמוקצה בבלעדיות במקום להשתתף במשאבים משותפים, וצורך בתחזוקת kernel נוסף. זוהי אותה החלטה שמתקבלת ב-Linux בין שימוש ב-containers לבין מכונות וירטואליות מלאות, והיא זו שקובעת האם אתם זקוקים ל-VPS התומך ב-nested virtualization ברמת התשתית.
האקו-סיסטם, הסיבה הכנה לכך שרוב הצוותים משתמשים ב-Docker
כל מה שנכתב לעיל עוסק במודל. מה שמכריע את הבחירה עבור רוב הצוותים הוא גודל העולם שסובב כל אחד מהם.
Docker מביא איתו את Docker Hub ו-GHCR, docker compose, את Kubernetes כאשר שרת יחיד מפסיק להספיק, CI runners עם תמיכה מובנית במכולות, ופקודת quickstart אחת כמעט בכל קובץ README של פרויקט. Jails מביאים את עץ ה-ports של FreeBSD, שהוא גדול ומתוחזק בקפידה, בתוספת קבוצה קטנה בהרבה של חבילות יישומים מוכנות להרצה. כאשר פרויקט מפרסם image של מכולה ותו לא, הדרך ב-FreeBSD היא לקרוא את התיעוד ולהרכיב את החלקים בעצמך.
Jails מצדיקים את מקומם בצד השני של הפשרה הזו. תרצה להשתמש בהם כאשר אתה כבר מריץ ZFS ומעריך יכולות snapshot ו-rollback של שירות שלם, כאשר השירותים שלך הם native ל-FreeBSD, כאשר אתה רוצה userland מלא לכל דייר במקום תהליך יחיד, או כאשר אתה רוצה שה-kernel, ה-packet filter, מערכת הקבצים והתיעוד יתוחזקו יחד כמערכת אחת. הנקודה האחרונה הזו היא מה שאנשים מתכוונים אליו כשהם מכנים את 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, ונכון לאוגוסט 2026, ה-port של Podman ב-FreeBSD עדיין מוגדר כניסיוני. אם הפריסה שלכם מבוססת על ערימת אימג'ים של Linux, הריצו אותה על Linux.
האם 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, שבהם מגבים את ה-volumes בעלי השמות ואת קובץ ה-Compose, ובונים מחדש את השאר מתוך האימג'.
האם אני זקוק ל-BastilleBSD, או שמערכת הבסיס מספיקה?
מערכת הבסיס מספיקה, והיא המקום הטוב ביותר להתחיל בו. הפקודות jail.conf, jls, jexec ו-service jail start מכסות את כל המודל, וברגע שתכירו אותן תוכלו לקרוא כל שרת FreeBSD מבלי ללמוד תחילה את כלי הניהול הספציפיים שלו. Bastille היא שכבת נוחות מעל: היא מבצעת bootstrap לגרסאות, יוצרת jails רזים, מחילה תבניות וכותבת עבורכם חוקי ניתוב ב-pf. למדו תחילה את פקודות הבסיס, ורק לאחר מכן הוסיפו את Bastille כאשר מספר ה-jails הופך את ההקלדה למייגעת.