The OpenNET Project / Index page

[ новости /+++ | форум | теги | ]

Атомарные обновления в OSTree
Введение

Если вы администрируете больше одного сервера, вы наверняка встречали
классическую драму: "обновили пакеты - и стало иначе".
Вроде бы обновление - рутинная операция, но на практике это часто лотерея из
мелких изменений: уехали зависимости, поменялись версии библиотек, подтянулись
новые настройки по умолчанию в фалах конфигурации, где-то пересобралось ядро, а
вишенкой сверху стало то, что два "одинаковых" узла после одинаковых команд
через пару недель оказываются уже не совсем одинаковыми.

Главная проблема не в том, что пакетные менеджеры плохие. Проблема в том, что
модель "обновление = набор независимых изменений" плохо сочетается с
требованиями инфраструктуры: предсказуемостью, быстрым восстановлением и
возможностью чётко ответить на вопрос: какая именно версия системы сейчас работает.

OSTree предлагает другой вариант: система поставляется как версия (коммит) и
переключается целиком. Не "вот вам 200 пакетов, удачи", а "вот вам собранное
состояние базовой системы, которое можно развернуть, загрузить и при
необходимости откатить".

Эта часть статьи - обзорная: без глубокой внутренней анатомии, но с теми
понятиями, которые реально нужны, чтобы решить, стоит ли вам это встраивать в
процесс. Практика на примере НАЙС.ОС будет во 2-3 части.

Что такое OSTree и в чём "атомарность"

OSTree - это механизм хранения и доставки "снимков" файловой системы (точнее,
базовой части системы) в виде коммитов. Коммит - это зафиксированное состояние
дерева файлов, которое можно получить из репозитория и развернуть на узле.

В OSTree есть несколько базовых сущностей, и их полезно выучить один раз:
Коммит - это версия системы, Ref - это "ветка/канал", которая указывает на
коммит. Например, ref может обозначать "НАЙС.ОС 5.2 minimal для x86_64". Remote
- это источник, откуда узел получает refs и коммиты (сервер/репозиторий,
опубликованный по сети). Администратор обычно работает не с "хешами коммитов",
а с ref. Ref - это человеческий контракт: стабильный идентификатор канала поставки.



Суть атомарности проявляется в терминах "deploy" и "rollback".
Deploy - это развёртывание выбранного коммита в sysroot так, чтобы система
могла загрузиться в этой версии. При deploy новая версия раскладывается как
отдельная, не разрушая текущую рабочую версию "в процессе".

Rollback - это откат на предыдущий deploy. Важно, что откат здесь - не
"откатываем 17 пакетов и надеемся", а переключаемся на ранее развёрнутую версию
системы. Поэтому rollback может быть быстрым и предсказуемым.

Отсюда и типичная фраза "две версии рядом": текущая рабочая и новая
подготовленная. Это нормальная и полезная конструкция: она позволяет
обновляться без превращения узла в полу-состояние "половина старого / половина нового".


В OSTree-модели существует sysroot - место, где живут:

* данные OSTree (репозиторий, метаданные),
* развёрнутые деплои,
* элементы, необходимые для загрузки.

На практике администратору достаточно понимать "где искать правду", когда что-то пошло не так:

* в sysroot живёт структура OSTree;
* в /boot/loader обычно находятся записи загрузки (entries), которые соответствуют деплоям.

Это не руководство по ремонту загрузчика - это ориентиры, чтобы не искать проблему "в воздухе".


OSTree отвечает за "версионирование и доставку состояния".
Но откуда берутся эти состояния (коммиты)?

Если базовый мир у вас RPM, то роль сборщика выполняет "rpm-ostree". Он берёт
декларацию состава системы (treefile, обычно JSON), тянет пакеты из указанных
репозиториев, собирает файловое дерево и коммитит результат в OSTree-репозиторий.

Treefile - это ключ к дисциплине: вы не обновляете систему "как получится", вы
выпускаете версию по описанию "как должно быть".



Чем OSTree отличается от "обычных обновлений"

Классическая модель: много пакетов, много зависимостей, много вариантов результата.

Обычный пакетный апдейт - это операция над множеством объектов. Даже если
команда одна, фактически вы получаете:

* разные комбинации зависимостей в зависимости от времени,
* разные обновления в разных репозиториях,
* разные настройки по умолчанию в конфигурации,
* и самое неприятное - разные результаты на узлах, где "состояние до
обновления" уже слегка различалось.

Из-за этого "обновление" перестаёт быть полностью повторяемой операцией.


В OSTree-модели вы говорите: "узел должен быть на ref X".
Ref X указывает на конкретный коммит. Коммит - это конкретное состояние.
Два узла, которые взяли один и тот же ref/коммит, получают одинаковую базу.

Это не означает, что у них будут одинаковые данные и конфиги приложения. Но
означает, что "скелет" ОС и системные компоненты соответствуют выпуску.

Подобный подход снижает "случайные поломки" так как обновление перестаёт быть
постепенным разрушением текущей установки.
Новая версия готовится отдельно, а переключение происходит на границе (обычно
на перезагрузке/переключении деплоя).

И главное: версия проверяется как единое целое на стороне сборки. Если у вас
нормальный процесс - вы тестируете выпуск (коммит) до того, как он пошёл в production.


OSTree - это не "поставил и забыл". Это другой стиль:

* Плюс: одинаковость и управляемость.
* Минус: вы не поощряете "а давай на этом сервере руками доустановим ещё 15
пакетов" как основной процесс.

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



Кому OSTree подходит, а кому - нет


Подходит:

1. Много одинаковых серверов/ВМ
Когда вы хотите, чтобы "узлы одной роли" реально были одинаковыми, а не "примерно похожими".

2. Edge и удалённые площадки
Где откат должен быть быстрым и не требовать "разбора пакетов по косточкам".

3. Регламентированные контуры и аудит
Где нужно уметь показать, что конкретный узел работает на конкретной версии
системы, с подтверждаемым составом.

4. Инфраструктурные роли
NAT, VPN, IDS/сенсоры, логирование, базовые сервисы наблюдаемости - где важна
стабильная база и меньше сюрпризов от апдейтов.



Не подходит или требует аккуратности:

1. Постоянная уникальная ручная донастройка базовой системы на каждом узле
Если вы регулярно меняете именно "базу" руками (не конфиги, а состав системы),
вы будете воевать с моделью.

2. Ожидание "пакетного конструктора" без выпуска образа/коммита
OSTree раскрывается, когда есть выпуск (коммит) и канал (ref). Без дисциплины
выпусков вы теряете смысл.

3. Нет готовности вести refs и релизный процесс
Нужно определить, что такое stable/testing, как именуются refs, кто публикует, как тестируется.

4. Смешивание BIOS/UEFI и разных виртуальных контроллеров без профилей
OSTree не отменяет реальность загрузки: root-устройство, fstab, режим BIOS/UEFI
- всё это нужно учитывать. Если вы хотите "один образ на все случаи", придётся
делать профили сборки и правила.


Атомарные обновления в OSTree: какой подход лучше?

В НАЙС.ОС практический процесс обычно делится на две независимые роли:

1. Сервер OSTree - это машина, где:

* из RPM-репозиториев собирают дерево ОС по treefile (JSON),
* результат коммитят в OSTree-репозиторий,
* обновляют summary,
* публикуют каталог OSTree-репозитория по HTTP(S) для клиентов.

2. Клиент - это узел (или процесс сборки образа), который:

* добавляет remote на этот сервер,
* делает pull нужного ref,
* делает deploy,
* дальше загружается в выбранный deploy.

Для НАЙС.ОС у вас уже есть две "точки автоматизации", которые отлично ложатся на этот поток:

* mkostreerepo - поднимает/обновляет OSTree-репозиторий на сервере (rpm-ostree
compose tree + summary).
* mk-ostree-host.sh - готовит дисковый образ и выполняет deploy нужного ref (это будет в 3 части).

Эта часть статьи - про сервер: как создать репозиторий, как проверить, что он
корректный, и что именно публиковать клиентам.




Серверная часть: создание OSTree-репозитория в НАЙС.ОС

Требования и предпосылки

Серверу нужны:

* rpm-ostree и ostree (если rpm-ostree отсутствует, mkostreerepo пытается
поставить его через tdnf);
* доступ к RPM-репозиториям (откуда rpm-ostree будет собирать дерево);
* место на диске: репозиторий растёт, плюс есть кэш compose.

mkostreerepo организует рабочий каталог так:

* REPOPATH/repо - данные OSTree (это то, что публикуем);
* REPOPATH/cache - кэш сборки (это внутреннее, клиентам не нужно).

Три сценария использования mkostreerepo:

1. Быстрый старт "с нуля" (без своего JSON)

Если вы не указали JSON, скрипт проверит наличие REPOPATH/niceos-base.json.
Если файла нет - он сгенерирует минимальный treefile (niceos-base.json) и продолжит.

Пример:

   sudo /usr/bin/rpm-ostree-server/mkostreerepo -r=/srv/ostree/niceos


Что произойдёт:

* создастся каталог /srv/ostree/niceos (если его нет);
* создадутся подкаталоги repo и cache;
* при необходимости будет создан niceos-base.json;
* будут сгенерированы дефолтные *.repo (если их нет и не включён custom режим);
* выполнится rpm-ostree compose tree, появится коммит и ref;
* обновится summary.

2. Управляемый вариант "своё дерево" (с treefile JSON)

Это нормальный путь для продакшена: вы фиксируете состав системы как декларацию.

Пример:

   sudo /usr/bin/rpm-ostree-server/mkostreerepo \
   -r=/srv/ostree/niceos \
   -p=/srv/ostree/niceos/treefiles/niceos-minimal.json


Поведение скрипта:

* проверит, что JSON существует,
* скопирует его в REPOPATH (и будет использовать уже копию внутри каталога репо),
* продолжит сборку.


3. "Custom repo files" (-c) - осторожно, там интерактив

Флаг -c/--customrepo говорит "репофайлы будут кастомные, дефолтные не генерировать".
Но дальше в скрипте есть интерактивный вопрос (Y/N), который может повесить CI/автоматизацию.


Практический смысл:

* если вы хотите полностью контролировать *.repo и не допускать автогенерации - используйте -c,
* но учтите, что для неинтерактивной сборки нужно либо патчить этот участок,
либо заранее обеспечить все нужные *.repo и выбрать режим без вопросов.

Пример (который может уйти в интерактив):


 sudo /usr/bin/rpm-ostree-server/mkostreerepo \
   -c \
   -r=/srv/ostree/niceos \
   -p=/srv/ostree/niceos/niceos-base.json


В treefile (JSON) на уровне процесса важны три вещи:

1. ref - имя канала поставки. Это "публичная API-точка" для клиентов.
2. repos - какие RPM-репозитории используются для сборки.
3. packages/units - состав системы и важные сервисы.

Минимальный фрагмент логики (по смыслу):

 {
   "osname": "niceos",
   "releasever": "5.2",
   "ref": "niceos/5.2/x86_64/minimal",
   "repos": ["niceos", "niceos-updates", "niceos-extras"],
   "packages": ["systemd","openssh","rpm-ostree"],
   "units": ["sshd.service"]
 }


Замечание по дисциплине:
ref лучше держать стандартизированным: версия -> архитектура -> профиль.
Это упрощает каналы stable/testing и жизнь тем, кто будет обслуживать систему через год.


Публикация repo по HTTP(S) и контроль (refs/summary)


Клиентам нужен только каталог OSTree-репозитория: REPOPATH/repo.

Каталог cache публиковать не нужно (и не стоит): это внутренний кэш сборки.

Summary критичен так как mkostreerepo после compose делает:

* ostree summary --repo=... --update
* ostree summary -v --repo=...

Summary - это метаданные, которые помогают клиентам быстро обнаруживать refs и
корректно работать с репозиторием.
Если summary не обновлён, вы получаете странные симптомы на клиентах: "ref не
виден", "pull ведёт себя не так", "не сходится список refs".


Базовые проверки на сервере

1. Посмотреть refs:

   ostree refs --repo=/srv/ostree/niceos/repo

2. Посмотреть историю по ref (пример):

   ostree log --repo=/srv/ostree/niceos/repo niceos/5.2/$(uname -m)/minimal | head -n 80

3. Проверить summary:

   ostree summary -v --repo=/srv/ostree/niceos/repo

4. Проверить структуру каталога:

   ls -la /srv/ostree/niceos
   ls -la /srv/ostree/niceos/repo | head -n 50
   ls -la /srv/ostree/niceos/cache | head -n 50


Идея публикации по HTTP(S): веб-сервер должен отдавать /srv/ostree/niceos/repo по URL вида:

   http://OSTREE_SERVER/ostree/repo/

Дальше клиент на своей стороне добавляет remote и делает pull/deploy (это будет в 3 части).

Проверка доступности с клиента (просто "жив ли HTTP"):

   curl -I "http://OSTREE_SERVER/ostree/repo/" || true


Мини-чеклист "сервер готов"

1. REPOPATH существует, внутри есть repo/ и cache/.
2. ostree refs показывает нужные refs (и они соответствуют вашей схеме именования).
3. ostree summary -v отрабатывает без ошибок.
4. repo/ доступен по HTTP(S) из сети клиентов.
5. В release-процессе есть правило: после каждого compose summary обновляется
(и это реально выполняется).


Нюансы и типовые грабли на сервере

Репозитории RPM и ключи

В дефолтных *.repo, которые mkostreerepo может сгенерировать, включён:

* gpgcheck=1
* gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-NICEOS

Это правильно по смыслу, но означает простую вещь:
на сервере сборки должны быть ключи и доверие, иначе compose упадёт не "из-за
OSTree", а из-за проверки подписей RPM.

Customrepo в CI

Флаг -c может вызвать интерактив (Y/N). Для CI это неприятно:
пайплайн повиснет, пока кто-то не придёт "нажать кнопку".
Если вы хотите автоматизацию, обычно лучше:

* не использовать интерактивный режим,
* либо заранее подготовить *.repo и доработать скрипт под non-interactive.

Разделение прав доступа

Репозиторий - это точка поставки системы.
Нормальная практика:

* запись в repo/ только у сборочного пользователя/процесса,
* веб-сервер имеет только чтение,
* доступ снаружи ограничен (HTTPS или закрытый контур).


Клиентская часть: развёртывание системы/образа в НАЙС.ОС

На клиентской стороне OSTree даёт приятную вещь: вы перестаёте "устанавливать
систему пакетами" как набор мелких действий.
Вы либо:

1. разворачиваете систему прямо в sysroot (условная "установка" через pull/deploy),
   либо
2. готовите дисковый образ, в котором уже есть нужный deploy и настроен загрузчик.

Для практики (особенно под виртуализацию) второй вариант удобнее: один раз
собрали образ - дальше тиражируете как шаблон.

В НАЙС.ОС под это уже есть скрипт: mk-ostree-host.sh, который делает образ
диска и разворачивает в него выбранный ref.

Пример "как развернуть" через mk-ostree-host.sh (RAW-образ)


Скрипт выполняет полный цикл подготовки загрузочного дискового образа:

1. Создаёт sparse-файл *.raw заданного размера (будущий диск).
2. Подключает его как loop-устройство.
3. Размечает GPT под BIOS-режим:

   * p1: BIOS boot (для GRUB2),
   * p2: /boot,
   * p3: / (корень).

4. Создаёт device-mapper устройства для разделов (kpartx).
5. Форматирует p2 и p3 в ext4.
6. Монтирует p3 как sysroot, p2 как /boot.
7. Инициализирует OSTree sysroot, добавляет remote, делает pull нужного ref и deploy.
8. Подмонтирует внутрь деплоя служебные FS (/dev, /proc, /sys, /run, /boot).
9. Устанавливает GRUB2 в образ, генерирует конфиги, выставляет kernel args.
10. Подменяет root= так, чтобы система загрузилась на целевой машине.
11. Прописывает /etc/fstab и задаёт временный пароль root.
12. Аккуратно размонтирует всё и снимает loop/devicemapper (даже при ошибках).


Скрипт принимает:

* FILE_SIZE - размер образа в ГБ.
* IMG_NAME - имя (получится IMG_NAME.raw).
* IP_ADDR - адрес OSTree-сервера (remote будет вида http://IP_ADDR).
* REPO_REF - ref, который нужно развернуть (например niceos/5.2/x86_64/minimal).
* MOUNT_POINT - временная точка монтирования sysroot при сборке.
* root-dev - важный параметр: какой диск "увидит" ОС при старте (обычно /dev/sda3 или /dev/vda3).


Во время сборки образа корень выглядит как что-то вроде:

   /dev/mapper/loop0p3


Но при запуске VM/на железе это устройство не существует.
Там корневой раздел будет, например:

* /dev/sda3 (классический SATA/SCSI контроллер),
* /dev/vda3 (virtio в KVM/QEMU),
* либо другие варианты в зависимости от платформы.

Поэтому скрипт делает важный трюк:
сначала настраивает root= на "текущее" устройство сборки, а затем заменяет его
на целевое root-dev, чтобы загрузчик в реальной машине искал корень там, где он
действительно будет.

Если root-dev выбран неверно - типичная ошибка: система падает на старте с "cannot mount root".


Пример запуска (BIOS + SATA: /dev/sda3)

   sudo ./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/sda3


Пример запуска (BIOS + virtio: /dev/vda3)


 sudo ./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/vda3


Скрипт пишет лог в /var/log/mk-ostree-host.sh-YYYY-MM-DD.log


Проверка:

   sudo tail -n 200 "/var/log/mk-ostree-host.sh-$(date +%Y-%m-%d).log"



Проверка результата на первом запуске и базовая эксплуатация

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

Первые обязательные действия

1. Сменить временный пароль root (скрипт ставит root:changeme):

   passwd

2. Посмотреть состояние OSTree:

   ostree admin status

Смысл вывода:

* вы увидите, какие deploy существуют,
* какой активен сейчас,
* какой будет активен после перезагрузки (если применимо).

3. Проверить монтирование / и /boot:

   mount | egrep ' on / | on /boot '

4. Проверить fstab:

   cat /etc/fstab

Ожидаемая логика (пример):

* /dev/sda3 -> /
* /dev/sda2 -> /boot

Если ваша VM использует virtio, там должно быть /dev/vda* (иначе вы заложили
неверный root-dev и fstab).

5. Проверить записи загрузки:

   ls -la /boot/loader/entries/

Это быстрый способ убедиться, что deploy действительно подготовил загрузочные entry.


Идея отката (rollback) без длинных портянок

OSTree хорош тем, что откат - это не "удалить 30 пакетов и вернуть 30 пакетов",
а переключение между deploy.

Модель простая:

* у вас есть "текущая версия" (deploy A),
* вы обновились/развернули новую (deploy B),
* если после перехода на B что-то не так - вы возвращаетесь на A.

На практике, прежде чем идти в сложную диагностику, в эксплуатационном контуре часто логичнее:

1. вернуть рабочее состояние откатом,
2. затем спокойно разбирать, почему новый deploy оказался проблемным.

В системах, где важен SLA, это прямое попадание в реальность: "сервис должен
работать", а не "мы сейчас два часа разгадываем зависимость".


Типовые ошибки и диагностика на клиенте

VM не загружается: неверный root-dev

Симптом:

   kernel panic / cannot mount root / ожидание root device.

Причина: в загрузчике root= указывает на /dev/sda3, а реально диск -
/dev/vda3 (или наоборот).

Решение:

* пересобрать образ с правильным --root-dev,
* либо (в аварийном режиме) править loader entries и fstab вручную - но это уже
"ремонт", а не нормальный процесс.


Клиент не тянет ref: сеть/summary/ref

Симптом:

   pull падает, ref не виден, список refs пустой.

Причины:

* нет доступа до HTTP(S) репозитория,
* сервер не публикует repo/,
* summary не обновлён,
* ref указан неправильно.

Быстрые проверки:

* curl -I до URL репозитория,
* ostree remote refs на клиенте,
* ostree summary -v на сервере.


Платформа UEFI, а образ BIOS

Симптом: VM не видит загрузчик, "No bootable device".

Причина: текущий mk-ostree-host.sh размечает BIOS-only (bios_grub), без EFI System Partition.

Решение:

* держать отдельный профиль сборки UEFI (ESP + grub2-efi),
* не пытаться "одним образом" закрыть все режимы загрузки без профилей.


Устройство загрузки поменялось, fstab не совпадает

Симптом: загрузка проходит частично, /boot не монтируется или корень монтируется не туда.

Причина: fstab жёстко привязан к /dev/sdX или /dev/vdX, а среда другая.

Решение:

* заранее стандартизировать профили (virtio/sata),
* либо перейти на UUID/labels в fstab как правило эксплуатации (это отдельное улучшение процесса).


Мини-чеклист "клиент готов"

1. Узел загрузился в нужный профиль (BIOS/virtio или BIOS/sata - как планировали).
2. Пароль root сменён (root:changeme не должен жить дальше первого входа).
3. ostree admin status показывает ожидаемый deploy/ref.
4. / и /boot смонтированы корректно, /etc/fstab соответствует реальной схеме диска.
5. /boot/loader/entries содержит записи, соответствующие deploy.
6. У вас есть регламент: как обновляем (ref stable/testing), и как откатываем при проблемах.


На этом "скелет" процесса закрыт: сервер публикует репозиторий и ref, клиент
разворачивает deploy в образ/узел и получает предсказуемые обновления и откаты.
 
25.12.2025 , Автор: Stan , Источник: https://niceos.ru/docs/ostree-v-naj...
Ключи: ostree, linux
Раздел:    Корень / Администратору / Система / Linux специфика / Установка и работа с пакетами программ в Linux

 Добавить комментарий
Имя:
E-Mail:
Заголовок:
Текст:




Партнёры:
PostgresPro
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2025 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру