FreeBSD jails a Docker: różnice w izolacji systemowej
Porównanie architektury FreeBSD jails oraz kontenerów Docker. Analiza różnic w zarządzaniu procesami, sieciami i stanem systemu. Dowiedz się, kiedy wybrać jail zamiast Dockera.
FreeBSD jails a kontenery Docker w jednym akapicie
FreeBSD jails oraz kontenery Docker rozwiązują ten sam problem w odmienny sposób. Oba rozwiązania uruchamiają odizolowane środowiska użytkownika na współdzielonym jądrze, więc żadne z nich nie jest maszyną wirtualną. Różnica polega na zawartości. Kontener Docker uruchamia jeden proces z warstwowego obrazu pobranego z rejestru. Jail uruchamia kompletne środowisko użytkownika FreeBSD: własny /etc, własne skrypty startowe rc, własną bazę danych pkg oraz dowolną liczbę procesów. Niemal każda inna różnica opisana na tej stronie wynika z tego podstawowego założenia.
SSD Nodes nie oferuje obrazów FreeBSD. Na tej platformie nie można wynająć serwera z systemem FreeBSD, a poniższa treść nie stanowi instrukcji instalacji dla maszyn dostępnych w tej ofercie. Jest to porównanie dwóch modeli izolacji, przygotowane w celu ułatwienia wyboru odpowiedniego rozwiązania dla danego obciążenia oraz zrozumienia konfiguracji stosowanych przez zespoły FreeBSD.
Czym w rzeczywistości jest jail
Mechanizm jail pojawił się w FreeBSD 4.0 w marcu 2000 roku, co czyni go starszym od cgroups i o około dekadę starszym od Docker. Mechanizm ten opiera się na jednym wywołaniu jądra. jail(8) pobiera drzewo katalogów i uruchamia w nim procesy z przypisanym identyfikatorem jail ID, a jądro odmawia wykonania określonego zestawu operacji dla każdego procesu posiadającego ten identyfikator. Proces działający wewnątrz jail nie widzi procesów poza nim, nie może montować ani odmontowywać systemów plików, nie może ładować modułów jądra ani wiązać się z adresami sieciowymi, które nie zostały przypisane do danego jail. Nie ma tu osobnych typów przestrzeni nazw do poznania ani opcji włączania poszczególnych funkcji: ograniczenia stanowią jedną całość, regulowaną parametrami w konfiguracji jail.
Na hoście polecenie jls wyświetla listę uruchomionych jail, a jexec web sh otwiera powłokę wewnątrz jail o nazwie web.
Jail tworzy się poprzez umieszczenie środowiska użytkownika FreeBSD w katalogu. System bazowy wykonuje to zadanie automatycznie:
sudo bsdinstall jail /usr/local/jails/containers/webPobiera to zestaw dystrybucyjny dla danej wersji systemu i uruchamia standardowe kroki instalacyjne, dzięki czemu można ustawić hasło root oraz strefę czasową dokładnie tak, jak na nowym serwerze. Wynikiem jest instalacja FreeBSD znajdująca się w folderze. Następnie należy opisać ją w pliku /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;
}Uruchom jail, a następnie sprawdź jego stan:
sudo service jail start web
jlsjls powinno teraz wyświetlić web wraz z identyfikatorem JID, nazwą hosta i adresem IP. Jeśli jail nie pojawia się na liście, uruchom sudo jail -c web bezpośrednio. Zastosuje to tę samą konfigurację na pierwszym planie i wyświetli parametr, którego nie udało się zaakceptować, zamiast pozostawiać informację o błędzie w wyjściu usługi.
Wierszem, który warto przeczytać dwukrotnie, jest exec.start = "/bin/sh /etc/rc". Uruchomienie jail wywołuje standardowy skrypt startowy FreeBSD wewnątrz niego, więc jail uruchamia każdą usługę włączoną w jego własnym pliku /etc/rc.conf. Kontener Docker nie posiada odpowiednika tego kroku, ponieważ uruchamia proces punktu wejścia (entrypoint) obrazu i zatrzymuje się, gdy proces ten zakończy działanie.
Sposoby dostarczania oprogramowania: obrazy i rejestry a środowisko użytkownika tworzone samodzielnie
To różnica odczuwalna już pierwszego dnia.
W Dockerze wskazujesz nazwę oprogramowania i je otrzymujesz. docker pull nginx pobiera warstwowy obraz z adresowalną zawartością, który został zbudowany i przetestowany przez kogoś innego, a docker compose up -d uruchamia go wraz z podpiętymi wolumenami i siecią. Rejestr jest tutaj kluczowym produktem. Większość wartości przepływu pracy w Dockerze wynika z faktu, że tysiące projektów publikuje gotowe obrazy, co sprawia, że uruchomienie Dockera na VPS jest krótkim zadaniem, a nie osobnym projektem.
FreeBSD nie udostępnia domyślnego publicznego rejestru obrazów jail. Tworzysz puste środowisko użytkownika i instalujesz w nim oprogramowanie w taki sam sposób, jak w przypadku zwykłego serwera. Wymaga to więcej pisania. Jest to jednak rozwiązanie bardziej przejrzyste, ponieważ w jail działa dokładnie to, co umieściło tam pkg, korzystając z tego samego zestawu pakietów, którego używa host.
Narzędzia skracają ten proces. BastilleBSD to popularny menedżer jail, dostępny jako pakiet:
sudo pkg install bastille
sudo sysrc bastille_enable=YES
sudo bastille setup
sudo bastille bootstrap 15.1-RELEASEbastille setup automatycznie konfiguruje sieć, pamięć masową i firewall. bastille bootstrap pobiera wydanie tylko raz, a każdy kolejny jail korzysta z tej samej bazy. FreeBSD 15.1 to aktualne wydanie produkcyjne, udostępnione w czerwcu 2026; należy podstawić wersję, z której się korzysta.
Tworzenie jaila to jedna komenda, a jego wypełnienie to kolejna:
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 zapewnia powłokę logowania wewnątrz jaila, a bastille list pokazuje, co istnieje na hoście. Aby powtórzyć proces budowy, szablony Bastille przechowują kroki w pliku i stosują je do jaila, co jest najbliższym odpowiednikiem Dockerfile w tym świecie. Szablon jest odtwarzany na każdym jailu. Nic nie jest dostarczane w formie gotowej.
Podsumowanie jest zatem krótkie. Docker dostarcza gotowe kompilacje innych osób. Jails wymagają samodzielnej instalacji. Jeśli oprogramowanie z Twojej listy jest dostępne wyłącznie jako obraz kontenera, rozstrzyga to kwestię wyboru przed rozważeniem jakichkolwiek innych kryteriów.
Stan i aktualizacje: rola ZFS
Docker celowo rozdziela stan aplikacji. System plików kontenera jest tymczasowy, dane znajdują się w wolumenach (named volumes) lub montowaniach typu bind mount, a aktualizacja polega na wykonaniu docker compose pull, a następnie docker compose up -d. Kontener jest zastępowany, a wszystko, co nie zostało umieszczone w wolumenie, zostaje utracone. Jest to zaleta, jeśli przestrzega się zasad, i przyczyna utraty danych, jeśli się o nich zapomni. Dlatego wybór między bind mounts a named volumes ma tak duże znaczenie w stosie Compose.
Jail nie rozdziela stanu, a ZFS jest powodem, dla którego to rozwiązanie działa. Cały jail stanowi jeden dataset:
sudo zfs snapshot zroot/jails/containers/web@pre-upgrade
sudo pkg -j web upgrade
sudo zfs rollback zroot/jails/containers/web@pre-upgradePrzed uruchomieniem polecenia sprawdź rzeczywistą nazwę datasetu za pomocą zfs list; powyższa ścieżka odpowiada układowi opisanemu w podręczniku. Snapshot zajmuje około sekundy i prawie nie zajmuje miejsca, dopóki zawartość jaila nie ulegnie zmianie. Jeśli aktualizacja uszkodzi usługę, rollback przywraca cały userland do wcześniejszego stanu, włącznie z bazą danych pakietów i plikami konfiguracyjnymi edytowanymi ręcznie w nocy. Docker nie posiada wbudowanego odpowiednika, ponieważ jego model zakłada, że użytkownik nigdy go nie potrzebuje.
zfs clone to druga połowa tego mechanizmu. Klon snapshotu to nowy, zapisywalny jail, który współdzieli niezmienione bloki z rodzicem. Dzięki temu kopia testowa 3 GB jaila prawie nie zajmuje miejsca na dysku, dopóki nie zaczniesz wprowadzać w niej zmian. W ten sposób administrator FreeBSD tworzy jail "identyczny z produkcyjnym", aby przeprowadzić próbę aktualizacji.
Aktualizacja systemu bazowego jest oddzielona od aktualizacji pakietów. W przypadku jaila posiadającego własną kopię userlandu:
sudo freebsd-update -b /usr/local/jails/containers/web fetch installThin jails pozwalają uniknąć powtarzania tej pracy. Montują one jeden współdzielony, tylko do odczytu system bazowy poprzez nullfs i udostępniają każdemu jailowi niewielką, zapisywalną warstwę własną. Dzięki temu system bazowy aktualizuje się raz, a każdy jail widzi wynik tych zmian. Bastille tworzy thin jails domyślnie.
Sieci: publikowanie portów a decyzje adresowe
Docker automatyzuje konfigurację sieci i wymaga jawnego publikowania wyjątków. Kontenery trafiają do mostka (bridge), komunikują się między sobą za pomocą nazw usług w sieciach zdefiniowanych przez użytkownika, a -p 8080:80 udostępnia jeden z nich na hoście. Docker automatycznie tworzy reguły filtrowania pakietów, co wyjaśnia, dlaczego opublikowany port kontenera omija ufw.
W przypadku jaili należy wybrać model pracy na wstępie; dostępne są dwa rozwiązania.
Shared IP. ip4.addr = "10.0.0.10" przypisuje adres IP do istniejącego interfejsu hosta i ogranicza jail do tego adresu. Jail nie posiada własnego stosu sieciowego, więc nie może uruchomić własnego firewalla. Nie może również wiązać się ze wszystkimi adresami: gniazdo wewnątrz jaila żądające 0.0.0.0 jest przepisywane przez jądro na adres przypisany do jaila. Dwa jaile nie mogą nasłuchiwać na porcie 80 tego samego adresu, dlatego należy przypisać każdemu z nich osobny adres lub umieścić przed nimi reverse proxy.
VNET. Po dodaniu vnet; do jaila, otrzymuje on pełny stos sieciowy: własne interfejsy, własną tablicę routingu oraz własne reguły firewalla. Jail łączy się z hostem za pomocą epair, czyli wirtualnego kabla, którego końce znajdują się po obu stronach, przy czym koniec po stronie hosta jest wpięty do mostka. Jest to rozwiązanie najbardziej zbliżone do modelu sieciowego Dockera i stanowi podstawę typów jaili -V oraz -B w Bastille.
Przekierowanie portu hosta do jaila odbywa się za pomocą reguły przekierowania pf. Bastille automatyzuje ten proces:
sudo bastille rdr web tcp 80 80Nie istnieje tu mechanizm EXPOSE ani automatyczne publikowanie portów. Żaden ruch nie dotrze do jaila, dopóki nie zezwoli na to jego adres lub reguła przekierowania. Wymaga to więcej pracy na starcie, ale zapewnia znacznie mniejszą liczbę otwartych portów w firewallu.
Limity zasobów: cgroups kontra rctl
Docker ogranicza kontener za pomocą cgroups, a limity znajdują się w miejscu definicji kontenera: --memory=1g --cpus=1.5 w wierszu poleceń lub w odpowiadających im kluczach w pliku Compose. Jeśli stos jest już przechowywany w pliku Docker Compose na serwerze VPS, limit znajduje się obok usługi, której dotyczy, i jest przenoszony wraz z nią w systemie git.
FreeBSD używa rctl, czyli podsystemu, który należy włączyć. Rozliczanie zasobów jest domyślnie wyłączone, ponieważ wiąże się z niewielkim kosztem przy każdej alokacji. Należy dodać odpowiedni parametr do /boot/loader.conf i zrestartować system:
kern.racct.enable=1Następnie należy ustawić regułę i monitorować jej działanie:
sudo rctl -a jail:web:vmemoryuse:deny=1g
rctl -hu jail:webrctl -hu jail:web wyświetla bieżące zużycie zasobów przez jail w czytelnych jednostkach, co pozwala sprawdzić, jak blisko limitu znajduje się proces, zanim wystąpi awaria. Akcja deny powoduje, że alokacja przekraczająca limit kończy się niepowodzeniem wewnątrz jaila, dzięki czemu widoczny jest błąd alokacji samej aplikacji, a nie komunikat o przerwaniu procesu na hoście.
Reguły dodane za pomocą rctl -a znikają po kolejnym restarcie. Usługa rctl w systemie FreeBSD wczytuje je ponownie z pliku /etc/rctl.conf, dlatego należy zapisać regułę w tym pliku i włączyć usługę:
sudo sysrc rctl_enable=YESJest to obszar, w którym Docker jest zdecydowanie wygodniejszy. Limit w pliku Compose jest weryfikowany wraz z usługą, którą ogranicza. Reguła rctl to linia w oddzielnym pliku, wskazująca na jail zdefiniowany w innym miejscu.
Gdy rozwiązaniem jest maszyna wirtualna: bhyve
Jail współdzieli jądro systemu gospodarza, dlatego niektóre funkcje są trwale niedostępne. Nie można w nim uruchomić innej wersji jądra, załadować modułu jądra ani uruchamiać plików binarnych Linux w sposób, w jaki robią to kontenery Linux. FreeBSD posiada warstwę kompatybilności z Linux, zwaną linuxulator, jednak implementuje ona jedynie podzbiór wywołań systemowych Linux i nie stanowi uniwersalnego rozwiązania dla dowolnych obrazów systemu Linux.
bhyve to hiperwizor systemu FreeBSD i jest właściwym narzędziem, gdy wymagana jest rzeczywista izolacja maszyn: inny system operacyjny, inne jądro lub najemca, z którym nie chcemy współdzielić jądra. Kosztem jest pamięć RAM, która zostaje zarezerwowana, a nie współdzielona, oraz konieczność aktualizacji drugiego jądra. Jest to ta sama decyzja, którą podejmuje się w systemie Linux, wybierając między kontenerami a pełnymi maszynami wirtualnymi. Decyduje ona również o tym, czy potrzebny jest VPS wspierający wirtualizację zagnieżdżoną.
Ekosystem, czyli rzeczywisty powód, dla którego większość zespołów wybiera Docker
Wszystko powyżej dotyczy modelu działania. O wyborze większości zespołów decyduje jednak skala środowiska otaczającego dane rozwiązanie.
Docker oferuje Docker Hub oraz GHCR, docker compose, Kubernetes, gdy jeden serwer przestaje wystarczać, CI runners z wbudowaną obsługą kontenerów oraz szybki start za pomocą jednej komendy w niemal każdym pliku README projektu. Jails oferują drzewo portów FreeBSD, które jest obszerne i starannie utrzymywane, oraz znacznie mniejszy zestaw gotowych do uruchomienia pakietów aplikacji. Gdy projekt publikuje wyłącznie obraz kontenera, w przypadku FreeBSD konieczne jest zapoznanie się z dokumentacją i samodzielne zestawienie komponentów.
Jails zyskują przewagę w innych aspektach. Są pożądane, gdy korzystasz już z ZFS i cenisz możliwość tworzenia migawek oraz przywracania całych usług, gdy usługi są natywne dla FreeBSD, gdy potrzebujesz pełnego środowiska użytkownika dla każdego najemcy zamiast pojedynczego procesu, lub gdy oczekujesz, że jądro, filtr pakietów, system plików i dokumentacja będą utrzymywane jako jeden spójny system. To ostatnie jest tym, co użytkownicy mają na myśli, nazywając FreeBSD rozwiązaniem spójnym. Temat ten jest szerzej omówiony w szerszym porównaniu Linux i FreeBSD jako platform serwerowych oraz w zmianach wprowadzonych we FreeBSD 15 pod kątem zastosowań serwerowych.
Podsumowanie. Jeśli Twój zespół zna już Docker, koszt migracji jest realny i musi przynieść konkretne korzyści. Nie zmieniaj rozwiązania ze względu na jakość izolacji; oba modele są na tyle zbliżone, że to konfiguracja ma większe znaczenie. Zmień rozwiązanie, jeśli oczekujesz przywracania całych usług opartego na ZFS lub jeśli już korzystasz z FreeBSD.
FAQ
Czy mogę uruchamiać obrazy Docker na FreeBSD?
Nie obrazy Linux i nie w ramach wspieranej ścieżki. FreeBSD posiada wsparcie dla kontenerów OCI: sudo pkg install -y podman-suite instaluje Podman, który uruchamia kontenery za pośrednictwem ocijail, środowiska uruchomieniowego tworzącego pod spodem rzeczywiste jails. Wymaga to zamontowania fdescfs w /dev/fd dla monitora kontenerów oraz pf dla NAT (network address translation) kontenerów. Obrazy OCI natywne dla FreeBSD działają najlepiej. Obrazy Linux wymagają dodatkowo warstwy kompatybilności z Linuxem, a według stanu na sierpień 2026 port Podman dla FreeBSD jest wciąż określany jako eksperymentalny. Jeśli wdrożenie opiera się na stosie obrazów Linux, należy uruchomić je na systemie Linux.
Czy FreeBSD jails są bezpieczniejsze niż kontenery Docker?
Oba rozwiązania współdzielą jedno jądro systemu hosta, więc błąd w jądrze stanowi ryzyko dla obu, i żadne z nich nie jest granicą, którą należałoby wybrać dla kodu całkowicie niezaufanego. Różnica tkwi w punkcie wyjścia. Jail zaczyna od szerokiego zestawu odmówionych operacji, które włącza się ponownie parametr po parametrze. Kontener Docker zaczyna jako root wewnątrz zestawu przestrzeni nazw (namespaces) z ograniczonymi uprawnieniami (capabilities), a dalsze utwardzanie jest opcjonalne. W praktyce o bezpieczeństwie decyduje konfiguracja, a nie model: jail uruchomiony z włączonymi allow.mount i allow.raw_sockets nie jest bezpieczniejszy niż starannie skonfigurowany kontener.
Jak wykonać kopię zapasową jail?
Należy utworzyć migawkę (snapshot) zbioru danych i wysłać ją. Użyj sudo zfs snapshot zroot/jails/containers/web@backup, a następnie zfs send tej migawki do innej puli lub do pliku, który zostanie skopiowany poza serwer. Ponieważ jail przechowuje całą przestrzeń użytkownika w jednym zbiorze danych, migawka rejestruje zainstalowane pakiety oraz dane w jednym spójnym punkcie, wraz z każdym plikiem konfiguracyjnym edytowanym ręcznie. Jest to przeciwieństwo praktyki Docker, gdzie tworzy się kopię zapasową nazwanych wolumenów oraz pliku Compose, a resztę odtwarza z obrazu.
Czy potrzebuję BastilleBSD, czy wystarczy system podstawowy?
System podstawowy jest wystarczający i stanowi lepszy punkt wyjścia. jail.conf, jls, jexec oraz service jail start obejmują cały model, a po ich opanowaniu można zarządzać dowolnym hostem FreeBSD bez konieczności wcześniejszego uczenia się narzędzi specyficznych dla danego hosta. Bastille to warstwa wygody umieszczona powyżej: automatyzuje pobieranie wydań, tworzenie lekkich jails, stosowanie szablonów oraz zapisywanie reguł przekierowań pf. Należy najpierw poznać polecenia podstawowe, a następnie wdrożyć Bastille, gdy liczba jails sprawi, że ręczne wpisywanie poleceń stanie się uciążliwe.