Nix: начало
Всем привет!
Итак, от идейной теории перейдём к практике. Надо же, наконец, попробовать этот Nix на вкус…
Платформа
Я долго думал, где же проводить все свои эксперименты, и решил-таки делать их на NixOS, через VirtualBox. Конечно, заявлено о работе Nix и на OS X, но уж лучше на NixOS, нативнее будет. ;-) Сказано - сделано. Иду сюда и скачиваю последний (а именно версии 15.09
) образ для VB. Импортирую, запускаю, вижу знакомый KDE. Начнём. Все манипуляции проводятся в Konsole.
ВАЖНО! У меня нет цели тупо продублировать официальное Nix-руководство на русском языке. В рамках этой и следующих статей я лишь хочу пройтись по интересующим меня практичным аспектам работы с Nix. Разумеется, особое ударение будет сделано на Haskell-разработку в Nix-среде. ;-)
Устанавливаем пакет
В начале мы имеем фактически голую систему, в которой нет даже Vim. Вот с него и начнём.
Простейший способ установить какой-либо пакет на NixOS таков: идём сюда и ищем пакет. Нахожу vim-7.4.827
, нажимаю на него - и вижу следующее:
Install command: $ nix-env -iA nixos.pkgs.vim (NixOS channel)
Nix expression: pkgs/applications/editors/vim/default.nix
Platforms: i686-linux, x86_64-linux
Homepage: http://www.vim.org
License: Not specified
Maintainers: Jason O'Conal <[email protected]>
Long description: Not specified
Первая строка - то что нам нужно, название Install command
говорит само за себя. Ввожу:
$ nix-env -iA nixos.pkgs.vim
installing ‘vim-7.4.827’
these paths will be fetched (5.65 MiB download, 27.06 MiB unpacked):
/nix/store/yblqgyrn4jgwfg89qp9041i0n2z26v5b-vim-7.4.827
fetching path ‘/nix/store/yblqgyrn4jgwfg89qp9041i0n2z26v5b-vim-7.4.827’...
*** Downloading ‘https://cache.nixos.org/nar/1pxi5nvpvfn27lxi789qjqb5gp2dbsabagc2y7ldnj8c20py9ix1.nar.xz’ (signed by ‘cache.nixos.org-1’) to ‘/nix/store/yblqgyrn4jgwfg89qp9041i0n2z26v5b-vim-7.4.827’...
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 5782k 100 5782k 0 0 601k 0 0:00:09 0:00:09 --:--:-- 681k
building path(s) ‘/nix/store/77qhgp784j2qia0v9dj98b76cra70qmm-user-environment’
created 2 symlinks in user environment
Сразу после этого команда vim
уже доступна в консоли. А теперь давайте разбираться.
Где
Во-первых, куда именно установился vim
? Как мы уже знаем, Nix устанавливает каждый пакет в своё уникальное место, где-то в недрах /nix/store/
. Более того, мы уже видели это место, оно было показано нам в сообщении при установке:
these paths will be fetched (5.65 MiB download, 27.06 MiB unpacked):
/nix/store/yblqgyrn4jgwfg89qp9041i0n2z26v5b-vim-7.4.827
Давайте проверим:
$ ls /nix/store/ | grep vim
yblqgyrn4jgwfg89qp9041i0n2z26v5b-vim-7.4.827
Да, это тот самый хэш. Заглянем внутрь:
$ ls /nix/store/yblqgyrn4jgwfg89qp9041i0n2z26v5b-vim-7.4.827/
bin share
Вполне ожидаемое содержимое: внутри bin
лежат исполняемые файлы (такие как vim
, vimdiff
, vimtutor
), а внутри share/vim/vim74/
- Vim-плагины.
Профиль и окружение
Итак, vim
теперь живёт в своём уютном мирке. Однако если мы проверим, откуда доступна команда vim
, то увидим следующее:
$ which vim
/home/demo/.nix-profile/bin/vim
Хм… Странно. Разве мы не должны были увидеть /nix/store/yblqgyrn4jgwfg89qp9041i0n2z26v5b-vim-7.4.827/bin/vim
? Что это за ~/.nix-profile/
такой?
Посмотрим-ка на него внимательнее:
$ ls -al ~/.nix-profile
... .nix-profile -> /nix/var/nix/profiles/per-user/demo/profile
Ага, перед нами символьная ссылка, и ведёт она куда-то в недра /nix
. Заглянем поглубже:
$ ls -al /nix/var/nix/profiles/per-user/demo/profile
... /nix/var/nix/profiles/per-user/demo/profile -> profile-1-link
Опять ссылка. Глубже:
$ ls -al /nix/var/nix/profiles/per-user/demo/profile-1-link
... /nix/var/nix/profiles/per-user/demo/profile-1-link -> /nix/store/77qhgp784j2qia0v9dj98b76cra70qmm-user-environment
И вновь ссылка, на этот раз на нечто любопытное: в хранилище /nix/store
мы видим некое user-environment
. Так, пользовательское окружение… Уж не то ли самое это окружение, куда устанавливается мой софт? Проверим:
$ ls -al /nix/store/77qhgp784j2qia0v9dj98b76cra70qmm-user-environment/
total 92
... bin -> /nix/store/yblqgyrn4jgwfg89qp9041i0n2z26v5b-vim-7.4.827/bin
... manifest.nix -> /nix/store/2i6jvy2q2m8gcgiszsh4zw5dvx7133hd-env-manifest.nix
... share -> /nix/store/yblqgyrn4jgwfg89qp9041i0n2z26v5b-vim-7.4.827/share
Так вот же оно! Внутри моего окружения я вижу ссылки на части установленного мною vim
. Теперь-то понятно, почему команда vim
была доступна через ~/.nix-profile/
: это просто цепочка ссылок. Концептуально её можно изобразить так:
my-profile -> user-environment -> software
Только что мы узнали нечто очень важное. Оказывается, утверждение, что “у каждого пользователя есть своё собственное окружение, куда он устанавливает софт”, не совсем корректно, ибо может ввести нас в заблуждение. Складывается впечатление, будто в моём домашнем каталоге есть какой-то особый каталог, в который устанавливаются мои пакеты, типа /home/demo/.my-nix-packages/trali-vali/vim-7.4.827/
. В действительности же это не так: все пакеты хранятся в /nix/store
, а в моём профиле есть лишь упоминание о некоторых из них.
Кстати, а давайте проверим эту догадку. Вдруг мы неправы?
Общий vim
Создадим-ка второго пользователя, по имени demo2
, и, войдя под ним, проверим:
$ vim
vim: command not found
Мы знаем, что vim
в системе уже существует, но пользователь demo2
не знает об этом. Давайте повторим процедуру установки vim
:
$ nix-env -iA nixos.pkgs.vim
installing ‘vim-7.4.827’
Опа, как мы и предполагали! Пользователь demo2
запросил установку пакета vim
, но поскольку этот пакет уже живёт в нашей системе, никакой установки в действительности не произошло. Уверен, вы уже догадались, что же произошло на самом деле: в окружении пользователя demo2
просто появились символьные ссылки на тот же самый пакет vim
. Убедимся же в этом:
$ which vim
/home/demo2/.nix-profile/bin/vim
$ ls -al ~/.nix-profile
... /home/demo2/.nix-profile -> /nix/var/nix/profiles/per-user/demo2/profile
$ ls -al /nix/var/nix/profiles/per-user/demo2/profile
... /nix/var/nix/profiles/per-user/demo2/profile -> profile-1-link
$ ls -al /nix/var/nix/profiles/per-user/demo2/profile-1-link
... /nix/var/nix/profiles/per-user/demo2/profile-1-link -> /nix/store/77qhgp784j2qia0v9dj98b76cra70qmm-user-environment
$ ls -al /nix/store/77qhgp784j2qia0v9dj98b76cra70qmm-user-environment
total 92
... bin -> /nix/store/yblqgyrn4jgwfg89qp9041i0n2z26v5b-vim-7.4.827/bin
... manifest.nix -> /nix/store/2i6jvy2q2m8gcgiszsh4zw5dvx7133hd-env-manifest.nix
... share -> /nix/store/yblqgyrn4jgwfg89qp9041i0n2z26v5b-vim-7.4.827/share
Это именно то, что мы и ожидали увидеть: тот же самый хэш, та же самая версия vim
! Таким образом, концептуально сложившуюся ситуацию можно изобразить так:
demo-profile -> /nix/store/user-environment -> /nix/store/vim-7.4.827
/
demo2-profile
Два пользователя пользуются общим пакетом vim
! Это в высшей степени рациональный подход, поскольку нет никакого дубляжа. Даже если бы в нашей системе было сто пользователей, то, когда все они запросят установку пакета vim
одной и той же версии, тогда один-единственный пакет vim
просто расшарится между ними, через ссылки в их профилях.
Видит око, да зуб неймёт
Постойте, возразите вы, но если два пользователя demo
и demo2
пользуются одним и тем же пакетом vim
- это же опасно! А что если demo2
возьмёт и удалит его! Ну или сломает чего-нибудь! Это что же, у demo
vim
тоже исчезнет или поломается??!
Резонное возражение. Давайте похулиганим от имени demo2
. Попробуем переименовать исполняемый файл vim
:
$ cd /nix/store/yblqgyrn4jgwfg89qp9041i0n2z26v5b-vim-7.4.827/bin
$ mv vim vimTraliVali
mv: cannot move ‘vim’ to ‘vimTraliVali’: Read-only file system
Ага, разбежались! Вы должны помнить из предыдущей статьи, что Nix воспринимает пакеты так же, как значения в Haskell-коде: будучи единожды созданными, они уже не могут быть изменены. В этом и заключается Nix-чистота (purity). Оба пользователя ссылаются на пакет vim
, но ни один из них не владеет им.
Ну хорошо, возразите вы вновь, но ведь Nix же умеет удалять пакеты?! Ладно, не будем ничего ломать, но что произойдёт, если demo2
удалит vim
?
Разумеется, Nix умеет удалять пакеты, сделаем же это:
$ nix-env --uninstall vim
uninstalling ‘vim-7.4.827’
building path(s) ‘/nix/store/0fw4wpbrgix115lh93ahfik2r0hvv7n0-user-environment’
created 0 symlinks in user environment
$ vim
vim: command not found
О нет, что же мы натворили-то?! Как же теперь быть бедному demo
? А давайте проверим. Перелогинимся под demo
и:
$ vim --version
VIM - Vi IMproved 7.4 (2013 Aug 10, compiled Jan 01 1970 00:00:01)
Included patches: 1-827
Магия? Вовсе нет. Когда мы выполнили команду:
$ nix-env --uninstall vim
никакого удаления в действительности не произошло. В самом деле, если бы пакет vim
реально исчез - как бы тогда его мог видеть пользователь demo
? Как вы уже, вероятно, догадались, произошло только одно: удаление той самой символьной ссылки на пакет vim
из профиля demo2
. И это вполне логично: если с конкретным пакетом пользователя связывает лишь соответствующая ссылка из его профиля, тогда удаление пакета сводится к удалению этой ссылки. Это похоже на указатель в языке C: если мы потеряли некую “ссылку” на область памяти в куче - всё, эта память потеряна для нас навсегда, однако это вовсе не означает, что эта память тут же была очищена, ведь на эту же область памяти вполне может ссылаться кто-то другой.
Пока пользователь demo
продолжает ссылаться на пакет vim
, этот пакет никак не может быть удалён. Оно и понятно: если бы мы могли удалить пакет, на который кто-то ссылается, тогда у кого-то что-нибудь сломалось бы, а это недопустимо.
Ну хорошо, а что же будет, если и demo
удалит vim
? Сказано - сделано:
$ nix-env --uninstall vim
uninstalling ‘vim-7.4.827’
$ vim
vim: command not found
Всё, скончался наш vim
. Впрочем, скончался ли? Ведь мы же помним тот самый хэш, ну-ка проверим:
$ ls -al /nix/store/yblqgyrn4jgwfg89qp9041i0n2z26v5b-vim-7.4.827/bin/vim
... /nix/store/yblqgyrn4jgwfg89qp9041i0n2z26v5b-vim-7.4.827/bin/vim
Опа… А почему же vim
никуда не исчез? А потому что мы мусор не почистили.
Помните идею GC (сборщика мусора)? Удалять только то, что никому не нужно. Так вот в Nix есть собственный GC. Пакет vim
больше никому не нужен, ведь оба пользователя, когда-то работающие с ним, удалили его. Значит, теперь можно почистить мусор. Это не происходит автоматически, нужно запустить команду:
$ nix-collect-garbage -d
И вот теперь - всё. Помимо всего прочего, мы увидим строку:
deleting ‘/nix/store/yblqgyrn4jgwfg89qp9041i0n2z26v5b-vim-7.4.827’
Покойся с миром, vim
.
А если воскресить?
Давайте заново установим vim
. В конце концов, он мне нужен, привык я к нему:
$ nix-env -iA nixos.pkgs.vim
installing ‘vim-7.4.827’
...
И вот vim
вновь с нами. Но самое интересное в том, что если мы посмотрим в /nix/store/
, то увидим:
$ ls /nix/store/ | grep vim
yblqgyrn4jgwfg89qp9041i0n2z26v5b-vim-7.4.827
Круто, правда? Хэш-то тот же самый! А всё потому, что это не просто какое-то случайное криптозначение, а хэш от входных конфигурационных параметров для данного пакета. Ну а раз vim
тот же самый и входные параметры те же - значит и хэш останется прежним. И сколько бы раз мы ни удаляли и не устанавливали пакет vim
одной и той же версии - результат будет гарантированно одинаковым. Да-да, это хорошо нам знакомая философия чистой функции - если на вход подали те же значения, то и на выходе всегда получим одно и то же.
В итоге
В итоге мы поняли, что Nix - весьма умная система. Лишнюю работу не делает, чистоту хранит, безопасно удаляет. Ну точно в духе Haskell! ;-)
Разумеется, всё вышеизложенное - это лишь азы азов. А вот в следующих статьях начнутся вещи поинтереснее.