Время от времени люди испытывают желание делать что-нибудь странное. Зачем — на самом деле не важно, какая-нибудь аргументация что надо сделать именно так — будет. Но главным ведь остаётся желание сделать что-нибудь странное. Ну а раз решил делать что-то странное, то почему бы этот процесс не описать.
Итак, как запустить LXC-контейнер с Debian не где-нибудь там, а на железке под управлением LEDE (так же известный как форк OpenWRT)?
(предполагается, что читатель сам придумает, зачем ему LXC на роутере и уже знаком с этой технологией контейнеров)
Попытка оптимистичная
Для желаемых махинаций берём железку (или лучше пока x86 виртуалку для теста) с установленным LEDE и смотрим:
root@lede:~# opkg find lxc*
lxc - 1.1.5-3 - LXC is the userspace control package for Linux Containers, a lightweight
virtual system mechanism sometimes described as "chroot on steroids".
lxc-attach - 1.1.5-3 - Utility lxc-attach from the LXC userspace tools
lxc-auto - 1.1.5-3 - LXC is the userspace control package for Linux Containers, a lightweight
virtual system mechanism sometimes described as "chroot on steroids".
This package adds and initscript for starting and stopping the containers
on boot and shutdown.
lxc-autostart - 1.1.5-3 - Utility lxc-autostart from the LXC userspace tools
lxc-cgroup - 1.1.5-3 - Utility lxc-cgroup from the LXC userspace tools
lxc-checkconfig - 1.1.5-3 - Utility lxc-checkconfig from the LXC userspace tools
lxc-clone - 1.1.5-3 - Utility lxc-clone from the LXC userspace tools
lxc-common - 1.1.5-3 - LXC common files
lxc-config - 1.1.5-3 - Utility lxc-config from the LXC userspace tools
lxc-configs - 1.1.5-3 - LXC virtual machine common config files
lxc-console - 1.1.5-3 - Utility lxc-console from the LXC userspace tools
lxc-create - 1.1.5-3 - Utility lxc-create from the LXC userspace tools
lxc-destroy - 1.1.5-3 - Utility lxc-destroy from the LXC userspace tools
lxc-device - 1.1.5-3 - Utility lxc-device from the LXC userspace tools
lxc-execute - 1.1.5-3 - Utility lxc-execute from the LXC userspace tools
lxc-freeze - 1.1.5-3 - Utility lxc-freeze from the LXC userspace tools
lxc-hooks - 1.1.5-3 - LXC virtual machine hooks
lxc-info - 1.1.5-3 - Utility lxc-info from the LXC userspace tools
lxc-init - 1.1.5-3 - LXC Lua bindings
lxc-ls - 1.1.5-3 - Utility lxc-ls from the LXC userspace tools
lxc-lua - 1.1.5-3 - LXC Lua bindings
lxc-monitor - 1.1.5-3 - Utility lxc-monitor from the LXC userspace tools
lxc-monitord - 1.1.5-3 - Utility lxc-monitord from the LXC userspace tools
lxc-snapshot - 1.1.5-3 - Utility lxc-snapshot from the LXC userspace tools
lxc-start - 1.1.5-3 - Utility lxc-start from the LXC userspace tools
lxc-stop - 1.1.5-3 - Utility lxc-stop from the LXC userspace tools
lxc-templates - 1.1.5-3 - LXC virtual machine templates
lxc-unfreeze - 1.1.5-3 - Utility lxc-unfreeze from the LXC userspace tools
lxc-unshare - 1.1.5-3 - Utility lxc-unshare from the LXC userspace tools
lxc-user-nic - 1.1.5-3 - Utility lxc-user-nic from the LXC userspace tools
lxc-usernsexec - 1.1.5-3 - Utility lxc-usernsexec from the LXC userspace tools
lxc-wait - 1.1.5-3 - Utility lxc-wait from the LXC userspace tools
Хм. Это кажется проще чем ожидалось. Правда всё разбито на отдельные утилиты, ну да ладно. Ставим для начала lxc-checkconfig
и смотрим, что тот думает о нашем ядре
root@lede:~# lxc-checkconfig
Kernel configuration not found at /proc/config.gz; searching...
lxc-checkconfig: unable to retrieve kernel configuration
Try recompiling with IKCONFIG_PROC, installing the kernel headers,
or specifying the kernel configuration path with:
CONFIG=<path> lxc-checkconfig
Упс. Ничего не думает. Некоторое время спустя выясняется, что дефолтное ядро LEDE собирается без поддержки LXC, а пакеты в репозитории только для ручных манипуляций. Но пакеты же есть, и ядро довольно свежее — значит можно пересобрать ядро и пользоваться. Так и сделаем.
Разбираемся со сборкой LEDE
Пойдём собирать по краткому руководству сборки LEDE. Ставим необходимые пакеты под свою ОС. Рекомендую для начала собрать с конфигом по-умолчанию для проверки что ошибки вызваны не включением поддержки LXC и копать причину надо отдельно от этой заметки.
git clone https://git.lede-project.org/source.git build
cd build
git checkout v17.01.4
./scripts/feeds update -a
./scripts/feeds install -a
Можно собрать LEDE с использованием настроек официального образа под желаемое железо. Для этого идём по адресу https://downloads.lede-project.org/releases/ выбираем актуальный релиз (на момент написания это 17.01.4), затем в targets/
свою архитектуру и находим рядом с образом LEDE файл config.seed
. Например, для x86 получается адрес https://downloads.lede-project.org/releases/17.01.4/targets/x86/generic/config.seed
Скачиваем этот файл в директорию сборки под именем .config
и вызовом make defconfig
разворачиваем его в полноценный файл сборки.
wget -O .config https://downloads.lede-project.org/releases/17.01.4/targets/x86/generic/config.seed
make defconfig
time make
У меня часа полтора всё собиралось, готовые образы искать в bin/targets/
.
Вообще make
можно вызвать в несколько потоков, но я впечатлился на предупреждение в документации по сборке But this might expose bugs!
и решил никуда не торопиться.
И обратите внимание, чтобы на диске места было достаточно. Занять 20гб вполне просто.
Добавляем LXC
Немного освоились со сборкой, теперь пора добавлять опции ядра для поддержки LXC. Большое спасибо чуть ли не единственной статье по сборке OpenWRT с LXC за отправную точку. Но статья старая, и опции под нынешний LXC уже не все. Добавляем в наш .config
пачку опций:
CONFIG_KERNEL_AIO=y
CONFIG_KERNEL_BLK_CGROUP=y
CONFIG_KERNEL_BLK_DEV_BSG=y
CONFIG_KERNEL_CGROUPS=y
CONFIG_KERNEL_CGROUP_CPUACCT=y
CONFIG_KERNEL_CGROUP_DEVICE=y
CONFIG_KERNEL_CGROUP_FREEZER=y
CONFIG_KERNEL_CGROUP_PIDS=y
CONFIG_KERNEL_CGROUP_SCHED=y
CONFIG_KERNEL_CPUSETS=y
# CONFIG_KERNEL_DEBUG_FS is not set
# CONFIG_KERNEL_DEBUG_INFO is not set
# CONFIG_KERNEL_DEBUG_KERNEL is not set
CONFIG_KERNEL_DEVPTS_MULTIPLE_INSTANCES=y
CONFIG_KERNEL_DEVTMPFS=y
CONFIG_KERNEL_DEVTMPFS_MOUNT=y
CONFIG_KERNEL_DIRECT_IO=y
CONFIG_KERNEL_FANOTIFY=y
CONFIG_KERNEL_FHANDLE=y
CONFIG_KERNEL_FREEZER=y
CONFIG_KERNEL_IPC_NS=y
# CONFIG_KERNEL_KALLSYMS is not set
CONFIG_KERNEL_LXC_MISC=y
CONFIG_KERNEL_MEMCG=y
CONFIG_KERNEL_MEMCG_SWAP=y
CONFIG_KERNEL_MM_OWNER=y
CONFIG_KERNEL_NAMESPACES=y
CONFIG_KERNEL_NETPRIO_CGROUP=y
CONFIG_KERNEL_NET_CLS_CGROUP=y
CONFIG_KERNEL_NET_NS=y
CONFIG_KERNEL_PID_NS=y
CONFIG_KERNEL_POSIX_MQUEUE=y
CONFIG_KERNEL_PROC_PID_CPUSET=y
CONFIG_KERNEL_RESOURCE_COUNTERS=y
CONFIG_KERNEL_USER_NS=y
CONFIG_KERNEL_UTS_NS=y
CONFIG_PACKAGE_ip-tiny=y
CONFIG_PACKAGE_ipset=y
CONFIG_PACKAGE_iptables-mod-conntrack-extra=y
CONFIG_PACKAGE_iptables-mod-ipopt=y
CONFIG_PACKAGE_kmod-8021q=y
CONFIG_PACKAGE_kmod-fuse=y
CONFIG_PACKAGE_kmod-ip6tables-extra=y
CONFIG_PACKAGE_kmod-ipt-conntrack-extra=y
CONFIG_PACKAGE_kmod-ipt-ipopt=y
CONFIG_PACKAGE_kmod-ipt-ipset=y
CONFIG_PACKAGE_kmod-ipt-nat-extra=m
CONFIG_PACKAGE_kmod-ipt-nat6=m
CONFIG_PACKAGE_kmod-macvlan=y
CONFIG_PACKAGE_kmod-nf-nat6=m
CONFIG_PACKAGE_kmod-nfnetlink=y
CONFIG_PACKAGE_kmod-veth=y
CONFIG_PACKAGE_terminfo=y
В принципе, это всё можно найти в обычном make menuconfig
. Сейчас его можно вызывать, эти параметры утеряны не будут и можно настроить сборку под себя.
Но, к сожалению, это ещё не все опции. Некоторых нет в make menuconfig
и требуется изменить конфиг самого ядра. По идее правильный путь заключается в
make kernel_menuconfig
И проставлением там всех соответствующих отметок. Но как-то муторно их искать, поэтому путь неправильный: на обновлённый .config
вновь выполняем
make defconfig # если вы что-то поменяли в menuconfig то не нужно, но и не навредит. Наверное
make prepare
Находим где-то примерно в build_dir/target-i386_pentium4_musl-1.1.16/linux-x86_generic/linux-4.4.92/.config
сгенерированный файл конфигурации ядра (путь, как видно, различается от кучи параметров). И добавляем в конец ещё пачку настроек
CONFIG_CHECKPOINT_RESTORE=y
CONFIG_CGROUP_FREEZER=y
CONFIG_FREEZER=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m
CONFIG_NETLINK_DIAG=y
CONFIG_PACKET_DIAG=y
CONFIG_INET_DIAG=y
CONFIG_UNIX_DIAG=y
CONFIG_FHANDLE=y
CONFIG_INET_UDP_DIAG=m
# CONFIG_PROC_STRIPPED is not set
CONFIG_CFQ_GROUP_IOSCHED=y
CONFIG_IKCONFIG_PROC
и CONFIG_IKCONFIG
как раз та штука, из-за которой lxc-checkconfig
ничего не мог сказать. А CONFIG_PROC_STRIPPED
— это патчи OpenWRT, убирающие некоторые файлы из псевдосистемы /proc
. Настройка должна уменьшать расходуемый объём памяти, но получались варнинги при lxc-start
. Впрочем LXC при недостатке памяти и не нужен.
После такого вмешательства (а его необходимо повторять после make prepare
каждый раз) делаем
time make
Теперь куча всего уже скомпилирована и перекомпилироваться не будет, поэтому сильно быстрее работает. Минут за 15 у меня обновление собралось.
Загружаем полученный образ на железку или виртуалку и пробуем выполнить lxc-checkconfig
. Должны увидеть опрятную картину вроде вот такой:
root@lede:~# lxc-checkconfig
--- Namespaces ---
Namespaces: enabled
Utsname namespace: enabled
Ipc namespace: enabled
Pid namespace: enabled
User namespace: enabled
Network namespace: enabled
Multiple /dev/pts instances: enabled
--- Control groups ---
Cgroup: enabled
Cgroup clone_children flag: enabled
Cgroup device: enabled
Cgroup sched: enabled
Cgroup cpu account: enabled
Cgroup memory controller: enabled
--- Misc ---
Veth pair device: enabled
Macvlan: enabled
Vlan: enabled
Bridges: enabled
Advanced netfilter: enabled
CONFIG_NF_NAT_IPV4: enabled
CONFIG_NF_NAT_IPV6: enabled
CONFIG_IP_NF_TARGET_MASQUERADE: enabled
CONFIG_IP6_NF_TARGET_MASQUERADE: enabled
CONFIG_NETFILTER_XT_TARGET_CHECKSUM: enabled
--- Checkpoint/Restore ---
checkpoint restore: enabled
CONFIG_FHANDLE: enabled
CONFIG_EVENTFD: enabled
CONFIG_EPOLL: enabled
CONFIG_UNIX_DIAG: enabled
CONFIG_INET_DIAG: enabled
CONFIG_PACKET_DIAG: enabled
CONFIG_NETLINK_DIAG: enabled
File capabilities: enabled
Note : Before booting a new kernel, you can check its configuration
usage : CONFIG=/path/to/config /usr/bin/lxc-checkconfig
Отлично, вот ядро и готово.
Подготовка контейнера
Теперь ставим небольшую пачку пакетов (на самом деле их можно закатать сразу в образ, который собирается в make
, но я с этим не разбирался)
opkg install lxc-start lxc-attach lxc-stop lxc-ls lxc-monitor lxc-monitord lxc-checkconfig
Я предпочитаю не использовать шаблоны из lxc-templates
— свои скрипты есть ещё со времён openvz, а конфиг LXC довольно типовой. Но сборку debian в LEDE для статьи проверил, нужны ещё пакеты:
opkg install lxc-create lxc-templates debootstrap bash getopt rsync
Затем сюрприз, из коробки всё равно не работает. /var/cache
существует в LEDE как nodev и debootstrap туда нельзя. Поэтому делаем вместо /var/cache
куда-нибудь симлинк.
mkdir /mnt/cache
ln -s /mnt/cache /var/cache
lxc-create -n testlxc -t debian -- -r jessie
Итак, rootfs контейнера готов. Попробуем запустить:
lxc-start -n testlxc -d
С задачей подключиться к контейнеру случилась беда. lxc-console
жалуется на bad file descriptor
. Но сам контейнер запущен и проявляет активность. Ну, хотели странного, странное получили. Делаем:
lxc-attach -n testlxc -- bash
И вот мы с консольным доступом внутри контейнера. Что не так с lxc-console
разобраться не получилось, но некритично, оставил так.
Размещать директорию с контейнерами можно по большому счёту где угодно, лишь бы в /etc/lxc/lxc.conf
был указан путь к этой директории.
Автозапуск контейнеров
Осталась последняя деталь: настроить автостарт контейнера (-ов) вместе с системой. Устанавливаем пакет lxc-auto
opkg install lxc-auto
И получаем init-скрипт и эталонный файл конфигурации /etc/config/lxc-auto
. Приводим его к желаемому виду из необходимого числа перечислений имён контейнеров:
config container
option name testlxc
И отправляем систему в перезагрузку для проверки. init скрипт к сожалению оказывается довольно глупым: он вызывает lxc-stop &
для всех контейнеров из конфига и уходит в sleep по-умолчанию на 300 секунд. Так что даже если контейнеры выключились быстро — скрипт ещё поспит.
PS: сначала я пробовал запустить LEDE в LXC контейнере на Debian. Спустя какое-то время разбирательств с gdb это даже удалось, но на задаче нормально запустить firewall
из штатных конфигов заглох и пошёл от обратного.
Автор: Melkij