Дистрибутив AIRA
26.09.2017, aira
Когда программы были простые, а 640 килобайт хватало всем, распространение ПО осуществлялось обыкновенно копированием на ПК пользователя. Некоторые современные операционные системы, например MacOS, до сих пор так делают. Практика копирования несет с собой довольно много очевидных проблем: обновления, зависимости, хаос файловой системы и др. Для решения этих задач были придуманы специальные программы - менеждеры пакетов. Они брали на себя отслеживание обновлений, зависимостей, учет изменений и многое другое.
Nixpkgs
$ nix-env -i hello
Nix - это пакетный менеждер. В своем подходе к управлению пакетами он полностью уходит от практики копирования файлов пакета в корневую файловую систему. Каждый пакет после установки на целевую систему хранится изолированно и не может быть перезаписан. Целостность файлов подтверждается хеш-суммой.
Как следствие, в системе может существовать множество версий одного и того же пакета, а обновление происходит атомарно и с возможностью отката изменений.
$ nix-env --upgrade hello
$ nix-env --rollback
Nix - это чистый функциональный язык программирования. Описание пакета в nix это чистая функция, параметры которой - зависимости, выход - бинарный пакет программного обеспечения. То, что пакет собирается чистой функцией естественно дает нам ряд интересных последствий:
повторяемость сборок; на заданных аргументах чистая функция всегда вернет один и тот же результат, а значит пакет всегда будет сформирован один и тот же;
кеширование; необязательно собирать пакет каждый раз, чистота дает нам возможность сохранить пакет в бинарном кеше (и расшарить его для других нуждающихся);
зависимости; изменение (например обновление версии) зависимости в аргументах пакета естественно приводит к изменению выхода и пересборке, что в свою очередь вызывает рекурсивную пересборку, если этот пакет также являлся чьей-то зависимостью.
Идея пакетного менеждера неразрывно связана с понятием дистрибутива - набора пaкетов программного обеспечения. Для nix такой набор пакетов поддерживается сообществом и называется nixpkgs. Как вы наверно догадались, он тоже представляет собой функцию. Эта функция не имеет аргументов и возвращает набор пакетов (функций) программного обеспечения.
$ git clone https://github.com/NixOS/nixpkgs && cd nixpkgs
$ nix-build -A hello
Нет, для установки одного пакета не нужно собирать все пакеты дистрибутива. Nix поддержвает ленивость, это означает, что вычисляются только те выражения, которые необходимы в данный момент.
Airalab channels
Иногда возникает необходимость сделать ответвление от основного набора пакетов. Например, для добавления пакетов, которые в силу совей нестабильности или редкости не могут быть оперативно включены в основной репозиторий. Nix предоставляет такую возможность, она называется каналы (channels).
$ nix-channel --add https://hydra.aira.life/project/aira/channel/latest aira
$ nix-channel --update
Каналы позволяют устанавливать пакеты из репозиториев, отличных от корневого nixpkgs. В канале Airalab, например, присутсвуют последнии версии модулей AIRA, parity и ROS.
$ nix-env -i parity
NixOS GNU/Linux
Дистрибутив программного обеспечения характеризует высокая степень интеграции между пакетами, совместимость и согласованность. Дистрибутивы операционных систем также имеют встроенные средства инсталляции и первоначального запуска, менеджмента ресурсов и сервисов. Внимательному читателю становится ясно, что общая конфигурация системы тоже может быть представлена в виде функции, вход которой - доступные пакеты, сервисы и окружение, а выход - декларативное описание желаемого состояния ОС.
{ config, pkgs, ...}:
{ boot.loader.grub.device = "/dev/sda";
fileSystems."/".device = "/dev/sda1";
services.openssh.enable = true;
}
Выше представлена минимальная конфигурация системы с запущенным демоном OpenSSH.
Единая конфигурация реализована в дистрибутиве NixOS GNU/Linux. Под капотом - пакетный менеджер nix и дополнительный набор скриптов для управления загрузкой и сервисами ОС. AIRA использует дистрибутив Linux на основе NixOS для запуска на железе или в виртуальной машине. Nix дает атомарность и надежность обновлений, что очень важно при автономной работе. А декларативная формальная конфигурация оптимальна для автоматического анализа и синтеза поколений AIRA.
{ config, pkgs, ... }:
{ boot.loader.grub.device = "/dev/sda";
fileSystems."/".label = "nixos";
services = {
openssh.enable = true;
parity.enable = true;
parity.chain = "kovan";
railway-game.enable = true;
};
}
Выше приведен пример конфигурации AIRA v0.10. Здесь видно, что в качестве сервиса активируется Ethereum нода parity
, вибрается тестовая сеть kovan
. Сервис railway-game
взаимодействует с контроллером Z21 и Ethereum нодой, следит за рынками метрик.
Hydra CI
Непрерывная интеграция позволяет наладить постоянный цикл сборки и автоматического тестирования программного обеспечения. Гарантировать, что в любой момент времени будет готовый к демонстрации программный продукт. Это прекрасная практика, мы с удовольствем применяем ее в Airalab.
Проект Nix предлагает свое решение для CI. До этого я работал в основном с Travis, но так как для AIRA мы используем NixOS, попробуем разобраться что к чему.
В основе Hydra лежит пакетный менеджер Nix. Это дает нативную поддержку большого числа его возможностей: каналы, бинарный кеш пакетов, распределенная сборка, Nix-expressions и др. Управление проектом разбивается на следующие части:
- Project - это наиболее общее описание задачи, проект позволяет группировать jobset’ы и формировать релизы.
- Jobset - это набор общих задач на исполнение, например, формирование отладочной и релизной сборки, сборка документации, подготовка инсталлятора. Jobset описывается функцией на nix.
- Job - это конкретная задача на исполнение, например, собрать ISO образ продукта.
- Build step - это минимальный отделимый этап выполнения задачи (сборка пакета).
{ nixpkgs }:
rec {
hello = nixpkgs.hello;
}
Пример jobset приведен выше. Здесь входом является nix-канал с программным обеспечением, а выходом одна задача на пакет hello
. Этот jobset считается успешно выполненым, если выход hello
успешно удается получить (собрать).
Каждый jobset периодически обновляется, в hydra это называется Evaluation. Это значит, что на вход jobset подставляются актуальные значения, в примере это git clone
по каналу, который указан во входах jobset. Обновленный jobset подается в очередь сборщика и каждая работа выполняется или завершается с ошибкой. В hydra прослеживается тесная интеграция с Nix, поддерживаются такие возможности как загрузка пакета с зависимостями (Nix closure), возможно повторить сборку локально.
$ bash <(curl https://hydra.aira.life/build/35/reproduce)
Заключение
Функциональный подход в конфигурации системы и управления пакетами выглядит привлекательно и при этом оказывается проще и надежнее, чем императивная альтернатива. Простой пример на parity: parity.nix vs Dockerfile. Этот пример показывает насколько сильно подход к решению задачи изменяет саму задачу и ее финальный результат.