LaurVas

Запускаем Debian в контейнере systemd-nspawn

   linux debian

Эта статья потеряла актуальность. Сейчас я не стал бы вручную собирать контейнер, а расчехлил бы докер, чтобы получить тот же самый результат. Тем не менее, она может быть полезной. Например, вы узнаете как сделать “докер руками”. Или может быть так, что докер вам по каким-то причинам не подходит, а изоляцию хочется.

Linux-контейнер — это линукс внутри линукса. Примерно как виртуализация (VirtualBox, Qemu), только без эмуляции железа. Для каких задач могут пригодиться контейнеры?

  • Когда хочется установить временно какой-то софт, не захлямляя основную ОС. Сделал контейнер, поработал, сохранил результат, удалил контейнер. Мусора в системе не осталось.

  • Когда нужна более свежая, или наоборот, устаревшая версия той или иной программы/библиотеки.

  • Когда нужного софта нет в репозиториях вашего дистрибутива, зато есть в репозиториях другого. Например мне пару раз нужен был pdftk, который в Archlinux есть в AUR, но не хочет собираться. Я нашёл готовый пакет в Debian и быстро установил его в контейнере.

  • Когда надо потестить свежий выпуск Debian/Ubuntu/whatever.

  • Чтобы проверить работоспособность своего скрипта/приложения в другой среде, погонять тесты/бенчмарки.

  • Для сборки пакетов под другой дистрибутив.

  • Когда вам по работе нужен какой-нибудь oldstable Debian, недостойный быть основной системой на компьютере.

  • Для изоляции приложений. Заводим отдельный контейнер под Skype и не боимся его шпионских действий.

Эта инструкция описывает создание и использование контейнера на примере Debian Jessie. Создание контейнера с другим Linux-дистрибутивом ничем принципиально не отличается.

Нам понадобятся…

Любой дистрибутив под управлением systemd. Всё. В отличие от OpenVZ (другая система контейнерной виртуализации), для работы контейнеров не требуется кастомное ядро. В отличие от LXC… Не знаю, не смотрел LXC.

Запускать 32-битную систему можно как из 32-битной, так и из 64-битной. А вот 64-битную из 32-битной нельзя.

Для создания контейнера с Debian или Ubuntu понадобится утилита debootstrap из одноимённого пакета. Он точно есть в Debian, Ubuntu, Archlinux и Gentoo. Наверняка есть и в других дистрибутивах. В конце концов это всего лишь набор shell-скриптов.

Где размещать контейнеры?

Там, где вам удобно. У меня контейнеры лежат в /home/chroot/, потому что /home вынесен на отдельный большой раздел и этот путь легко набирать на клавиатуре. Стандартным местом считается /var/lib/machines/, поэтому я буду использовать его в этой инструкции.

Шаг 1. Debootstrap

Debootstrap разворачивает в указанном каталоге минимальный набор пакетов, необходимый для самостоятельной работы системы. Это самый важный шаг. Если сейчас допустить ошибку, то всё придётся переделывать сначала. Инструкцию я вынес в отдельный пост.

Рекомендую сделать бэкап сразу после этого шага. Я устанавливаю Debian Jessie, поэтому мои бэкапы содержат jessie в названии. Команды подсмотрены в Ubuntu community help wiki.

# cd /var/lib/machines
# tar cpzf jessie-deboostrapped.tar.gz --one-file-system -C jessie .
или
# tar cpjf jessie-deboostrapped.tar.bz2 --one-file-system -C jessie .
или
# tar cpJf jessie-deboostrapped.tar.xz --one-file-system -C jessie .

Здесь jessie — это каталог с корневой ФС контейнера. У меня наименьший размер получился у .tar.xz архива.

Восстановление из бэкапа:

# cd /var/lib/machines
# rm -rf jessie && mkdir jessie
# tar xpf /path/to/backup.tar.gz -C jessie --numeric-owner
или
# tar xpf /path/to/backup.tar.bz2 -C jessie --numeric-owner
или
# tar xpf /path/to/backup.tar.xz -C jessie --numeric-owner

Шаг 2. Запуск контейнера

Команды для 32-битной и 64-битной системы соответственно:

# cd /var/lib/machines
# linux32 systemd-nspawn -D jessie
# systemd-nspawn -D jessie

Я ставлю 32-битную, т.к. она занимает меньше места и не имеет ощутимых недостатков перед 64-битной.

По умолчанию в качестве имени контейнера будет использоваться имя каталога с системой. Если нужно другое, то его необходимо указать после ключа -M. Имя будет использоваться для идентификации и в качестве хостнэйма.

Если systemd-nspawn ругается на machine ID и не стартует, просто создаём пустой файл:

# touch jessie/etc/machine-id
# linux32 systemd-nspawn -D jessie

Шаг 3. Первичная настройка системы

Все команды, которые здесь отмечены приглашением (chroot)#, надо вводить в контейнере.

Задаём пароль рута:

(chroot)# passwd

Устанавливать часовой пояс не надо, он возьмётся с хостовой системы. Имя хоста тоже задавать не надо.

Чтобы df корректно работал, нужно сделать mtab:

(chroot)# cat >/etc/mtab <<EOF
rootfs / rootfs rw 0 0
EOF

Сеть должна работать без дополнительных манипуляций. Проверяем:

(chroot)# apt update

Устанавливаем локаль:

(chroot)# apt install locales

(chroot)# echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen
(chroot)# locale-gen
(chroot)# update-locale LANG=en_US.UTF-8
  • Вы можете не использовать echo, а раскомментировать необходимые локали в /etc/locale.gen вручную.
  • Вместо en_US.UTF-8 можно прописать ru_RU.UTF-8, тогда всё будет по-русски.
  • Последние 3 команды можно заменить интерактивной dpkg-reconfigure locales.

Проверяем список доступных локалей и текущую локаль:

(chroot)# locale -a
(chroot)# locale

Локаль у рута будет пустой даже если перезайти в контейнер. Дело в том, что переменные окружения устанавливает не шелл, а процесс, который делает логин, а мы входим в контейнер без логина. У меня есть два решения:

  • Вручную проэкспортировать все необходимые переменные окружения через шелл:
(chroot)# echo "export LANG=en_US.UTF-8" >> ~/.profile
(chroot)# source ~/.profile
  • Входить в чрут по ssh. Требует запущенного ssh-сервера в контейнере.

Конфигурируем менеджер пакетов

При необходимости добавляем репозитории, указываем локальное зеркало и т.д.

/etc/apt/sources.list для Debian Squeeze
deb http://archive.debian.org/debian squeeze main contrib non-free
deb http://archive.debian.org/debian-security squeeze/updates main contrib non-free
deb http://archive.debian.org/debian squeeze-lts main contrib non-free
# deb http://archive.debian.org/backports.org squeeze-backports main contrib non-free
/etc/apt/sources.list для Debian Wheezy
deb http://mirror.yandex.ru/debian wheezy main contrib non-free
deb http://mirror.yandex.ru/debian-security wheezy/updates main contrib non-free
deb http://mirror.yandex.ru/debian wheezy-updates main contrib non-free
# deb http://mirror.yandex.ru/debian wheezy-backports main
/etc/apt/sources.list для Debian Jessie
deb http://mirror.yandex.ru/debian jessie main contrib non-free
deb http://mirror.yandex.ru/debian-security jessie/updates main contrib non-free
deb http://mirror.yandex.ru/debian jessie-updates main contrib non-free
# deb http://mirror.yandex.ru/debian jessie-backports main
/etc/apt/sources.list для Ubuntu Precise Pangolin
deb http://mirror.yandex.ru/ubuntu precise main multiverse restricted universe
deb http://mirror.yandex.ru/ubuntu precise-security main multiverse restricted universe
deb http://mirror.yandex.ru/ubuntu precise-updates main multiverse restricted universe
/etc/apt/sources.list для Ubuntu Trusty Tahr
deb http://mirror.yandex.ru/ubuntu trusty main multiverse restricted universe
deb http://mirror.yandex.ru/ubuntu trusty-security main multiverse restricted universe
deb http://mirror.yandex.ru/ubuntu trusty-updates main multiverse restricted universe
/etc/apt/sources.list для Ubuntu Xenial Xerus
deb http://mirror.yandex.ru/ubuntu xenial main multiverse restricted universe
deb http://mirror.yandex.ru/ubuntu xenial-security main multiverse restricted universe
deb http://mirror.yandex.ru/ubuntu xenial-updates main multiverse restricted universe
/etc/apt/sources.list для Kali Sana
deb http://old.kali.org/kali sana main contrib non-free
deb http://old.kali.org/kali-security sana/updates main contrib non-free
/etc/apt/sources.list для Kali Rolling
deb http://http.kali.org/kali kali-rolling main non-free contrib

Спортсмены отключают установку рекомендованных пакетов:

(chroot)# echo 'APT::Get::Install-Recommends "false";' >>/etc/apt/apt.conf
(chroot)# echo 'APT::Get::Install-Suggests "false";' >>/etc/apt/apt.conf

Мне нравится устанавливать и обновлять пакеты без подтверждения Do you want to continue? [Y/n]. Ведь если я приказал установить пакет, значит я в этом уверен и не надо переспрашивать. К тому же так быстрее получается. Отключаем подтверждение:

(chroot)# echo 'APT::Get::Assume-Yes "true";' >>/etc/apt/apt.conf

В простейшей конфигурации контейнера гостевая и хостовая система используют один и тот же сетевой стэк. Это значит, что если 22 порт уже занят ssh-сервером хостовой системы, то ssh-сервер гостевой системы не запустится. Необходимо в его настройках изменить номер порта. Это же справедливо для веб-серверов и прочих сервисов, работающих на стандартных портах.

Чтобы сервисы не стартовали сразу после установки пакетов:

(chroot)# cat > /usr/sbin/policy-rc.d <<EOF
#!/bin/sh
exit 101
EOF
(chroot)# chmod a+x /usr/sbin/policy-rc.d

Самое время сделать второй бэкап:

# tar cpJf jessie-configured.tar.xz --exclude='./var/cache/apt/archives/*.deb' \
--exclude='./var/lib/apt/lists/*' --exclude='./var/cache/apt/*.bin' \
--one-file-system -C jessie .

Чтобы архив занимал меньше места, мы исключаем:
/var/cache/apt/archives/*.deb — кэш deb-пакетов apt’а,
/var/lib/apt/lists/* — сохранённые индексы репозиториев (что лежит в таком-то репозитории),
/var/cache/apt/*.bin — какая-то база данных apt’а.

Шаг 4. Доведение до ума

Дополнительные пакеты, которые могут понадобиться:
build-essential для сборки deb-пакетов
cron — планировщик задач
file — утилита для определения типа файла
less — утилита просмотра текста с возможностью скроллить вверх и вниз
logrotate
nano — простенький консольный текстовый редактор
ncurses-term для поддержки терминала rxvt-unicode-256color и других
openssh-server
rsync — мощная утилита синхронизации файлов и каталогов, в т.ч. по сети
sudo
vim или vim-tiny
zsh

Напоминаю, что посмотреть описание пакета можно командой

(chroot)# apt show ПАКЕТ
или
# linux32 systemd-nspawn -D jessie apt show ПАКЕТ

По умолчанию при старте контейнера запускается bash --login от рута, независимо от установленного шелла. Как получить нужный шелл? Можно указать команду для запуска в качестве аргумента:

# linux32 systemd-nspawn -D jessie /usr/bin/zsh

Но писать такую длинную команду вам быстро надоест, поэтому применим хак:

(chroot)# echo "exec zsh --login" >> ~/.profile

Создаём пользователя vas, входящего в группу sudo. Вы можете создать другого :)

(chroot)# useradd -m -g users -G sudo -s /usr/bin/zsh vas
(chroot)# passwd vas

Переключение с рута на пользователя делается командой:

(chroot)# su - vas

Иногда бывает удобно сделать sudo без ввода пароля. На хостовой системе я себе таких шалостей не позволяю, а в контейнере можно. Не редактируйте /etc/sudoers напрямую! Используйте команду visudo.

Разрешаем sudo без пароля для всех пользователей, входящих в группу sudo:

/etc/sudoers
%sudo    ALL=(ALL:ALL) NOPASSWD:ALL

В качестве вишенки на торте возьмём zshrc, vimrc и т.п. с хостовой системы. Теперь в контейнере есть всё необходимое для комфортной работы.

Напоследок можно немного облегчить систему за счёт man-страниц, документации, локалей. Это для особо целеустремлённых спортсменов.

(chroot)# rm -rf /usr/share/doc/*
(chroot)# find /usr/share/locale -maxdepth 1 -mindepth 1 ! -name en_US -exec rm -rf {} \;
(chroot)# find /usr/share/i18n/locales -maxdepth 1 -mindepth 1 ! -name en_US -exec rm -rf {} \;
(chroot)# rm -rf /usr/share/man/*
(chroot)# rm -rf /usr/share/groff/*
(chroot)# rm -rf /usr/share/info/*
(chroot)# rm -rf /usr/share/lintian/*
(chroot)# rm -rf /usr/include/*

И последний бэкап на сегодня:

# tar cpJf jessie-vas.tar.xz --exclude='./var/cache/apt/archives/*.deb' \
--exclude='./var/lib/apt/lists/*' --exclude='./var/cache/apt/*.bin' \
--one-file-system -C /var/lib/machines/jessie .

Всё, контейнер jessie готов к бою! А благодаря бэкапам, в любой момент можно откатиться к чистому состоянию или сделать ещё несколько однотипных контейнеров.