SSD Nodes Learn 🎉 VPS $5.50/月起
指南 Matt Connor作者: Matt Connor · 已更新 2026-08-13

FreeBSD jails 與 Docker containers 差異比較

FreeBSD jails 與 Docker containers 運作機制有何不同?本文分析兩者在隔離模型、檔案系統層級、網路架構及資源限制的技術差異,協助您評估工作負載需求。

FreeBSD jails 與 Docker containers 的比較

FreeBSD jails 與 Docker containers 以兩種不同形式解決相同的問題。兩者皆在共享核心上執行隔離的使用者空間(userland),因此皆非虛擬機器。兩者的差異在於內部運作方式:Docker container 執行從 registry 拉取的層級化映像檔中的單一處理程序;而 jail 則執行完整的 FreeBSD 使用者空間,包含專屬的 /etc、rc 啟動腳本、pkg 資料庫,以及任意數量的處理程序。本頁面提及的其他差異幾乎皆源於此核心架構。

SSD Nodes 並未提供 FreeBSD 映像檔。您無法在此平台租用 FreeBSD 伺服器,以下內容亦非針對此處可購買之機器所撰寫的安裝指南。本文旨在比較兩種隔離模型,協助您判斷工作負載的實際需求,並讓您在閱讀 FreeBSD 團隊的設定時能準確理解其意涵。

Jail 的本質

Jail 於 2000 年 3 月隨 FreeBSD 4.0 發布,其歷史比 cgroups 更悠久,且比 Docker 早了約十年。此機制僅需一個核心呼叫。jail(8) 會選定一個目錄樹,並在其中啟動帶有 Jail ID 的處理程序,隨後核心會拒絕任何帶有該 ID 的處理程序執行特定操作。處於 Jail 中的處理程序無法看見 Jail 外部的處理程序、無法掛載或卸載檔案系統、無法載入核心模組,也無法綁定未分配給該 Jail 的網路位址。此機制無需學習額外的命名空間類型,也沒有針對個別功能的啟用選項:所有限制皆為一個整體,並透過 Jail 設定檔中的參數進行調整。

在宿主機上,jls 可列出執行中的 Jail,而 jexec web sh 則可讓您進入名為 web 的 Jail 內部的 shell。

建立 Jail 的方式是將 FreeBSD 使用者空間(userland)放入一個目錄中。基礎系統可為您完成此步驟:

sudo bsdinstall jail /usr/local/jails/containers/web

此指令會抓取您當前發行版的基礎發行集,並執行標準的安裝後步驟,因此您可以像設定新伺服器一樣設定 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
jls

jls 現在應列出 web,並顯示其 JID、主機名稱與 IP 位址。若 Jail 未出現,請直接執行 sudo jail -c web。它會在前景套用相同的設定,並印出無法接受的參數,而不會將失敗訊息僅留在服務輸出中。

值得仔細閱讀的一行是 exec.start = "/bin/sh /etc/rc"。啟動 Jail 會在內部執行 FreeBSD 的標準開機指令碼,因此 Jail 會啟動其自身 /etc/rc.conf 中所有已啟用的服務。Docker 容器沒有對應的步驟,因為它僅執行映像檔的進入點(entrypoint)處理程序,並在該處理程序停止時隨之停止。

軟體部署方式:映像檔與登錄庫對比自行建構的 Userland

這是您在第一天就會感受到的差異。

使用 Docker 時,您只需指定軟體名稱即可取得。docker pull nginx 會抓取由他人建構並測試過的層級化、內容定址映像檔,而 docker compose up -d 則會啟動該映像檔,並掛載其儲存卷與網路。登錄庫(Registry)即是核心產品。Docker 工作流程的大部分價值在於數千個專案會發布可運作的映像檔,這使得 在 VPS 上執行 Docker 成為一項短時間內即可完成的任務,而非一項大工程。

FreeBSD 並未提供預設的 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 會為您設定網路、儲存與防火牆。bastille bootstrap 只需下載一次發行版本,後續建立的每個 Jail 都會重複使用它。FreeBSD 15.1 是目前的正式發行版本,於 2026 年 6 月發布;請替換為您實際執行的版本。

建立 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 會讓您進入 Jail 內部的登入 Shell,而 bastille list 則會顯示宿主機上存在的內容。若要重複建構,Bastille 範本(Templates)會將步驟儲存在檔案中並套用到 Jail,這是此領域中最接近 Dockerfile 的機制。範本會在每個 Jail 上重播。沒有任何東西是預先建構好的。

因此,誠實的總結很簡單。Docker 提供的是他人建構的成果。Jail 則提供您自行安裝的環境。如果您的候選軟體僅以容器映像檔形式發布,那麼在考慮其他因素之前,這就已經決定了選擇。

狀態與升級:ZFS 的變更機制

Docker 有意將狀態分離。容器檔案系統是拋棄式的,您的資料存放在具名儲存卷(named volume)或綁定掛載(bind mount)中,升級流程為執行 docker compose pull 後接著執行 docker compose up -d。容器會被替換,任何未放入儲存卷的資料都會消失。若您遵循此規則,這是一項功能;若您遺忘此規則,則會導致資料遺失。這正是為什麼在 Compose 堆疊中,選擇綁定掛載或具名儲存卷顯得如此重要。

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

執行此指令前,請先使用 zfs list 確認實際的資料集名稱;上述路徑為手冊所使用的配置方式。快照建立僅需約一秒,且在 Jail 內容變更前幾乎不佔用空間。若升級導致服務損壞,復原(rollback)可將整個使用者空間(userland)恢復至先前的狀態,包含套件資料庫以及您在凌晨兩點手動修改的設定檔。Docker 沒有內建的對等功能,因為其模型假設您不需要這類機制。

zfs clone 是另一半關鍵。快照的複製(clone)是一個新的可寫入 Jail,它與父層共享未變更的區塊,因此在開始變更前,建立一個 3 GB Jail 的測試副本幾乎不佔用磁碟空間。這就是 FreeBSD 管理員如何建立「與生產環境相同」的 Jail 來演練升級的方式。

基礎系統升級與套件升級是分開的。對於持有自身使用者空間副本的 Jail:

sudo freebsd-update -b /usr/local/jails/containers/web fetch install

精簡型 Jail(thin jails)可避免重複上述工作。它們透過 nullfs 掛載一個共享的唯讀基礎系統,並為每個 Jail 提供一個小的專屬可寫入層,因此您只需修補基礎系統一次,所有 Jail 都能看到結果。Bastille 預設會建立精簡型 Jail。

網路:發布埠口與定址決策

Docker 會自動處理網路設定,並要求您發布例外項目。容器會部署在橋接器(bridge)上,透過使用者定義網路中的服務名稱互相連線,而 -p 8080:80 則會將其中一個容器暴露給宿主機。Docker 會自行寫入封包過濾規則來達成此目的,這也是為何 已發布的容器埠口會直接繞過 ufw 的原因。

Jail 則要求您預先選擇網路模式,共有兩種。

共用 IP (Shared IP)。 ip4.addr = "10.0.0.10" 會將該位址新增至現有的宿主機介面,並將 Jail 限制在該位址。Jail 本身沒有獨立的網路堆疊,因此無法執行自己的防火牆。它也無法真正綁定到所有位址:當 Jail 內的 socket 請求 0.0.0.0 時,核心會將其重寫為該 Jail 專屬的位址。兩個 Jail 無法同時監聽同一個位址的 80 埠,因此您必須為每個 Jail 分配一個位址,或是在前方架設反向代理。

VNET。 為 Jail 新增 vnet; 後,它將擁有完整的網路堆疊:獨立的介面、路由表與防火牆規則。您可以使用 epair 將其連接至宿主機,這是一條兩端分別連接兩側的虛擬纜線,並將宿主機端掛載至橋接器上。此模式最接近 Docker 提供的功能,也是 Bastille 的 -V-B Jail 類型背後的運作模式。

將宿主機埠口轉發至 Jail 內部是透過 pf 重新導向規則達成。Bastille 將其封裝如下:

sudo bastille rdr web tcp 80 80

系統不會執行 EXPOSE,也不會自動發布埠口。除非透過位址或重新導向規則允許,否則任何流量都無法抵達 Jail。此機制的啟動速度較慢,但防火牆環境會安靜許多。

資源限制:cgroups 與 rctl

Docker 使用 cgroups 限制容器,且限制設定與容器定義綁定:透過 --memory=1g --cpus=1.5 指令列參數,或是在 Compose 檔案中對應的鍵值。若您已將服務堆疊維護在 VPS 上的 Docker Compose 檔案 中,限制設定會與其所屬服務並存,並隨 git 版本控制一同遷移。

FreeBSD 使用 rctl,這是一個必須手動啟用的子系統。資源統計預設為關閉,因為每次配置資源時都會產生些許開銷。請將此可調參數加入 /boot/loader.conf 並重新開機:

kern.racct.enable=1

接著設定規則並進行監控:

sudo rctl -a jail:web:vmemoryuse:deny=1g
rctl -hu jail:web

rctl -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 的虛擬機器監控程式(hypervisor)。當您需要真正的機器邊界時,它是正確的工具:例如需要不同的作業系統、不同的核心,或是您不想與其共用核心的租戶。您付出的代價是記憶體必須預留而非共用,且需要維護第二個核心的修補程式。這與您在 Linux 上選擇容器或完整虛擬機器時所做的決策相同,這也決定了您是否需要在底層使用 支援巢狀虛擬化的 VPS

生態系統,這是大多數團隊使用 Docker 的真實原因

上述內容皆圍繞在模型本身。對大多數團隊而言,決定選擇的關鍵在於各個生態系統的規模。

Docker 帶來了 Docker Hub 與 GHCR、docker compose、當單台伺服器不敷使用時的 Kubernetes、已內建容器支援的 CI runners,以及幾乎每個專案 README 中都提供的一鍵快速啟動指令。Jails 則擁有 FreeBSD ports tree,其規模龐大且維護嚴謹,但現成的應用程式套件相對較少。當專案僅發布容器映像檔時,若選擇 FreeBSD,您必須閱讀其文件並自行組裝各個組件。

Jails 在這種權衡中佔有一席之地。當您已經在使用 ZFS,並重視對整個服務進行快照與還原,或是您的服務是 FreeBSD 原生應用,又或者您希望每個租戶擁有完整的 userland 而非單一處理程序,亦或是您希望核心、封包過濾器、檔案系統與文件能作為單一系統共同維護時,Jails 就是您的選擇。最後一點正是人們稱 FreeBSD 具備一致性的原因,關於此點,在 Linux 與 FreeBSD 作為伺服器平台的廣泛比較 以及 FreeBSD 15 對伺服器用途的變更 中有更多討論。

最後的建議:如果您的團隊已經熟悉 Docker,遷移的成本是實質存在的,且必須有明確的獲益。請勿為了隔離品質而切換;這兩種模型的差異極小,您的設定方式影響更大。若要切換,應是因為您需要基於 ZFS 的完整服務還原功能,或是因為您已經在使用 FreeBSD。

FAQ

我可以在 FreeBSD 上執行 Docker 映像檔嗎?

無法執行 Linux 映像檔,且此路徑不受官方支援。FreeBSD 確實具備 OCI 容器支援:sudo pkg install -y podman-suite 可安裝 Podman,它透過 ocijail 執行容器,這是一種在底層建立真實 jail 的執行時期環境。它需要將 fdescfs 掛載至 /dev/fd 以供容器監控程式使用,並需 pf 進行容器 NAT(網路位址轉譯)。FreeBSD 原生的 OCI 映像檔運作效果最佳。Linux 映像檔則額外需要 Linux 相容層,且截至 2026 年 8 月,FreeBSD 的 Podman port 仍被標記為實驗性質。若您的部署環境是一組 Linux 映像檔,請在 Linux 上執行。

FreeBSD jail 是否比 Docker 容器更安全?

兩者皆共用同一個主機核心,因此核心漏洞對兩者皆構成風險,且兩者皆非處理真正不可信程式碼時的首選邊界。兩者的差異在於起點。Jail 的預設狀態是拒絕大部分操作,您必須逐一啟用所需參數;Docker 容器則是以 root 權限在命名空間(namespaces)中啟動,並預設移除部分能力(capabilities),後續的強化措施皆需手動加入。實際上,設定方式的影響大於模型本身:一個啟用 allow.mountallow.raw_sockets 的 jail,其安全性並不優於經過嚴謹設定的容器。

我該如何備份 jail?

對資料集(dataset)進行快照(snapshot)並傳送。執行 sudo zfs snapshot zroot/jails/containers/web@backup,然後將該快照 zfs send 到另一個儲存池(pool)或匯出成檔案並移出主機。由於 jail 將整個使用者空間(userland)保留在單一資料集中,快照能捕捉到安裝的套件、資料,以及您手動編輯的所有設定檔,並確保這些資料在同一時間點保持一致。這與 Docker 的習慣相反,Docker 通常是備份具名儲存卷(named volumes)與 Compose 檔案,其餘部分則從映像檔重新建構。

我需要 BastilleBSD,還是基礎系統就足夠了?

基礎系統已足夠,且是更好的入門選擇。jail.confjlsjexecservice jail start 涵蓋了整個模型,一旦熟悉這些指令,您無需先學習特定主機的工具,即可管理任何 FreeBSD 主機。Bastille 是其上的便利層:它能協助引導發行版本、建立輕量級 jail、套用範本並為您撰寫 pf 重新導向規則。請先學習基礎指令,待 jail 數量多到輸入指令變得繁瑣時,再加入 Bastille。