diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index d91c424..f5d8a25 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -186,7 +186,7 @@ function themeConfigEnglish() { text: "NixOS with Flakes Enabled", link: "/nixos-with-flakes/nixos-with-flakes-enabled.md", }, - { + { text: "NixOS's flake.nix Explained", link: "/nixos-with-flakes/nixos-flake-configuration-explained.md", }, @@ -233,6 +233,20 @@ function themeConfigEnglish() { }, ], }, + { + text: "Nix Store & Binary Cache", + items: [ + { text: "Introduction", link: "/nix-store/intro.md" }, + { + text: "Add Binary Cache Servers", + link: "nix-store/add-binary-cache-servers.md", + }, + { + text: "Host Your Own Binary Cache Server", + link: "/nix-store/host-your-own-binary-cache-server.md", + }, + ], + }, { text: "Best Practices", items: [ @@ -261,10 +275,6 @@ function themeConfigEnglish() { text: "Debugging Derivations and Nix Expressions", link: "/best-practices/debugging.md", }, - { - text: "Host Custom Binary Cache with S3", - link: "/best-practices/host-custom-binary-cache-with-s3.md", - }, ], }, @@ -439,6 +449,20 @@ function themeConfigChinese() { }, ], }, + { + text: "Nix Store 与二进制缓存", + items: [ + { text: "简介", link: "/zh/nix-store/intro.md" }, + { + text: "添加二进制缓存服务器", + link: "/zh/nix-store/add-binary-cache-servers.md", + }, + { + text: "搭建你自己的缓存服务器", + link: "/zh/nix-store/host-your-own-binary-cache-server.md", + }, + ], + }, { text: "NixOS 最佳实践", items: [ @@ -467,10 +491,6 @@ function themeConfigChinese() { text: "调试 Nix 软件包与 Nix 表达式", link: "/zh/best-practices/debugging.md", }, - { - text: "使用 S3 托管自定义二进制缓存", - link: "/zh/best-practices/host-custom-binary-cache-with-s3.md", - }, ], }, { diff --git a/docs/best-practices/host-custom-binary-cache-with-s3.md b/docs/best-practices/host-custom-binary-cache-with-s3.md deleted file mode 100644 index 6fa3f74..0000000 --- a/docs/best-practices/host-custom-binary-cache-with-s3.md +++ /dev/null @@ -1,267 +0,0 @@ -# Host Custom Binary Cache with S3 {#host-custom-binary-cache-with-s3} - -## TL;DR - -A guide on how to set up your own S3 nix binary cache using MinIO S3 server. - -## How Software Stored in Nix? {#how-software-stored-in-nix} - -Multiple versions of the same software package can be installed on a system, making it -possible to satisfy various dependency chains at a time. This enables the installation of -multiple packages that depend on the same third package, but on different versions of it. -To achieve this, all packages are installed in the global nix store under `/nix/store/` -and are then symlinked to the respective locations. To verify a package's uniqueness, its -whole directory is hashed, and the hash put into the name of the package's main folder. -Every software built from the same Nix expression that uses the same dependency software -versions results in the same hash, no matter what system it was built on. If any of the -dependency software versions were changed, this will result in a new hash for the final -package. - -Using symlinks to install a package and link all the right dependencies to it also enables -atomic updating. To make this clearer, let's think of an example where software X is -installed in an older version and should be updated. Software X is installed in its very -own directory in the global nix store and symlinked to the right directory, let's say -`/usr/local/bin/`. When the update is triggered, the new version of X is installed into -the global nix store without interfering with its older version. Once the installation -with all its dependencies in the nix store is completed, the final step is to change the -symlink to `/usr/local/bin/`. Since creating a new symlink that overwrites the old one is -an atomic operation in Unix, it is impossible for this operation to fail and leave the -package in a corrupted state. The only possible problem would be that it fails before or -after the symlink creation. Either way, the result would be that we either have the old -version of X or the newly installed version, but nothing in between. - -Quoted from the original work of -https://medium.com/earlybyte/the-s3-nix-cache-manual-e320da6b1a9b - -## Nix Binary Caches {#nix-binary-caches} - -No matter how great every aspect of Nix sounds, its design has also a major drawback, -which is that every package build triggers the build process for the whole dependency -chain from scratch. This can take quite a while, since even compilers such as gcc or ghc -must be built in advance. Such build processes can eat up a remarkable amount of memory, -introducing an additional hurdle if one wants to use it on restricted platforms such as -the Raspberry Pi. - -To overcome the drawback of always building everything from scratch and the chance of -losing access to prebuilt versions of packages, it is also possible to build your Nix -binary cache using an S3 server such as MinIO (https://min.io/). - -The process of setting up your cache, populating it with binaries and use the binaries -from your newly built cache will be described step by step. For this manual, I assume that -you already have nix in place, and that you already have a running MinIO server somewhere -in your environment. If not, you may check out the -[official deployment guide](https://min.io/docs/minio/linux/operations/installation.html) -from MinIO. You'll need to ensure that MinIO is accessible via `HTTPS` using a trusted -certificate. Let's Encrypt will be helpful here. - -In this post, let's explore how we can self-host an S3-compatible server, MinIO, as a -binary cache store. - -Quoted from the original work of -https://medium.com/earlybyte/the-s3-nix-cache-manual-e320da6b1a9b - -## How To Use S3 as a Binary Cache Server {#how-to-use-s3-as-a-binary-cache-server} - -### Prerequisites {#prerequisites} - -- Set up MinIO somewhere in your environment. -- Hold a valid SSL certificates either public or private. For demonstration purpose, we - will use `minio.homelab.local` (private certificate) in the steps mentioned in this - tutorial. If you plan to use a private certificate, you MUST resolve the DNS challenges - on your own. Hence, it is recommended to use a public certificate. -- Install `minio-client` in your environment. - -### Generate Password {#generate-password} - -```bash -nix run nixpkgs#pwgen -- -c -n -y -s -B 32 1 -# oenu1Yuch3rohz2ahveid0koo4giecho -``` - -### Set Up MinIO Client {#set-up-minio-client} - -Install the MinIO command-line client `mc`. - -```nix -{ pkgs, ... }: - -{ - environment.systemPackages = with pkgs; [ - minio-client # A replacement for ls, cp, mkdir, diff, and rsync commands for filesystems and object storage - ]; -} -``` - -Create or edit `~/.mc/config.json`. - -```json -{ - "version": "10", - "aliases": { - "s3": { - "url": "https://s3.homelab.local", - "accessKey": "minio", - "secretKey": "oenu1Yuch3rohz2ahveid0koo4giecho", - "api": "s3v4", - "path": "auto" - } - } -} -``` - -On the machine we use to build the nix packages, we need the S3 credentials in places. Set -up a file `~/.aws/credentials` and populate it with the credentials of our nixbuilder -user. Replace `` with the password generated by the `pwgen` command. - -```bash -[default] -aws_access_key_id=nixbuilder -aws_secret_access_key= -``` - -### Setup S3 Bucket as Binary Cache {#setup-s3-bucket-as-binary-cache} - -Create the `nix-cache` bucket. - -```bash -mc mb s3/nix-cache -``` - -Create the `nixbuilder` MinIO user and assign a password. - -```bash -mc admin user add s3 nixbuilder -``` - -Create a file called `nix-cache-write.json` in your current working directory with the -following contents: - -```json -{ - "Id": "AuthenticatedWrite", - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "AuthenticatedWrite", - "Action": [ - "s3:AbortMultipartUpload", - "s3:GetBucketLocation", - "s3:GetObject", - "s3:ListBucket", - "s3:ListBucketMultipartUploads", - "s3:ListMultipartUploadParts", - "s3:PutObject" - ], - "Effect": "Allow", - "Resource": ["arn:aws:s3:::nix-cache", "arn:aws:s3:::nix-cache/*"], - "Principal": "nixbuilder" - } - ] -} -``` - -Create a policy with `nix-cache-write.json` that allows `nixbuilder` to upload files to -the cache. - -```bash -mc admin policy add s3 nix-cache-write nix-cache-write.json -``` - -Associate the policy that we created above with the `nixbuilder` user. - -```bash -mc admin policy set s3 nix-cache-write user=nixbuilder -``` - -Allow anonymous users to download files without authenticating. - -```bash -mc anonymous set download s3/nix-cache -``` - -Create a file called `nix-cache-info` in your working directory. This file tells Nix that -the bucket is indeed a binary cache. - -```bash -cat > nix-cache-info < ~/.config/nix/secret.key -nix key convert-secret-to-public < ~/.config/nix/secret.key > ~/.config/nix/public.key -cat ~/.config/nix/public.key -# s3.homelab.local-1:m0J/oDlLEuG6ezc6MzmpLCN2MYjssO3NMIlr9JdxkTs= -``` - -### Activate Binary Cache with Flake {#activate-binary-cache-with-flake} - -Put the following lines in `configuration.nix` or any of your custom NixOS module: - -```nix -{ - nix = { - settings = { - # Substituters will be appended to the default substituters when fetching packages. - extra-substituters = [ - "https://s3.homelab.local/nix-cache/" - ]; - extra-trusted-public-keys = [ - "s3.homelab.local-1:m0J/oDlLEuG6ezc6MzmpLCN2MYjssO3NMIlr9JdxkTs=" - ]; - }; - }; -} -``` - -Rebuild the system. - -```bash -sudo nixos-rebuild switch --upgrade --flake .# -``` - -### Push Paths to the Store {#push-paths-to-the-store} - -Sign some paths in the local store. - -```bash -nix store sign --recursive --key-file ~/.config/nix/secret.key /run/current-system -``` - -Copy those paths to the cache. - -```bash -nix copy --to 's3://nix-cache?profile=nixbuilder&endpoint=s3.homelab.local' /run/current-system -``` - -### Add Automatic Object Expiration Policy {#add-automatic-object-expiration-policy} - -```bash -mc ilm rule add s3/nix-cache --expire-days "DAYS" -# Example: mc ilm rule add s3/nix-cache --expire-days "7" -``` - -### References {#references} - -Here are some of the sources that I used in making this document: - -- [Blog post by Jeff on Nix binary caches](https://jcollie.github.io/nixos/2022/04/27/nixos-binary-cache-2022.html) -- [Binary cache in the NixOS wiki](https://nixos.wiki/wiki/Binary_Cache) -- [Serving a Nox store via S3 in the NixOS manual](https://nixos.org/manual/nix/stable/package-management/s3-substituter.html) -- [Serving a Nix store via HTTP in the NixOS manual](https://nixos.org/manual/nix/stable/package-management/binary-cache-substituter.html) diff --git a/docs/nixos-with-flakes/add-custom-cache-servers.md b/docs/nix-store/add-binary-cache-servers.md similarity index 95% rename from docs/nixos-with-flakes/add-custom-cache-servers.md rename to docs/nix-store/add-binary-cache-servers.md index 7ecfb31..4fb0daa 100644 --- a/docs/nixos-with-flakes/add-custom-cache-servers.md +++ b/docs/nix-store/add-binary-cache-servers.md @@ -1,23 +1,21 @@ -# Adding Custom Cache Servers {#add-custom-cache-servers} +# Adding Binary Cache Servers -## What is Nix Cache Server {#what-is-nix-cache-server} +We have introduced the concepts of Nix Store and binary cache. Here, we will see how to +add multiple cache servers to speed up package downloads. + +## Why Add Cache Servers {#why-add-cache-servers} Nix provides an official cache server, [https://cache.nixos.org](https://cache.nixos.org), -which caches build results for all packages in nixpkgs under commonly used CPU -architectures. When you execute Nix build commands locally, if Nix finds a corresponding -cache on the server, it directly downloads the cached file, skipping the time-consuming -local build process and significantly improving build speed. - -## Why Add Custom Cache Servers {#why-add-custom-cache-servers} - -Two reasons: +which caches build results for most commonly used packages. However, it may not meet all +users' needs. In the following cases, we need to add additional cache servers: 1. Add cache servers for some third-party projects, such as the nix-community cache server [https://nix-community.cachix.org](https://nix-community.cachix.org), which can significantly improve the build speed of these third-party projects. 1. Add cache server mirror sites closest to the user to speed up downloads. +1. Add a self-built cache server to speed up the build process of personal projects. -## How to Add Custom Cache Servers {#how-to-add-custom-cache-servers} +## How to Add Cache Servers {#how-to-add-custom-cache-servers} In Nix, you can configure cache servers using the following options: diff --git a/docs/nix-store/host-your-own-binary-cache-server.md b/docs/nix-store/host-your-own-binary-cache-server.md new file mode 100644 index 0000000..32ec803 --- /dev/null +++ b/docs/nix-store/host-your-own-binary-cache-server.md @@ -0,0 +1,228 @@ +# Using S3 for Custom Binary Cache Hosting + +## Introduction + +The Nix binary cache is an implementation of the Nix Store that stores data on a remote +server rather than locally, facilitating the sharing of binary caches across multiple +machines. + +The official Nix binary cache server only provides binaries built with standard +parameters. If you've customized build parameters or are using packages outside of +Nixpkgs, Nix won't find the corresponding binary cache, resulting in local builds. + +Relying solely on your local Nix Store `/nix/store` can be cumbersome, as you'd need to +rebuild all your custom packages on each machine, which can be time-consuming and +memory-intensive. This situation is exacerbated on lower-performance platforms like +Raspberry Pi. + +This document will show you how to set up your own Nix binary cache server using an S3 +service (like MinIO) to share build results across machines and address the aforementioned +issues. + +## Prerequisites + +1. A NixOS host +1. Deployed MinIO server + 1. If not, you can follow MinIO's + [official deployment guide](https://min.io/docs/minio/linux/operations/installation.html). +1. The MinIO server needs a valid TLS digital certificate, which can be public or private. + This example will use `https://minio.homelab.local` with a private certificate. +1. Install `minio-client` + +## Generating a Password + +```bash +nix run nixpkgs#pwgen -- -c -n -y -s -B 32 1 +# => oenu1Yuch3rohz2ahveid0koo4giecho +``` + +## Setting Up the MinIO Client + +Install the MinIO command-line client `mc`. + +```nix +{ pkgs, ... }: +{ + environment.systemPackages = with pkgs; [ + minio-client # Alternatives for ls, cp, mkdir, diff, and rsync commands for file systems and object storage + ]; +} +``` + +Create `~/.mc/config.json` with the following content (replace the key parameters with +your own): + +```json +{ + "version": "10", + "aliases": { + "s3": { + "url": "https://s3.homelab.local", + "accessKey": "minio", + "secretKey": "oenu1Yuch3rohz2ahveid0koo4giecho", + "api": "s3v4", + "path": "auto" + } + } +} +``` + +Since Nix will interact directly with the S3 bucket, we need to configure S3 credentials +for all machines that require access to the Nix binary cache. + +Create `~/.aws/credentials` with the following content (replace `` with +the password generated by the `pwgen` command). + +```toml +[default] +aws_access_key_id=nixbuilder +aws_secret_access_key= +``` + +## Setting Up S3 Bucket as Binary Cache + +Create the `nix-cache` bucket using the minio client: + +```bash +mc mb s3/nix-cache +``` + +Create the `nixbuilder` user for MinIO and assign it a password: + +```bash +mc admin user add s3 nixbuilder +``` + +Create a file named `nix-cache-write.json` in the current working directory with the +following content: + +```json +{ + "Id": "AuthenticatedWrite", + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "AuthenticatedWrite", + "Action": [ + "s3:AbortMultipartUpload", + "s3:GetBucketLocation", + "s3:GetObject", + "s3:ListBucket", + "s3:ListBucketMultipartUploads", + "s3:ListMultipartUploadParts", + "s3:PutObject" + ], + "Effect": "Allow", + "Resource": ["arn:aws:s3:::nix-cache", "arn:aws:s3:::nix-cache/*"], + "Principal": "nixbuilder" + } + ] +} +``` + +Now, create a policy for uploading files to S3 using the `nix-cache-write.json` file: + +```bash +mc admin policy add s3 nix-cache-write nix-cache-write.json +``` + +Associate the S3 policy we just created with the `nixbuilder` user: + +```bash +mc admin policy set s3 nix-cache-write user=nixbuilder +``` + +Allow anonymous users to download files without authentication, so all Nix servers can +pull data directly from this S3 cache: + +```bash +mc anonymous set download s3/nix-cache +``` + +Finally, add the `nix-cache-info` file to the S3 bucket root directory, as Nix requires +this file to record some information related to the binary cache: + +```bash +cat > nix-cache-info < ~/.config/nix/secret.key +nix key convert-secret-to-public < ~/.config/nix/secret.key > ~/.config/nix/public.key +cat ~/.config/nix/public.key +# => s3.homelab.local-1:m0J/oDlLEuG6ezc6MzmpLCN2MYjssO3NMIlr9JdxkTs= +``` + +## Using S3 Binary Cache in `flake.nix` + +Add the following to your `configuration.nix` or any custom NixOS module: + +```nix +{ + nix = { + settings = { + # The substituter will be appended to the default substituters when fetching packages. + extra-substituters = [ + "https://s3.homelab.local/nix-cache/" + ]; + extra-trusted-public-keys = [ + "s3.homelab.local-1:m0J/oDlLEuG6ezc6MzmpLCN2MYjssO3NMIlr9JdxkTs=" + ]; + }; + }; +} +``` + +Rebuild the system to start using our newly created S3 binary cache: + +```bash +sudo nixos-rebuild switch --upgrade --flake .# +``` + +## Pushing Store Paths to Binary Cache + +Sign some paths in the local store. + +```bash +nix store sign --recursive --key-file ~/.config/nix/secret.key /run/current-system +``` + +Copy these paths to the cache: + +```bash +nix copy --to 's3://nix-cache?profile=nixbuilder&endpoint=s3.homelab.local' /run/current-system +``` + +## Adding Automatic Object Expiration Policy + +```bash +mc ilm rule add s3/nix-cache --expire-days "DAYS" +# For example: mc ilm rule add s3/nix-cache --expire-days "7" +``` + +This will set an expiration policy for objects in the S3 bucket, ensuring that they are +automatically removed after a specified number of days. + +This is useful for keeping the cache size manageable and ensuring that outdated binaries +are not stored indefinitely. + +### References {#references} + +- [Blog post by Jeff on Nix binary caches](https://jcollie.github.io/nixos/2022/04/27/nixos-binary-cache-2022.html) +- [Binary cache in the NixOS wiki](https://nixos.wiki/wiki/Binary_Cache) +- [Serving a Nox store via S3 in the NixOS manual](https://nixos.org/manual/nix/stable/package-management/s3-substituter.html) +- [Serving a Nix store via HTTP in the NixOS manual](https://nixos.org/manual/nix/stable/package-management/binary-cache-substituter.html) diff --git a/docs/nix-store/intro.md b/docs/nix-store/intro.md new file mode 100644 index 0000000..c75c778 --- /dev/null +++ b/docs/nix-store/intro.md @@ -0,0 +1,178 @@ +# Nix Store and Binary Cache + +Here we provide a brief introduction to the Nix Store, Nix binary cache, and related +concepts, without delving into specific configurations and usage methods, which will be +covered in detail in subsequent chapters. + +## Nix Store + +The Nix Store is one of the core concepts of the Nix package manager. It is a read-only +file system used to store all files that require immutability, including the build results +of software packages, metadata of software packages, and all build inputs of software +packages. + +The Nix package manager uses the Nix functional language to describe software packages and +their dependencies. Each software package is treated as the output of a pure function, and +the build results of the software package are stored in the Nix Store. + +Data in the Nix Store has a fixed path format: + +``` +/nix/store/b6gvzjyb2pg0kjfwrjmg1vfhh54ad73z-firefox-33.1 +|--------| |------------------------------| |----------| +store directory digest name +``` + +As seen, paths in the Nix Store start with a hash value (digest), followed by the name and +version number of the software package. This hash value is calculated based on all input +information of the software package (build parameters, dependencies, dependency versions, +etc.), and any changes in build parameters or dependencies will result in a change in the +hash value, thus ensuring the uniqueness of each software package path. Additionally, +since the Nix Store is a read-only file system, it ensures the immutability of software +packages - once a software package is built, it will not change. + +Because the storage path of the build result is calculated based on all input information +of the build process, **the same input information will yield the same storage path**. +This design is also known as the input-addressed model (_Input-addressed Model_). + +### How NixOS Uses the Nix Store + +NixOS's declarative configuration calculates which software packages need to be installed +and then soft-links the storage paths of these packages in the Nix Store to +`/run/current-system`, and by modifying environment variables like `PATH` to point to the +corresponding folder in `/run/current-system`, the installation of software packages is +achieved. Each time a deployment is made, NixOS calculates the new system configuration, +cleans up old symbolic links, and re-creates new symbolic links to ensure that the system +environment matches the declarative configuration. + +home-manager works similarly, soft-linking the software packages configured by the user to +`/etc/profiles/per-user/your-username` and modifying environment variables like `PATH` to +point to this path, thus installing user software packages. + +```bash +# Check where bash in the environment comes from (installed using NixOS) +› which bash +╭───┬─────────┬─────────────────────────────────┬──────────╮ +│ # │ command │ path │ type │ +├───┼─────────┼─────────────────────────────────┼──────────┤ +│ 0 │ bash │ /run/current-system/sw/bin/bash │ external │ +╰───┴─────────┴─────────────────────────────────┴──────────╯ + +› ls -al /run/current-system/sw/bin/bash +lrwxrwxrwx 15 root root 76 1970年 1月 1日 /run/current-system/sw/bin/bash -> /nix/store/1zslabm02hi75anb2w8zjrqwzgs0vrs3-bash-interactive-5.2p26/bin/bash + +# Check where cowsay in the environment comes from (installed using home-manager) +› which cowsay +╭───┬─────────┬────────────────────────────────────────┬──────────╮ +│ # │ command │ path │ type │ +├───┼─────────┼────────────────────────────────────────┼──────────┤ +│ 0 │ cowsay │ /etc/profiles/per-user/ryan/bin/cowsay │ external │ +╰───┴─────────┴────────────────────────────────────────┴──────────╯ + +› ls -al /etc/profiles/per-user/ryan/bin/cowsay +lrwxrwxrwx 2 root root 72 1970年 1月 1日 /etc/profiles/per-user/ryan/bin/cowsay -> /nix/store/w2czyf82gxz4vy9kzsdhr88112bmc0c1-home-manager-path/bin/cowsay +``` + +The `nix develop` command, on the other hand, directly adds the storage paths of software +packages to environment variables like `PATH` and `LD_LIBRARY_PATH`, enabling the newly +created shell environment to directly use these software packages or libraries. + +For example, in the source code repository for this book, +[ryan4yin/nixos-and-flakes-book](https://github.com/ryan4yin/nixos-and-flakes-book), after +executing the `nix develop` command, we can examine the contents of the `PATH` environment +variable: + +```bash +› nix develop +node v20.9.0 +› env | egrep '^PATH' +PATH=/nix/store/h13fnmpm8m28qypsba2xysi8a90crphj-pre-commit-3.6.0/bin:/nix/store/2mqyvwp96d4jynsnzgacdk5rg1kx2a9a-node2nix-1.11.0/bin:/nix/store/a1hckfqzyys4rfgbdy5kmb5w0zdr55i5-nodejs-20.9.0/bin:/nix/store/gjrfcl2bhv7kbj883k7b18n2aprgv4rf-pnpm-8.10.2/bin:/nix/store/z6jfxqyj1wq62iv1gn5b5d9ms6qigkg0-yarn-1.22.19/bin:/nix/store/2k5irl2cfw5m37r3ibmpq4f7jndb41a8-prettier-3.0.3/bin:/nix/store/zrs710jpfn7ngy5z4c6rrwwjq33b2a0y-git-2.42.0/bin:/nix/store/dkmyyrkyl0racnhsaiyf7rxf43yxhx92-typos-1.16.23/bin:/nix/store/imli2in1nr1h8qh7zh62knygpl2zj66l-alejandra-3.0.0/bin:/nix/store/85jldj870vzcl72yz03labc93bwvqayx-patchelf-0.15.0/bin:/nix/store/90h6k8ylkgn81k10190v5c9ldyjpzgl9-gcc-wrapper-12.3.0/bin:/nix/store/hf2gy3km07d5m0p1lwmja0rg9wlnmyr7-gcc-12.3.0/bin:/nix/store/cx01qk0qyylvkgisbwc7d3pk8sliccgh-glibc-2.38-27-bin/bin:/nix/store/bblyj5b3ii8n6v4ra0nb37cmi3lf8rz9-coreutils-9.3/bin:/nix/store/1alqjnr40dsk7cl15l5sn5y2zdxidc1v-binutils-wrapper-2.40/bin:/nix/store/1fn92b0783crypjcxvdv6ycmvi27by0j-binutils-2.40/bin:/nix/store/bblyj5b3ii8n6v4ra0nb37cmi3lf8rz9-coreutils-9.3/bin:/nix/store/l974pi8a5yqjrjlzmg6apk0jwjv81yqw-findutils-4.9.0/bin:/nix/store/8q25nyfirzsng6p57yp8hsaldqqbc7dg-diffutils-3.10/bin:/nix/store/9c5qm297qnvwcf7j0gm01qrslbiqz8rs-gnused-4.9/bin:/nix/store/rx2wig5yhpbwhnqxdy4z7qivj9ln7fab-gnugrep-3.11/bin:/nix/store/7wfya2k95zib8jl0jk5hnbn856sqcgfk-gawk-5.2.2/bin:/nix/store/xpidksbd07in3nd4sjx79ybwwy81b338-gnutar-1.35/bin:/nix/store/202iqv4bd7lh6f7fpy48p7q4d96lqdp7-gzip-1.13/bin:/nix/store/ik7jardq92dxw3fnz3vmlcgi9c8dwwdq-bzip2-1.0.8-bin/bin:/nix/store/v4iswb5kwj33l46dyh2zqh0nkxxlr3mz-gnumake-4.4.1/bin:/nix/store/q1c2flcykgr4wwg5a6h450hxbk4ch589-bash-5.2-p15/bin:/nix/store/cbj1ph7zi009m53hxs90idl1f5i9i941-patch-2.7.6/bin:/nix/store/76z4cjs7jj45ixk12yy6k5z2q2djk2jb-xz-5.4.4-bin/bin:/nix/store/qmfxld7qhk8qxlkx1cm4bkplg1gh6jgj-file-5.45/bin:/home/ryan/.local/bin:/home/ryan/go/bin:/home/ryan/.config/emacs/bin:/home/ryan/.local/bin:/home/ryan/go/bin:/home/ryan/.config/emacs/bin:/nix/store/jsc6jydv5zjpb3dvh0lxw2dzxmv3im9l-kitty-0.32.1/bin:/nix/store/ihpdcszhj8bdmyr0ygvalqw9zagn0jjz-imagemagick-7.1.1-28/bin:/nix/store/2bm2yd5jqlwf6nghlyp7z88g28j9n8r0-ncurses-6.4-dev/bin:/run/wrappers/bin:/guix/current/bin:/home/ryan/.guix-home/profile/bin:/home/ryan/.guix-profile/bin:/home/ryan/.nix-profile/bin:/nix/profile/bin:/home/ryan/.local/state/nix/profile/bin:/etc/profiles/per-user/ryan/bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin:/nix/store/c53f8hagyblvx52zylsnqcc0b3nxbrcl-binutils-wrapper-2.40/bin:/nix/store/fpagbmzdplgky01grwhxcsazvhynv1nz-pciutils-3.10.0/bin:/nix/store/4cjqvbp1jbkps185wl8qnbjpf8bdy8j9-gcc-wrapper-13.2.0/bin +``` + +Clearly, `nix develop` has added the storage paths of many software packages directly to +the `PATH` environment variable. + +## Nix Store Garbage Collection + +The Nix Store is a centralized storage system where all software package build inputs and +outputs are stored. As the system is used, the number of software packages in the Nix +Store will increase, and the disk space occupied will grow larger. + +To prevent the Nix Store from growing indefinitely, the Nix package manager provides a +garbage collection mechanism for the local Nix Store, to clean up old data and reclaim +storage space. + +According to +[Chapter 11. The Garbage Collector - nix pills](https://nixos.org/guides/nix-pills/garbage-collector), +the `nix-store --gc` command performs garbage collection by recursively traversing all +symbolic links in the `/nix/var/nix/gcroots/` directory to find all referenced packages +and delete those that are no longer referenced. The `nix-collect-garbage --delete-old` +command goes a step further by first deleting all old +[profiles](https://nixos.org/manual/nix/stable/command-ref/files/profiles) and then +running the `nix-store --gc` command to clean up packages that are no longer referenced. + +It's important to note that build results from commands like `nix build` and `nix develop` +are not automatically added to `/nix/var/nix/gcroots/`, so these build results may be +cleaned up by the garbage collection mechanism. You can use `nix-instantiate` with +`keep-outputs = true` and other means to avoid this, but I currently prefer setting up +your own binary cache server and configuring a longer cache time (e.g., one year), then +pushing data to the cache server. This way, you can share build results across machines +and avoid having local build results cleaned up by the local garbage collection mechanism, +achieving two goals in one. + +## Binary Cache + +The design of Nix and the Nix Store ensures the immutability of software packages, +allowing build results to be shared directly between multiple machines. As long as these +machines use the same input information to build a package, they will get the same output +path, and Nix can reuse the build results from other machines instead of rebuilding the +package, thus speeding up the installation of software packages. + +The Nix binary cache is designed based on this feature; it is an implementation of the Nix +Store that stores data on a remote server instead of locally. When needed, the Nix package +manager downloads the corresponding build results from the remote server to the local +`/nix/store`, avoiding the time-consuming local build process. + +Nix provides an official binary cache server at , which caches +build results for most packages in nixpkgs for common CPU architectures. When you execute +a Nix build command on your local machine, Nix first attempts to find the corresponding +binary cache on the cache server. If found, it will directly download the cache file, +bypassing the time-consuming local compilation and greatly accelerating the build process. + +## Nix Binary Cache Trust Model + +The **Input-addressed Model** only guarantees that the same input will produce the same +output path, but it does not ensure the uniqueness of the output content. This means that +even with the same input information, multiple builds of the same software package may +produce different output content. + +While Nix has taken measures such as disabling network access in the build environment and +using fixed timestamps to minimize uncertainty, there are still some uncontrollable +factors that can influence the build process and produce different output content. These +differences in output content typically do not affect the functionality of the software +package but do pose a challenge for the secure sharing of binary cache - the uncertainty +in output content makes it difficult to determine whether the binary cache downloaded from +the cache server was indeed built with the declared input information, and whether it +contains malicious content. + +To address this, the Nix package manager uses a public-private key signing mechanism to +verify the source and integrity of the binary cache. This places the responsibility of +security on the user. If you wish to use a non-official cache server to speed up the build +process, you must add the public key of that server to `trusted-public-keys` and assume +the associated security risks - the cache server might provide cached data that includes +malicious content. + +### Content-addressed Model + +[RFC062 - content-addressed store paths](https://github.com/NixOS/rfcs/blob/master/rfcs/0062-content-addressed-paths.md) +is an attempt by the community to improve build result consistency. It proposes a new way +to calculate storage paths based on the build results (outputs) rather than the input +information (inputs). This design ensures consistency in build results - if the build +results are different, the storage paths will also be different, thus avoiding the +uncertainty in output content inherent in the input-addressed model. + +However, this approach is still in an experimental stage and has not been widely adopted. + +## References + +- [Nix Store - Nix Manual](https://nixos.org/manual/nix/stable/store/) diff --git a/docs/zh/best-practices/host-custom-binary-cache-with-s3.md b/docs/zh/best-practices/host-custom-binary-cache-with-s3.md deleted file mode 100644 index ec91b43..0000000 --- a/docs/zh/best-practices/host-custom-binary-cache-with-s3.md +++ /dev/null @@ -1,243 +0,0 @@ -# 使用 S3 自定义二进制缓存托管 - -## 简介 - -一个关于如何使用 MinIO S3 服务器设置自己的 S3 Nix 二进制缓存的指南。 - -## Nix 中的软件如何存储? - -可以在系统上安装同一软件包的多个版本,从而能够同时满足各种依赖链。这使得可以安装多个依赖于 -同一个第三方软件包的软件包,但使用不同版本的它。为了实现这一点,所有软件包都安装在全局的 -Nix 存储中,路径为 `/nix/store/`,然后通过符号链接到相应的位置。为了验证软件包的唯一性,其 -整个目录被哈希,并将哈希放入软件包的主文件夹的名称中。从使用相同依赖软件版本构建的相同 Nix -表达式构建的每个软件包都会产生相同的哈希,无论它是在什么系统上构建的。如果任何依赖软件的版 -本发生更改,这将导致最终软件包的新哈希。 - -使用符号链接来“安装”软件包并将所有正确的依赖项链接到它还使得原子更新成为可能。为了使这一点 -更清楚,让我们来考虑一个例子,其中软件 X 安装在旧版本中并且应该进行更新。软件 X 安装在全局 -Nix 存储中的自己的目录中,并符号链接到正确的目录,比如 `/usr/local/bin/`。当触发更新时,X -的新版本会安装到全局 Nix 存储中,而不会干扰其旧版本。一旦安装完成,包括其所有依赖项在内的 -最终软件包在 Nix 存储中,最后一步是将符号链接更改为 `/usr/local/bin/`。由于在 Unix 中创建 -新的符号链接来覆盖旧的符号链接是一个原子操作,因此这个操作不可能失败并使软件包处于损坏状 -态。唯一可能的问题是在符号链接创建之前或之后失败。无论哪种方式,结果都是我们要么有旧版本的 -X,要么有新安装的版本,但中间没有任何东西。 - -引用自原作:https://medium.com/earlybyte/the-s3-nix-cache-manual-e320da6b1a9b - -## Nix 二进制缓存 - -无论 Nix 的每个方面听起来多么棒,它的设计也有一个主要缺点,那就是每次构建软件包都会触发整 -个依赖链的构建过程。这可能需要相当长的时间,因为甚至像 gcc 或 ghc 这样的编译器都必须提前构 -建。这样的构建过程可能会消耗大量内存,在受限平台(如树莓派)上使用它时会引入额外的障碍。 - -为了克服总是从头开始构建一切以及丢失对软件包预构建版本的访问权限的缺点,还可以使用 S3 服务 -器(如 MinIO)构建自己的 Nix 二进制缓存。 - -设置您的缓存,填充它的二进制文件并使用您新构建的缓存中的二进制文件的过程将被逐步描述。对于 -本手册,我假设您已经有了 Nix,并且在您的环境中已经运行了 MinIO 服务器。如果没有,您可以查 -看 MinIO 的[官方部署指南](https://min.io/docs/minio/linux/operations/installation.html)。 -您需要确保通过信任的证书以 `HTTPS` 方式访问 MinIO。在这里,Let's Encrypt 将非常有用。 - -在本文中,让我们探讨如何自托管一个 S3 兼容服务器 MinIO 作为二进制缓存存储。 - -引用自原作:https://medium.com/earlybyte/the-s3-nix-cache-manual-e320da6b1a9b - -## 如何将 S3 用作二进制缓存服务器 - -### 先决条件 - -- 在您的环境中设置 MinIO。 -- 拥有有效的 SSL 证书,可以是公共证书也可以是私有证书。在本教程中,我们将使用 - `minio.homelab.local`(私有证书)的示例步骤。如果您计划使用私有证书,您必须自己解决 DNS - 挑战。因此,建议使用公共证书。 -- 在您的环境中安装 `minio-client`。 - -### 生成密码 - -```bash -nix run nixpkgs#pwgen -- -c -n -y -s -B 32 1 -# oenu1Yuch3rohz2ahveid0koo4giecho -``` - -### 设置 MinIO 客户端 - -安装 MinIO 命令行客户端 `mc`。 - -```nix -{ pkgs, ... }: - -{ - environment.systemPackages = with pkgs; [ - minio-client # 用于文件系统和对象存储的 ls、cp、mkdir、diff 和 rsync 命令的替代品 - ]; -} -``` - -创建或编辑 `~/.mc/config.json`。 - -```json -{ - "version": "10", - "aliases": { - "s3": { - "url": "https://s3.homelab.local", - "accessKey": "minio", - "secretKey": "oenu1Yuch3rohz2ahveid0koo4giecho", - "api": "s3v4", - "path": "auto" - } - } -} -``` - -在我们用来构建 Nix 包的机器上,我们需要在一些地方使用 S3 凭据。设置一个文件 -`~/.aws/credentials` 并用我们的 nixbuilder 用户的凭据填充它。用 `pwgen` 命令生成的密码替换 -``。 - -```plaintext -[default] -aws_access_key_id=nixbuilder -aws_secret_access_key= -``` - -### 设置 S3 存储桶作为二进制缓存 - -创建 `nix-cache` 存储桶。 - -```bash -mc mb s3/nix-cache -``` - -创建 `nixbuilder` MinIO 用户并分配密码。 - -```bash -mc admin user add s3 nixbuilder -``` - -在当前工作目录中创建名为 `nix-cache-write.json` 的文件,并具有以下内容: - -```json -{ - "Id": "AuthenticatedWrite", - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "AuthenticatedWrite", - "Action": [ - "s3:AbortMultipartUpload", - "s3:GetBucketLocation", - "s3:GetObject", - "s3:ListBucket", - "s3:ListBucketMultipartUploads", - "s3:ListMultipartUploadParts", - "s3:PutObject" - ], - "Effect": "Allow", - "Resource": ["arn:aws:s3:::nix-cache", "arn:aws:s3:::nix-cache/*"], - "Principal": "nixbuilder" - } - ] -} -``` - -使用 `nix-cache-write.json` 创建允许 `nixbuilder` 上载文件到缓存的策略。 - -```bash -mc admin policy add s3 nix-cache-write nix-cache-write.json -``` - -将我们上面创建的策略与 `nixbuilder` 用户关联。 - -```bash -mc admin policy set s3 nix-cache-write user=nixbuilder -``` - -允许匿名用户在不进行身份验证的情况下下载文件。 - -```bash -mc anonymous set download s3/nix-cache -``` - -在工作目录中创建名为 `nix-cache-info` 的文件。此文件告诉 Nix 桶确实是一个二进制缓存。 - -```bash -cat > nix-cache-info < ~/.config/nix/secret.key -nix key convert-secret-to-public < ~/.config/nix/secret.key > ~/.config/nix/public.key -cat ~/.config/nix/public.key -# s3.homelab.local-1:m0J/oDlLEuG6ezc6MzmpLCN2MYjssO3NMIlr9JdxkTs= -``` - -### 使用 Flake 激活二进制缓存 - -将以下内容放入 `configuration.nix` 或您的任何自定义 NixOS 模块中: - -```nix -{ - nix = { - settings = { - # 在获取软件包时,替代器将被附加到默认的替代器。 - extra-substituters = [ - "https://s3.homelab.local/nix-cache/" - ]; - extra-trusted-public-keys = [ - "s3.homelab.local-1:m0J/oDlLEuG6ezc6MzmpLCN2MYjssO3NMIlr9JdxkTs=" - ]; - }; - }; -} -``` - -重新构建系统。 - -```bash -sudo nixos-rebuild switch --upgrade --flake .# -``` - -### 推送路径到存储 - -对本地存储中的一些路径进行签名。 - -```bash -nix store sign --recursive --key-file ~/.config/nix/secret.key /run/current-system -``` - -将这些路径复制到缓存。 - -```bash -nix copy --to 's3://nix-cache?profile=nixbuilder&endpoint=s3.homelab.local' /run/current-system -``` - -### 添加自动对象到期策略 - -```bash -mc ilm rule add s3/nix-cache --expire-days "DAYS" -# 例如:mc ilm rule add s3/nix-cache --expire-days "7" -``` - -### 参考资料 - -以下是我在编写本文档时使用的一些来源: - -- [Jeff 的博客文章:Nix 二进制缓存](https://jcollie.github.io/nixos/2022/04/27/nixos-binary-cache-2022.html) -- [NixOS wiki 上的二进制缓存](https://nixos.wiki/wiki/Binary_Cache) -- [NixOS 手册中关于通过 S3 提供 Nix 存储](https://nixos.org/manual/nix/stable/package-management/s3-substituter.html) -- [NixOS 手册中关于通过 HTTP 提供 Nix 存储](https://nixos.org/manual/nix/stable/package-management/binary-cache-substituter.html) diff --git a/docs/zh/nixos-with-flakes/add-custom-cache-servers.md b/docs/zh/nix-store/add-binary-cache-servers.md similarity index 94% rename from docs/zh/nixos-with-flakes/add-custom-cache-servers.md rename to docs/zh/nix-store/add-binary-cache-servers.md index 97534c8..ffb851f 100644 --- a/docs/zh/nixos-with-flakes/add-custom-cache-servers.md +++ b/docs/zh/nix-store/add-binary-cache-servers.md @@ -1,20 +1,19 @@ -# 添加自定义缓存服务器 {#add-custom-cache-servers} +# 添加二进制缓存服务器 -## 什么是 Nix 缓存服务器 {#what-is-nix-cache-server} +前面介绍了 Nix Store 与二进制缓存的概念,这里我们来看看如何添加多个缓存服务器,以加速包的 +下载速度。 -Nix 提供了官方缓存服务器 ,它缓存了 nixpkgs 中所有 packages 在常 -用 CPU 指令集下的构建结果,当你在本地执行 Nix 构建指令时,如果 Nix 在服务器中匹配到对应的 -缓存,就会直接下载该缓存文件,跳过耗时的本地编译构建从而大大提升构建速度。 +## 为什么要添加缓存服务器 {#why-add-cache-servers} -## 为什么要添加自定义缓存服务器 {#why-add-custom-cache-servers} - -两个原因: +Nix 提供的官方缓存服务器 提供了绝大部分常用软件包的二进制缓存, +但它并不能满足所有用户的需求。在以下情况下,我们会需要添加额外的缓存服务器: 1. 添加一些第三方项目的缓存服务器,例如 nix-community 的缓存服务器 - ,这可以大大提升这些第三方项目的构建速度。 + 提供了社区项目的二进制缓存,可以加速这些项目的构建。 1. 添加离用户最近的缓存服务器镜像站,用于加速下载。 +1. 添加自己搭建的缓存服务器,用于加速个人项目的构建速度。 -## 如何添加自定义缓存服务器 {#how-to-add-custom-cache-servers} +## 如何添加缓存服务器 {#how-to-add-custom-cache-servers} Nix 中通过如下几个 options 来配置缓存服务器: diff --git a/docs/zh/nix-store/host-your-own-binary-cache-server.md b/docs/zh/nix-store/host-your-own-binary-cache-server.md new file mode 100644 index 0000000..54654c7 --- /dev/null +++ b/docs/zh/nix-store/host-your-own-binary-cache-server.md @@ -0,0 +1,218 @@ +# 使用 S3 自定义二进制缓存托管 + +## 简介 + +Nix 二进制缓存是 Nix Store 的一个实现,它不把数据存储在本地,而是存储在远程服务器上,方便 +二进制缓存的多机共享。 + +Nix 官方的二进制缓存服务器只提供了使用标准参数构建的二进制缓存。如果你自定义了构建参数,或 +者你使用了 Nixpkgs 之外的软件包,那就会导致 Nix 找不到对应的二进制缓存,从而执行本地构建流 +程。 + +单纯依赖你本地的 Nix Store `/nix/store` 有时候会变得很痛苦,因为你需要在每台机器上重新构建 +所有你自定义的这些软件包,这可能需要相当长的时间,而且构建过程可能会消耗大量内存。如果是在 +Raspberry Pi 等性能较低的平台上使用 Nix,这种情况会变得更加糟糕。 + +本文档将介绍如何使用 S3 服务(如 MinIO)搭建你自己的 Nix 二进制缓存服务器,以便在多台机器 +之间共享构建结果,从而解决上述问题。 + +## 准备工作 + +1. 一台 NixOS 主机 +1. 部署好 MinIO 服务器 + 1. 如果没有,您可以参考 MinIO + 的[官方部署指南](https://min.io/docs/minio/linux/operations/installation.html) 进行 + 部署。 +1. MinIO 服务器需要具备有效的 TLS 数字证书,可以是公共证书也可以是私有证书。本文将使用 + `https://minio.homelab.local` 加上私有证书作为示例。 +1. 安装好 `minio-client` + +## 生成密码 + +```bash +nix run nixpkgs#pwgen -- -c -n -y -s -B 32 1 +# => oenu1Yuch3rohz2ahveid0koo4giecho +``` + +## 设置 MinIO 客户端 + +安装 MinIO 命令行客户端 `mc`。 + +```nix +{ pkgs, ... }: + +{ + environment.systemPackages = with pkgs; [ + minio-client # 用于文件系统和对象存储的 ls、cp、mkdir、diff 和 rsync 命令的替代品 + ]; +} +``` + +创建 `~/.mc/config.json`,内容格式如下(注意将其中的关键参数替换为你自己的): + +```json +{ + "version": "10", + "aliases": { + "s3": { + "url": "https://s3.homelab.local", + "accessKey": "minio", + "secretKey": "oenu1Yuch3rohz2ahveid0koo4giecho", + "api": "s3v4", + "path": "auto" + } + } +} +``` + +Nix 将直接与 S3 存储桶交互,因此我们都需要给所有需要访问 Nix 二进制缓存的机器配置好对应的 +S3 凭据。创建 `~/.aws/credentials`,内容如下(请注意用前面 `pwgen` 命令生成的密码替换 +``)。 + +```toml +[default] +aws_access_key_id=nixbuilder +aws_secret_access_key= +``` + +## 设置使用 S3 存储桶作为二进制缓存 + +先通过 minio 客户端创建 `nix-cache` 存储桶: + +```bash +mc mb s3/nix-cache +``` + +创建 `nixbuilder` 这个 MinIO 用户并为其分配密码: + +```bash +mc admin user add s3 nixbuilder +``` + +在当前工作目录中创建名为 `nix-cache-write.json` 的文件,内容如下: + +```json +{ + "Id": "AuthenticatedWrite", + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "AuthenticatedWrite", + "Action": [ + "s3:AbortMultipartUpload", + "s3:GetBucketLocation", + "s3:GetObject", + "s3:ListBucket", + "s3:ListBucketMultipartUploads", + "s3:ListMultipartUploadParts", + "s3:PutObject" + ], + "Effect": "Allow", + "Resource": ["arn:aws:s3:::nix-cache", "arn:aws:s3:::nix-cache/*"], + "Principal": "nixbuilder" + } + ] +} +``` + +现在再使用刚刚创建好的 `nix-cache-write.json` 文件创建一个上载文件到 S3 的策略: + +```bash +mc admin policy add s3 nix-cache-write nix-cache-write.json +``` + +将我们上面创建的 S3 策略与 `nixbuilder` 用户关联: + +```bash +mc admin policy set s3 nix-cache-write user=nixbuilder +``` + +再允许匿名用户在不进行身份验证的情况下下载文件,这样所有 Nix 服务器就都能直接从这个 S3 缓 +存拉数据了: + +```bash +mc anonymous set download s3/nix-cache +``` + +最后,添加 `nix-cache-info` 文件到 S3 桶根目录中,Nix 需要这个文件记录一些二进制缓存相关的 +信息: + +```bash +cat > nix-cache-info < ~/.config/nix/secret.key +nix key convert-secret-to-public < ~/.config/nix/secret.key > ~/.config/nix/public.key +cat ~/.config/nix/public.key +# => s3.homelab.local-1:m0J/oDlLEuG6ezc6MzmpLCN2MYjssO3NMIlr9JdxkTs= +``` + +## 在 `flake.nix` 中使用 S3 二进制缓存 + +将以下内容放入 `configuration.nix` 或您的任何自定义 NixOS 模块中: + +```nix +{ + nix = { + settings = { + # 在获取软件包时,替代器将被附加到默认的替代器。 + extra-substituters = [ + "https://s3.homelab.local/nix-cache/" + ]; + extra-trusted-public-keys = [ + "s3.homelab.local-1:m0J/oDlLEuG6ezc6MzmpLCN2MYjssO3NMIlr9JdxkTs=" + ]; + }; + }; +} +``` + +重新构建系统,就可以使用上我们创建好的 S3 二进制缓存了: + +```bash +sudo nixos-rebuild switch --upgrade --flake .# +``` + +## 推送存储路径到二进制缓存 + +对本地存储中的一些路径进行签名。 + +```bash +nix store sign --recursive --key-file ~/.config/nix/secret.key /run/current-system +``` + +将这些路径复制到缓存: + +```bash +nix copy --to 's3://nix-cache?profile=nixbuilder&endpoint=s3.homelab.local' /run/current-system +``` + +## 添加自动对象过期策略 + +```bash +mc ilm rule add s3/nix-cache --expire-days "DAYS" +# 例如:mc ilm rule add s3/nix-cache --expire-days "7" +``` + +## 参考 + +- [Blog post by Jeff on Nix binary caches](https://jcollie.github.io/nixos/2022/04/27/nixos-binary-cache-2022.html) +- [Binary cache in the NixOS wiki](https://nixos.wiki/wiki/Binary_Cache) +- [Serving a Nox store via S3 in the NixOS manual](https://nixos.org/manual/nix/stable/package-management/s3-substituter.html) +- [Serving a Nix store via HTTP in the NixOS manual](https://nixos.org/manual/nix/stable/package-management/binary-cache-substituter.html) diff --git a/docs/zh/nix-store/intro.md b/docs/zh/nix-store/intro.md new file mode 100644 index 0000000..e954532 --- /dev/null +++ b/docs/zh/nix-store/intro.md @@ -0,0 +1,150 @@ +# Nix Store 与二进制缓存 + +这里我们先简单介绍下 Nix Store、Nix 二进制缓存以及其他相关概念,但不涉及具体的配置与使用方 +法,这些内容会在后续章节中详细介绍。 + +## Nix Store + +Nix Store 是 Nix 包管理器的核心概念之一,它是一个只读文件系统,用于存储所有需要不可变这一 +特性的文件,包括软件包的构建结果、软件包的元数据、软件包的所有构建输入等等。 + +Nix 包管理器使用 Nix 函数式语言来描述软件包及其依赖关系,每个软件包都被视为一个纯函数的输 +出,软件包的构建结果被保存在 Nix Store 中。 + +Nix Store 中的数据具有固定的路径格式: + +``` +/nix/store/b6gvzjyb2pg0kjfwrjmg1vfhh54ad73z-firefox-33.1 +|--------| |------------------------------| |----------| +store directory digest name +``` + +可以看到,Nix Store 中的路径以一个哈希值(digest)为前缀,后面跟着软件包的名称和版本号。这 +个哈希值是基于软件包的所有输入信息(构建参数、依赖关系、依赖版本等等)计算出来的,任何构建 +参数或依赖关系的变化都会导致哈希值的变化,从而保证了每个软件包路径的唯一性。再加上 Nix +Store 是一个只读文件系统,这就保证了软件包的不可变性,即软件包一旦构建完成,就不会再发生变 +化。 + +因为构建结果的存储路径是基于构建流程的所有输入信息计算出来的,**同样的输入信息会得到同样的 +存储路径** 这种设计也被称为输入寻址模型(_Input-addressed Model_)。 + +### NixOS 如何使用 Nix Store + +NixOS 的声明式配置将会计算出哪些软件包需要被安装,然后将这些软件包在 Nix Store 中的存储路 +径软链接到 `/run/current-system` 中,再通过修改 `PATH` 等环境变量指向 +`/run/current-system` 中对应的文件夹,从而实现软件包的安装。每次部署时,NixOS 会计算出新的 +系统配置,清理掉旧的软链接,再重新创建新的软链接,从而确保系统环境与声明式配置一致。 + +home-manager 也是类似的,它会将用户配置的软件包软链接到 +`/etc/profiles/per-user/your-username` 这个路径下,再通过修改 `PATH` 等环境变量指向这个路 +径,从而实现用户软件包的安装。 + +```bash +# 查看环境中的 bash 来自哪个路径(使用 NixOS 安装) +› which bash +╭───┬─────────┬─────────────────────────────────┬──────────╮ +│ # │ command │ path │ type │ +├───┼─────────┼─────────────────────────────────┼──────────┤ +│ 0 │ bash │ /run/current-system/sw/bin/bash │ external │ +╰───┴─────────┴─────────────────────────────────┴──────────╯ + +› ls -al /run/current-system/sw/bin/bash +lrwxrwxrwx 15 root root 76 1970年 1月 1日 /run/current-system/sw/bin/bash -> /nix/store/1zslabm02hi75anb2w8zjrqwzgs0vrs3-bash-interactive-5.2p26/bin/bash + +# 查看环境中的 cowsay 来自哪个路径(使用 home-manager 安装) +› which cowsay +╭───┬─────────┬────────────────────────────────────────┬──────────╮ +│ # │ command │ path │ type │ +├───┼─────────┼────────────────────────────────────────┼──────────┤ +│ 0 │ cowsay │ /etc/profiles/per-user/ryan/bin/cowsay │ external │ +╰───┴─────────┴────────────────────────────────────────┴──────────╯ + +› ls -al /etc/profiles/per-user/ryan/bin/cowsay +lrwxrwxrwx 2 root root 72 1970年 1月 1日 /etc/profiles/per-user/ryan/bin/cowsay -> /nix/store/w2czyf82gxz4vy9kzsdhr88112bmc0c1-home-manager-path/bin/cowsay +``` + +而 `nix develop` 命令则是直接将软件包的存储路径添加到 `PATH` `LD_LIBRARY_PATH` 等环境变量 +中,使新创建的 shell 环境中可以直接使用这些软件包或库。 + +以本书的源码仓库 +[ryan4yin/nixos-and-flakes-book](https://github.com/ryan4yin/nixos-and-flakes-book) 为例, +在该仓库中执行 `nix develop` 命令,再查看下 `PATH` 环境变量的内容: + +```bash +› nix develop +node v20.9.0 + +› env | egrep '^PATH' +PATH=/nix/store/h13fnmpm8m28qypsba2xysi8a90crphj-pre-commit-3.6.0/bin:/nix/store/2mqyvwp96d4jynsnzgacdk5rg1kx2a9a-node2nix-1.11.0/bin:/nix/store/a1hckfqzyys4rfgbdy5kmb5w0zdr55i5-nodejs-20.9.0/bin:/nix/store/gjrfcl2bhv7kbj883k7b18n2aprgv4rf-pnpm-8.10.2/bin:/nix/store/z6jfxqyj1wq62iv1gn5b5d9ms6qigkg0-yarn-1.22.19/bin:/nix/store/2k5irl2cfw5m37r3ibmpq4f7jndb41a8-prettier-3.0.3/bin:/nix/store/zrs710jpfn7ngy5z4c6rrwwjq33b2a0y-git-2.42.0/bin:/nix/store/dkmyyrkyl0racnhsaiyf7rxf43yxhx92-typos-1.16.23/bin:/nix/store/imli2in1nr1h8qh7zh62knygpl2zj66l-alejandra-3.0.0/bin:/nix/store/85jldj870vzcl72yz03labc93bwvqayx-patchelf-0.15.0/bin:/nix/store/90h6k8ylkgn81k10190v5c9ldyjpzgl9-gcc-wrapper-12.3.0/bin:/nix/store/hf2gy3km07d5m0p1lwmja0rg9wlnmyr7-gcc-12.3.0/bin:/nix/store/cx01qk0qyylvkgisbwc7d3pk8sliccgh-glibc-2.38-27-bin/bin:/nix/store/bblyj5b3ii8n6v4ra0nb37cmi3lf8rz9-coreutils-9.3/bin:/nix/store/1alqjnr40dsk7cl15l5sn5y2zdxidc1v-binutils-wrapper-2.40/bin:/nix/store/1fn92b0783crypjcxvdv6ycmvi27by0j-binutils-2.40/bin:/nix/store/bblyj5b3ii8n6v4ra0nb37cmi3lf8rz9-coreutils-9.3/bin:/nix/store/l974pi8a5yqjrjlzmg6apk0jwjv81yqw-findutils-4.9.0/bin:/nix/store/8q25nyfirzsng6p57yp8hsaldqqbc7dg-diffutils-3.10/bin:/nix/store/9c5qm297qnvwcf7j0gm01qrslbiqz8rs-gnused-4.9/bin:/nix/store/rx2wig5yhpbwhnqxdy4z7qivj9ln7fab-gnugrep-3.11/bin:/nix/store/7wfya2k95zib8jl0jk5hnbn856sqcgfk-gawk-5.2.2/bin:/nix/store/xpidksbd07in3nd4sjx79ybwwy81b338-gnutar-1.35/bin:/nix/store/202iqv4bd7lh6f7fpy48p7q4d96lqdp7-gzip-1.13/bin:/nix/store/ik7jardq92dxw3fnz3vmlcgi9c8dwwdq-bzip2-1.0.8-bin/bin:/nix/store/v4iswb5kwj33l46dyh2zqh0nkxxlr3mz-gnumake-4.4.1/bin:/nix/store/q1c2flcykgr4wwg5a6h450hxbk4ch589-bash-5.2-p15/bin:/nix/store/cbj1ph7zi009m53hxs90idl1f5i9i941-patch-2.7.6/bin:/nix/store/76z4cjs7jj45ixk12yy6k5z2q2djk2jb-xz-5.4.4-bin/bin:/nix/store/qmfxld7qhk8qxlkx1cm4bkplg1gh6jgj-file-5.45/bin:/home/ryan/.local/bin:/home/ryan/go/bin:/home/ryan/.config/emacs/bin:/home/ryan/.local/bin:/home/ryan/go/bin:/home/ryan/.config/emacs/bin:/nix/store/jsc6jydv5zjpb3dvh0lxw2dzxmv3im9l-kitty-0.32.1/bin:/nix/store/ihpdcszhj8bdmyr0ygvalqw9zagn0jjz-imagemagick-7.1.1-28/bin:/nix/store/2bm2yd5jqlwf6nghlyp7z88g28j9n8r0-ncurses-6.4-dev/bin:/run/wrappers/bin:/guix/current/bin:/home/ryan/.guix-home/profile/bin:/home/ryan/.guix-profile/bin:/home/ryan/.nix-profile/bin:/nix/profile/bin:/home/ryan/.local/state/nix/profile/bin:/etc/profiles/per-user/ryan/bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin:/nix/store/c53f8hagyblvx52zylsnqcc0b3nxbrcl-binutils-wrapper-2.40/bin:/nix/store/fpagbmzdplgky01grwhxcsazvhynv1nz-pciutils-3.10.0/bin:/nix/store/4cjqvbp1jbkps185wl8qnbjpf8bdy8j9-gcc-wrapper-13.2.0/bin +``` + +显然 `nix develop` 将很多软件包的存储路径直接添加到了 `PATH` 环境变量中。 + +## Nix Store 的垃圾回收 + +Nix Store 是一个中心化的存储系统,所有的软件包构建输入跟输出都会被存储在这里。随着系统的使 +用,Nix Store 中的软件包会越来越多,占用的磁盘空间也会越来越大。 + +为了避免 Nix Store 无限制地增长,Nix 包管理器为本地 Nix Store 提供了垃圾回收机制,用于清理 +`/nix/store` 中的旧数据、回收存储空间。 + +根据 +[Chapter 11. The Garbage Collector - nix pills](https://nixos.org/guides/nix-pills/garbage-collector) +的说法, `nix-store --gc` 命令会执行垃圾回收操作,它会递归遍历 `/nix/var/nix/gcroots/` 目 +录下的所有软链接,找出所有被引用的软件包,然后将不再被引用的软件包删除。而 +`nix-collect-garbage --delete-old` 则更进一步,它会先删除掉所有旧的 +[profiles](https://nixos.org/manual/nix/stable/command-ref/files/profiles),再执行 +`nix-store --gc` 命令清理掉不再被引用的软件包。 + +需要注意的是,`nix build`, `nix develop` 等命令的构建结果并不会被自动添加到 +`/nix/var/nix/gcroots/` 目录中,所以这些构建结果会被垃圾回收机制清理掉。你可以通过 +`nix-instantiate` 跟 `keep-outputs = true` 等手段来避免这种情况,但我目前觉得搭建一个自己 +的二进制缓存服务器,然后在你在缓存服务器上配置一个较长的缓存时间(比如一年),将数据推送到 +缓存服务器上,这样既可以在所有机器上共享构建结果,又可以避免本地构建结果被本地的垃圾回收机 +制清理掉,一举两得。 + +## 二进制缓存 + +Nix 包管理器与 Nix Store 的设计保证了软件包的不可变性,使得 Nix Store 中的构建结果可以直接 +被在多台机器之间共享。只要这些机器使用了同样的输入信息构建软件包,它们就会得到相同的输出路 +径,Nix 则可以据此直接复用其他机器上的构建结果,而不需要重新构建软件包,从而提升软件包的安 +装速度。 + +Nix 二进制缓存就是基于这个特性而设计的,它实质也是 Nix Store 的一个实现,只不过它不把数据 +存储在本地,而是存储在远程服务器上。需要使用的时候,Nix 包管理器会从远程服务器上下载对应的 +构建结果到本地的 `/nix/store` 中,避免耗时的本地构建。 + +Nix 提供了官方二进制缓存服务器 ,它缓存了 nixpkgs 中绝大部分 +packages 在常用 CPU 指令集下的构建结果。当你在本地执行 Nix 构建指令时,Nix 首先会尝试从缓 +存服务器中查找对应的二进制缓存,如果查找到了,就会直接下载该缓存文件,跳过耗时的本地编译构 +建从而大大提升构建速度。 + +## Nix 二进制缓存的信任模型 + +**Input-addressed Model** **只保证同样的输入得到同样的输出路径,并不保证输出内容的唯一 +性**。也就是说即使输入信息相同,多次构建同一个软件包得到的输出内容也可能不同。 + +虽然 Nix 已经通过在构建环境中默认禁用网络、使用固定的时间戳等方式尽量减少不确定性,但软件 +包构建过程中仍然可能受到一些不可控因素的影响而产生不同的输出内容。这些不可控因素导致的输出 +内容不同通常不会对软件包的功能产生影响,但却给二进制缓存的安全共享带来了挑战——输出内容的不 +确定性使我们无法判断从缓存服务器中下载的二进制缓存是否真的是使用我们声明的输入信息构建的, +是否包含恶意内容。 + +Nix 包管理器目前给出的解决方案是——使用公私钥签名机制来验证二进制缓存的数据来源与完整性。这 +种验证方式实际是将安全责任转嫁给了用户。用户如果希望使用某个非官方缓存服务器来加快某些软件 +包的构建速度,那就必须将该缓存服务器的公钥添加进 `trusted-public-keys` 中,并自己承担对应 +的安全风险——该缓存服务器提供的缓存数据可能夹带了私货(恶意内容)。 + +### Content-addressed model + +[RFC062 - content-addressed store paths](https://github.com/NixOS/rfcs/blob/master/rfcs/0062-content-addressed-paths.md) +是社区在提升构建结果的一致性上的一次尝试。它提出了一种新的存储路径计算方式,即基于构建结果 +(outputs)而不是输入信息(inputs)来计算最终的存储路径。这种设计可以保证构建结果的一致 +性——如果构建结果不同,那么存储路径也会不同,从而避免了 input-addressed model 中存在的输出 +内容不确定性问题。 + +不过它目前还在实验性阶段,尚未被广泛应用。 + +## 参考 + +- [Nix Store - Nix Manual](https://nixos.org/manual/nix/stable/store/)