FreeBSD jails לעומת Docker: מה ההבדל בניהול מכולות?
מה ההבדל בין 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. המנגנון מבוסס על קריאת ליבה אחת. jail(8) לוקח עץ ספריות ומפעיל בתוכו תהליכים עם מזהה Jail צמוד, והליבה מסרבת לבצע קבוצה קבועה של פעולות עבור כל תהליך הנושא מזהה זה. תהליך בתוך Jail אינו יכול לראות תהליכים מחוץ ל-Jail שלו, אינו יכול לבצע mount או unmount למערכות קבצים, אינו יכול לטעון מודולי ליבה, ואינו יכול לבצע 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 מריצה את סקריפט האתחול הרגיל של FreeBSD בתוכו, כך שה-Jail מעלה כל שירות שמוגדר כפעיל ב-/etc/rc.conf שלו. לקונטיינר Docker אין שלב מקביל, כיוון שהוא מריץ את תהליך ה-entrypoint של האימג' ועוצר כאשר תהליך זה מסתיים.
כיצד מקבלים תוכנה: תמונות ורישומים לעומת סביבת משתמש שאתם ממלאים
זהו ההבדל המורגש כבר ביום הראשון.
ב-Docker אתם מציינים שם של תוכנה ומקבלים אותה. הפקודה docker pull nginx מושכת תמונה מבוססת שכבות וממוענת-תוכן, שמישהו אחר בנה ובדק, והפקודה docker compose up -d מפעילה אותה עם ה-volumes והרשת המחוברים אליה. ה-registry הוא המוצר. רוב הערך בשימוש ב-Docker נובע מכך שאלפי פרויקטים מפרסמים תמונה עובדת, מה שהופך את הרצת Docker על גבי VPS למשימה קצרה במקום לפרויקט מורכב.
ב-FreeBSD אין registry ציבורי מובנה של תמונות 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 מגדירה עבורכם את הרשת, האחסון וה-firewall. הפקודה bastille bootstrap מורידה גרסה פעם אחת, וכל jail שתצרו לאחר מכן יעשה בה שימוש חוזר. FreeBSD 15.1 היא גרסת ה-production הנוכחית, שיצאה ביוני 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 מציגה מה קיים על המארח. כדי לחזור על תהליך בנייה, תבניות (templates) של Bastille שומרות את השלבים בקובץ ומחילות אותם על ה-jail, וזהו הדבר הקרוב ביותר בעולם הזה ל-Dockerfile. תבנית מופעלת מחדש על כל jail. דבר אינו מגיע מוכן מראש.
לכן, הסיכום הכנה הוא קצר. Docker מגיש לכם בנייה של אחרים. Jails מגישים לכם התקנות שלכם. אם התוכנה ברשימה שלכם מופצת רק כתמונת container, זה מכריע את השאלה לפני שכל שיקול אחר נלקח בחשבון.
מצב ושדרוגים: החלק של שינויי 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 למצבו הקודם, כולל מסד הנתונים של החבילות וקובצי התצורה שערכתם ידנית בשעה 2 לפנות בוקר. ל-Docker אין שום מנגנון מובנה מקביל, כיוון שהמודל שלו מניח שמעולם לא היה לכם צורך בכך.
zfs clone הוא החצי השני של המשוואה. Clone של snapshot הוא jail חדש וניתן לכתיבה, החולק בלוקים שלא השתנו עם ה-parent שלו; לכן, עותק staging של jail בנפח 3 GB כמעט אינו עולה דבר בנפח האחסון עד שמתחילים לבצע בו שינויים. כך מנהל מערכת FreeBSD בונה jail שזהה ל-production כדי לתרגל שדרוג.
שדרוג מערכת הבסיס נפרד משדרוג חבילות. עבור jail שמחזיק עותק משלו של ה-userland:
sudo freebsd-update -b /usr/local/jails/containers/web fetch installThin jails מונעים כפילות של עבודה זו. הם מעגנים (mount) בסיס משותף לקריאה בלבד באמצעות nullfs ומעניקים לכל jail שכבה קטנה וניתנת לכתיבה משלו; כך מעדכנים את הבסיס פעם אחת וכל ה-jails רואים את התוצאה. Bastille יוצר thin jails כברירת מחדל.
רשת: פורטים מפורסמים לעומת החלטת מיעון
Docker קובע עבורכם את הגדרות הרשת ומבקש שתפרסמו את החריגים. מכולות ממוקמות על גבי bridge, הן מגיעות זו לזו לפי שם השירות ברשת שהוגדרה על ידי המשתמש, ו--p 8080:80 חושף אחת מהן למארח. Docker כותב חוקי סינון חבילות משלו כדי לאפשר זאת, וזו גם הדרך שבה פורט של מכולה שפורסם עוקף ישירות את ufw.
ב-jail עליכם לבחור את המודל מראש, וישנם שניים כאלו.
Shared IP. ה-ip4.addr = "10.0.0.10" מוסיף את הכתובת הזו לממשק מארח קיים ומגביל את ה-jail אליה. ל-jail אין מחסנית רשת משלו, לכן הוא אינו יכול להריץ firewall משלו. הוא גם לא יכול באמת לבצע bind לכל כתובת: socket בתוך jail שמבקש את 0.0.0.0 נכתב מחדש על ידי ה-kernel לכתובת של ה-jail עצמו. שני jails לא יכולים להאזין שניהם לפורט 80 באותה כתובת, לכן אתם מעניקים לכל אחד כתובת, או מציבים reverse proxy בחזית.
VNET. הוסיפו vnet; ל-jail והוא יקבל מחסנית רשת מלאה: ממשקים משלו, טבלת ניתוב משלו וחוקי firewall משלו. אתם מחברים אותו למארח באמצעות epair, כבל וירטואלי עם קצה אחד בכל צד, ומציבים את קצה המארח על גבי bridge. זהו המודל הקרוב ביותר למה ש-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 נעלמים באתחול הבא. השירות rctl ב-FreeBSD טוען אותם מחדש מתוך /etc/rctl.conf, לכן כתבו את החוק לקובץ זה והפעילו את השירות:
sudo sysrc rctl_enable=YESזהו הציר שבו Docker נוח יותר באופן מובהק. מגבלה בקובץ Compose נסקרת יחד עם השירות שהיא מגבילה. חוק rctl הוא שורה בקובץ נפרד המציין jail שמוגדר במקום אחר.
כאשר התשובה היא מכונה וירטואלית: bhyve
jail חולק את הליבה (kernel) של המארח, ולכן כמה דברים נותרים מחוץ להישג יד באופן קבוע. הוא אינו יכול להריץ גרסת ליבה שונה, הוא אינו יכול לטעון מודול ליבה, והוא אינו יכול להריץ קובצי binary של Linux כפי שעושה container ב-Linux. ל-FreeBSD יש שכבת תאימות ל-Linux, ה-linuxulator, אך היא מיישמת רק תת-קבוצה של קריאות מערכת (system calls) של Linux ואינה מהווה פתרון כללי עבור image של Linux שרירותי.
bhyve הוא ה-hypervisor של FreeBSD, והוא הכלי הנכון כאשר נדרש גבול של מכונה אמיתית: מערכת הפעלה שונה, ליבה שונה, או דייר שאינכם מעוניינים לחלוק עמו ליבה. המחיר הוא זיכרון שמוקצה בבלעדיות במקום להשתתף במשאבים, וליבה נוספת שיש לעדכן. זוהי אותה החלטה שמתקבלת ב-Linux בין containers לבין מכונות וירטואליות מלאות, וזו ההחלטה שקובעת האם אתם זקוקים ל-VPS התומך ב-nested virtualization ברמת התשתית.
האקו-סיסטם, הסיבה הכנה לכך שרוב הצוותים משתמשים ב-Docker
כל מה שנכתב לעיל עוסק במודל. מה שמכריע את הבחירה עבור רוב הצוותים הוא היקף העולם שסובב כל פתרון.
Docker מביא איתו את Docker Hub ו-GHCR, docker compose, את Kubernetes כאשר שרת יחיד מפסיק להספיק, את רצי ה-CI עם תמיכה מובנית במכולות, ואת האפשרות להפעלה מהירה בפקודה אחת כמעט בכל קובץ 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 יהיה מותקן (mounted) ב-/dev/fd עבור מנטר המכולות, ו-pf עבור NAT (תרגום כתובות רשת) של המכולות. אימג'ים של OCI המותאמים ל-FreeBSD עובדים בצורה הטובה ביותר. אימג'ים של Linux דורשים בנוסף את שכבת התאימות ל-Linux, ונכון לאוגוסט 2026, ה-port של Podman ב-FreeBSD עדיין מוגדר כניסיוני. אם הפריסה שלכם מבוססת על ערימת אימג'ים של Linux, הריצו אותה על Linux. במשפחת RHEL המשמעות היא התקנת Docker על Rocky Linux או AlmaLinux, שם Podman מופיע כחבילה שמחזיקה כבר בפקודה docker עוד לפני שהתקנתם דבר מה.
האם FreeBSD jails מאובטחים יותר ממכולות Docker?
שניהם חולקים kernel מארח אחד, לכן באג ב-kernel מהווה סיכון לשניהם, ואף אחד מהם אינו הגבול שהייתם בוחרים עבור קוד שאינו מהימן באמת. ההבדל טמון בנקודת המוצא. 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 ובונים מחדש את השאר מתוך ה-image.
האם אני זקוק ל-BastilleBSD, או שמערכת הבסיס מספיקה?
מערכת הבסיס מספיקה, והיא המקום הטוב יותר להתחיל בו. הפקודות jail.conf, jls, jexec ו-service jail start מכסות את כל המודל, וברגע שתכירו אותן תוכלו לקרוא כל שרת FreeBSD מבלי ללמוד תחילה את כלי הניהול הספציפיים שלו. Bastille היא שכבת נוחות מעל: היא מבצעת bootstrap לגרסאות, יוצרת jails רזים, מחילה תבניות וכותבת עבורכם חוקי ניתוב ב-pf. למדו תחילה את פקודות הבסיס, ולאחר מכן הוסיפו את Bastille כאשר מספר ה-jails הופך את ההקלדה למייגעת.