March 13, 2020

Работа с systemd-nspawn

Значит в один день мне захотелось поднять Ubuntu для деб-сервера в каком нибудь роде контейнере. Тогда я решил, что использовать chroot - не то. Мне нужно было обойтись без виртуализации, и в то же время получить возможность работы с systemd и её systemctl. Тогда я начал гуглить, и первой ссылкой в гугле по запросу "systemd in chroot" оказалась статья на ArchWiki про systemd-nspawn. Там прямым языком говорилось, что systemd-nspawn - chroot на стеройдах. Я прочитал несколько строчек и охренел насколько же легко им пользоваться. Но обо всём по порядку. Для начала, давайте вообще попробуем создать контейнер для будущего использования. Если у вас уже есть система, установленная как chroot-контейнер, можете пропустить инструкцию по созданию. И так, давайте сделаем контейнер с убунту, используя Arch Linux(можете команды с пакетным менеджером заменить на подобные-свои.)

Ставим необходимые пакеты

После, войдем под рутом и сделаем папку с нашим будущим контейнером

После создания папки, установим в неё систему.

debootstrap --arch=amd64 bionic /var/lib/machines/ubuntu/ http://archive.ubuntu.com/ubuntu/

Процесс может быть длинным. У меня это заняло 10 минут.

После установки системы, можем сразу одной командой запускать наш контейнер. Для более легкого процесса, я подготовил свой собственный скрипт - nctl

который вы можете найти в самом низу статьи. Скопируйте его и поместите в /usr/bin/nctl

systemd-nspawn -D ubuntu

Далее установим текстовый редактор, и починим логин от рута

Добавим в конец файла pts/0 до pts/9, а затем сохраним, используя Ctrl+X Y Enter

После этого зададим пароль рута

passwd root

выйдем из машины через Ctrl+]]] (быстро нажать), и на этот раз сделаем в неё загрузку systemd как PID 1

nctl -b ubuntu или systemd-nspawn -b -D ubuntu

Введём наш логин, root. и пароль

Далее создадим нового пользователя, зададим ему пароль и добавим в группу.

groupadd myusers

useradd -m -g myusers name

passwd name

Установим sudo

apt install sudo

EDITOR=nano visudo

Добавим строчку

name ALL=(ALL:ALL) ALL

Перезагрузим систему и войдем от нового пользователя.

poweroff

nctl -b ubuntu

Логинимся от нашего созданного пользователя.

Теперь, отредачим репозитории, что бы были не только версии программ на момент выхода Ubuntu 18.04, но и появились обновления.

sudo nano /etc/apt/sources.list

Пишем всё вот так, далее сохраняем.

Теперь давайте выключим стандартный systemd-resolved для сети. Так как он будет мешать нам получать DNS адреса, соответственно интернет работать не будет.

sudo systemctl disable --now systemd-resolved

sudo rm /etc/resolv.conf

sudo touch /etc/resolv.conf && sudo echo 8.8.8.8 >> /etc/resolv.conf && sudo chattr +i /etc/resolv.conf

Далее обновляем базу репозиториев и обновим программы.

sudo apt update && sudo apt upgrade

Ну вот и всё! У вас теперь есть система в контейнере. Если хочется запускать систему в фоне, то тут уже понадобиться использовать machinectl. Но стоп, для начала надо написать для него .nspawn файл, иначе не будет работать сеть и похерятся права на файлы. У вас будет не что то вроде anatoliy users, а 19274610 19274610. И так, выйдем из машины используя Ctrl+]]], или откроя терминал и написав там nctl -k ubuntu

cd /etc/systemd && mkdir nspawn && nano nspawn/ubuntu.nspawn

Файл должен быть вот такого содержания:

Теперь, запустим наш контейнер.

machinectl start ubuntu

(через nctl -k можно также выключать и контейнеры, запущенные через machinectl)

Что бы зайти в контейнер под каким либо пользователем, введите

machinectl shell name@ubuntu

Готово! Теперь у вас есть контейнер system-nspawn. Можно использовать какой либо дистрибутив в контейнере, имея при этом доступ к функциям systemd. Спасибо за прочтение.

Скрипт

#!/bin/bash if [ -z "$1" ]; then echo "-b / --boot -s / --start -k / --kill -l / --list" exit else true fi case "$1" in -b|--boot) if [ -z "$2" ]; then echo "Type machine you need to boot" exit else true fi if [ -z "$($0 back-list- | grep -x $2)" ]; then true else echo "Machine is already running" exit fi if [ "$(systemctl is-active systemd-nspawn@$2.service)" == active ]; then echo -e "Machine is already running using \e[4mmachinectl\e[0m systemd-nspawn service" exit else true fi su -c "systemd-nspawn -b -D /var/lib/machines/$2" ;; -s|--start) if [ -z "$2" ]; then echo "Type machine you need to start" exit else true fi if [ -z "$($0 back-list- | grep -x $2)" ]; then true else echo "Machine is already running" exit fi if [ "$(systemctl is-active systemd-nspawn@$2.service)" == active ]; then echo -e "Machine is already running using \e[4mmachinectl\e[0m systemd-nspawn service" exit else true fi su -c "systemd-nspawn -D /var/lib/machines/$2" ;; -k|--kill) if [ -z "$(systemctl | grep "machine-\|systemd-nspawn@")" ]; then echo "Nothing started" exit else true fi if [ -z "$2" ]; then echo "Type machine you need to kill" exit else true fi if [ "$(systemctl is-active systemd-nspawn@$2.service)" == active ]; then su -c "systemctl stop systemd-nspawn@$2.service" exit else true fi if [ -z "$($0 back-list- | grep -x $2)" ]; then echo "Machine with this name isn't running" exit else true fi su -c "systemctl stop machine-$2.scope" ;; back-list-) if [ -z "$(systemctl | grep "machine-")" ]; then echo "No machines are running" exit else true fi systemctl | grep "machine-" | cut -c1-50 | sed "s/.*-//" | cut -d '.' -f -1 ;; -l|--list) if [ -z "$(systemctl | grep "machine-\|systemd-nspawn@")" ]; then echo "No machines are running" exit else true fi systemctl | grep "machine-" | cut -c1-50 | sed "s/.*-//" | cut -d '.' -f -1 if [ -z "$(systemctl | grep "systemd-nspawn@")" ]; then true; else echo -e "\e[95m machinectl:\e[m\e[1;92m" fi systemctl | grep "systemd-nspawn@" | cut -c1-50 | sed "s/.*@//" | cut -d '.' -f -1 ;; *) echo "-b / --boot -s / --start -k / --kill -l / --list" ;; esac