我有一堆 libvirt-lxc 容器,它们的配置我已从 Debian jessie 迁移到新的 Debian buster 主机。我使用重新创建了容器的 rootfs,lxc-create -t debian -- --release buster
然后使用我知道可以正常工作的脚本重新映射了 rootfs 的 uid/gid 编号。
容器配置如下:
<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
输出中也显示为正在运行,但容器的 root 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
(注:我尝试过使用和不使用 apparmor)
我变得非常绝望,将 strace 连接到strace -ff -o somedir/foo -p
libvirtd,然后启动一个容器。经过大量挖掘,我发现 libvirt/sbin/init
在容器内启动,然后容器很快以状态代码 255 退出。这是在使用 cgroups 执行某些操作后执行 EACCESS 之后的结果:
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 子树的所有权,而其他用户命名空间的容器则无法使用它。非用户命名空间的容器可以简单地接管 cgroup 树,因为它们以 UID 0 运行。
现在的问题是:为什么 cgroup 配置不正确?是 libvirt 错误吗?还是我的系统配置错误?
答案1
我想到了一个想法,尝试<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 又systemd-container
未安装。修复方法是:
apt install systemd-container
这既解决了原始问题,也解决了我尝试解决的问题。