Logo Craft Homelab Docs Контакты Telegram
Трендовые github проекты в нашем телеграм канале. Подпишись →
20 июля 2026 г.

Когда systemd становится execution plane для Kubernetes

Стандартная Kubernetes-нода обычно состоит из kubelet, CRI-совместимого рантайма, shim-процессов и низкоуровневого запуска контейнеров. Такая схема хорошо известна, поддерживается экосистемой и подходит для большинства кластеров. Однако на небольших VPS, edge-узлах и плотных внутренних стендах служебные процессы занимают заметную долю памяти ещё до запуска полезной нагрузки.

Альтернативная архитектура переносит выполнение подов в systemd. Агент ноды принимает состояние из Kubernetes API, собирает конфигурацию и через D-Bus создаёт systemd units. OCI-образы и изоляция контейнера сохраняются, но путь до процесса становится короче:

Обычная нода:
kubelet → CRI → containerd → containerd-shim → runc → процесс

Нода поверх systemd:
агент → D-Bus → systemd → процесс

В такой модели systemd отвечает за жизненный цикл процессов, cgroups, журналы и перезапуск. Агент остаётся реконсайлером: он сообщает системе, какое состояние нужно поддерживать, и восстанавливает связь с API после перезапуска.

Откуда берётся экономия ресурсов

На типичной ноде containerd и kubelet потребляют память постоянно, а containerd-shim создаётся для каждого контейнера. При большом числе подов расход на shim-процессы становится существенным. Systemd уже работает на Linux-хосте и умеет управлять cgroups и namespaces, поэтому в альтернативной схеме не нужен отдельный супервизор на каждую нагрузку.

Контейнеры можно запускать через systemd-nspawn. Для них сохраняются привычные примитивы Linux: namespaces, cgroups v2, seccomp и capabilities. Журналы сразу попадают в journald, поэтому оператор видит их через journalctl, а список запущенных машин — через machinectl. Состояние workload не привязано к процессу агента: перезапуск управляющего демона не останавливает уже созданные units.

В опубликованных измерениях такой подход дал RSS около 70–130 МБ для демона с несколькими логическими нодами. На тестовом хосте с 30 логическими нодами и 1660 подами управляющий слой занимал 365 МБ. Эти результаты характеризуют конкретную реализацию и стенд; перед планированием ёмкости их нужно повторять на собственных образах, CNI и профиле нагрузки.

Логическая нода перестаёт совпадать с хостом

Kubelet обычно связывает ноду с одной машиной. В systemd-ориентированной реализации один хост способен зарегистрировать несколько самостоятельных идентичностей нод. Их можно назвать pawn: у каждой есть собственный сертификат, HTTP endpoint kubelet-совместимого API, pod CIDR, cgroup slice и лимиты ресурсов.

Планировщик Kubernetes воспринимает такие сущности как обычные ноды. Это помогает собрать многонодовый стенд на небольшом числе машин и проверить распределение подов, anti-affinity, topology spread, Services и NetworkPolicy без слоя виртуализации. Количество подов задаётся для каждой логической ноды отдельно, поэтому ограничение в 110 подов на kubelet-ноду не становится пределом для всего физического хоста.

Важно правильно описывать отказоустойчивость. Несколько логических нод не создают новые failure domain: отказ физической машины одновременно затрагивает все размещённые на ней pawn. Для высокой доступности всё равно нужны несколько независимых хостов, корректные правила affinity и репликация на уровне приложения.

Контейнер как systemd-машина

Под может быть представлен как systemd-nspawn-машина из OCI-образа. Образы извлекаются в content-addressable store, их слои дедуплицируются и могут раздаваться между нодами. Сам под получает привычные сетевой namespace, IP-адрес и правила CNI, а systemd создаёт unit, ограничивает ресурсы и отправляет stdout/stderr в журнал.

Полезная часть подхода — тесная связь между декларацией Kubernetes и фактическим состоянием хоста. resources.limits отображаются в лимиты cgroup v2, включая memory.max, cpu.max и pids.max. Ограничение числа процессов защищает от fork bomb, а событие OOM можно отразить в статусе контейнера как OOMKilled.

Политика безопасности должна быть fail-closed. Если рантайм не умеет честно применить поле SecurityContext, под следует отклонить с понятным событием, а не запускать с ослабленными ограничениями. Для контейнеров применимы runAsUser, группы, fsGroup, allowPrivilegeEscalation, capabilities, seccomp и read-only root filesystem. User namespaces дополнительно отображают root внутри контейнера в непривилегированный UID хоста.

Логи, обновления и эксплуатация

Systemd даёт несколько полезных свойств без отдельной подсистемы в агенте. Логи контейнеров изначально находятся в journald, поэтому их можно искать едиными командами с логами ОС. При обновлении агента units продолжают жить: после запуска новый процесс обнаруживает их и продолжает reconciliation. После перезагрузки хоста агент сверяет желаемое состояние и поднимает отсутствующие workload.

Эта схема не отменяет наблюдаемость. Нужны метрики cgroups, событий Kubernetes, состояния CNI и самого агента. Для диагностики стоит заранее определить, какие команды используют дежурные инженеры: systemctl, journalctl, machinectl, Kubernetes API и метрики должны давать согласованную картину одного пода.

Граница между плотностью и изоляцией

Общее ядро и namespaces подходят для доверенной внутренней инфраструктуры, CI, edge-сценариев и homelab. Они не решают задачу изоляции враждебных арендаторов на уровне microVM. Нагрузки с повышенными требованиями к границам безопасности разумнее запускать через Kata Containers, полноценные виртуальные машины либо отдельный WASM sandbox с ограниченным набором хост-интерфейсов.

Переход от CRI-стека к systemd создаёт и вопросы совместимости. Чарты, init-контейнеры и диагностические инструменты могут рассчитывать на детали containerd или файловую структуру kubelet. Поддержку CNI, CSI, SecurityContext, exec, attach, port-forward и обработку eviction нужно проверять интеграционными тестами. Отдельного внимания требуют обновления systemd и systemd-nspawn: рантайм опирается на их реализацию namespaces и mount-операций.

Архитектура Kubernetes-ноды поверх systemd полезна там, где важны низкий базовый расход памяти, высокая плотность подов и прозрачное управление процессами Linux. Её стоит воспринимать как специализированный execution plane с ясными компромиссами: экономия слоёв достигается ценой более тщательной проверки совместимости и строгого ограничения сценариев мультитенантности.