在 Docker 容器中使用 libvirt

Using libvirt within Docker container

提问人:aaries 提问时间:7/14/2023 更新时间:7/14/2023 访问量:806

问:

我希望在我的虚拟机上执行一个简单的测试,看看它在容器中的行为方式,我想使用 libvirt 和 QEMU/KVM 进行测试。我有一个域 .xml 文件,可以在裸机服务器上很好地运行 VM,但我在容器中使用 libvirt 时遇到了问题。

我的 Dockerfile 如下所示:

FROM ubuntu:18.04

RUN apt-get update -qy \
 && apt-get upgrade -qy \
 && apt-get install -y \
    bridge-utils \
    iproute2 \
    python3-ipy \
    socat \
    qemu-kvm \
    libvirt-daemon-system \
    libvirt-clients \
    virtinst \
    bridge-utils \
    iptables \
    iproute2 \
    dnsmasq \
    vim \
 && rm -rf /var/lib/apt/lists/*

COPY *.qcow2 /
COPY *.xml /

我成功地从这个运行的 Dockerfile 创建了一个简单的映像,并运行了我使用的容器。这执行得很好,但是一旦我插入到我的容器 shell 中,libvirt 就会给我带来一些问题。我正在使用已确认工作的 libvirt 域 XML 文件,但是在定义它时,我在 virsh 中出现以下错误:sudo docker build . -t foosudo docker run -i --privileged -t --rm foo /bin/bash

error: failed to connect to the hypervisor
error: Failed to connect socket to '/var/run/libvirt/libvirt-sock': No such file or directory

在未嵌套或容器化的正常环境中,使用 libvirt 调试问题更容易,通常守护程序未启用或未运行,对于容器,我不确定如何准确解决问题。当我检查 libvirt 守护进程的状态时,我收到此消息

System has not been booted with systemd as init system (PID 1). Can't operate.

我认为在运行与我的网络有关的容器时,我应该传递一些参数?

Docker qemu libvirt

评论

0赞 larsks 7/14/2023
容器内没有运行任何服务管理器(如 systemd)。如果你想启动 libvirtd,你需要手动启动它(例如,通过 Dockerfile 中的语句),当它退出时,你的容器就会退出。CMD
0赞 David Maze 7/14/2023
...但 Docker 容器本身就是一个隔离的环境(如果您使用的是 Docker Desktop 或类似工具,它实际上位于虚拟机内部);在那里运行虚拟化框架可能没有意义。此工具可能需要直接在主机上运行。
1赞 larsks 7/14/2023
它并不像你想象的那么疯狂;您可能希望在容器中运行 libvirt 的原因有很多。例如,kubevirt 项目可以做到这一点,它允许 VM 访问容器化工作负载可见的相同网络环境。

答:

0赞 larsks 7/14/2023 #1

根据我的评论,这里有一个基本的 Dockerfile,用于运行容器化的 libvirt:

FROM ubuntu:23.04

RUN export DEBIAN_FRONTEND=noninteractive && \
    apt-get update && \
    apt-get -y install \
        bridge-utils \
        dmidecode \
        dnsmasq \
        ebtables \
        iproute2 \
        iptables \
        libvirt-clients \
        libvirt-daemon-system \
        ovmf \
        qemu-efi \
        qemu-kvm \
        tini \
        && \
    apt-get clean

RUN sed -i '/^#stdio_handler/ a\stdio_handler = "file"' /etc/libvirt/qemu.conf

COPY config/pools/* /etc/libvirt/storage/
COPY config/networks/* /etc/libvirt/qemu/networks/
RUN mkdir -p /etc/libvirt/storage/autostart /etc/libvirt/qemu/networks/autostart && \
    for pool in /etc/libvirt/storage/*.xml; do \
        ln -sf "../${pool##*/}" /etc/libvirt/storage/autostart/; \
    done && \
    for net in /etc/libvirt/qemu/networks/*.xml; do \
        ln -sf "../${net##*/}" /etc/libvirt/qemu/networks/autostart/; \
    done

CMD ["/usr/bin/tini", "/usr/sbin/libvirtd"]

在我有:config/pools/default.xml

<pool type='dir'>
  <name>default</name>
  <target>
    <path>/var/lib/libvirt/images</path>
  </target>
</pool>

在我有:config/networks/default.xml

<network>
  <name>default</name>
  <forward mode='nat'>
    <nat>
      <port start='1024' end='65535'/>
    </nat>
  </forward>
  <bridge name='virbr0' stp='on' delay='0'/>
  <ip address='192.168.122.1' netmask='255.255.255.0'>
    <dhcp>
      <range start='192.168.122.10' end='192.168.122.200'/>
    </dhcp>
  </ip>
</network>

如果我从中构建一个映像并像这样运行它:libvirtdDockerfile

docker run -it --rm \
  -v /tmp/libvirt:/run/libvirt \
  --privileged \
  --name libvirt \
  --device /dev/kvm \
  -v /sys/fs/cgroup:/sys/fs/cgroup \
  libvirtd

然后我可以做这样的事情:

$ virsh -c qemu:///system'?socket=/tmp/libvirt/libvirt-sock' net-list
 Name      State    Autostart   Persistent
--------------------------------------------
 default   active   yes         yes

甚至:

export LIBVIRT_DEFAULT_URI='qemu:///system?socket=/tmp/libvirt/libvirt-sock'

virsh vol-create-as --pool default jammy 653M
virsh vol-upload jammy \
  jammy-server-cloudimg-amd64.img \
  --pool default
virt-install -n jammy -r 4096 \
  --disk pool=default,size=10,backing_store=jammy,backing_format=qcow2 \
  --network network=default \
  --os-variant ubuntujammy \
  --import