У меня есть несколько контейнеров libvirt-lxc, конфигурацию которых я перенес с Debian jessie на новый хост Debian buster. Я воссоздал rootfs для контейнеров, используя lxc-create -t debian -- --release buster
и позже переназначил номера uid / gid rootfs с помощью скрипта, который, как я знаю, работает правильно.
Конфигурация контейнера выглядит так:
<domain type='lxc'>
<name>some-container</name>
<uuid>1dbc80cf-e287-43cb-97ad-d4bdb662ca43</uuid>
<title>Some Container</title>
<memory unit='KiB'>2097152</memory>
<currentMemory unit='KiB'>2097152</currentMemory>
<memtune>
<swap_hard_limit unit='KiB'>2306867</swap_hard_limit>
</memtune>
<vcpu placement='static'>1</vcpu>
<resource>
<partition>/machine</partition>
</resource>
<os>
<type arch='x86_64'>exe</type>
<init>/sbin/init</init>
</os>
<idmap>
<uid start='0' target='200000' count='65535'/>
<gid start='0' target='200000' count='65535'/>
</idmap>
<clock offset='utc'/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash>
<devices>
<emulator>/usr/lib/libvirt/libvirt_lxc</emulator>
<filesystem type='mount' accessmode='passthrough'>
<source dir='/var/lib/lxc/some-container/rootfs/'/>
<target dir='/'/>
</filesystem>
<filesystem type='mount' accessmode='passthrough'>
<source dir='/var/www/some-container/static/'/>
<target dir='/var/www/some-container/static/'/>
</filesystem>
<interface type='bridge'>
<mac address='52:54:00:a1:98:03'/>
<source bridge='guests0'/>
<ip address='192.0.2.3' family='ipv4' prefix='24'/>
<ip address='2001:db8::3' family='ipv6' prefix='112'/>
<route family='ipv4' address='0.0.0.0' prefix='0' gateway='192.0.2.1'/>
<route family='ipv6' address='2000::' prefix='3' gateway='fe80::1'/>
<target dev='vcontainer0'/>
<guest dev='eth0'/>
</interface>
<console type='pty' tty='/dev/pts/21'>
<source path='/dev/pts/21'/>
<target type='lxc' port='0'/>
<alias name='console0'/>
</console>
<hostdev mode='capabilities' type='misc'>
<source>
<char>/dev/net/tun</char>
</source>
</hostdev>
</devices>
</domain>
(IP-адреса были изменены для использования документации / примеров префиксов IPv4 / IPv6.) Точки монтирования существуют и подготовлены. У меня есть около 15 таких контейнеров. Происходит следующее:
Когда хост только что загружен, я могу:
Когда я бегу virsh -c lxc:/// start some-container
после того, как любой другой контейнер уже запущен, libvirt утверждает, что запустил контейнер:
# virsh -c lxc:/// start some-container
Domain some-container started
Он также отображается как работающий в virsh -c lxc:/// list
вывод, но под корневым UID контейнера нет процесса. Бег systemctl restart libvirtd
заставляет libvirt распознать, что контейнер действительно мертв, и пометить его как shut off
в virsh -c lxc:/// list
очередной раз.
Заглянув в логи libvirt, я не нашел ничего полезного:
2019-05-09 15:38:38.264+0000: starting up
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin LIBVIRT_DEBUG=4 LIBVIRT_LOG_OUTPUTS=4:stderr /usr/lib/libvirt/libvirt_lxc --name some-container --console 25 --security=apparmor --handshake 52 --veth vnet0
PATH=/bin:/sbin TERM=linux container=lxc-libvirt HOME=/ container_uuid=1dbc80cf-e287-43cb-97ad-d4bdb662ca43 LIBVIRT_LXC_UUID=1dbc80cf-e287-43cb-97ad-d4bdb662ca43 LIBVIRT_LXC_NAME=some-container /sbin/init
(NB: пробовал с аппармом и без него)
Я пришел в отчаяние и привязался к strace -ff -o somedir/foo -p
в libvirtd, а затем запустил контейнер. После долгих поисков я обнаружил, что libvirt запускается /sbin/init
внутри контейнера, который затем быстро выходит с кодом состояния 255. Это происходит после EACCESS после выполнения чего-либо с cgroups:
openat(AT_FDCWD, "/sys/fs/cgroup/systemd/system.slice/libvirtd.service/init.scope/cgroup.procs", O_WRONLY|O_NOCTTY|O_CLOEXEC) = -1 EACCES (Permission denied)
writev(3, [{iov_base="\33[0;1;31m", iov_len=9}, {iov_base="Failed to create /system.slice/l"..., iov_len=91}, {iov_base="\33[0m", iov_len=4}, {iov_base="\n", iov_len=1}], 4) = 105
epoll_ctl(4, EPOLL_CTL_DEL, 5, NULL) = 0
close(5) = 0
close(4) = 0
writev(3, [{iov_base="\33[0;1;31m", iov_len=9}, {iov_base="Failed to allocate manager objec"..., iov_len=52}, {iov_base="\33[0m", iov_len=4}, {iov_base="\n", iov_len=1}], 4) = 66
openat(AT_FDCWD, "/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC) = 4
ioctl(4, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(4, TIOCGWINSZ, {ws_row=0, ws_col=0, ws_xpixel=0, ws_ypixel=0}) = 0
writev(4, [{iov_base="[", iov_len=1}, {iov_base="\33[0;1;31m!!!!!!\33[0m", iov_len=19}, {iov_base="] ", iov_len=2}, {iov_base="Failed to allocate manager objec"..., iov_len=34}, {iov_base="\n", iov_len=1}], 5) = 57
close(4) = 0
writev(3, [{iov_base="\33[0;1;31m", iov_len=9}, {iov_base="Exiting PID 1...", iov_len=16}, {iov_base="\33[0m", iov_len=4}, {iov_base="\n", iov_len=1}], 4) = 30
exit_group(255) = ?
+++ exited with 255 +++
Копая дальше, я понял, что libvirt не создает пространство имен Cgroup для контейнеров, и, очевидно, все они пытаются использовать один и тот же путь к cgroup. При этом поведение имеет смысл: если первый запущенный контейнер находится в пространстве имен пользователя, он становится владельцем поддерева cgroup, а другие контейнеры с пространством имен пользователей не могут его использовать. Контейнеры, не относящиеся к пользовательскому пространству имен, могут просто взять на себя управление деревом контрольной группы, потому что они работают как UID 0.
Теперь возникает вопрос: почему контрольные группы настроены неправильно? Это ошибка libvirt? Это неправильная конфигурация моей системы?
Мне пришла в голову идея попробовать использовать отдельные <partition/>
s для каждого контейнера, чтобы попытаться изолировать друг от друга.
Когда я попробовал это, я получил следующую ошибку:
error: internal error: guest failed to start: Failure in libvirt_lxc startup: Failed to create v1 controller cpu for group: No such file or directory
И это было действительно знакомо. Однажды я открыл неверный отчет об ошибке из-за этого.
Эта ошибка вызвана тем, что libvirt неправильно определяет systemd, что, в свою очередь, вызвано systemd-container
не устанавливается. Исправление:
apt install systemd-container
Это устраняет как исходную проблему, так и мою попытку ее обойти.