Назад | Перейти на главную страницу

Контейнер libvirt-lxc на Debian buster с пространством имен пользователей не всегда запускается

У меня есть несколько контейнеров 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

Это устраняет как исходную проблему, так и мою попытку ее обойти.