幾十年前,將東西傳送到伺服器並使其能在網際網路上存取的唯一眾所周知的方法,是透過 Total Commander、FileZilla 或 FAR Manager 等工具使用 FTP 傳輸檔案,手動將檔案和資料夾從左窗格複製到右窗格。較進階的使用者則偏好 scp 或 rsync 等標準 UNIX 工具,但過程本質上是相同的。
這並非高深莫測的技術(這也是最棒的部分),而且確實有效!唯一明顯的問題是我們都曾有過的「哎呀」時刻——不小心點錯、意外刪除、在錯誤的地方編輯。當你是一個獨立專案的獨立開發者時,這沒什麼大不了。但當你負責數十個客戶專案時,這就是一場災難。
一種非常常見的後端設定是,由同一個 Apache 網頁伺服器實例提供多個網站的服務,它們共享相同的生命週期。如果 Apache 當機,所有網站都會停擺。如果系統依賴項損壞,一切都會崩潰。
值得一提的是,當流量激增時,出現了一個更微妙的問題。當你的超級明星網站消耗了所有可用資源時,同一台伺服器上的其他所有網站都在默默地窒息。
系統管理員們爭相自動化例行任務,分享充滿巧妙技巧和程序邏輯的 shell 腳本。當時還沒有標準化的方法來處理任何事情,包括用於審計的版本控制或在出現問題時進行回滾。我們大多數人使用諸如在專案資料夾名稱後附加遞增數字或時間戳記之類的慣例。在大多數情況下,手動傳輸檔案效果相當不錯。直到它失效為止。
至少有兩個明顯的問題需要解決:
部署。你如何可靠地傳輸?你如何避免常見的人為失誤?你如何實現版本控制和回滾?你如何使解決方案足夠通用以涵蓋所有業務案例?
進程隔離。你如何保護應用程式免受系統影響,以及保護系統免受應用程式影響?你如何處理一個應用程式的需求會悄悄破壞另一個應用程式,或者客戶需要相同事物的略微不同版本的情況?你如何解決依賴項?
解決部署問題的嘗試為我們帶來了一個全新的工具和方法論宇宙,最終演變成現代的 CI/CD 管道、封裝標準和版本控制。然而,隔離的故事卻鮮為人知。
1979 年,Bell UNIX 引入了 chroot——一種讓進程擁有孤立檔案系統視圖的方法,將其限制在一個子樹中,使其無法觸及上層的任何內容。這是一個原始但確實有用的想法。限制在於 chroot 只隔離了檔案系統。進程仍然可以干擾網路、其他進程、系統資源。這是一個不完整的解決方案,一個有決心的應用程式可以逃脫它。
第一個嚴肅的企業級解決方案是虛擬機器。VMware 在 1990 年代後期將虛擬機器推向主流,為每個應用程式提供自己的完全隔離的作業系統環境。問題在於成本。每個虛擬機器都帶有一個完整的作業系統,具有顯著的開銷,啟動時間以分鐘計。雖然比購買更多實體伺服器便宜,但它效率低下且昂貴。
一場靜默的革命發生在 2000 年。不是在 Windows Server 上,也不是在 Linux 上,而是在 FreeBSD 上,這是一個基於 UNIX 的作業系統,在 Linux 主導該領域之前很久,它一直是 IT 專業人員的預設選擇。
這裡值得對 FreeBSD 做一個簡短的說明,因為它與 Linux 在根本上有區別。Linux 是一個核心。大多數人所說的「Linux」實際上是該核心與 GNU 使用者空間、套件生態系統以及一套因發行版而異的選擇的結合——Ubuntu、Fedora 和 Arch 都運行相同的核心,但底層系統卻有顯著差異。
FreeBSD 作為一個完整、連貫的作業系統發佈——核心、使用者空間、基礎工具和函式庫共同開發、共同版本化、共同測試作為一個單元。這種連貫性很重要。這也是 FreeBSD 解決方案往往更乾淨、基礎系統在不同安裝之間行為一致的原因之一。
FreeBSD 在這個連貫的基礎上構建的解決方案稱為 jails。由 Poul-Henning Kamp 和 Robert Watson 於 2000 年 3 月在 FreeBSD 4.0 中作為原生核心功能發佈,jails 繼承了 chroot 的概念並將其完善,增加了完整的網路隔離、進程隔離和適當的安全邊界。
每個 jail 都有自己的檔案系統視圖、自己的網路堆疊、自己的進程空間。主機系統對其不可見。至關重要的是,它共享主機核心,意味著幾乎為零的開銷和幾乎即時的啟動時間。
FreeBSD 開創了我們現在稱為容器的實用實現。不是概念上的,而是在生產環境中,比業界其他公司早了數年。
Sun Microsystems 於 2004 年跟進了 Solaris Zones,為其企業客戶調整了 jails 的概念,並回饋了 ZFS——有史以來最先進的檔案系統,於 2005 年開源並於 2007 年移植到 FreeBSD。ZFS 透過即時快照和高效分層來補充 jails。
隔離問題的實際時間線如下:
FreeBSD 在 2000 年達到了第三階段。Linux 直到 2008 年才透過 LXC 達到這一點。Docker——大多數開發者認為是容器起源的工具——直到 2013 年才出現。當 Docker 被譽為革命性時,FreeBSD jails 已經有十三年的歷史,成熟且經過實戰考驗。
技術優勢並不能贏得生態系統戰爭。Linux 透過快速決策、病毒式的 GPL 授權以及 Red Hat 和 IBM 的強大企業支援贏得了勝利。然後出現了 Google、Facebook 和 Amazon——它們渴望數據中心,開發工具來大規模管理不斷增長的基礎設施。它們為整個行業設定了方向。
Linux 迅速從「買不起商業授權的人的免費作業系統」變成了「伺服器唯一可接受的作業系統」。
為了解決分發和隔離問題,Linux 工程師構建了一套核心原語(命名空間、cgroups、seccomp),然後,以一種非常 Linux 的方式,在頂部構建了整個抽象層生態系統來「簡化」事情:
不知何故,我們最終得到了一個過度設計的、充滿洩漏式抽象的混亂局面,用於基於雲的、廠商鎖定的基礎設施。
這種複雜性已經悄悄地重塑了業界對部署軟體的思考方式。如今,如果你想在一個更大的系統中運行一個應用程式,預設的假設是你用 Docker 將其容器化,並用 Kubernetes 編排它。它並不是作為多個選項之一出現——它被呈現為顯而易見的預設,是你天真或魯莽才會跳過的。
Docker 實際上解決得很好的問題是傳輸問題:一種將應用程式及其所有依賴項打包、透過註冊中心分發並在任何地方的任何機器上以相同方式運行的通用標準。這確實很有用,而 OCI 映像格式成為了真正的行業標準。
Jails 完美地解決了隔離問題,但它們沒有原生的傳輸解決方案。這個差距是真實存在的,這也是圍繞 jails 的生態系統感覺不如 Docker 世界發達的主要原因之一。
社群意識到了這一點。一些工具試圖透過模仿現代容器生態系統提供的功能來縮小差距,並取得了一定的成功。但也有其他方法,利用了多年來一直靜靜存在的原生 FreeBSD 原語。
在接下來的部分中,你將看到基於 FreeBSD 的基礎設施可以多麼簡單優雅,jails 如何從頭開始工作,jail 管理器如何幫助減少樣板代碼,你如何使用 Ansible 來配置和部署,為什麼 ZFS 快照是一個值得你關注的殺手級功能,以及我們如何將所有這些結合起來為 Hypha 構建健壯且可擴展的基礎設施。