Перейти к основному содержимому

Почему Fedora CoreOS — это container optimized дистрибутив

· 10 мин. чтения
Иван Давыдов

Fedora CoreOS на официальном сайте представлена как container optimized, container-focused, container based и так далее OS. Но что это вообще значит? Там предустановлен какой-то container runtime? А еще что? В этой статье попытаемся разобраться.

fcos

к сведению

Ссылка на статью на хабре – https://habr.com/ru/companies/selectel/articles/817299/

Что такое optimized

Optimized в нашем случае стоит переводить не как «оптимизированная», а скорее как «оптимальная». Так и поступили неизвестные переводчики сайта Fedora Project. Потому что слово «оптимизированный» намекает на производительность, но никакого ее прироста при запуске контейнеров на FCOS (Fedora CoreOS) в сравнении с другими дистрибутивами мы не получим.

fcos-screenshot

Почему вообще OS может называться оптимальной для контейнеров? Надо понимать, что для их запуска потребуется всего две вещи:

  • Linux, потому что, будем честны, запускать контейнеризованные приложения на Windows — это не то, чем вы хотите заниматься;
  • Платформа для запуска контейнеров, например, docker или podman.

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

Fedora CoreOS зачастую используется для запуска Kubernetes кластеров. Собственно, для этого и была разработана. FCOS редко когда используют для того, чтобы запускать единичные контейнеры. Это основная система для OKD, а также апстрим для RHEL CoreOS, которая является дефолтной для OpenShift.

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

Иммутабельная система и транзакционные обновления

Начну с самой интересной, на мой взгляд, особенности. FCOS использует систему rpm-ostree. Это своего рода git для вашей операционки. Разберемся подробнее, что это такое.

В основе лежит ostree — система контроля обновлений операционной системы, очень похожая на git. Вот как она работает. У вас есть базовый образ ОС, к примеру, последняя стабильная версия. Вам нужно установить на нее нужное программное обеспечение и сконфигурировать его. Вы это выполняете, тестируете и коммитите изменения. Тем самым вы делаете их резистентными к перезагрузкам системы, так как установленное, но незакомиченное ПО будет слетать после каждого ребута.

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

Обновления в ostree происходят транзакционно: последние два-три образа системы хранятся на диске. Именно за счет этого и можно быстро откатиться на них.

ostree-scheme

А почему просто не использовать git? Зачем эта git-подобная система? Дело в том, что git отслеживает далеко не все. К примеру, без его внимания остаются метаданные файлов: права, владелец, дата создания и т. д. Для git все это — user specific data, которая ему не нужна.

А теперь вернемся к rpm-ostree. Она дополняет ostree, встраивая пакетный менеджер dnf. По итогу мы получаем image-based систему с гибкостью package-based системы. Rpm-ostree позволяет наслаивать установленные юзером пакеты на базовый образ. То есть их установка через rpm-ostree создает новый коммит в ostree-дерево. Таким образом, поддерживается так называемый client-side layering. Соответственно, когда у вас появится новая версия базового образа и вы хотите до нее обновиться, вы, можно сказать, делаете git rebase. :)

Изначально rpm-ostree проектировалась именно для поддержки удобного client-side layering, но сейчас все стало сложнее. Теперь система реализует ряд других полезных функций. Например, во время установки пакетов она изолирует запускаемые скрипты в контейнерах, используя утилиту bubblewrap, чтобы они не влияли на работающую систему. Также поддерживается функционал native containers, который позволяет собирать базовый образ системы через Docker-файлы (подробнее об этом — чуть ниже).

Если ostree считается иммутабельной системой больше из-за ее принципа обновления, то rpm-ostree к этому добавила почти полностью read-only файловую систему. Вносить изменения вы можете только в /etc или /run, в которых хранятся конфигурационные файлы, а также в /var, в котором находятся все остальные данные приложений.

Вот что происходит с файлами внутри этих директорий при обновлении системы:

  • /run приводится в первоначальное состояние, не сохраняя пользовательских изменений или добавленных юзером файлов с прошлого запуска. Оно и понятно, эта директория предназначена для добавления конфигурационных файлов, относящихся только к текущему запуску системы;
  • /etc будет принимать новые изменения от обновлений (если они есть), но в то же время не будет трогать пользовательские изменения;
  • /var никак не меняется.

Привычные директории типа /home — это симлинки на /var/home.

Автоматические background-обновления

Система rpm-ostree позволяет организовать транзакционные обновления в фоновом режиме. То есть когда появляется обновление, система об этом узнает и устанавливает его. После перезапуска будет использован новый образ.

fcos-autoupdate-scheme

По дефолту FCOS поставляется с запущенным приложением-демоном Zincati. Оно отвечает за автоматические обновления и перезапуски системы. Zincati узнает о доступных апдейтах с помощью удаленного сервера Cincinnati, периодически запрашивая у него информацию. Cincinnati, можно сказать, подписывается на нужный образ системы и по запросу предоставляет граф обновлений. По нему и проходится Zincati (пример: FCOS Updates Graph).

Так как перезапуск системы — действие, которое теоретически может поломать (пусть даже временно) работу всего кластера, Zincati поддерживает несколько стратегий обновления:

  • Immediate (default) — перезагружает систему сразу после установки обновления;
  • FleetLock — использует стороннее решение, которое устанавливает максимальное количество одновременно обновляющихся машин. Машина пошла обновляться — забрала замочек, обновилась — отдала. Если замков нет, она ждет, когда появятся;
  • Periodic — позволяет указать конкретные временные окна, в которые система должна перезагружаться. Например, 29 мая в 10 утра.

По информации с форума Fedora Project, команда Fedora планирует упростить этот процесс и убрать из схемы Cincinati. Разработчикам кажется, что сейчас процесс излишне усложнен. Планируется оставить только Zincati, предварительно улучшив его и сделав умнее.

zincati-forum-screen

Запуск системы на основе контейнер-образа

Система rpm-ostree наследует часть кода библиотеки ostree-rs-ext, которая реализует функционал ostree native containers. Он позволяет создавать базовый образ системы на основе Docker-файлов.

Примеры:

# Перемещение библиотек, установленных в /opt (симлинк на /var/opt), в /usr
FROM quay.io/fedora/fedora-coreos:testing-devel
RUN mkdir /var/opt && \
rpm -Uvh https://downloads.linux.hpe.com/repo/stk/rhel/7/x86_64/current/hp-scripting-tools-11.60-20.rhel7.x86_64.rpm && \
mv /var/opt/hp/ /usr/lib/hp && \
echo 'L /opt/hp - - - - ../../usr/lib/hp' > /usr/lib/tmpfiles.d/hp.conf && \
ostree container commit

# Установка пакетов
FROM quay.io/fedora/fedora-coreos:stable
RUN rpm-ostree install kubernetes-client && ostree container commit

# Изменение boolean значения SELinux
FROM quay.io/fedora/fedora-coreos:stable
RUN setsebool -P -N container_manage_cgroup 1

Чтобы использовать в системе «сбилженный» и опубликованный базовый образ, достаточно выполнить rebase системы (rpm-ostree rebase ostree-unverified-registry:REGISTRY/IMAGE:VERSION) и перезапуститься.

Дополнительную информацию по этой теме вы можете найти в документации, а больше примеров — на GitHub.

Безопасность

В целом из статьи понятны некоторые security-фичи, которые вытекают из факта использования rpm-ostree системы. Вот они:

  • Иммутабельная файловая система;
  • Изоляция скриптов, запускаемых при установке пакетов, в контейнеры;
  • Ограниченный и контролируемый список установленных пакетов в базовом образе;
  • Автообновления.

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

Именно поэтому FCOS идет с предварительно установленным и настроенным SELinux. Правда, в базовом образе операционки не установлены некоторые дополнительные программы для него типа semanage, так как они зависят от python, которого в FCOS нет (по поводу зависимостей от python есть отдельная дискуссия на GitHub). Из-за этого управлять политиками SELinux может быть затруднительно, но вы все равно можете скомпилировать свои модули и установить их в систему.

Если вам все же нужен semanage, установите его вместе с питоном, используя пакет policycoreutils-python-utils:

sudo rpm-ostree install policycoreutils-python-utils 
...
Added:
checkpolicy-3.5-3.fc39.x86_64
libb2-0.98.1-9.fc39.x86_64
libgomp-13.2.1-7.fc39.x86_64
mpdecimal-2.5.1-7.fc39.x86_64
policycoreutils-python-utils-3.5-8.fc39.noarch
python-pip-wheel-23.2.1-2.fc39.noarch
python-unversioned-command-3.12.3-2.fc39.noarch
python3-3.12.3-2.fc39.x86_64
python3-audit-3.1.2-8.fc39.x86_64
python3-distro-1.8.0-6.fc39.noarch
python3-libs-3.12.3-2.fc39.x86_64
python3-libselinux-3.5-5.fc39.x86_64
python3-libsemanage-3.5-4.fc39.x86_64
python3-policycoreutils-3.5-8.fc39.noarch
python3-setools-4.4.3-1.fc39.x86_64
python3-setuptools-67.7.2-7.fc39.noarch

Так как мы все-таки говорим о container optimized системе, отмечу следующее. Благодаря политикам SELinux в FCOS есть дополнительная изоляция контейнеров от хоста. Ниже приведены примеры на данный момент уже закрытых с помощью SELinux дыр в безопасности runc:

Сама система в целом рассчитана на то, что большая часть юзер-софта будет запускаться в контейнерах. В сочетании с политиками SELinux это дает плюсик к безопасности.

Кстати, часто дыры в этой самой безопасности связаны с неправильным использованием памяти. Это так называемые «memory safety issues». Чтобы уменьшить количество связанных с ними багов, команда Fedora старается все чаще использовать в кодовой базе своих приложений языки Rust и Go.

Предустановленные инструменты для контейнеров

В FCOS из коробки идут podman и moby-engine (docker). А зачем их два? Одновременно запускать контейнеры и в одном, и в другом не получится — это вызовет конфликты, да и смысла нет. Разработчики FCOS оставляют moby-engine только ради удобства пользователей, которым привычнее работать с docker cli. Цитата с недавней встречи разработчиков:

«When we started building Fedora CoreOS one of the things we wanted to do was keep Container Linux users happy. Users who wanted to continue to use docker could do so without issue. We've held to that principle for a long time. I would like to continue to ship it because I know there are good number of people who do use it».

Очень вольный перевод

Когда мы начинали создавать Fedora CoreOS, мы хотели, чтобы пользователи Container Linux (предшественник Fedora CoreOS) были довольны. Чтобы пользователи, которые хотели продолжать использовать Docker, могли бы делать это без проблем. Мы следовали этому принципу на протяжении длительного времени. Я хотел бы продолжить эту практику, потому что знаю, что многие люди используют Docker.

Кстати, на этой встрече как раз обсуждалась идея выпиливания moby-engine из базового образа FCOS (подробности на GitHub). Основным мотивом была нехватка волонтеров и мейнтейнеров в команде Fedora. Именно из-за нее версия moby-engine в стабильной FCOS на момент написания статьи — 24.0.5, когда для docker-ce доступна версия 26.1.2.

Кроме того, иногда в рамках одного обновления FCOS происходят большие скачки версии moby-engine. Например, так было в переходе с версии 38.20231027.3.2 на 39.20231101.3.0: moby-engine подняли сразу с 20.10.23 до 24.0.5. По итогу этой встречи в документацию решили добавить инструкцию по замене moby-engine на docker-ce.

Сейчас, насколько я понимаю по репозиторию, идет работа по обновлению пакета moby-engine до 26.0.0. Но когда этот переход произойдет, неизвестно.

Гибкий деплой

FCOS доступен для установки, по сути, на все платформы, которые вам только могут понадобиться. На ту же виртуалку в Selectel его можно установить с помощью образа для OpenStack.

fcos-download

Предварительно сконфигурировать систему во время первого запуска можно с помощью Ignition-конфигурации. Ignition — это машиночитаемый формат, который можно скормить системе при первом запуске, как Cloud-Init, и она примет вид, описанный в файле. В свою очередь, Ignition-конфигурация формируется путем транспиляции файла в формате Butane. Butane — это YAML-файлы, данные в которых описаны человекочитаемой Butane-схемой.

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

variant: fcos
version: 1.5.0
passwd:
users:
- name: core
ssh_authorized_keys:
- ssh-ed25519 AA...
storage:
files:
- path: /etc/NetworkManager/system-connections/ens3.nmconnection
mode: 0600
contents:
inline: |
[connection]
id=ens3
type=ethernet
interface-name=ens3
[ipv4]
address1=192.168.0.3/24,192.168.0.1
dns=8.8.8.8;
dns-search=
may-fail=false
method=manual
- path: /etc/hostname
mode: 0644
contents:
inline: fcos
systemd:
units:
- name: zincati.service
enabled: false

Заключение

Возвращаемся к вопросу, который и стал причиной написания этой статьи: почему Fedora CoreOS — это оптимальная ОС для контейнеров?

В целом все описанные выше фичи FCOS способствуют тому, чтобы ее можно было просто и удобно использовать для работы с контейнерами. В первую очередь, как основную систему для узлов Kubernetes-кластера. Коробочное решение защищено с помощью политик SELinux, система невелика с точки зрения количества пакетов, в ней предустановлены две протестированные платформы для запуска контейнеров: moby-engine и podman.

Последний пункт, кстати, в этой статье я называл одним из минимальных требований к «оптимальной» для контейнеров ОС. Но во время своих поисков я наткнулся на этот занятный комментарий одного из разработчиков CoreOS в треде про удаление moby-engine. Он говорит, что они хотели бы выпустить версию FCOS вообще без предустановленного container engine, чтобы юзеры сами могли выбрать, какой им нужен. Можно ли считать такой образ FCOS «container optimized»? Я думаю да, если доступные rpm-пакеты всех возможных container-платформ будут протестированы для него. ¯(ツ)/¯

fcos-dev-comment