На Хабре про OSTree обычно вспоминают не как про самостоятельную технологию, а как про "то, на чём держатся" Fedora CoreOS / Silverblue / Kinoite и вообще вся тема immutable / atomic desktop / container-optimized OS. Это видно по типовым материалам: обзор CoreOS с объяснением rpm-ostree и layering, обзор Silverblue, новости/разборы релизов Fedora, где rpm-ostree фигурирует как механизм поставки базовой системы.
При этом намного хуже покрыта практическая сторона "как сделать свой цикл поставки": поднять свой OSTree-репозиторий, выпускать свои refs и раскатывать их на узлы так, чтобы можно было обновиться и откатиться.
Ещё одна причина, почему тема ощущается нами недораскрытой — постоянная путаница терминов. В текстах и обсуждениях часто смешиваются:
OSTree (хранилище/формат поставки коммитов дерева ОС),
rpm-ostree (инструмент сборки/обновления OSTree-системы из RPM + layering),
immutable как философия (/usr "не трогаем руками"),
"атомарные обновления" как пользовательское ощущение ("применилось целиком после перезагрузки — и можно откатить").
А ещё в комментах легко встретить полярные оценки: от "классная инженерия" до "катастрофа и головная боль" — обычно потому, что люди пытаются использовать image-based как обычный пакетный конструктор (или наоборот).
Постараемся показать полный замкнутый цикл на примере НАЙС.ОС — не в теории, а на практике:
сервер → OSTree repo (refs+summary) → клиентский образ → проверка → обновление/rollback.
В НАЙС.ОС для этого уже есть специальные скрипты в пакетах rpm-ostree и rpm-ostree-host.
1. Короткий словарь: что есть что (и что с чем не путать)
OSTree — это "Git-подобная доставка дерева ОС"
OSTree хранит снимки файлового дерева (коммиты). Это не "набор пакетов", а именно результат: вот такое /usr, вот такие файлы, вот такая версия. Плюс метаданные и ссылки-ветки.
Три базовых термина:
commit — конкретная версия дерева (снимок состояния).
ref — "ветка/канал": имя, которое указывает на текущий commit (например
niceos/5.2/x86_64/minimal).remote — источник, откуда клиент подтягивает refs/commits (обычно HTTP(S)).
Почему сравнение с Git вообще уместно: вы не "чините систему пакетами по месту", вы переключаетесь между версиями дерева, как между коммитами/ветками.

rpm-ostree — это "как собрать и обслуживать OSTree-систему из RPM"
rpm-ostree — прослойка, которая:
берёт RPM-репозитории,
по декларации (treefile JSON) собирает из них дерево,
коммитит результат в OSTree repo,
а на клиенте умеет делать upgrade/rebase/rollback по этой модели.
Отдельный важный термин — client-side layering: когда вы “наслаиваете” дополнительные пакеты поверх базового образа, и это тоже становится новым коммитом/деплоем на клиенте.
"Immutable" — это не религия, а договор
В большинстве rpm-ostree-подходов смысл примерно такой:
базовый слой (часто /usr) — не правим руками, он приходит как версия;
изменения и данные живут в местах, которые для этого предназначены (/etc, /var);
системные изменения применяются транзакционно: подготовили новую версию → переключили → перезагрузили.
Если пытаться жить "как в обычном mutable Linux" (править /usr, ставить и удалять что попало на каждом сервере уникально) — будет боль.
2. Где тут "атомарность"
Слово "атомарно" в этой теме означает не "магия, которая никогда не ломается", а более приземлённое:
Обновление собирается заранее (на сервере или на клиенте, в зависимости от сценария).
На узле появляется новый deployment (новая версия рядом со старой).
В момент применения вы не "заменяете файлы на лету", а переключаетесь на новую версию при перезагрузке.
Если не взлетело — rollback: выбираете предыдущий deploy и опять перезагрузка.

Тест "атомарности/rollback" для ostree-режима:
посмотреть deployments,
сделать upgrade → перезагрузка,
сделать rollback → перезагрузка.
То есть "атомарность" здесь — это договор: у тебя всегда есть известные состояния системы, и переход между ними контролируемый.
3. Чем это отличается от “обычных обновлений пакетами”
Классическая пакетная модель (dnf/apt/yum и т.д.)
У вас тысячи пакетов, их зависимости, скрипты пост-установки, конфиги, сервисы.
Два одинаковых сервера могут “разъехаться” по состоянию просто из-за порядка действий, разных репозиториев, ручных доустановок и т.д.
Откат часто превращается в отдельный проект: “а что мы поменяли, а какие конфиги трогали, а какой пакет по��янул другой пакет…”.

OSTree/rpm-ostree модель
Есть ref → ожидаемый результат. Это прям мощная штука для одинаковых узлов.
Обновление становится ближе к релизному процессу: “вот версия N, вот версия N+1”.
Возврат назад обычно проще концептуально: "верни предыдущий deploy".
Компромиссы
Нужно принять дисциплину: база — как артефакт, а не как песочница.
Не всё удобно делать "на каждом сервере руками": лучше выпускать новый ref или использовать управляемые механизмы (layering, контейнеры, конфиги в /etc, данные в /var).
Желательно продумать профили (BIOS/UEFI, virtio/sata), иначе вы упрётесь в банальное "не совпало имя root-диска" и будете считать, что "OSTree это ад", хотя это просто инженерная гигиена.

4. Кому подходит (и кому нет)
Подходит почти идеально
Много одинаковых серверов/ВМ (десятки/сотни) — и вы хотите, чтобы “одинаковость” была не мечтой, а свойством процесса.
Edge / удалённые площадки, где вы цените предсказуемость и быстрый откат больше, чем свободу ручного тюнинга.
Инфраструктурные роли, где важна стабильная база: NAT/VPN/IDS/логирование/агенты наблюдаемости.
Контуры с аудитом: проще отвечать на вопрос "что именно развёрнуто на узле", когда это версия/ref, а не набор исторических событий.
Не подходит (или потребует аккуратной перестройки процессов)
Если у вас "каждый сервер уникален": уникальные ручные донастройки базовой системы, пакеты ставятся/сносятся хаотично.
Если вы ожидаете, что "обновление = поставили пару пакетов и всё", и при этом не хотите менять подход.
Если вы не готовы держать релизную дисциплину refs (stable/testing, кто и когда выпускает новые коммиты, как тестируем перед раскаткой).
5. Что будет дальше
В следующих частях посмотрим полный цикл на примере НАЙС.ОС:
Часть 2 (сервер): как поднять свой OSTree-репозиторий, собрать ref из treefile и опубликовать repo так, чтобы клиенты могли делать pull (скрипт
mkostreerepo).Часть 3 (клиент): как собрать загрузочный клиентский образ (RAW) с deploy нужного ref, проверить, обновиться и откатиться (скрипт
mk-ostree-host.sh).
Часть 2. Сервер: поднимаем свой OSTree-репозиторий в НАЙС.ОС (и делаем его пригодным для клиентов)
В первой части мы договорились: OSTree/rpm-ostree — это не "обновления пакетами", а поставка версии системы как артефакта (commit/ref), с нормальным откатом. Теперь переходим к практическому "как сделать свой канал поставки", то есть сервер OSTree.
Сразу важный тезис: OSTree-сервер — это не обязательно "сложный сервис". В 90% случаев это просто каталог OSTree-репозитория, который вы публикуете по HTTP(S), и обновляете в нём summary.
1) Что считается “сервером OSTree” в реальности
У вас есть рабочая директория (назовём её REPOPATH), внутри которой:
REPOPATH/repo— данные OSTree-репозитория (это то, что будет видеть клиент),REPOPATH/cache— кэш сборки (внутреннее, клиенту не нужно).
И главное правило публикации:
Клиентам отдаём только
repo/,cache/не трогаем.
2) Инструмент НАЙС.ОС: mkostreerepo
На серверной стороне в НАЙС.ОС задача "создать/обновить OSTree repo" решается скриптом:
/usr/bin/rpm-ostree-server/mkostreerepoПоставляется пакетом:
# rpm-ostree-2025.10-1.niceosc5.x86_64Смысл скрипта очень практичный:
гарантирует структуру
repo/+cache/,готовит treefile (если не задан — умеет сгенерировать минимальный),
при необходимости генерирует
.repoфайлы для RPM-источников,запускает
rpm-ostree compose tree,обновляет
ostree summary.

3) Быстрый старт: репозиторий "с нуля" за одну команду
Выбираем место, где будет жить репозиторий. Пример:
sudo /usr/bin/rpm-ostree-server/mkostreerepo -r=/srv/ostree/niceosЧто произойдёт "под капотом" (важно понимать, чтобы потом диагностировать):
если
/srv/ostree/niceosне существует — создаст;создаст
/srv/ostree/niceos/repoи/srv/ostree/niceos/cache;если
niceos-base.jsonотсутствует — сгенерирует минимальный treefile (niceos-base.json);если не указан режим
-cи.repoфайлов нет — нагенерирует дефолтныеniceos.repo / niceos-updates.repo / niceos-extras.repoв этот же каталог;выполнит сборку и коммит в OSTree-репо;
обновит summary.
Что должно получиться в конце:
ls -la /srv/ostree/niceos
# repo/ cache/ niceos-base.json niceos.repo niceos-updates.repo niceos-extras.repo ...
4) Нормальный прод-путь: ваш treefile JSON (управляемый состав системы)
Если вы хотите контролировать состав, ref и профиль системы — используйте свой treefile (JSON). Скрипт умеет его принять и скопировать внутрь каталога репо.
Пример:
sudo /usr/bin/rpm-ostree-server/mkostreerepo \
-r=/srv/ostree/niceos \
-p=/srv/ostree/treefiles/niceos-minimal.jsonПрактический смысл treefile (без философии):
вы фиксируете ref (имя канала),
фиксируете набор RPM-реп, из которых собирается дерево,
фиксируете packages/units.
Рекомендация из жизни: ref лучше сразу стандартизировать, например:
niceos/5.2/x86_64/minimalniceos/5.2/x86_64/baseniceos/5.2/x86_64/ndr-sensor
И тогда у вас автоматически появляется дисциплина “каналы и профили”, а не “каша из артефактов”.
5) Режим -c/--customrepo: аккуратно, там интерактив (грабля №1 для CI)
Флаг -c означает: "репофайлы будут кастомные, дефолтные не генерировать".
Но в текущем поведении скрипта есть интерактивный вопрос (Y/N). В CI это прямой путь к зависанию пайплайна: сборка ждёт ввода.
Если вам нужен неинтерактивный выпуск, обычно делают так:
не используют
-c, а контролируют.repoчерез окружение/шаблоны,либо заранее кладут все нужные
.repoфайлы, и скрипт уже не пытается ничего создавать,либо правят скрипт (вводят флаг “assume-yes/no” без
read).
6) Проверяем репозиторий: refs, история и summary (грабля №2 — забыли summary)
После сборки важно проверить, что репозиторий действительно "публично пригоден":
1) refs существуют:
ostree refs --repo=/srv/ostree/niceos/repo2) по нужному ref есть история:
ostree log --repo=/srv/ostree/niceos/repo niceos/5.2/$(uname -m)/minimal | head -n 803) summary актуален:
ostree summary -v --repo=/srv/ostree/niceos/repoПочему summary важен: клиенты часто ориентируются на него при обнаружении refs и корректной работе с репозиторием. Если summary не обновлять — симптомы на клиенте будут выглядеть как "ref не виден / pull странный / кажется сломано".
Хорошая новость: mkostreerepo делает summary --update сам. Плохая новость: если вы потом руками что-то меняли и забыли обновить — клиент первым делом споткнётся именно здесь.
7) Публикация по HTTP(S): отдаём repo/, не отдаём cache/
Дальше вам нужно просто сделать repo/ доступным по сети.
Схема URL обычно такая:
сервер:
/srv/ostree/niceos/repoклиент видит:
http://OSTREE_SERVER/ostree/niceos/repo
Минимальный пример для nginx (смысловой, без наворотов):
server {
listen 80;
server_name ostree.example.local;
location /ostree/niceos/repo/ {
alias /srv/ostree/niceos/repo/;
autoindex off;
# Важно: это статика. Никаких php/fastcgi.
}
}
Проверка “жив ли сервер” с машины клиента:
curl -I "http://ostree.example.local/ostree/niceos/repo/" || true8) Безопасность
RPM-репозитории: если у вас
gpgcheck=1, убедитесь, что ключи на сервере сборки на месте, иначе compose будет падать “не из-за OSTree”, а из-за подписи пакетов.OSTree remote: в лаборатории иногда отключают
gpg-verify, но для взрослой поставки лучше иметь подпись и доверенные ключи (плюс HTTPS или закрытый контур).
Мини-чеклист "сервер готов"
REPOPATH/repoсуществует и содержит объекты OSTreeostree refsпоказывает ваш refostree summary -vотрабатывает без ошибокrepo/доступен по HTTP(S) из сети клиентовсборка воспроизводима: treefile/
.repoлежат и контролируются
В третьей части соберём клиентский загрузочный образ в НАЙС.ОС через mk-ostree-host.sh, подключим его к серверу, сделаем deploy нужного ref и покажем "проверка → обновление → откат" на практике (и разберём главную боль: root-dev и почему VM иногда не грузится).
Часть 3. Клиент: делаем загрузочный образ, проверяем deploy, обновляемся и откатываемся (на примере НАЙС.ОС)
Во второй части мы подняли серверную сторону: есть OSTree-репозиторий (repo/), есть ref, есть summary, и всё это публикуется по HTTP(S). Теперь превращаем это в реальную пользу: делаем клиентский образ, который загружается, знает свой ref, умеет обновляться и (главное) откатываться.
В НАЙС.ОС для этого уже есть готовый скрипт — mk-ostree-host.sh, который не просто делает deploy, а собирает полноценный дисковый RAW-образ с разметкой, /boot, GRUB2, kargs и fstab.
1) Инструмент НАЙС.ОС: mk-ostree-host.sh и откуда он берётся
Скрипт лежит здесь:
/usr/bin/rpm-ostree-host/mk-ostree-host.shПакетная принадлежность такая:
rpm -qf /usr/bin/rpm-ostree-host/mk-ostree-host.sh
# rpm-ostree-host-2025.10-1.niceosc5.x86_642) Что именно делает скрипт (смысловой разбор, чтобы понимать грабли)

mk-ostree-host.sh делает весь цикл сборки образа:
Создаёт файл-диск (
IMG_NAME.raw) нужного размера (FILE_SIZEГБ).Подключает его как loop-устройство (
/dev/loopX).Делает GPT-разметку под BIOS-режим:
p1:
bios_grub(тип ef02) — чтобыgrub2-installмог жить на GPT в BIOS режимеp2:
/boot(ext4, ~300 MiB)p3:
/(ext4, всё остальное)
Через
kpartxсоздаёт /dev/mapper/…p2 и …p3.Форматирует p2/p3 в ext4.
Монтирует p3 в
MOUNT_POINT, p2 — вMOUNT_POINT/boot.Делает OSTree-часть:
init repo
init-fs sysroot
remote add
http://IP_ADDR(важно: часто без gpg-verify в стендах)pull нужного
REPO_REFostree admin deploy
Подмонтирует внутрь деплоя системные fs (
/dev,/proc,/sys,/run,/boot,sysroot), чтобы внутри chroot всё работало корректно.Ставит GRUB2 в образ, генерирует конфиги, настраивает kernel args.
Записывает
fstabи ставит временный пароль root (root:changeme).Аккуратно размонтирует и снимает loop/mapper (даже при ошибках — через trap cleanup).
Из этого важный вывод: скрипт создаёт не “файловую систему”, а полноценный загрузочный диск.
3) Параметры запуска, которые реально важны
Скрипт требует:
FILE_SIZE— размер диска (ГБ)IMG_NAME— имя образа без расширенияIP_ADDR— адрес OSTree-сервера (куда клиент будет ходить за ref)REPO_REF— ref, который вы хотите развернутьMOUNT_POINT— временная точка монтирования при сборке--root-dev— целевое устройство корня на той машине/VM, где образ будет грузиться
И вот тут начинается главная инженерная правда:
Грабля №1: --root-dev (sda3 vs vda3) — причина "не грузится" №1
Во время сборки корень выглядит как:
/dev/mapper/loop0p3(или похожее)
Но после запуска VM это устройство исчезает. Внутри VM вы увидите:
/dev/sda3(если диск подключили как SATA/SCSI),/dev/vda3(если virtio в KVM/QEMU),и т.д.
Поэтому скрипт:
сначала выставляет root= на текущий mapper,
потом заменяет root= в загрузочных конфигурациях на
--root-dev, чтобы при реальной загрузке ОС нашла корень.
Если --root-dev неверный — вы получите kernel panic “cannot mount root”.
4) Практика: собрать клиентский RAW-образ
4.1 Сценарий “BIOS + SATA/SCSI” (обычно /dev/sda3)
sudo /usr/bin/rpm-ostree-host/mk-ostree-host.sh \
-s 20 \
-n niceos-5.2-minimal \
-i 10.0.0.10 \
-r niceos/5.2/x86_64/minimal \
-m /mnt/niceos-root \
--root-dev /dev/sda34.2 Сценарий “BIOS + virtio” (обычно /dev/vda3)
sudo /usr/bin/rpm-ostree-host/mk-ostree-host.sh \
-s 20 \
-n niceos-5.2-virtio \
-i 10.0.0.10 \
-r niceos/5.2/x86_64/minimal \
-m /mnt/niceos-root \
--root-dev /dev/vda34.3 Где лог
sudo tail -n 200 "/var/log/mk-ostree-host.sh-$(date +%Y-%m-%d).log"5) Как загрузить образ в виртуалке
Частая практика: конвертировать raw → qcow2, если вы на KVM/QEMU.
qemu-img convert -f raw -O qcow2 niceos-5.2-virtio.raw niceos-5.2-virtio.qcow2И запустить (пример):
qemu-system-x86_64 \
-m 2048 -smp 2 \
-drive file=niceos-5.2-virtio.qcow2,if=virtio,format=qcow2 \
-net nic -net user \
-serial mon:stdioВажно: если вы используете if=virtio, то root-диск станет /dev/vda, значит корень — /dev/vda3. Это должно совпасть с --root-dev.
6) Первая загрузка: контроль результата (5 минут, которые экономят часы)
После старта VM:
6.1 Сменить пароль root
Скрипт обычно ставит временный пароль (root:changeme). Меняем сразу:
passwd6.2 Проверить deploy
ostree admin statusСмысл проверки:
вы видите список deployments,
какой активный,
какие ещё доступны (в том числе для rollback).
6.3 Проверить /boot и entries
mount | egrep ' on / | on /boot '
ls -la /boot/loader/entries/6.4 Проверить fstab
cat /etc/fstabЕсли там /dev/sda3, а у вас virtio (/dev/vda3) — это сигнал, что образ собран под другой профиль (или требует правки).
7) Обновление и откат (концепт + минимум действий)
Смысл rpm-ostree/ostree-подхода:
обновление готовит новый deploy,
переключение обычно происходит после перезагрузки,
откат — это переключение на предыдущий deploy (тоже обычно с перезагрузкой).
Практический ритуал проверки “оно действительно атомарное”:
посмотреть текущий статус deployments
обновиться
перезагрузиться
если плохо — rollback
перезагрузиться
Да, звучит слишком просто — в этом и фишка: возврат — не ручной разбор пакетов.
8) Типовые ошибки на клиенте и как быстро понять причину
Ошибка A: "не грузится / cannot mount root"
почти всегда: неверный
--root-dev(sda3 vs vda3)
Решение: пересобрать образ с правильным--root-dev(или чинить loader/fstab вручную, но это уже аварийный ремонт).
Ошибка B: "ref не тянется"
нет доступа до сервера,
сервер не публикует
repo/,summary не обновлён,
ref указан неверно.
Диагностика:curl -I http://SERVER/.../repo/на сервере
ostree refs,ostree summary -v
Ошибка C: "UEFI, а образ BIOS"
Скрипт, ориентирован на BIOS GPT + bios_grub. Если VM/железо в UEFI-only — нужен отдельный профиль разметки с ESP (FAT32, ef00) и grub2-efi.
Мини-чеклист "клиент готов"
Образ загрузился в нужной VM/на железе
Пароль root сменён
ostree admin statusпоказывает ожидаемый deploy/ref/bootсмонтирован, есть/boot/loader/entries/…fstabсоответствует реальному устройству (sdavsvda)Вы понимаете и умеете сделать rollback (проверено на тесте)
На этом цикл закрыт: сервер выпускает ref → клиентский образ развёрнут из ref → у вас есть обновление и откат как управляемая операция.
Есть ещё один слой смысла, который для НАЙС.ОС — не "приятный бонус", а почти идеальная посадка этой модели на философию системы.
9) Почему OSTree/rpm-ostree особенно хорошо ложится на НАЙС.ОС
Во многих дистрибутивах OSTree выглядит как "надстройка поверх традиционного комбайна": огромная базовая ОС, на которую пытаются натянуть image-based обновления.
У НАЙС.ОС логика обратная и более современная:
база минималистична и существует как стабильный фундамент;
приложения подразумеваются в контейнерах (Docker/Kubernetes), а не как "зоопарк сервисов, установленных на хост руками".

И вот тут OSTree идеально совпадает с моделью эксплуатации:
хост = стабильная платформа,
приложения = контейнеры,
обновление хоста = версия/коммит,
откат хоста = переключение deploy,
а контейнерный стек живёт своей жизнью поверх предсказуемой базы.
Это не "комбайн системы из прошлого", где на хосте одновременно крутится всё подряд и обновления превращаются в квест. Это подход "хост как платформенный слой", который и предполагает: не шаманить руками на /usr, а держать базу контролируемой и одинаковой, а прикладной слой — переносимым и оркестрируемым.
10) Почему это снижает эксплуатационную боль именно в контейнерной инфраструктуре
Если вы реально эксплуатируете Docker/Kubernetes, у вас обычно болит не "как поставить 100 пакетов на хост", а:
чтобы хосты одного пула были одинаковыми;
чтобы обновления не ломали неожиданно сеть/iptables/драйверы/ядро;
чтобы можно было быстро откатить "плохой апдейт ноды" без ручной археологии.
OSTree-модель отвечает на это прямолинейно:
узлы пула сидят на одном ref → одинаковый базовый слой;
обновление готовится как новый deploy → не портит текущий на лету;
rollback возвращает предыдущий deploy → быстро возвращает ноду в рабочее состояние.
И да — НАЙС.ОС включена в реестр российского ПО, что в проектах импортозамещения часто упрощает формальную часть внедрения.
