CSDN · 程式 ·

Nix 會超越 Docker 嗎?

作者 | Connor Brewster

譯者 | 丁廣輝

出品 | CSDN(ID:CSDNnews)

在討論 Nix 的時候,大家總是喜歡拿它和 Docker 做比較。但我認為 Nix 和 Docker 是解決不同問題的工具,前者是構建和部署容器的工具包,而後者則是包和配置管理器。這兩個工具在功能上確實會有一些重疊之處,都可用於創建可重現的環境。所謂「可重現的環境」指的以相同的方式創建完全相同的新環境。也就是說,這些環境擁有相同的工具、版本和配置。

可重現的環境可以確保項目中的所有開發人員都擁有完全相同的工具集。此外,開發人員可以在與生產環境類似的環境中開發軟體,因此可以避免部署時發生意外。

Nix 和 Docker 這兩種工具都可以解決:「它(可重現環境)在我的機器上運行」的問題,不過,二者採用的方法卻不相同。

具包,而後者則是包和配置管理器。這兩個工具在功能上確實會有一些重疊之處,

使用 Docker 構建可重現的環境

Docker 提供了創建容器鏡像的工具。鏡像存儲了容器的內容和配置,通常包含文件系統、一些環境變量和命令。

有了鏡像,你就可以在不同的機器上創建出功能完全相同的新容器。

你可以將鏡像分發給其他開發人員,這樣他們就可以構建出相同的環境了,而且你也可以通過鏡像將服務部署到生產環境。

Docker 鏡像可以由 Dockerfile 創建。該文件告訴 Docker 運行構建鏡像的命令,包括從宿主系統複製文件,或使用包管理器(如 apt 或 apk)安裝軟體包。

下面是一個 Dockerfile 的示例:

 FROM node:12-alpine RUN apk add --no-cache python g++ make WORKDIR /app COPY .  RUN yarn install --production CMD ["node", "src/index.js"]

Docker 會創建一個新容器,並在其中構建鏡像。在執行 Dockerfile 中的每條命令之前,Docker都會創建一個新的文件系統層。Docker 使用的是聯合掛載(Union mount)文件系統,這種系統可以將多個文件系統或目錄疊加起來,顯示為一個文件系統。

由於每條命令都有自己的文件系統層,因此 Docker 可以檢測哪些層仍然有效,而哪些層需要根據項目的變化重建。正確排序的命令可以提高 Docker 重建鏡像的速度。

此外,每個文件系統層可以在多個鏡像之間共享。當需要利用同一個基礎 Docker 鏡像多個新鏡像時,這會非常有用。例如,上述 Dockerfile 的基礎鏡像為 node:12-alpine,只要將其保存在某個地方,其他 Docker 鏡像就可以共享了。

構建好鏡像後,還需要命名、打標籤,然後上傳到鏡像存儲庫,然後就可以輕鬆與他人共享了。

雖然有了鏡像就可以構建可重現的 Docker 容器,但 Docker 並不能保證創建的鏡像是可重現的,也就是說即便使用同一個 Dockerfile,運行兩次 docker build,也有可能得到兩個行為不同的鏡像。例如,第三方軟體包可能在不知不覺間發布了更新,因此導致構建的鏡像也發生變化。確保使用固定的某個版本可以從一定程度上緩解這個問題,但也不能完全杜絕。

另一個需要注意的問題是,在創建鏡像時,Docker 只允許從單個文件系統層繼承。你可以在 Dockerfile 中指定基礎鏡像,但不能將兩個鏡像合併起來。假設你想使用 node 和 rustc 構建容器,由於不能將 node 和 rustc 的鏡像合併起來,所以只能先利用 node 鏡像構建容器,然後再手動安裝 rustc,或者反過來,先用 rustc 構建容器,然後再手動安裝 node 。

Nix和Docker是解決不同問題的工具,前者是構建和部署容器的工

使用 Nix 構建可重現的環境

為了構建可重現的環境,Nix 採用了最基本的方式。Nix 提供了一個完整的構建系統,可以單獨構建各個軟體包。

在構建軟體包時,你需要執行如下步驟:

  1. 使用所需工具構建環境。

  2. 執行腳本,運行所有構建命令。

每個軟體包的構建都可以重複這個過程,為了確保每個環境都是可重現的,Nix 做了大量努力。首先,Nix 會限制網絡訪問、文件系統訪問,有時甚至還會在構建過程中在沙盒容器中運行,以防止構建過程中受到任何外部因素的影響。

由於軟體包會依賴第三方庫,形成巨大的依賴關係網,因此 Nix 會在構建過程中遍歷所有依賴項。

而你可以使用 Nix 創建可重現的環境,只需在關係網中添加一個節點,然後讓 Nix 完成第一步構建環境的工作即可。

在開發中,你可能不希望或不需要 Nix 執行第二步。相反,你可以將 Nix 放入bash shell,自行運行構建命令。

Nix 自帶一個名叫 nix-shell 的工具,它可以為你完成這項工作。將 shell.nix 文件添加到項目的根目錄,nix-shell 就可以根據所有指定的軟體包構建一個新環境。

下面是一個 shell.nix 文件的示例:

 { pkgs ? import <nixpkgs> {} }: pkgs.mkShell { nativeBuildInputs = [ pkgs.rustc pkgs.cargo ]; }

Nix 並不會使用容器,它只會修改環境變量。例如,將某個二進位包添加到環境變量 PATH 中。

由於 Nix 可以保證構建可重複的環境,因此,只需共享此 shell.nix 文件,即可為開發人員提供功能完全相同的開發環境。

與 Docker 不同,Nix 是一個成熟的包管理器,因此 Nix 可以自由組合環境。以上述 Docker 示例為例,如果我們想在同一個環境中同時部署 node 和 rust,則只需告訴 Nix 將二者添加到構建的輸入中:

 { pkgs ? import <nixpkgs> {} }: pkgs.mkShell { nativeBuildInputs = [ pkgs.rustc pkgs.cargo pkgs.nodejs-16_x ]; }
論Nix的時候,大家總是喜歡拿它和Docker做比較。但我認為

應該使用哪個?

在考慮這個問題之前,我們首先應該回顧一下:Docker 和 Nix 是完全不同的工具。

Docker 鏡像只是 Docker 提供的一小部分,Docker 為整個容器生態系統提供了工具。

而 Nix 的設計旨在構建可重現的包和環境。

如果你的目的是構建可重現的開發環境,則選用 Nix 更合適。

如果你希望尋找一種方法來構建、打包和部署自己的服務,則 Docker 提供的更豐富的工具更適合你。畢竟,容器乃是如今部署 Web 服務的標準方式。

即便如此,Docker 鏡像的構建仍有很多不足之處。不過,好在我還有一張王牌:利用 Nix 構建Docker 鏡像例如:

{ pkgs ? import <nixpkgs> { }
, pkgsLinux ? import <nixpkgs> { system = "x86_64-linux"; }
}:pkgs.dockerTools.buildImage { name = "cowsay-container";
config = { Cmd = [ "${pkgsLinux.cowsay}/bin/cowsay" "I'm a container" ];
};}

如此一來,我們就可以魚與熊掌兼得:

  • 利用 Nix 構建可重現的 Docker 鏡像。

  • 利用容器簡化部署。

廣輝出品|CSDN(ID:CSDNnews)在討

Replit 如何使用 Nix

目前,我們正在從通過巨大的 Docker 鏡像 Polygott 提供開發工具朝著通過 Nix 提供軟體包過渡。你可以看一看用戶使用Nix構建的應用(https://replit.com/apps/nix)。

我們發現, Nix 解決了我們在使用 Docker 鏡像時遇到的下列問題:

  • 很難獲知兩種不同版本的 Polygott 之間發生了什麼變化。

  • 第三方庫的更新,可能會導致用戶的執行環境出現各種問題。

  • 第三方庫的下線,會導致鏡像構建出問題。

  • 很難將所有支持語言的開發工具組合到一個 Docker 鏡像中。

Polygott 提供了一堆腳本和配置文件,用於安裝每種語言所需的工具,這些維護工作非常痛苦。

我們做了很多嘗試,但最終發現沒有辦法在多個鏡像構建之間共享一些文件系統層。Polygott 本身的大小為 20~30GB,如果在生產環境中安裝多個版本,規模會迅速膨脹。

如今在使用了 Nix 後,我們可以讓用戶自由選擇他們需要的工具。同時還解決了上述問題,減輕了我們內部的維護負擔。雙贏!

雖然我們使用了 Nix,但我們也不會因此而放棄 Docker 容器。我們仍然需要容器來為每個用戶執行環境提供一個隔離的環境。

Nix 會超越 Docker 嗎?不會,這兩款工具實現了不同的目標,二者的結合可以提供完美的解決方案:可重現的環境與容器化的部署。

原文連結:https://blog.replit.com/nix-vs-docker

聲明:本文為 CSDN 翻譯,轉載請註明來源。

作者|ConnorBrewster譯者|丁

END

成就一億技術人

聲明:文章觀點僅代表作者本人,PTTZH僅提供信息發布平台存儲空間服務。
喔!快樂的時光竟然這麼快就過⋯
繼續其他精彩內容吧!
more