Погружаемся в среду

Добро пожаловать на третью Nix-пилюлю. Во второй пилюле мы установили Nix в свою систему. Сейчас мы, наконец, устроим пару экспериментов. Эта статья будет полезной, даже если вы используете NixOS, а не просто Nix.

Начинаем погружение

Если вы используете NixOS, то можете пропустить следующий шаг.

В прошлый раз мы создали пользователя Nix. Давайте переключимся на него с помощью команды su - nix. Если ваш ~/.profile уже создан, вам должны быть доступны команды наподобие nix-env и nix-store.

Если нет, запустите:

$ source ~/.nix-profile/etc/profile.d/nix.sh

Ранее мы выяснили, что ~/.nix-profile/etc указывает на деривацию nix-2.1.3. В данный момент мы находимся в профиле пользователя Nix.

Устанавливаем что-нибудь

И вот она, практика! Установка в окружение Nix — интересный процесс. Начнём с hello — простой командной утилиты, которая печатает Hello world и часто используется для проверки компиляторов и пакетных менеджеров.

Итак, установка:

$ nix-env -i hello
installing 'hello-2.10'
[...]
building '/nix/store/0vqw0ssmh6y5zj48yg34gc6macr883xk-user-environment.drv'...
created 36 symlinks in user environment

Теперь программу можно запускать. Прежде, чем двигаться дальше, зафиксируем несколько важных моментов:

  • Мы установили программу с правами пользователя Nix, и только для пользователя Nix.
  • Из-за этого появилось новое окружение пользователя (иногда его называют средой пользователя). Это — новое поколение профиля нашего пользователя Nix.
  • Утилита nix-env управляет окружениями, профилями и их поколенями.
  • Мы установили hello, используя только имя, без указания версии. Повторяю: мы указали только имя деривации (без версии) для установки.

Мы можем вывести список поколений без блужданий по каталогу /nix:

$ nix-env --list-generations
   1   2014-07-24 09:23:30
   2   2014-07-25 08:45:01   (current)

Вывести установленные деривации:

$ nix-env -q
nix-2.1.3
hello-2.10

Куда мы установили hello на самом деле? which hello говорит нам ~/.nix-profile/bin/hello, который указывает на хранилище. Мы также можем вывести путь деривации с помощью команды nix-env -q --out-path. Этот путь деривации называется выходом сборки.

Слияние путей

Сейчас вы, возможно, хотите запустить man чтобы получить кое-какую информацию. Даже если у вас уже есть man в основной системе, вы можете установить её в окружение Nix с помощью команды nix-env -i man-db.

Исследуем профиль:

$ ls -l ~/.nix-profile/
dr-xr-xr-x 2 nix nix 4096 Jan  1  1970 bin
lrwxrwxrwx 1 nix nix   55 Jan  1  1970 etc -> /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3/etc
[...]

Мы видим кое-что интересное. Когда у нас была установлена только одна деривация nix-2.1.3, bin был символической ссылкой на nix-2.1.3. Теперь, когда мы дополнительно установили несколько программ (man, hello), bin стал реальным каталогом, не симлинком.

$ ls -l ~/.nix-profile/bin/
[...]
man -> /nix/store/83cn9ing5sc6644h50dqzzfxcs07r2jn-man-1.6g/bin/man
[...]
nix-env -> /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3/bin/nix-env
[...]
hello -> /nix/store/58r35bqb4f3lxbnbabq718svq9i2pda3-hello-2.10/bin/hello
[...]

Так, кое-что стало проясняться. nix-env слил пути из установленных дериваций. which man указывает на профиль Nix, вместо того, чтобы указывать на системный man, потому что ~/.nix-profile/bin находится в начале $PATH1.

Откат и переключение поколений

Последняя установленная команда — это man. Сейчас мы находимся в поколении профиля номер 3, если вы ничего не меняли на предыдущих шагах. Мы можем откатиться к предыдущему поколению:

$ nix-env --rollback
switching from generation 3 to 2

Теперь nix-env -q не показывет man. Команда ls -l \which man`` должна вывести содержимое каталога основной системы.

Разобравшись с откатом, вернёмся обратно в поколение 3:

$ nix-env -G 3
switching from generation 2 to 3

Самое время познакомиться со справкой на команду nix-env. Для запуска nix-env нужно указывать операцию с опциями. Часть из них общие, а часть — специфичные для отдельных операций.

Конечно, вы можете и удалять, и обновлять пакеты.

Запросы в хранилище

К текущему моменту мы научились исследовать и изменять окружение. Поскольку все компоненты окружения указывают на хранилище, нам необходимо научиться работать с ним.

Для этого есть команда nix-store. Она предоставляет широкие возможности, но пока мы познакомимся только с несколькими видами запросов.

Чтобы вывести непосредственные зависимости программы hello:

$ nix-store -q --references `which hello`
/nix/store/fg4yq8i8wd08xg3fy58l6q73cjy8hjr2-glibc-2.27
/nix/store/58r35bqb4f3lxbnbabq718svq9i2pda3-hello-2.10

Аргумент nix-store может иметь любой тип, если он находится в хранилище Nix. Утилита следует симлинкам.

Сейчас это может показаться неважным, но давайте просмотрим деривации, зависящие от hello:

$ nix-store -q --referrers `which hello`
/nix/store/58r35bqb4f3lxbnbabq718svq9i2pda3-hello-2.10
/nix/store/fhvy2550cpmjgcjcx5rzz328i0kfv3z3-env-manifest.nix
/nix/store/yzdk0xvr0b8dcwhi2nns6d75k2ha5208-env-manifest.nix
/nix/store/mp987abm20c70pl8p31ljw1r5by4xwfw-user-environment
/nix/store/ppr3qbq7fk2m2pa49i2z3i32cvfhsv7p-user-environment

Призайтесь, вы этого не ждали? Оказывается, наши окружения зависят от hello. Да, это значит, что окружения также находятся в хранилище, и поскольку они содержат симлинки на hello, каждое из них зависит от hello.

Мы видим в списке два окружения, относящиеся к поколениям 2 и 3, так как именно они содержат установленную программу hello.

Файл manifest.nix содержит метаданные, относящиеся к окружению, например, список установленных дериваций. Команда nix-env может выводить, обновлять и удалять их. И снова — текущий manifest.nix находится по пути ~/.nix-profile/manifest.nix.

Замыкания

Замыкание деривации — это дерево всех её зависимостей, которое включает абсолютно всё, что нужно для использования данной деривации.

$ nix-store -qR `which man`
[...]

Копирование всех этих дериваций в хранилище на другой машине позволит запустить на ней утилиту man, ничего больше не настраивая. Это — основа развёртывания с помощю Nix. Думаю, вы уже догадываетесь, насколько это мощный и удобный инструмент для развёртывания приложений в облаках (подсказка: nix-copy-closures и nix-store --export).

Просмотр замыкания в виде дерева зависимостей:

$ nix-store -q --tree `which man`
[...]

С помощью этой команды вы можете точно выяснить, почему у данной деривации существует зависимость времени выполнения, прямая или косвенная.

То же самое относится и к окружениям. В качестве упражнения запустите nix-store -q --tree ~/.nix-profile и убедитесь, что прямыми наследниками окружения пользователя являются установленные деривации и файл manifest.nix.

Разрешение зависимостей

В Nix нет ничего похожего на apt, которая решает задачу выполнимости булевых формул (SAT problem), чтобы подобрать зависимости с учётом верхних и нижних границ версий. Вся эта сложность не нужна, потому что все зависимости статичны: если деривация X зависит от деривации Y, она зависит от неё всегда. Деривация X, которая зависит от Z, должна быть другой деривацией.

Ручное восстановление

$ nix-env -e '*'
uninstalling 'hello-2.10'
uninstalling 'nix-2.1.3'
[...]

Ой, команда удалила все деривации из окружения, включая сам Nix! Это значит, что теперь мы не можем запустить даже nix-env. Что делать?

Раньше мы получали доступ к nix-env из окружения. Окружения удобны для пользователей, но Nix всё ещё находится в хранилище!

Сначала выберем одну из дериваций nix-2.1.3: ls /nix/store/*nix-2.1.3, пусть это будет /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3.

Первый способ всё починить — откатить изменения:

$ /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3/bin/nix-env --rollback

Второй способ — заново установить nix-env, тем самым создав новое поколение:

$ /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3/bin/nix-env -i /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3/bin/nix-env

Каналы

Откуда берутся пакеты? Мы уже говорили об этом во второй статье. Есть список каналов, откуда мы получаем пакеты, хотя обычно мы используем только один. И есть утилита для управления каналами — nix-channel.

$ nix-channel --list
nixpkgs http://nixos.org/channels/nixpkgs-unstable

Если вы используете NixOS, вы можете не увидеть вывода этой команды (если используете настройки по умолчанию), либо вы можете увидеть канал, чьё имя начинается с “nixos-” вместо “nixpkgs”.

По сути, это содержимое файла ~/.nix-channels.

ℹ️ ~/.nix-channels — это не символическая ссылка на хранилище!

Для обновления канала запустите nix-channel --update. Эта команда скачает новые выражения Nix (описания пакетов), создаст новое поколнение профиля каналов и распакует его в ~/.nix-defexpr/channels.

Она немного похожа на apt-get update. (См. таблицу, где в первом приближении сравниваются средства управления пакетами Ubuntu и NixOS).

Заключение

Мы узнали, как исследовать окружение пользователя и изменять его, устанавливая и удаляя программы. Обновлять программы тоже не сложно, подробности см. в руководстве (вкратце: nix-env -u обновит все пакеты в окружении).

Всякий раз, когда мы меняем среду, создаётся новое поколение профиля. Переключаться между поколениями не только просто, но и быстро.

Далее мы выяснили, как работать с хранилищем. Мы изучили прямые и обратные зависимости путей в нём.

Мы увидели, как симлинки используются для слияния путей из хранилища Nix — полезный трюк.

Подходящая аналогия с языками программирования: у вас есть куча с объектами, и она похожа на хранилище Nix. У вас есть объекты, которые указывают на другие объекты, они похожи на деривации. Возможно, эта метафора поможет вам понять, как деривации ссылаются друг на друга.

В следующей пилюле

…мы изучим основы языка Nix. Язык Nix используется для описания того, как строить деривации. Он является основой всего, включая NixOS. Поэтому очень важно понимать как синтаксис, так и семантику языка.

1

Это значит, что путь к системному man находится в списке путей позже и программа which его не видит — примечание переводчика.