Мои приключения с NixOS, часть 1: начало работы

Sergey, 01 February 2019

Недавно у меня стало заканчиваться место на диске, в связи с экспериментами с виртуализацией, и я решил попробовать включить сжатие на всю ФС. Беглый гуглёж показал, что связка LVM + ext4 сжатие не поддерживает, и чтобы получить возможность компрессии ФС, нужно мигрировать на другую ФС, я выбрал btrfs, поскольку она поддерживает компрессию. Самый простой способ сменить разметку диска - переразметить его с переустановкой системы, так что я решил воспользоваться поводом и поставить NixOS в качестве эксперимента, благо времени на настройку у меня в данный момент достаточно.

NixOS - дистрибутив GNU/Linux, основанный на пакетном менеджере Nix, использующем свой функциональный DSL. Вот некоторые из её преимуществ, которые и заинтересовали меня:

  • Возможность полностью декларативной настройки системы. Вся конфигурация системы описывается в едином файле /etc/nixos/configuration.nix. Это касается и загрузчика, и initramfs, и запускаемых сервисов, и их настроек. Более того, поскольку конфигурация пишется на чистом функциональном языке, результат сборки системы из файла /etc/nixos/configuration.nix не зависит от текущего состояния системы и всегда один и тот же, что подводит ко второму достоинству.
  • Надёжные обновления. Из сказанного в предыдущем пункте также следует, что обновление системы так же надёжно, как установка системы с нуля.
  • Все изменения конфигурации атомарны. Каждое изменение конфигурационного файла с последующей перестройкой системы порождает новое поколение, при этом система всегда остаётся в консистентном состоянии, отвечающем текущему состоянию конфигурационного файла, и всегда есть возможность откатиться на любое предыдущее состояние системы.
  • Интеграция nix с пакетными менеджерами различных языков. Более того, его можно использовать как менеджер зависимостей для C++.
    Более полное завлекалово можно посмотреть здесь. Для меня одним из сигналов стало то, что на NixOS Wiki я нашёл более актуальную информацию по GVT-g, чем на Arch Wiki.

Теперь, когда мы разобрались, зачем это нам может быть нужно, посмотрим, что здесь с установкой. Скачать образ NixOS можно здесь. Доступны различные варианты загрузки, как классические образы дисков, так и VirtualBox appliances, AMI для Amazon EC2 и образы для Microsoft Azure. Рассмотрим установку с образа LiveCD. Записать образ на диск/флешку можно с помощью dd.

После загрузки мы получим в управление консоль, откуда можно либо запустить графический интерфейс с помощью systemctl start display-manager, либо провести установку прямо из консоли. Графического установщика пока нет. На tty8(Alt-F8) доступен мануал NixOS. В принципе, весь порядок установки описан в нём достаточно подробно, поэтому я лишь кратко перескажу порядок установки:

  1. Настроить сеть в установщике с помощью NetworkManager, либо вручную.
  2. Разбить диск на разделы и отформатировать их.
  3. Примонтировать все разделы системы в /mnt и поддиректории.
  4. Сгенерировать начальный файл конфигурации:
    # nixos-generate-config --root /mnt
    
  5. Поправить файл конфигурации /etc/nixos/configuration.nix. Он прокомментирован, поэтому с начальной настройкой проблем быть не должно. Если настраиваете Wi-Fi с помощью networkmanager, нужно не забыть его добавить в environment.systemPackages и сказать NixOS, что используется он, а не wpa_supplicant:
    # networking.wireless.enable = true;  # Enables wireless support via wpa_supplicant.
    networking.networkmanager.enable = true;
    

    Также имеет смысл сразу включить в список системных пакетов w3m или links для просмотра документации, либо какой-нибудь DM. Настройка для разных DE и DM отличается, поэтому здесь я не буду на ней останавливаться. Также стоит заметить, что помимо системного набора пакетов, у каждого пользователя есть свой собственный набор пакетов, задаваемый в переменной users.user.<username>.packages. Кроме того, пакеты можно устанавливать, не прописывая их в системной конфигурации, с помощью команды nix-env -i <package>. Удалять, соответственно nix-env -e <package>. Эти команды можно выполнять, как от имени суперпользователя, так и от имени обычного пользователя. В первом случае пакет устанавливается общесистемно, во втором - локально для установившего пользователя. При этом также используются различные каналы установки, т. е. система у вас может обновляться из канала nixos-stable, а пользовательские пакеты - из nixos-unstable. Можно включить автообновление:

    system.autoUpgrade.enable = true;
    

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

  6. Собственно установка:
    # nixos-install
    

    В конце установки вас попросят установить пароль для root. После этого шага, если вы где-то напартачили в конфигурации, nixos-install после правок можно будет запустить заново.

  7. Система установлена, можно перезагружаться.

Теперь несколько слов об опыте использования. Останавливаться на том, как выполняется администрирование системы, я не буду, это описано в мануале, да и я сам с этим не до конца разобрался. Поэтому просто напишу о некоторых особенностях, с которыми мне довелось столкнуться.

Как я уже упоминал ранее, каждая пересборка системы с помощью nixos-rebuild switch генерирует новую запись в загрузчике. Если конфиг часто меняется, это приводит к захламлению меню загрузчика, что может мешать в случае, если загрузчиком является systemd-boot. Почистить мусор от старых поколений можно командами:

# nix-collect-garbage -d
# nix-rebuild switch

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

Также мне понадобилось использовать программу, не вошедшую в репозитории NixOS - acpilight. Эта программа позволяет рулить подсветкой дисплея и клавиатуры через ACPI. В мануале NixOS(да и nixpkgs) предлагается не совсем удобный способ создания локального хранилища пакетов, который подразумевает скачивание всего дерева пакетов NixOS. Более удобный способ состоит в использовании оверлеев. Пример оверлея можно посмотреть у меня в репозитории.

Из интересных фич, на которые я наткнулся при чтении мануала по nixpkgs, можно отметить возможность создания кастомных пакетов для Vim и Emacs, включающих в себя уже готовую конфигурацию и набор плагинов.

Также можно отметить возможность создания отдельных окружений для разработки с помощью nix-shell. Например, вот так выглядит создание окружения для этого блога:
Создаём файл shell.nix для nix-shell:

$(nix-build '<nixpkgs>' -A bundix)/bin/bundix

Полученный файл shell.nix будет иметь вид вроде:

with (import <nixpkgs> {});
let
  env = bundlerEnv {
    name = "setser.github.io-bundler-env";
    inherit ruby;
    gemfile  = ./Gemfile;
    lockfile = ./Gemfile.lock;
    gemset   = ./gemset.nix;
    groups   = [ "default" "jekyll_plugins" ];
  };
in stdenv.mkDerivation {
  name = "setser.github.io";
  buildInputs = [ env ];
}

Теперь можно запустить nix-shell, и в нём собирать проект. С помощью nix-build можно собрать пакет с приложением(в данном случае блогом), он будет лежать в result.

Похожим образом можно сделать окружение для проектов, например, на Haskell, но перед этим стоит внимательно ознакомиться с соответствующим разделом мануала nixpkgs. Вот некоторые грабли, которые могут подстерегать на этом пути:

  1. https://nixos.org/nixpkgs/manual/#users-guide-to-the-haskell-infrastructure
  2. https://github.com/commercialhaskell/stack/issues/2586
  3. https://github.com/commercialhaskell/stack/issues/4362
  4. https://github.com/commercialhaskell/stack/issues/2975
    Кроме того, у меня так и не получилось добиться того, чтобы проект собирался с помощью nix-build, даже с использованием cabal2nix.

Что касается количества пакетов и их свежести, repology.org говорит, что у NixOS с этим всё очень даже неплохо: по количеству пакетов и по их свежести она входит в первые пять репозиториев. Необходимость освоения нового языка для конфигурации несколько повышает порог вхождения.

NixOS - весьма перспективная система, но в данный момент есть всё ещё некоторые шероховатости, особенно при работе с проектами на Haskell. Некоторые её особенности, такие как, например, воспроизводимость сборки системы, будут полезны в CI/CD и при отладке. Декларативная конфигурация полезна для облачных приложений, а атомарные обновления - крайне полезная вещь для десктопа, да и не только.

UPD: залил свой конфиг на GitHub. Также добавил в статью пример оверлея.