FreeBSD jails чи Docker: що краще для ізоляції
Порівняйте FreeBSD jails і Docker: повний userland проти layered images із registry, а також відмінності в програмному забезпеченні, стані, мережі та лімітах.
FreeBSD jails і Docker-контейнери в одному абзаці
FreeBSD jails і Docker-контейнери вирішують ту саму проблему, але мають різну структуру. Обидва запускають ізольовані userland на одному спільному ядрі, тому жоден із них не є віртуальною машиною. Відрізняється вміст. Docker-контейнер запускає один процес із шаруватого образу, отриманого з registry. Jail запускає повний userland FreeBSD: власний /etc, власні startup-скрипти rc, власну базу даних pkg і будь-яку кількість процесів. Майже всі інші відмінності на цій сторінці випливають із цього.
SSD Nodes не пропонує образи FreeBSD. На цій платформі не можна орендувати сервер FreeBSD, і наведений нижче матеріал не є інструкцією зі встановлення системи на машину, яку можна придбати тут. Це порівняння двох моделей ізоляції, підготовлене для того, щоб визначити, яка з них фактично потрібна певному робочому навантаженню, а також ознайомитися з налаштуванням команди FreeBSD без припущень.
Що таке jail
Jails з’явилися у FreeBSD 4.0 у березні 2000 року. Вони старші за cgroups і приблизно на десятиліття старші за Docker. Механізм складається з одного kernel call. jail(8) отримує дерево каталогів і запускає в ньому процеси з attached jail ID. Після цього kernel забороняє фіксований набір операцій будь-якому процесу з цим ID. Процес усередині jail не бачить процесів за межами свого jail, не може монтувати або демонтувати файлові системи, завантажувати kernel modules або прив’язуватися до network addresses, які не було надано jail. Окремого типу namespace для вивчення немає. Також немає окремого opt-in для кожної функції. Обмеження застосовуються як єдиний набір і налаштовуються параметрами в конфігурації jail.
На host команда jls виводить список запущених jails, а jexec web sh відкриває shell усередині jail із назвою web.
Jail створюють, розміщуючи FreeBSD userland у каталозі. Base system робить це за вас:
sudo bsdinstall jail /usr/local/jails/containers/webКоманда завантажує base distribution set для вашого release і виконує стандартні post-install кроки. Тому root password і timezone потрібно налаштувати так само, як на новому сервері. У результаті в каталозі буде інсталяція 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;
}Запустіть jail і перевірте його стан:
sudo service jail start web
jlsТепер jls має вивести web із JID, hostname та IP address. Якщо jail не відображається, безпосередньо виконайте sudo jail -c web. Команда застосовує ту саму конфігурацію у foreground і виводить параметр, який не вдалося прийняти, замість того щоб залишити помилку лише у service output.
Рядок, який варто перечитати двічі, — exec.start = "/bin/sh /etc/rc". Запуск jail виконує всередині нього звичайний boot script FreeBSD. Тому jail запускає кожен service, увімкнений у власному /etc/rc.conf. У Docker container немає еквівалентного кроку, оскільки він запускає entrypoint process image і зупиняється, коли цей process зупиняється.
Як ви отримуєте програмне забезпечення: images і registries проти userland, який ви заповнюєте
Цю різницю ви відчуєте вже в перший день.
З Docker ви вказуєте програмне забезпечення й отримуєте його. docker pull nginx завантажує багатошаровий image з адресацією за вмістом, який хтось інший зібрав і протестував, а docker compose up -d запускає його з підключеними volumes і network. Registry є основою цього підходу. Більша частина цінності робочого процесу Docker полягає в тому, що тисячі проєктів публікують готові робочі images. Саме тому запуск Docker на VPS займає мало часу, а не перетворюється на окремий проєкт.
FreeBSD не постачається з public registry images для jail за замовчуванням. Ви створюєте порожній userland і встановлюєте програмне забезпечення в нього так само, як налаштовували б bare server. Це потребує більше команд. Водночас цей підхід прозоріший, оскільки в jail працює саме те, що встановили ви pkg, з того самого набору пакетів, який використовує host.
Інструменти скорочують цей процес. BastilleBSD — поширений менеджер jail, і він доступний як package:
sudo pkg install bastille
sudo sysrc bastille_enable=YES
sudo bastille setup
sudo bastille bootstrap 15.1-RELEASEbastille setup налаштовує для вас network, storage і firewall. bastille bootstrap завантажує release один раз, а кожен наступний jail повторно використовує його. FreeBSD 15.1 — поточний production release, випущений у June 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 webbastille console web надає вам login shell усередині jail, а bastille list показує, що існує на host. Щоб повторювати збірку, Bastille templates зберігають кроки у файлі й застосовують їх до jail. Це найближчий аналог Dockerfile у цьому середовищі. Template повторно застосовується до кожного jail. Нічого не надходить попередньо зібраним.
Отже, підсумок простий. Docker надає збірки інших людей. Jails надають ваші власні установки. Якщо потрібне вам програмне забезпечення постачається лише як container image і в іншому вигляді недоступне, це одразу вирішує питання, ще до порівняння інших критеріїв.
Стан і оновлення: що змінює ZFS
Docker навмисно розділяє стан. Файлова система контейнера одноразова, а ваші дані зберігаються в іменованому томі або bind mount. Оновлення складається з docker compose pull і docker compose up -d. Контейнер замінюється, а все, що не було розміщено в томі, втрачається. Це перевага, якщо дотримуватися правила, і інцидент із втратою даних, якщо про нього забути. Саме тому вибір між bind mounts та іменованими томами має таке важливе значення в 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, який спільно використовує незмінені блоки з батьківським dataset. Тому staging-копія jail розміром 3 GB майже не займає додаткового дискового простору, доки ви не почнете її змінювати. Так адміністратор FreeBSD створює jail, ідентичний production, щоб перевірити оновлення.
Оновлення base system виконується окремо від пакетів. Для jail, який містить власну копію userland:
sudo freebsd-update -b /usr/local/jails/containers/web fetch installThin jail дає змогу не повторювати цю роботу. Він монтує спільну base system лише для читання через nullfs і надає кожному jail власний невеликий шар для запису. Тому base system достатньо оновити один раз, після чого зміни побачать усі jail. Bastille створює thin jail за замовчуванням.
Мережа: опубліковані порти та вибір моделі адресації
Docker самостійно визначає мережеву конфігурацію та пропонує опублікувати лише винятки. Контейнери підключаються до bridge, взаємодіють один з одним за іменами сервісів у user-defined network, а -p 8080:80 відкриває один із них для host. Docker додає власні правила фільтрації пакетів, щоб це працювало. Саме тому опублікований порт контейнера обходить ufw.
У jail модель потрібно вибрати заздалегідь. Є дві моделі.
Shared IP. ip4.addr = "10.0.0.10" додає цю адресу до наявного інтерфейсу host і обмежує jail цією адресою. У jail немає власного network stack, тому він не може запускати власний firewall. Він також не може повноцінно прив’язатися до всіх адрес: socket у jail, який запитує 0.0.0.0, kernel переписує на власну адресу jail. Два jail не можуть одночасно слухати порт 80 на одній адресі, тому кожному потрібно надати окрему адресу або розмістити reverse proxy перед ними.
VNET. Додайте vnet; до jail, і він отримає повний network stack: власні інтерфейси, власну routing table і власні firewall rules. Підключіть його до host через epair — virtual cable, по одному кінцю з кожного боку — і під’єднайте кінець host до bridge. Це найближча відповідність моделі Docker. Саме цей режим використовується для типів jail -V і -B у Bastille.
Переспрямування порту host до jail — це правило pf redirect. Bastille надає для нього обгортку:
sudo bastille rdr web tcp 80 80Тут немає EXPOSE і немає автоматичної публікації портів. Трафік не потрапить до jail, якщо цього не дозволяє його адреса або правило redirect. Початкове налаштування займає більше часу, зате firewall працює тихіше.
Обмеження ресурсів: 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:webrctl -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 — це гіпервізор FreeBSD. Він підходить, коли потрібна реальна межа ізоляції: інша операційна система, інше ядро або tenant, з яким ви не хочете спільно використовувати ядро. За це доведеться заплатити пам’яттю, яку резервують замість спільного використання, і необхідністю оновлювати друге ядро. Це та сама дилема, що й у Linux між контейнерами та повноцінними віртуальними машинами. Саме вона визначає, чи потрібен вам VPS із підтримкою вкладеної віртуалізації на нижньому рівні.
Екосистема — чесна причина, через яку більшість команд використовує Docker
Усе сказане вище стосується моделі. Для більшості команд вибір визначає масштаб екосистеми навколо кожного підходу.
Docker надає Docker Hub і GHCR, docker compose, Kubernetes, коли одного сервера вже недостатньо, CI runners із готовою підтримкою контейнерів і quickstart з однією командою майже в кожному README проєкту. Jails мають FreeBSD ports tree, який є великим і ретельно підтримується, а також значно менший набір готових до запуску пакетів застосунків. Якщо проєкт публікує лише container image, у FreeBSD потрібно прочитати його документацію та самостійно зібрати всі компоненти.
Jails виправдовують свій вибір з іншого боку цього компромісу. Вони потрібні, якщо ви вже використовуєте ZFS і цінуєте snapshot та rollback усього сервісу, якщо ваші сервіси нативні для FreeBSD, якщо для кожного tenant потрібен повний userland замість одного процесу або якщо ви хочете, щоб kernel, packet filter, filesystem і документація підтримувалися разом як єдина система. Саме це мають на увазі, коли називають FreeBSD цілісною системою. Докладніше про це йдеться у ширшому порівнянні Linux і FreeBSD як серверних платформ та в матеріалі що змінилося у FreeBSD 15 для використання на серверах.
Підсумкова оцінка така. Якщо ваша команда вже добре знає Docker, перехід матиме реальну вартість, а переваги мають бути конкретними. Не переходьте заради якості ізоляції: ці дві моделі достатньо близькі, тому конфігурація має більше значення. Переходьте, якщо вам потрібен rollback цілих сервісів на базі ZFS або якщо ви вже використовуєте FreeBSD.
FAQ
Чи можна запускати Docker images на FreeBSD?
Не Linux images і не як підтримуваний варіант. FreeBSD підтримує OCI containers: sudo pkg install -y podman-suite встановлює Podman, який запускає containers через ocijail — runtime, що створює справжні jails під ними. Для container monitor потрібно змонтувати fdescfs у /dev/fd, а для container NAT (network address translation) — pf. Найкраще працюють OCI images, створені для FreeBSD. Для Linux images додатково потрібен Linux compatibility layer, а станом на August 2026 port FreeBSD для Podman усе ще має статус experimental. Якщо ваш deployment складається зі stack Linux images, запускайте його на Linux. У сімействі RHEL це означає встановлення Docker на Rocky Linux або AlmaLinux, де Podman вдруге з’являється як пакет, якому вже належить команда docker, ще до встановлення будь-яких компонентів.
Чи безпечніші FreeBSD jails за Docker containers?
Обидва варіанти використовують одне kernel хоста, тому помилка в kernel становить ризик для обох. Жоден із них не слід обирати як межу ізоляції для справді ненадійного коду. Різниця полягає у початковій конфігурації. Jail починається з відмови в широкому наборі операцій, а потім ви повторно вмикаєте їх по одному параметру. Docker container запускається як root у наборі namespaces, де деякі capabilities вимкнено, а додаткове посилення захисту є opt-in. На практиці конфігурація важливіша за саму модель: jail із увімкненими allow.mount і allow.raw_sockets не є безпечнішим за правильно налаштований container.
Як створити резервну копію jail?
Створіть snapshot dataset і передайте його. sudo zfs snapshot zroot/jails/containers/web@backup, потім zfs send цей snapshot до іншого pool або у файл, який ви скопіюєте з сервера. Оскільки jail зберігає весь userland в одному dataset, snapshot містить встановлені packages і дані в одному узгодженому стані, а також усі config files, які ви редагували вручну. Це протилежність підходу Docker, де ви створюєте резервні копії named volumes і Compose file, а решту відновлюєте з image.
Чи потрібен BastilleBSD, або достатньо base system?
Base system достатньо, і саме з нього краще почати. jail.conf, jls, jexec і service jail start охоплюють усю модель. Знаючи їх, ви зможете працювати з будь-яким FreeBSD host, не вивчаючи спочатку інструменти конкретного host. Bastille — це convenience layer над base system: він bootstrap-ить releases, створює thin jails, застосовує templates і сам записує для вас pf redirect rules. Спочатку вивчіть базові команди, а потім додайте Bastille, коли кількість jails зробить ручне введення команд надто обтяжливим.