Docker是用于构建和部署容器的工具包,Nix是包和配置管理器。这些工具确实有一些重叠:它们都可以用于创建可重现的环境。可重现的环境是可以以相同的方式(最好是逐位)从头开始重新创建的环境。实际上,这意味着在环境之间具有相同的工具、版本和配置。
可重现的环境有助于确保项目中的所有开发人员都拥有完全相同的工具集。此外,您可以在与生产环境类似的环境中进行开发——从而减少部署时的意外情况。
这两种工具都可以解决“它在我的机器上可以工作”的古老问题。
虽然这两种工具都旨在解决这个问题,但它们采用了不同的方法。
使用 Docker 的可复制环境
Docker 提供了创建容器镜像的工具。镜像存储容器的内容和配置。这通常是文件系统、一些环境变量和运行命令。
从镜像中,您可以创建功能相同的新容器,即使在不同的机器上也是如此。
您可以将镜像分发给其他开发人员,为他们提供相同的环境,或者您可以使用它们将您的服务交付到生产环境。
Docker 镜像可以通过Dockerfile. 该文件告诉 docker 运行以构建镜像的命令。这可能包括从主机系统复制文件或使用包管理器(如 apt 或 apk)安装包。
FROM node:12-alpine |
Docker 将创建一个用于构建镜像的新容器。在Dockerfile执行命令之前,将为每个命令创建一个新的文件系统层。Docker 使用联合文件系统,它允许堆叠多个文件系统或目录以显示为单个文件系统。
由于每个命令都有自己的层,Docker 可以检测哪些层仍然有效,哪些层在对项目进行更改后需要重建。正确排序命令将导致更快的 docker 镜像重建。
每个层也可以跨多个镜像共享。当从同一个基础 Docker 镜像构建多个 docker 镜像时,这很有用。例如,上面Dockerfile有node:12-alpine作为基础 Docker 镜像。来自的层node:12-alpine只需要存在于一个地方,并且可以被其他 Docker 镜像共享。
构建镜像后,可以对其进行命名、标记和上传到镜像注册表,以便轻松与他人共享。
虽然镜像允许可重现的 Docker 容器,但 Docker 不保证创建的镜像是可重现的——如果你docker build用同一个Dockerfile镜像运行两次,你可能会得到 2 个以不同方式运行的镜像。例如,第三方包可能会悄悄更新并导致 损坏。积极固定依赖版本有帮助,但并不能完全防止这个问题。
另一个值得注意的是,Docker 只允许通过从单个层继承来组合镜像。您可以在 中为新镜像指定基本镜像Dockerfile,但不能拍摄 2 张个镜像并将它们组合起来。如果您想要一个带有nodeand的容器rustc,则不能组合node和rust镜像。您必须从node镜像开始并在 中rustc手动安装,Dockerfile反之亦然。
Nix 的可重现环境
Nix 采用第一原则方法进行可重复构建和包管理。Nix 提供了一个完整的构建系统,允许以隔离的方式构建包。
要构建任何包,您需要:
- 使用构建包所需的所有工具构建环境。
- 执行构建脚本以运行所有构建步骤。
对每个包重复此过程,Nix 竭尽全力确保内置包的每个环境都是可重现的。Nix 将限制网络访问、文件系统访问,有时甚至在构建过程中在沙盒容器中运行,以防止在包构建过程中受到任何类型的外部影响。
包可以依赖于其他包,这会创建一个大型依赖图,Nix 将在此过程中遍历并构建包。
碰巧的是,您可以通过向该图中添加新节点来创建新的可重现环境。然后你可以使用 Nix 来完成包构建的第 1 部分:构建环境。
在开发中,您可能不希望或不需要 Nix 执行第 2 步。相反,您可以让 Nix 将您放入 bash shell,您可以在其中自己运行构建命令。
Nix 附带了一个名为的工具nix-shell,它可以为您做到这一点。将shell.nix文件添加到项目的根目录,然后nix-shell将您带入一个包含所有指定输入包可用的环境。
例子:
{ pkgs ? import <nixpkgs> {} }: |
Nix 在这里不使用容器,它只修改环境变量。例如,将二进制包添加到PATH环境变量中。
由于 Nix 为可重复性提供了强有力的保证,因此共享这shell.nix一切就是为开发人员提供功能等效的开发环境所需要的1。
与 Docker 不同,Nix 是一个成熟的包管理器。这使得组合环境变得微不足道。以上面来自 Docker 的示例为例,如果我们希望 node 和 rust 在同一环境中,只需告诉 Nix 它们都是构建输入:
{ pkgs ? import <nixpkgs> {} }: |
你应该使用哪个?
在考虑这个问题之前,我们应该回到开头:Docker 和 Nix 是完全不同的工具。
Docker 镜像只是 Docker 提供的一小部分。Docker 为整个容器生态系统提供工具。
而 Nix 主要设计用于以可重复的方式构建包和环境。
如果您只追求可复制的开发环境,Nix 将为您提供良好的服务。
如果您正在寻找一种方法来构建、打包和部署您自己的服务。Docker 将提供更丰富的工具。毕竟,容器是当今部署 Web 服务的事实上的方式。
即便如此,docker 镜像构建仍有很多不足之处。幸运的是,我有一张王牌:Nix 可用于构建 Docker 镜像。
{ pkgs ? import <nixpkgs> { } |
也许我们可以两全其美:
- 通过 Nix 可重现的 Docker 镜像
- 通过容器简化部署
Nix 会替代 Docker 吗?
不,这些工具实现了不同的目标,但是它们可以结合使用以提供两全其美的效果:可重复构建和容器化部署。