Пробуем NixOS. Первые впечатления.
kayo — Пт, 24/04/2015 - 16:32
Про NixOS мне было известно уже давно, но всё никак не было повода попробовать. В этой статье постараюсь разложить по полочкам, что да как. И да, мы займёмся практикой установки, настройки и использования. Поскольку в данный момент мне была нужна операционная система для сервера, то настройку окружения рабочего стола здесь мы пройдём мимо.
Введение
NixOS — уникальный дистрибутив. В общих словах, уникален он тем, что в нём решена проблема ада зависимостей (dependency hell). Если вы хорошо представляете суть этой проблемы, то введение можете смело пропустить.
Любая сколько-нибудь сложная программа, как правило, использует возможности множества библиотек через систему типов данных и вызовов спецификации API, что делает программу зависимой от этой спецификации. В свою очередь, библиотеки тоже могут зависеть от других библиотек подобным образом. Мы живём в реальном мире, а реальный мир развивается крайне не синхронно. В то время как одни программы и библиотеки часто не поспевают за развитием своих зависимостей, — другие требуют возможностей более новых версий API. Всё хорошо, покуда изменения сохраняют обратную совместимость, то есть новая спецификация API дополняет старую, но не изменяет её.
Другими словами, пока разработчики не изменят типы данных и определения функций в API одной из зависимостей, никаких проблем нет. Нет никакой необходимости менять что-то в зависимых библиотеках и программах. Проблема возникает тогда, когда нарушается обратная совместимость, но это становится лишь потенциальной проблемой. А дело в том, что мы всегда можем использовать те версии зависимостей, которые необходимы нашей программе. Однако мы используем не одну программу а множество, и в некоторый момент может возникнуть ситуация, когда одна из наших программ зависит от старой версии некой библиотеки, тогда как другая требует новую версию её же.
И такие ситуации очень часты, жаргонно они называются адом зависимостей (dependency hell). Сопровождающие дистрибутивов решают эту проблему путём создания разных экземпляров одной и той же библиотеки в виде обособленных пакетов с суффиксами в именах, например libjpeg62 и libjpeg8 для libjpeg веток 6.2 и 8 соответственно. Но такой подход имеет ряд недостатков и является скорее обходом проблемы, а не её решением. Когда в системе одновременно существуют разные версии одних и тех же библиотек, необходимо их как-то различать. libjpeg в нашем примере собрана в виде разделяемого объекта (shared object) с именем libjpeg.so, для различения версий этот файл переименован на этапе пакетизации в libjpeg.so.62 и libjpeg.so.8. С другой стороны необходимы разные наборы заголовочных файлов для сборки под разные версии API. Такое разделение создаёт проблемы при сборке (компиляции) и связывании (линковке). Возникает необходимость в вещах типа pkg-config и autotools которые генерируют соответствующие флаги для сборки и связывания программ с нужными версиями библиотек.
Ад зависимостей особенно актуален для динамично развивающихся компонентов для языков Python (PIP), JavaScript (NodeJS NPM), Haskell (Cabal Hackage), D (DUB). Дистрибутивы обычно существенно отстают от их развития и мы имеем ситуацию, когда пакеты из официальных репозиториев слишком старые и не подходят нам по этой причине. В системе Debian с которой я работаю уже очень давно, приходится устанавливать пакеты из веток testing и sid поверх stable, чтобы иметь актуальные версии. Но прямое обновление зачастую вызывает обновление других зависимостей, которые не хотелось бы трогать. Когда отставание было существенным, приходилось целиком сидеть на ветке sid. Но каждое обновление системы было довольно рискованным занятием, поскольку в ветке sid часто оказывались пакеты, работоспособность котрых в бою принципиально никем не проверялась. А откат пакетной базы (downgrade) сопряжен с рядом трудностей и в общем случае не всегда возможен, поскольку старые версии пакетов могли быть уже удалены из репозиториев.
NixOS
Подход NixOS к управлению пакетами и конфигурированию в корне отличается от подхода, используемого в дистрибутивах традиционно. Каждый пакет устанавливается в своё собственное изолированное окружение. Таким образом, вполне нормально сосуществование в системе разных версий одного и того же пакета. Как написано на сайте дистрибутива, пакетный менеджер Nix реализует чисто функциональный подход к управлению пакетами и конфигурации.
Вся конфигурация системы описывается в файле /etc/nixos/configuration.nix на специально разработанном декларативном языке. Конфигуратор обрабатывает этот файл вместе с множеством других, содержащих описания пакетов и их конфигурации, в результате чего устанавливаются и настраиваются пакеты в системе. Разработчики заверяют, что с одним и тем же файлом конфигурации результат обновления системы будет тем же, что и результат установки с нуля.
Подготовка
Пробовать новую для нас систему будем, как водится, в виртуальной машине. Я использую менеджер виртуальных машин libvirtd, но с не меньшим успехом можно воспользоваться qemu/kvm.
Первое, что необходимо сделать, скачать подходящий для вас live-образ дистрибутива с официального сайта. Я выбрал ISO образ минимального инсталляционного CD диска под архитектуру x86_64.
Затем создадим образ диска формата qcow2 размера 10G средствами qemu-img:
qemu-img create -f qcow2 rootfs-x86_64.qcow2 10G
В virt-manager:
Затем подключаем установочный ISO образ и стартуем виртуальную машину через virt-manager или kvm:
kvm -m 1024M \ -cdrom nixos-minimal-14.12.501.2635bde-x86_64-linux.iso \ -drive file=rootfs-x86_64.qcow2,if=virtio \ -net nic,model=virtio -net user
После загрузки установочного CD имеем следующее:
Теперь необходимо залогиниться под пользователем root без пароля. Как говорит подсказка, руководство по установке и настройке доступно в 8-й виртуальной консоли. Если ставим на реальной машине и поблизости нет других машин с сетью, то оно будет весьма кстати.
Установка
Итак, самое время приступить к установке. Процесс мало отличается от установки любого другого дистрибутива в ручном режиме и включает в себя привычные стадии подготовки носителя для корневой файловой системы, бутстрэпинга и настройки.
Установка по ssh
Сделаем маленькое отступление. Мне привычнее выполнять установку по ssh, поэтому я сконфигурировал ssh сервер для входа пользователем root. Для этого надо добавить следующую секцию в файл /etc/nixos/configuration.nix:
{ config, pkgs, ... }: { imports = [ <nixos/modules/installer/cd-dvd/installation-cd-minimal.nix> ]; # Добавленная секция, разрешающая запуск ssh сервера и вход суперпользователя с паролем services.openssh = { enable = true; permitRootLogin = "yes"; }; }
Теперь применим конфигурацию и установим пароль суперпользователя:
nixos-rebuild switch passwd
У меня сервер sshd не запустился сам, поэтому потребовалось запустить его вручную:
# Запускаем start sshd # проверяем, что запустился status sshd
Если вас устраивает графическая консоль виртуальной машины, всё это делать не нужно.
Разбивка диска
Я решил использовать менеджер виртуальных томов LVM для корневой файловой системы и таблицу разделов GPT. Виртуальное блочное устройство для размещения наших разделов в системе распознано как /dev/vda. Воспользуемся утилитой gdisk для разбивки:
[root@nixos:~]# gdisk /dev/vda GPT fdisk (gdisk) version 0.8.8 Partition table scan: MBR: not present BSD: not present APM: not present GPT: not present Creating new GPT entries. Command (? for help): ? b back up GPT data to a file c change a partition's name d delete a partition i show detailed information on a partition l list known partition types n add a new partition o create a new empty GUID partition table (GPT) p print the partition table q quit without saving changes r recovery and transformation options (experts only) s sort partitions t change a partition's type code v verify disk w write table to disk and exit x extra functionality (experts only) ? print this menu # создаём новую таблицу разделов GPT Command (? for help): o This option deletes all partitions and creates a new protective MBR. Proceed? (Y/N): Y # создаём раздел для системного загрузчика grub2 # 4 мегабайта должно хватить # но не забываем выставить правильный тип ef02 Command (? for help): n Partition number (1-128, default 1): First sector (34-20971486, default = 2048) or {+-}size{KMGTP}: Last sector (2048-20971486, default = 20971486) or {+-}size{KMGTP}: +4M Current type is 'Linux filesystem' Hex code or GUID (L to show codes, Enter = 8300): L 0700 Microsoft basic data 0c01 Microsoft reserved 2700 Windows RE 4100 PowerPC PReP boot 4200 Windows LDM data 4201 Windows LDM metadata 7501 IBM GPFS 7f00 ChromeOS kernel 7f01 ChromeOS root 7f02 ChromeOS reserved 8200 Linux swap 8300 Linux filesystem 8301 Linux reserved 8302 Linux /home 8400 Intel Rapid Start 8e00 Linux LVM a500 FreeBSD disklabel a501 FreeBSD boot a502 FreeBSD swap a503 FreeBSD UFS a504 FreeBSD ZFS a505 FreeBSD Vinum/RAID a580 Midnight BSD data a581 Midnight BSD boot a582 Midnight BSD swap a583 Midnight BSD UFS a584 Midnight BSD ZFS a585 Midnight BSD Vinum a800 Apple UFS a901 NetBSD swap a902 NetBSD FFS a903 NetBSD LFS a904 NetBSD concatenated a905 NetBSD encrypted a906 NetBSD RAID ab00 Apple boot af00 Apple HFS/HFS+ af01 Apple RAID af02 Apple RAID offline af03 Apple label af04 AppleTV recovery af05 Apple Core Storage be00 Solaris boot bf00 Solaris root bf01 Solaris /usr & Mac Z bf02 Solaris swap bf03 Solaris backup bf04 Solaris /var bf05 Solaris /home bf06 Solaris alternate se bf07 Solaris Reserved 1 bf08 Solaris Reserved 2 bf09 Solaris Reserved 3 bf0a Solaris Reserved 4 bf0b Solaris Reserved 5 c001 HP-UX data c002 HP-UX service ea00 Freedesktop $BOOT eb00 Haiku BFS ed00 Sony system partitio ef00 EFI System ef01 MBR partition scheme ef02 BIOS boot partition Press the <Enter> key to see more codes: fb00 VMWare VMFS fb01 VMWare reserved fc00 VMWare kcore crash p fd00 Linux RAID Hex code or GUID (L to show codes, Enter = 8300): ef02 Changed type of partition to 'BIOS boot partition' # создаём раздел для менеджера томов LVM # используя всё оставшееся место Command (? for help): n Partition number (2-128, default 2): First sector (34-20971486, default = 10240) or {+-}size{KMGTP}: Last sector (10240-20971486, default = 20971486) or {+-}size{KMGTP}: Current type is 'Linux filesystem' Hex code or GUID (L to show codes, Enter = 8300): 8e00 Changed type of partition to 'Linux LVM' # применяем изменения Command (? for help): w Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING PARTITIONS!! Do you want to proceed? (Y/N): Y OK; writing new GUID partition table (GPT) to /dev/vda. The operation has completed successfully.
Теперь оприходуем физический том в LVM:
# создаём физический том [root@nixos:~]# pvcreate /dev/vda2 Physical volume "/dev/vda2" successfully created # создаём группу томов с именем nixos [root@nixos:~]# vgcreate nixos /dev/vda2 Volume group "nixos" successfully created # создаём логический том с именем root в группе nixos [root@nixos:~]# lvcreate --extents 100%FREE --name root nixos Logical volume "root" created # я сразу расширил его до 100%
Теперь можно отформатировать наш том и смонтировать для дальнейшей установки на него системы:
[root@nixos:~]# mkfs.ext4 /dev/nixos/root mke2fs 1.42.12 (29-Aug-2014) Creating filesystem with 2619392 4k blocks and 655360 inodes Filesystem UUID: 010ef059-a9dc-4416-a4df-e1f7a2cedbda Superblock backups stored on blocks: 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632 Allocating group tables: done Writing inode tables: done Creating journal (32768 blocks): done Writing superblocks and filesystem accounting information: done [root@nixos:~]# mount /dev/nixos/root /mnt
Установка системы
Осталось установить NixOS на свежесозданный раздел:
# сгенерируем начальную конфигурацию # опцией root мы указали куда смонтирован корневой каталог [root@nixos:~]# nixos-generate-config --root /mnt writing /mnt/etc/nixos/hardware-configuration.nix... writing /mnt/etc/nixos/configuration.nix...
Теперь необходимо подшаманить файл /etc/nixos/configuration.nix в /mnt. Как минимум нужно правильно указать устройство для установки начального загрузчика grub2:
# раскомментируем строку и укажем правильное имя устройства boot.loader.grub.device = "/dev/vda";
Затем я включил сервер ssh, и добавил пользователя kayo, чтобы после установки можно было сразу логиниться в систему:
# раскомментируем строку services.openssh.enable = true; # и добавим секцию users.extraUsers.kayo = { isNormalUser = true; uid = 1000; # я одмин ^_^ extraGroups = [ "wheel" ]; };
Потом я хотел бы, чтобы в новой системе уже были установлены некоторые программы:
# раскомментируем секцию и добавим нужные программы environment.systemPackages = with pkgs; [ wget htop screen ];
Всё, наконец-то можно делать бутстрэпинг:
[root@nixos:~]# nixos-install building the system configuration... # затем побежит установка
Когда установка системы завершена и загрузчик grub2 установился без ошибок, можно перезагружать систему с образа диска.
Продвинутый вариант установки
Когда нужно просто развернуть систему используя уже имеющийся файл конфигурации, можно поступить следующим образом:
- Загрузить конфигурацию в git репозиторий на один из хостингов вроде github.com или bitbucket.org
- В установочной системе установить пакет git
- Превратить каталог /etc/nixos в полноценный репозиторий git
- Добавить ссылку на удалённый репозиторий
- Затянуть содержимое удалённого репозитория
- Установить систему с использованием конфига из репозитория
Такой вариант может показаться слишком сложным, но он очень удобен, ведь теперь конфигурация находится в надёжном месте и может быть легко развёрнута на любой машине. Вы можете, к примеру, создать отдельные ветки для каждого типа конфигурации и вытягивать нужную на каждую из ваших машин. Также удобно объединять изменения одних веток с другими.
Итак, делаем следующее:
# Устанавливаем пакет git [root@nixos:~]$ nix-env -i git # Переходим в каталог с конфигом [root@nixos:~]$ cd /mnt/etc/nixos # Создаём репозиторий [root@nixos:/mnt/etc/nixos]$ git init Initialized empty Git repository in /mnt/etc/nixos/.git/ # Добавляем ссылку на внешний апстрим [root@nixos:/mnt/etc/nixos]$ git remote add origin git@bitbucket.org:kayo/nixos-config # Генерируем rsa ключи для доступа к git # если репозиторий публичный и доступен по HTTP # можно обойтись и без этого [root@nixos:/mnt/etc/nixos]$ ssh-keygen # Передаём публичный ключ хостеру репозитория # нам потребуется доступ на чтение по нему # обычно это называют ключи для деплоя [root@nixos:/mnt/etc/nixos]$ cat ~/.ssh/id_rsa.pub # Не забудем потом скопировать ключи в корневую # файловую систему устанавливаемой системы # Переименовываем имеющийся конфиг # чтобы избежать конфликта [root@nixos:/mnt/etc/nixos]$ mv configuration.nix configuration.nix~orig # Затягиваем контент из внешнего репозитория [root@nixos:/mnt/etc/nixos]$ git pull origin master From bitbucket.org:kayo/nixos-config * branch master -> FETCH_HEAD # Устанавливаем в качестве апстрима удалённую ветку master [root@nixos:/mnt/etc/nixos]$ git branch --set-upstream-to=origin/master master Branch master set up to track remote branch master from origin. # Запускаем установку [root@nixos:/mnt/etc/nixos]$ nixos-install
Это работает, я гарантирую это :)
Настройка и использование
Как мы уже видели при установке, вся настройка производится путём редактирования файла /etc/nixos/configuration.nix. Чтобы изменения вступили в силу, нужно вызвать утилиту nixos-rebuild <действие>. Существует несколько вариантов действий:
- switch — соберёт и применит новую конфигурацию и сделает её загружаемой по-умолчанию. При этом предыдущие конфигурации по прежнему будут доступны из меню загрузки.
- boot — соберёт новую конфигурацию и сделает её загружаемой по умолчанию, но не применит её.
- test — соберёт и применит новую конфигурацию, но не сделает её загружаемой.
- build — только соберёт конфигурацию. Можно использовать для выявления ошибок сборки и установки.
- dry-build — показывает какие пути в хранилище будут задействованы в операции сборки, но не выполняет сборку.
- dry-activate — делает сборку но вместо активации показывает какие изменения будут произведены в процессе активации.
- build-vm — собирает виртуальную машину для проверки работоспособности конфигурации до применения на реальной машине. В результате будет создано изолированное окружение, так что если что-то пойдёт не так, ваши реальные файлы не пострадают, но без лишнего оверхеда в виде образов дисков.
Не бедно, правда? Таких возможностей я не встречал в других дистрибутивах. Так как же NixOS достигает всего этого? Давайте разбираться.
Во первых, как уже было сказано выше, каждый пакет существует в своём персональном окружении. На практике это реализовано вот как: пакеты живут в своих поддиректориях директории /nix/store. С помощью символьных ссылок пакетный менеджер Nix слинковывает зависимости пакетов из других директорий, так, чтобы программы могли запускаться и работать. Ад зависимостей забыт как страшный сон, программы всегда будут иметь правильно настроенные зависимости.
Одна из причин моего интереса к NixOS заключалась в том, что пакеты из Npm и Hackage актуальных версий доступны в репозитории пакетов Nix. Как оказалось, в NixOS имеются инструменты автоматического конвертирования Npm2Nix и Cabal2Nix и другие подобные им.
Управление пакетами
Инструментарий Nix позволяет делать вещи, которые мы всегда считали преступными и незаслуженно осуждали. Например, теперь обычные пользователи могут устанавливать пакеты из репозитория. В традиционных дистрибутивах сие занятие весьма опасное, поскольку в них пакеты устанавливаются глобально для всей системы. В NixOS же управление пакетами отделена от настройки рабочих окружений. Другими словами, окружение уровня системы никак не будет затронуто, когда пользователь установит нужный ему пакет. Установленные пользователем пакеты будут доступны только ему и тем пользователям, кто тоже установил их, при этом не будет никакого дублирования, если версии пакетов одинаковые.
Вот как обычный пользователь может установить и удалить программы:
# Получить список доступных пакетов можно так: nix-env -qa '.*' # (как видим, тут можно регекспы) # Получаем доступные версии нужной нам программы для редактирования configuration.nix nix-env -qa emacs # Устанавливаем хороший годный, кхм, текстовый редактор nix-env -i emacs # Удаляем другой текстовый, кхм, редактор nix-env -e vim # Просмотрим список установленных нами пакетов nix-env -q
Ставить и удалять программы глобально для всей системы можно через /etc/nixos/configuration.nix как мы уже делали при установке.
Может возникнуть вполне закономерен вопрос: а что происходит с хранилищем пакетов при удалении и обновлении пакетов? Происходит то, что ничего не происходит. Покуда существуют конфигурации, ссылающиеся на старые версии пакетов, они так и будут оставаться в системе нетронутыми. Это сделано, чтобы всегда иметь возможность откатиться на предыдущие конфигурации, если при очередном обновлении что-то сломалось.
Но имеющийся инструментарий позволяет чистить систему от ненужных конфигураций и старых версий пакетов:
# Удаляем все не текущие генерации nix-env --delete-generations old # Или можем удалить все старее прошлой недели nix-env --delete-generations 7d # Удаляем неиспользуемые пакеты из хранилища навсегда nix-collect-garbage
Или глобально для всех профилей это же можно сделать так (разумеется от рута):
# Удалить все не текущие nix-collect-garbage --delete-old # Или всё старше недели nix-collect-garbage --delete-older-than 7d
Конфигурирование
NisOS предлагает функциональный декларативный подход к конфигурированию системы. Традиционные файлы конфигурации unix вычисляются по правилам, разработанным сопровождающими пакетов. NixOS, конечно, пока довольно молодой дистрибутив и далеко не всегда вас устроит гибкость предлагаемых правил. Однако, даже если сопровождающие не предусмотрели гибкости, всегда можно переопределить их правила своими. Здесь же мы не будем углубляться в дебри декларативного языка менеджера пакетов Nix, а рассмотрим базовые возможности настройки.
Существует полезная утилита nixos-option, которая позволяет исследовать возможности конфигурации, например так:
[kayo@nixos:~]$ nixos-option This attribute set contains: assertions boot containers ec2 environment fileSystems fonts gnu gtkPlugins hardware i18n ids jobs kde krb5 lib meta nesting networking nix nixpkgs passthru power powerManagement programs qtPlugins security services sound swapDevices system systemd time uim users virtualisation warnings zramSwap
Без аргументов команда возвращает список всех настроек в корне. Предположим, что мы хотим настроить сервис sshd. Настройки сервисов, очевидно, живут в группе services, посмотрим, какие сервисы нам доступны для настройки:
[kayo@nixos:~]$ nixos-option services This attribute set contains: … openssh … (вывод был сокращён)
Где-то в длинном списке затесалось заветное название сервиса openssh. Теперь посмотрим, что мы можем настроить:
[kayo@nixos:~]$ nixos-option services.openssh This attribute set contains: allowSFTP authorizedKeysFiles challengeResponseAuthentication enable extraConfig forwardX11 gatewayPorts hostKeys knownHosts listenAddresses passwordAuthentication permitRootLogin ports startWhenNeeded
Не все возможности но наиболее часто используемые есть. Посмотрим, как настроена аутентификация по паролю:
[kayo@nixos:~]$ nixos-option services.openssh.passwordAuthentication Value: true Default: true Description: Specifies whether password authentication is allowed. Declared by: "/nix/var/nix/profiles/per-user/root/channels/nixos/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix" Defined by: "/nix/var/nix/profiles/per-user/root/channels/nixos/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix"
Видим текущее значение и значение по-умолчанию, а также, где находится определение настроек. Итак, давайте запретим вход в оболочку с использованием пароля:
# После строки services.openssh.enable = true; # Добавим строку services.openssh.passwordAuthentication = false; # Или уберём эти строки и запишем конфигурацию # в более удобоваримой форме в виде секций services = { openssh = { enable = true; passwordAuthentication = false; }; };
Теперь проверим, изменилось ли значение:
[kayo@nixos:~]$ nixos-option services.openssh.passwordAuthentication Value: false Default: true Description: Specifies whether password authentication is allowed. Declared by: "/nix/var/nix/profiles/per-user/root/channels/nixos/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix" Defined by: "/etc/nixos/configuration.nix"
Как видим, Value стало false. Команда nixos-option ориентируется на значение в файле конфигурации, даже если конфигурация ещё не была применена.
Чтобы изменения вступили в силу, вызовем команду nixos-rebuild с соответствующим действием в зависимости от того, что мы желаем получить, как было описано выше.
На этом наше погружение в сий уникальный дистрибутив NixOS подходит к концу. Надеюсь, вам понравилось. Все свободны, спасибо за внимание ;)

Ну и как оно! Впечатления
Гость (не проверено) — Вс, 07/05/2017 - 07:53Ну и как оно! Впечатления есть? Для десктопного варианта можно использовать?
Использую на лэптопе
kayo — Ср, 02/08/2017 - 17:27Отправить комментарий