Створити ізольоване тестове середовище з systemd-nspawn: кроки, мережа та безпечний відкат

Створити ізольоване тестове середовище з systemd-nspawn: кроки, мережа та безпечний відкат
Категорії: Системне адміністрування

Привіт, це Kernelka! Якщо ви хочете швидко підняти безпечний пісочний майданчик для тестів без важких віртуалок — systemd-nspawn саме те. Це легка контейнеризація від systemd, де можна запускати повноцінну систему зі своїм PID 1, контролювати мережу, порти і дуже легко робити відкат змін 🛡️

Як це працює і навіщо використовувати systemd-nspawn

systemd-nspawn — це інструмент, що запускає rootfs як контейнер із ізоляцією просторів імен. На відміну від важких VM, тут мінімальні накладні витрати, а від Docker — простіший стек і відсутність демона. Ідеально для: швидких тестів пакетів, відпрацювання скриптів, ізоляції розробки та навчання. А ще він дружить із systemd-networkd, що спрощує мережеві налаштування.

Основний How-to: створення, запуск, мережа та відкат

Підготовка

На хості потрібні пакети (приклад для Debian/Ubuntu):

sudo apt update
sudo apt install -y debootstrap systemd-container systemd-networkd

Рекомендовано зберігати контейнери в /var/lib/machines — systemd це любить за замовчуванням.

Крок 1. Створюємо базовий rootfs

Приклад з Debian Stable (замініть stable на bookworm/тріб'ют дистрибутиву):

sudo debootstrap stable /var/lib/machines/debtest http://deb.debian.org/debian

Додайте резолвер (на перший час можна взяти з хоста):

sudo cp /etc/resolv.conf /var/lib/machines/debtest/etc/resolv.conf

Крок 2. Перше завантаження контейнера

Запустимо з системою PID 1 усередині і veth-інтерфейсом. Створиться пара ve-debtest (хост) ↔ vb-debtest (контейнер):

sudo systemd-nspawn -D /var/lib/machines/debtest \
  --machine=debtest --boot \
  --private-network --network-veth

Увійти всередину можна окремо:

machinectl shell debtest

Крок 3. Налаштовуємо мережу з NAT і DHCP

Найпростіше — дати контейнеру приватну підмережу, а на хості вмикнути NAT і DHCP через systemd-networkd:

# Файл на хості: /etc/systemd/network/80-ve-debtest.network
sudo tee /etc/systemd/network/80-ve-debtest.network >/dev/null <<'EOF'
[Match]
Name=ve-debtest

[Network]
Address=10.0.0.1/24
IPForward=ipv4
IPMasquerade=yes
DHCPServer=yes

[DHCPServer]
DNS=1.1.1.1
EOF

sudo systemctl enable --now systemd-networkd
sudo sysctl -w net.ipv4.ip_forward=1

У контейнері увімкніть systemd-networkd (щоб він взяв DHCP):

machinectl shell debtest -- loginctl enable-linger root || true
machinectl shell debtest -- systemctl enable --now systemd-networkd

Перевірте адресу в контейнері (має бути 10.0.0.x):

machinectl shell debtest -- ip a

Крок 4. Форвардимо порти з хоста в контейнер

Можна одразу в nspawn або через firewall Linux (firewalld). Варіант через nspawn-конфіг:

# Файл: /etc/systemd/nspawn/debtest.nspawn
sudo tee /etc/systemd/nspawn/debtest.nspawn >/dev/null <<'EOF'
[nspawn]
PrivateUsers=pick
PrivateNetwork=yes
NetworkVeth=yes
# Проброс порту 8080 (хост) -> 80 (контейнер)
Port=tcp:8080:80
EOF

sudo systemctl restart systemd-nspawn@debtest

Або через firewalld (якщо він використовується на хості):

# Приклад: форвардимо 8080 до 10.0.0.2:80
sudo firewall-cmd --permanent --add-masquerade
sudo firewall-cmd --permanent --add-forward-port=port=8080:proto=tcp:toaddr=10.0.0.2:toport=80
sudo firewall-cmd --reload

Крок 5. Автозапуск контейнера як сервісу systemd

sudo systemctl enable --now systemd-nspawn@debtest.service

Крок 6. Безпечний відкат: снапшоти та архіви

Найзручніше, якщо /var/lib/machines на Btrfs: робимо снапшот перед ризикованим тестом і повертаємось за секунди.

# Створити read-only снапшот (Btrfs)
sudo btrfs subvolume snapshot -r \
  /var/lib/machines/debtest \
  /var/lib/machines/.snapshots/debtest-$(date +%F)

# Відкат до снапшоту
sudo machinectl terminate debtest || true
sudo btrfs subvolume delete /var/lib/machines/debtest
sudo btrfs subvolume snapshot \
  /var/lib/machines/.snapshots/debtest-YYYY-MM-DD \
  /var/lib/machines/debtest

Якщо у вас Ext4/XFS — робіть архів перед тестом. Це теж надійне резервне копіювання системи всередині контейнера:

# Створити архів
sudo tar -C /var/lib/machines -cpf /var/backups/debtest-$(date +%F).tar debtest

# Відновити з архіву
sudo machinectl terminate debtest || true
sudo rm -rf /var/lib/machines/debtest
sudo tar -C /var/lib/machines -xpf /var/backups/debtest-YYYY-MM-DD.tar

Для одноразових тестів існує режим, що сам відкотить усе після зупинки:

sudo systemd-nspawn -M debtest --ephemeral --boot

Альтернативні способи створення rootfs

  • Arch Linux: pacstrap /var/lib/machines/archtest base
  • Fedora/RHEL: dnf --installroot=/var/lib/machines/fedtest --releasever=XX install @core
  • machinectl pull-raw: завантажити готові образи (qcow2/raw) і запустити їх через nspawn
  • mkosi: автоматизує збірку образів під nspawn для відтворюваних тестів

Ці підходи добре вписуються у сценарії «контейнеризація без Docker».

GUI-спосіб (через NetworkManager)

Окремого GUI для systemd-nspawn майже немає, зате мережу можна налаштувати мишею через NetworkManager:

  1. Відкрийте редактор підключень (nm-connection-editor).
  2. Створіть міст (Bridge) br0. У властивостях IPv4 оберіть «Shared to other computers».
  3. Збережіть і активуйте br0.
  4. Запустіть контейнер, підвісивши його до моста:
sudo systemd-nspawn -D /var/lib/machines/debtest \
  --machine=debtest --boot \
  --network-bridge=br0

NetworkManager сам підніме DHCP/NAT для bridge, і контейнер отримає адресу без зайвих ручних дій 🌱

FAQ

Чим nspawn відрізняється від Docker?

nspawn — частина systemd, працює без окремого демона і ближчий до «легких контейнерів ОС». Він чудовий для системних тестів, де потрібен systemd усередині. Docker краще для розгортання застосунків за моделлю immutable-образів.

Контейнер не виходить в інтернет

  • Перевірте, що ви ввімкнули IPForward і IPMasquerade на хості.
  • Переконайтесь, що в контейнері працює DHCP-клієнт (systemd-networkd) і є валідний /etc/resolv.conf.
  • Перевірте правила вашого firewall Linux — вони не мають блокувати NAT/форвардинг.

Як пробросити кілька портів?

Додайте кілька рядків Port= у .nspawn, наприклад Port=udp:5353:5353 і перезапустіть сервіс systemd-nspawn@name.

Як копіювати файли в контейнер і назад?

# У контейнер
sudo machinectl copy-to debtest ./app /root/app
# З контейнера
sudo machinectl copy-from debtest /var/log/syslog ./syslog

Чи безпечно запускати nspawn як root?

Так, за умови грамотних обмежень: використовуйте PrivateUsers=pick, DropCapability=ALL і додавайте лише потрібні capability. Для чутливих даних монтуйте їх як BindReadOnly.

Apt/dnf не працює з ReadOnly образом

Якщо виставили ReadOnly у .nspawn — тимчасово приберіть або використайте --ephemeral, де writable overlay дозволить тест встановлення, а після зупинки все відкотиться.

Порада від Kernelka

Щоб отримати контроль над ресурсами, запускайте контейнер у cgroup зі лімітами:

sudo systemd-run --unit=debtest.scope -p MemoryMax=1G -p CPUQuota=150% \
  systemd-nspawn -D /var/lib/machines/debtest --machine=debtest --boot --private-network --network-veth

Так ви не «з'їсте» весь RAM під час важких тестів.

Підсумок

  • Створили ізольоване середовище через systemd-nspawn і запустили його з systemd усередині.
  • Налаштували мережу veth + NAT і порт-форвардинг; за бажання — через GUI NetworkManager.
  • Забезпечили безпечний відкат: Btrfs-снапшоти або архіви як резервне копіювання системи.
  • Зміцнили безпеку: user namespaces, обрізані capabilities, контроль firewall Linux.
  • Отримали швидкий і легкий майданчик для тестів без зайвих ризиків.

Прокоментувати

На сайті відображається лише твоє ім'я та коментар. Електронна пошта використовується виключно для зв'язку з тобою за потреби.