Docker приучил всех думать о контейнере как об одном процессе в коробке: один сервис, эфемерное состояние, образ из Dockerfile. Но есть целый класс задач, где нужен не процесс, а полноценная машина без накладных расходов гипервизора: dev-стенды под конкретный дистрибутив, легаси-приложение со своим init, CI-раннеры, мультитенантный хостинг. Тут на сцену выходят системные контейнеры. В этом уроке разберем LXC как низкоуровневый рантайм, LXD (и его форк Incus) как менеджер поверх него, научимся запускать и обслуживать инстансы, работать с профилями, образами, сетью, хранилищем, снимками и миграцией.

Как это работает
Прикладной контейнер (Docker, podman) запускает PID 1 равным вашему приложению. Умер процесс - умер контейнер. Системный контейнер запускает внутри настоящий init (systemd), который поднимает свои юниты, cron, журнал, ssh - изнутри это неотличимо от обычной виртуалки, но ядро общее с хостом. Технически это те же неймспейсы (pid, net, mnt, uts, ipc, user) и cgroups, что и у Docker, просто собранные так, чтобы держать целую ОС, а не один бинарь.
LXC - это базовый рантайм и библиотека (liblxc), он умеет создавать контейнер из шаблона и держать его, но управление у него низкоуровневое и многословное. LXD - это демон-надстройка с REST API поверх liblxc: он добавляет образный репозиторий, профили, пулы хранилищ, управляемые сети, снимки, кластеризацию и живую миграцию. Команда у обоих одна - lxc, но это разные lxc: бинарь LXC (lxc-create, lxc-start) и клиент LXD (lxc launch, lxc list). Не путайте.
Важный сдвиг 2026 года. В конце 2023 Canonical забрала LXD под себя и сменила лицензию на AGPLv3, а сообщество во главе со Stephane Graber форкнуло проект в Incus под крылом Linux Containers. Сегодня LXD поставляется почти только через snap и заточен под Ubuntu, а Incus уже в Debian 13, Fedora, openSUSE и Arch как нативный пакет. CLI и концепции совпадают на 95 процентов, отличия косметические: группа incus-admin вместо lxd, мост incusbr0 вместо lxdbr0, домен .incus вместо .lxd. Дальше я показываю команды LXD как канон экзамена и в скобках даю эквивалент Incus.
Команды и примеры
Установка. На Ubuntu LXD идет снапом, на Debian/Fedora ставят Incus из репозитория:
Код: Выделить всё
# Debian/Ubuntu (LXD через snap)
sudo snap install lxd
sudo lxd init --auto
# Debian 13 / Fedora / RHEL 10 (Incus, нативный пакет)
sudo apt install incus # Debian
sudo dnf install incus # Fedora/RHEL
sudo incus admin init --auto
sudo usermod -aG incus-admin $USER
Код: Выделить всё
lxc launch images:debian/13 web1
lxc list
lxc exec web1 -- bash
lxc exec web1 -- systemctl status
lxc stop web1
lxc delete web1
# Incus: incus launch images:debian/13 web1 (и так далее)
Профили - это именованные наборы настроек (устройства, ресурсы), которые наследуются инстансами. Профиль default есть всегда. Сделаем профиль с лимитами:
Код: Выделить всё
lxc profile create webtier
lxc profile set webtier limits.cpu 2
lxc profile set webtier limits.memory 2GiB
lxc launch images:ubuntu/24.04 app1 --profile default --profile webtier
lxc config show app1 --expanded
Сеть. По умолчанию мост lxdbr0 (incusbr0) с NAT и встроенным DHCP/DNS. Можно создать свою сеть или прокинуть порт:
Код: Выделить всё
lxc network list
lxc network create labnet ipv4.address=10.20.0.1/24 ipv4.nat=true
lxc launch images:debian/13 db1 --network labnet
# проброс порта 8080 хоста на 80 контейнера
lxc config device add web1 http proxy \
listen=tcp:0.0.0.0:8080 connect=tcp:127.0.0.1:80
Код: Выделить всё
lxc storage create fast zfs source=/dev/sdb
lxc launch images:debian/13 cache1 --storage fast
lxc storage volume create fast shared-data
Код: Выделить всё
lxc snapshot web1 before-upgrade
lxc restore web1 before-upgrade
lxc copy web1 web1-clone
lxc move web1 node2:web1 # живая миграция в кластере
lxc export web1 web1.tar.gz # перенос на другой хост
- Путают два lxc. lxc-create/lxc-start - это голый LXC, а lxc launch - клиент LXD/Incus. На одном хосте они сосуществуют, но это разные миры со своими хранилищами.
- Запускают Docker внутри неконфигурированного контейнера и упираются в права. Нужно lxc config set c1 security.nesting true, а для systemd-in-systemd иногда и доступ к нужным cgroup.
- Берут storage пул dir и удивляются, что снимок занимает столько же места, сколько инстанс, и делается медленно. Снапшоты дешевы только на zfs/btrfs.
- Тащат привычки Docker: монтируют код через bind и ждут эфемерности. Системный контейнер stateful, он переживает перезагрузку и копит мусор в журнале и логах - его надо обслуживать как машину.
- Делают unprivileged-контейнер и не понимают, почему UID 0 внутри это UID 100000 на хосте. Это user namespace и это правильно, но bind-монтирование host-каталогов требует учета сдвига idmap.
- Ставят LXD снапом на не-Ubuntu и ловят странности автообновлений. В 2026 на Debian/Fedora/RHEL ставьте Incus нативным пакетом - меньше боли.
- Установите LXD (snap, Ubuntu) или Incus (пакет, Debian/Fedora) и проведите init с дефолтами.
- Создайте zfs- или btrfs-пул на отдельном файле или диске и сделайте его дефолтным.
- Запустите два инстанса: web (debian/13) и db (ubuntu/24.04), убедитесь через lxc exec, что внутри работает systemctl.
- Создайте профиль с limits.cpu и limits.memory, примените к web, проверьте lxc config show --expanded.
- Сделайте снимок web, измените что-то внутри (например удалите пакет), откатитесь снимком и убедитесь, что изменения исчезли.
- Пробросьте порт 8080 хоста на веб-сервер внутри web через proxy-устройство и проверьте curl с хоста.
- Экспортируйте db в tar.gz и импортируйте обратно под новым именем - это ваша мини-миграция.
- Чем системный контейнер принципиально отличается от прикладного на уровне PID 1 и жизненного цикла?
- Почему в 2026 на Debian и Fedora корректнее ставить Incus, а не LXD, и в чем технические отличия форка?
- Как накладываются несколько профилей на один инстанс и что произойдет при конфликте параметров?
- Почему снимки на пуле dir дороги, а на zfs/btrfs мгновенны?
- Что такое unprivileged-контейнер с точки зрения user namespace и как сдвиг idmap влияет на bind-монтирование?
- Какие команды отвечают за клонирование, живую миграцию в кластере и перенос инстанса между несвязанными хостами?