FreeBSD jails a kontenery Docker: różnice w izolacji
Analiza techniczna różnic między FreeBSD jails a Docker. Dowiedz się, jak odmienne podejście do izolacji procesów, warstwowych obrazów oraz sieci wpływa na stabilność systemów.
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 rozróżnienia.
SSD Nodes nie oferuje obrazów FreeBSD. Nie można wynająć serwera z systemem FreeBSD na tej platformie, a poniższy tekst nie jest instrukcją instalacji dla maszyny, którą można tutaj zakupić. Jest to porównanie dwóch modeli izolacji, przygotowane w celu określenia wymagań danego obciążenia oraz umożliwienia zrozumienia konfiguracji stosowanych przez zespoły FreeBSD bez konieczności zgadywania.
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 w jail nie widzi procesów poza swoim środowiskiem, 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 mu przydzielone. Nie ma tu osobnych typów przestrzeni nazw do nauki ani opcji włączania poszczególnych funkcji: ograniczenia są nakładane jako całość, regulowana parametrami w konfiguracji jail.
Na hoście jls wyświetla listę działających jaili, a jexec web sh otwiera powłokę wewnątrz jaila 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/webPolecenie to pobiera 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 /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 JID, nazwą hosta oraz adresem IP. Jeśli jail nie pojawia się na liście, uruchom sudo jail -c web bezpośrednio. Polecenie to zastosuje tę samą konfigurację w pierwszym planie i wyświetli parametr, którego nie udało się zaakceptować, zamiast pozostawiać informację o błędzie w wyjściu usługi.
Linią wartą dwukrotnego przeczytania jest exec.start = "/bin/sh /etc/rc". Uruchomienie jaila wywołuje standardowy skrypt startowy FreeBSD wewnątrz środowiska, 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 wejściowy obrazu i kończy działanie, gdy proces ten zostanie zatrzymany.
Metody 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 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 posiada domyślnego publicznego rejestru obrazów jail. Tworzysz pusty system użytkownika i instalujesz w nim oprogramowanie w taki sam sposób, jak w przypadku zwykłego serwera. Wymaga to wpisania większej liczby poleceń. Jest to jednak rozwiązanie bardziej przejrzyste, ponieważ wewnątrz jail działa dokładnie to, co umieściło tam pkg, korzystając z tego samego zestawu pakietów, co system hosta.
Narzędzia skracają ten proces. BastilleBSD jest popularnym menedżerem jail i jest dostępny jako pakiet:
sudo pkg install bastille
sudo sysrc bastille_enable=YES
sudo bastille setup
sudo bastille bootstrap 15.1-RELEASEbastille setup konfiguruje sieć, pamięć masową i zaporę sieciową. bastille bootstrap pobiera wydanie tylko raz, a każdy kolejny jail korzysta z tego samego źródła. FreeBSD 15.1 to aktualne wydanie produkcyjne, dostępne od czerwca 2026; należy podstawić wersję, która jest aktualnie używana.
Tworzenie jaila to jedno polecenie, a jego wypełnienie to kolejne:
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 budowania, szablony Bastille przechowują kroki w pliku i stosują je do jaila, co jest najbliższym odpowiednikiem Dockerfile w tym środowisku. Szablon jest odtwarzany dla każdego jaila. Nic nie jest dostarczane w formie gotowej do uruchomienia.
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, kwestia wyboru jest rozstrzygnięta, zanim pozostałe argumenty zostaną wzięte pod uwagę.
Stan i aktualizacje: rola zmian w ZFS
Docker celowo rozdziela stan aplikacji. System plików kontenera jest tymczasowy, dane znajdują się w nazwanym wolumenie lub montowaniu 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 przy przestrzeganiu zasad i przyczyna utraty danych w przypadku ich pominięcia, dlatego wybór między bind mounts a nazwanymi wolumenami ma tak duże znaczenie w stosie Compose.
Jail nie rozdziela stanu, a powodem, dla którego to rozwiązanie działa, jest ZFS. 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 tego polecenia należy sprawdzić 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 spowoduje awarię usługi, wycofanie zmian (rollback) przywraca całe środowisko użytkownika do poprzedniego 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 stanowi drugą połowę tego mechanizmu. Klon snapshotu to nowy, zapisywalny jail, który współdzieli niezmienione bloki z rodzicem, więc kopia testowa 3 GB jaila prawie nie zajmuje miejsca na dysku, dopóki nie zaczną być wprowadzane zmiany. 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ę środowiska użytkownika:
sudo freebsd-update -b /usr/local/jails/containers/web fetch installCienkie jaile (thin jails) pozwalają uniknąć powtarzania tej pracy. Montują one jeden współdzielony system bazowy w trybie tylko do odczytu za pomocą nullfs i zapewniają każdemu jailowi małą, własną warstwę zapisywalną, dzięki czemu system bazowy aktualizuje się raz, a każdy jail widzi rezultat. Bastille domyślnie tworzy cienkie jaile.
Sieć: publikowanie portów a decyzja o adresowaniu
Docker automatycznie zarządza siecią i wymaga jawnego publikowania wyjątków. Kontenery trafiają do mostka, komunikują się ze sobą za pomocą nazw usług w sieci zdefiniowanej przez użytkownika, a -p 8080:80 udostępnia jeden z nich na hoście. Docker automatycznie tworzy reguły filtrowania pakietów, co sprawia, że opublikowany port kontenera omija ufw.
Jail wymusza wybór modelu na etapie konfiguracji; dostępne są dwa warianty.
Shared IP. ip4.addr = "10.0.0.10" dodaje adres 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 nadpisywane 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. Dodanie vnet; do jaila zapewnia mu pełny stos sieciowy: własne interfejsy, własną tablicę routingu i własne reguły firewalla. Jail łączy się z hostem za pomocą epair, czyli wirtualnego kabla z końcami po obu stronach, przy czym koniec po stronie hosta jest wpięty do mostka. Jest to rozwiązanie najbardziej zbliżone do modelu Dockera i stanowi podstawę dla 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 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 przy konfiguracji, ale zapewnia znacznie bardziej przejrzysty stan firewalla.
Limity zasobów: cgroups a rctl
Docker ogranicza kontener za pomocą cgroups, a limity znajdują się tam, gdzie zdefiniowano kontener: --memory=1g --cpus=1.5 w wierszu poleceń lub w odpowiadających im kluczach w pliku Compose. Jeśli stos utrzymywany jest w pliku Docker Compose na VPS, limit znajduje się obok usługi, której dotyczy, i jest przenoszony wraz z nią w 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ć parametr tunable 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 własny błąd alokacji aplikacji, a nie komunikat o przerwaniu procesu na hoście.
Reguły dodane za pomocą rctl -a znikają po kolejnym restarcie. Usługa rctl we FreeBSD przeładowuje je 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 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 Linux.
bhyve to hypervisor 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 zamiast współdzielonej, 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; to ona decyduje o tym, czy potrzebny jest VPS wspierający wirtualizację zagnieżdżoną.
Ekosystem, czyli rzeczywisty powód, dla którego większość zespołów korzysta z Docker
Wszystko powyżej dotyczy modelu działania. O wyborze większości zespołów decyduje 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 złożenie komponentów.
Jails zyskują przewagę w innych aspektach. Są pożądane, gdy korzystasz już z ZFS i cenisz możliwość tworzenia snapshotów oraz przywracania całych usług, gdy Twoje usługi są natywne dla FreeBSD, gdy potrzebujesz pełnego środowiska użytkownika (userland) 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 określa się mianem spójności FreeBSD, co zostało szerzej omówione w szerszym porównaniu Linux i FreeBSD jako platform serwerowych oraz w tym, co FreeBSD 15 zmieniło w zastosowaniach serwerowych.
Podsumowanie. Jeśli Twój zespół zna już Docker, koszt migracji jest realny i musi przynieść konkretne korzyści. Nie zmieniaj rozwiązania tylko 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 potrzebujesz przywracania całych usług opartego na ZFS lub jeśli korzystasz już 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 poprzez ocijail, środowisko uruchomieniowe tworzące wewnątrz rzeczywiste jails. Wymaga ono zamontowanego 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 Linux, a według stanu na sierpień 2026 port Podman dla FreeBSD jest wciąż określany jako eksperymentalny. Jeśli wdrożenie składa się ze stosu obrazów Linux, należy uruchomić je na systemie Linux. W rodzinie RHEL oznacza to instalację Docker na Rocky Linux lub AlmaLinux, gdzie Podman pojawia się ponownie jako pakiet, który przejmuje komendę docker jeszcze przed instalacją jakiegokolwiek oprogramowania.
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 pojedynczo. Kontener Docker zaczyna jako root wewnątrz zestawu przestrzeni nazw (namespaces) z ograniczonymi uprawnieniami, a dalsze utwardzanie jest opcjonalne. W praktyce konfiguracja decyduje o bezpieczeństwie bardziej niż 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ą. sudo zfs snapshot zroot/jails/containers/web@backup, a następnie zfs send tę migawkę do innej puli lub do pliku, który zostanie skopiowany poza serwer. Ponieważ jail przechowuje całe środowisko 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 nawyków w Docker, gdzie tworzy się kopie zapasowe 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 uprzedniego poznawania specyficznych narzędzi danego hosta. Bastille to warstwa wygody ponad systemem: automatyzuje pobieranie wydań, tworzenie lekkich jail, stosowanie szablonów oraz zapisywanie reguł przekierowań pf. Należy najpierw poznać podstawowe polecenia, a następnie dodać Bastille, gdy liczba jail sprawi, że ręczne wpisywanie komend stanie się uciążliwe.