Почему вам стоит попробовать Nix
Введение
Добро пожаловать на первую пилюлю из цикла «Nix в пилюлях». Nix — это чистый функциональный пакетный менеджер и система развёртывания для POSIX-совместимых ОС.
Есть немало материалов, посвящённых Nix, NixOS и связанным проектам. И, возможно, вы даже не стали бы их читать, так что цель этой статьи — убедить вас попробовать Nix. Установка NixOS не потребуется, но иногда я буду ссылаться на NixOS, как на реальный пример операционной системы, построенной на базе Nix.
Почему появился этот цикл?
Руководства по Nix, Nixpkgs и NixOS вместе с вики — великолепные ресурсы, объясняющие как устроены Nix/NixOS, как их использовать, и насколько крутые штуки можно делать с их помощью.
Цель этих статей — дополнить существующие документы чуть менее формальными объяснениями.
А теперь давайте познакомимся с Nix. Пилюли бывают горькими, поэтому постараемся закончить всё как можно быстрее.
Когда пакетный менеджер — не чистый функциональный
Большинство, если не все, популярные пакетные менеджеры (dpkg, rpm, …) изменяют глобальное состояние системы.
Установив пакет foo-1.0
в каталог /usr/bin/foo
, вы не сможете установить туда же новую версию foo-1.1
, т.к. имя исполняемого файла будет одно и то же.
Впрочем, его изменение может ввести в заблуждение пользователей.
Есть разные способы избежать этой проблемы. Скажем, Debian частично решает её с помощью системы альтернатив.
Поэтому в теории можно установить несколько версий одного пакета, но на практике этот опыт может быть болезненным.
Скажем, вам нужен сервис nginx
и — кроме него — сервис nginx-openresty
.
Вы должны создать новый пакет, и поменять в нём все пути, например, добавив к ним суффикс -openresty
.
Или, представим, вам надо запустить разные версии mysql
: 5.2 и 5.5.
Возникнет та же свистопляска с путями, что и в предыдущем случае.
Кроме того, вам надо будет убедиться, что разные версии библиотеки mysqlclient
не конфликтуют друг с другом.
Такая ситуация может возникнуть, и в этом случае справиться с ней будет очень непросто.
А если вы захотите установить два различных программных стека, скажем, GNOME 3.10
и GNOME 3.13
, вам можно только посочувствовать.
Администраторы скажут: вы можете использовать контейнеры. Типичный подход в наши дни — создать контейнер для каждого сервиса, особенно, если речь идёт о разных версиях. Подход в какой-то степени решает проблему, но на другом уровне и с другими побочными эффектами. Скажем, вам потребутся средства оркестрации, настройка общего кэша пакетов и новые машины для мониторинга всего этого счастья — вместо пары простых сервисов.
Разработчики скажут: вы можете использовать virtualenv
для python
, или jhbuild
для gnome
, или что-то подобное для других языков.
Но как вы смешаете разные стеки?
Как вам избежать перекомпиляции исходников, если их надо сделать общими для нескольких проектов?
А ещё вам придётся настроить инструменты разработки, чтобы они загружали библиотеки из правильных каталогов.
И, наконец, всегда остаётся риск, что часть софта некорректно работает с системными библиотеками.
В общем, проблем много. Nix решает эти проблемы на уровне пакетов, и решает их хорошо. Один инструмент — чтобы управлять всеми пакетами1.
Когда пакетный менеджер — чистый функциональный
Nix не делает никаких предположений о глобальном состоянии системы.
У такого подхода есть много преимуществ, но, конечно, есть и недостатки.
Сердцем системы Nix является хранилище, обычно расположенное в /nix/store
, а также кое-какие инструменты для работы с ним.
В Nix вместо понятия пакет существует понятие деривация2 (derivation).
Для новичков различия между ними кажутся слишком тонкими, поэтому я буду использовать эти слова, как синонимы.
Деривации/пакеты находятся в хранилище Nix в подкаталогах, чьи имена соответствуют формату /nix/store/hash-name
, где хэш (hash) — уникальный идентификатор деривации (с некоторыми оговорками, на которые мы не будет отвлекаться), а имя (name) — её имя.
Например, взглянем, на деривацию bash: /nix/store/s4zia7hhqkin1di0f187b79sa2srhv6k-bash-4.2-p45/
.
Это каталог в хранилище Nix, где находится утилита bin/bash
.
Фактически это значит, что в системе нет никакой глобальной оболочки, а есть только эта конкретная версия в одном из каталогов хранилища.
То же касается и других утилит, да и вообще всего.
Чтобы утилиты можно было вызывать из командной строки, Nix следит за тем, чтобы в переменной PATH
были правильные пути.
В итоге у нас есть хранилище всех пакетов (разные версии пакетов хранятся в разных каталогах), и всё, что там есть — менять нельзя.
В системе нет даже кэша ldconfig
, так что вы вправе спросить: как в таком случае bash
находит libc
?
$ ldd `which bash`
libc.so.6 => /nix/store/94n64qy99ja0vgbkf675nyk39g9b978n-glibc-2.19/lib/libc.so.6 (0x00007f0248cce000)
Оказывается, когда bash
был собран, он был собран с конкретной версией glibc
из хранилища Nix, и при запуске он загружает именно эту версию glibc
.
Пусть вас не смущает номер версии в имени деривации: это имя для нас, людей. Можно создать две деривации с одним и тем же именем, но разными хэшами: значение имеет только хэш.
Для чего все эти сложности?
Благодаря им, теперь можно запускать mysql 5.2
с glibc-2.18
и mysql 5.5
с glibc-2.19
.
Можно использовать модуль c python 2.7
, собранным gcc 4.6
и тот же самый модуль — с python 3
, собранным gcc 4.8
, в одной и той же системе.
Никакого больше геморроя с зависимостями и даже никакого алгоритма разрешения зависимостей. Прямые зависимости дериваций от других дериваций.
Администраторы скажут: если вам нужна старая версия PHP для одного приложения, но вы хотите обновить всю остальную систему, это можно сделать безболезненно.
Разработчики скажут: если вы хотите разрабатывать webkit
и с llvm 3.4
, и с llvm 3.3
, это можно сделать безболезненно.
Изменяемое против неизменного
При обновлении библиотеки большинство пакетных менеджеров просто перезаписывают файл в каталоге.
Потом все приложения просто запускаются с новой версией.
Ненадёжно, но кого это волнует?
В конце концов, все приложения динамически ссылаются на libc6.so
.
Поскольку деривации Nix неизменны (иммутабельны), обновление библиотеки наподобие glibc
требует перекомпиляции всех приложений, потому что путь к glibc
в хранилище Nix зависит от версии.
Как же нам быть с обновлениями безопасности? В Nix есть несколько трюков (всё ещё чистых), чтобы справиться с этой проблемой, но к ним мы вернёмся позже.
Другая проблема заключается в том, что софт, который рассчитывает на глобальные пути, не так то и просто заставить работать в Nix.
Для примера возьмём Firefox. В большинстве системы вы устанавливаете flash, и он просто начинает работать, потому что Firefox ищет плагины по глобальному пути.
В Nix не существует никого глобального пути для плагинов. Firefox должен точно знать, где находится flash. Мы справляемся с этой проблемой, создавая для Firefox особое окружение, позволяющее найти flash в хранилище Nix. Придётся создать новую деривацию Firefox: это займёт несколько секунд, и сделает настройку чуть более сложной.
Зато вам больше не нужны скрипты обновления/удаления ваших данных. В этом просто нет смысла, потому что не бывает дериваций, которые можно было бы обновлять. В Nix вы переключаетесь на другой софт со своим собственным набором зависимостей, но при этом нет никаких обновлений или удалений.
Если изменился формат данных, то миграция на новый формат — отвественность автора программы.
Заключение
Nix позволяет гибко управлять сборкой программ, делая их настолько воспроизводимыми (идентичными), насколько это вообще возможно. Кроме того, из-за природы Nix, разворачивать приложения в облаке настолько просто, что в мире Nix все инструменты контейнеризации и оркестрации безнадёжно устарели по сравнению с NixOps.
Тем не менее, Nix пока не справляется с динамической компоновкой при работе программ, или с заменой низкоуровневых библиотек, из-за того, что всё это требует перекомпиляции.
Звучит пугающе, но на практике NixOS нормально работает и на сервере, и на десктопе. Да, некоторые архитектурные проблемы ждут своего решения, но всему своё время.
Взглянув на Nixpkgs (ссылка на github) — репозиторий всего существующего софта, построенный с нуля, с непривычным подходом, с небольшим количеством основных разработчиков, но с растущим год от года вкладом сообщества, мы должны признать, что он вышел из стадии эксперимента и находится в прекрасной рабочей форме. Он стоит потраченного на него времени.
В следующей пилюле
…мы установим Nix в вашу систему (предположительно GNU/Linux, но подойдёт и OSX), и начнём его изучать.
Отсылка на «Властелина колец».
Сначала я решил, что перевод должен быть переводом. Слово деривация уже заимствовано в русском языке, но широко не используется и большинству читателей неизвестно. Так что я выбрал слово порождение, которое передаёт смысл, присутствующий в оригинальном английском термине. Однако, волна возмущения в telegram-чате NixOS RU оказалась настолько высокой, что я вынужден был отказаться от своей идеи. Derivation — ключевое для Nix понятие, которое встречается в названии функций, в сообщениях об ошибках, при поиске в Google — короче, почти везде. Оно похоже на термин file, который гораздо практичнее оказалось просто заимствовать. Мне пришлось переписывать первые восемь пилюль, которые я уже успел перевести, но зато теперь везде в тексте использован термин деривация.