1 虚拟化管理面无感卸载
1.1 简介
虚拟化管理面,即libvirtd,而虚拟化管理面卸载,即是将libvirtd卸载到与虚拟机所在机器(以下称为host)之外的另一台机器(以下称为DPU)上运行。
我们使用了qtfs将host的一些与虚拟机运行相关的目录挂载到DPU上,使得虚拟化管理面工具可以访问到这些目录,为kvm虚拟机准备运行所需要的环境,此处,因为需要挂载远端的proc和sys,所以,我们创建了一个专门的rootfs以作为libvirtd的运行环境(以下称为/another_rootfs
)。
并且通过rexec执行虚拟机的拉起、删除等操作,使得可以将虚拟化管理面和虚拟机分离在不同的两台机器上,远程对虚拟机进行管理。
1.2 相关组件介绍
1.2.1 rexec介绍
rexec是一个用c语言开发的远程执行组件,分为rexec client和rexec server。server端为一个常驻服务进程,client端为一个二进制文件,client端被执行后会基于udsproxyd服务与server端建立uds连接,并由server常驻进程在server端拉起指定程序。在libvirt虚拟化卸载中,libvirtd卸载到DPU上,当它需要在HOST拉起虚拟机qemu进程时调起rexec client进行远程拉起。
2 操作环境
物理机操作系统: openEuler 22.03 LTS 及之后的版本
libvirt版本: libvirt-6.9.0
qemu版本:6.2.0
文件下载列表:
- DPU & HostOS 机器上下载相关patch包 0001-libvirt_6.9.0_1201_offload.patch, 0003-fix-get-affinity.patch, 0004-qmp-port-manage.patch
- DPU & HostOS 机器上下载并编译dpu-utilities https://gitee.com/openeuler/dpu-utilities.git :本仓库中的qtfs/rexec目录中编译。
git clone https://gitee.com/openeuler/dpu-utilities.git
cd dpu-utilities/qtfs/rexec/
make
yes | cp ./rexec* /usr/bin/
- DPU 机器上下载对应软件包和库文件 qtfs.ko, libvirt-6.9.0.tat.xz
- host 机器上安装qemu yum install qemu
3 虚拟化管理面卸载操作指南
说明:
- 在host端和DPU端,都要拉起rexec_server;host侧可使用 [dpu IP]:[dpu rexec端口号] 远程操作DPU上的二进制,反之亦然;
- host端拉起rexec_server,主要是用于DPU创建虚拟机时用rexec拉起qemu-kvm;
3.1 拉起rexec_server
3.1.1 拷贝二进制文件
rexec_server安装到DPU和host上
cp rexec_server /usr/bin/
chmod +x rexec_server
3.1.2 配置rexec_server服务
为方便起见,可以将rexec_server作为一个systemd服务;
- 同时在DPU和host上的
/usr/lib/systemd/system/
目录下,添加rexec.service
服务文件,内容如下:
/usr/lib/systemd/system/rexec.service
,service文件中的<端口号>根据用户的意愿自行分配
[Unit]
Description=Rexec_server Service
After=network.target
[Service]
Type=simple
Environment=CMD_NET_ADDR=tcp://0.0.0.0:<端口号>
ExecStart=/usr/bin/rexec_server
ExecReload=/bin/kill -s HUP $
KillMode=process
[Install]
WantedBy=multi-user.target
- 通过systemctl start rexec,拉起rexec_server服务
systemctl daemon-reload
systemctl enable --now rexec
3.1.2 rexec操作示例
配置好rexec_server服务后,如果要在DPU上调用host上的二进制,可先将rexec
拷贝到/usr/bin
下面,再使用如下命令远程执行对端服务器上的二进制;
CMD_NET_ADDR=tcp://<host ip>:<host端rexec_server端口号> rexec [要执行的指令]
例如,在DPU上操作host(假设ip为192.168.1.1,rexec端口号为6666)上的ls指令:
CMD_NET_ADDR=tcp://192.168.1.1:6666 rexec /usr/bin/ls
补充说明:
如果不想systemd服务的形式拉起rexec_server,可使用命令手动拉起。
例如,在host端执行
CMD_NET_ADDR=tcp://0.0.0.0:<端口号> rexec_server
命令,可拉起rexec_server进程;
3.2 准备libvirtd运行的rootfs
注:本步骤仅需在DPU上执行的
在下面的文档中,我们将这个rootfs称为/another_rootfs
(具体的目录名称,可以根据自己的需求进行调整)
rootfs可使用如下3.2.1或3.2.2两种方案之一,推荐使用openEuler官方qcow2镜像。在准备好rootfs后,按照3.2.3中的方式,向/another_rootfs中安装软件包
3.2.1 拷贝根目录
一般来说,只需要将根目录直接拷贝到这个文件夹即可
请使用如下命令操作拷贝动作
mkdir /another_rootfs
cp -r /usr /another_rootfs
cp -r /sbin /another_rootfs
cp -r /bin /another_rootfs
cp -r /lib64 /another_rootfs
cp -r /lib /another_rootfs
mkdir /another_rootfs/boot
mkdir /another_rootfs/dev
mkdir /another_rootfs/etc
mkdir /another_rootfs/home
mkdir /another_rootfs/mnt
mkdir /another_rootfs/opt
mkdir /another_rootfs/proc
mkdir /another_rootfs/root
mkdir /another_rootfs/run
mkdir /another_rootfs/var
mkdir /another_rootfs/etc
mkdir /another_rootfs/sys
mkdir /another_rootfs/local_proc
3.2.2 使用openEuler官方qcow2镜像
如果根目录并非完全干净的新环境,可以使用openEuler官方提供的qcow2镜像,来准备一个新的rootfs:
3.2.2.1 工具安装
需要用yum安装xz、kpartx、qemu-img
yum install xz kpartx qemu-img
3.2.2.2 下载qcow2镜像
在openEuler官网获取22.03版本openEuler-x86虚拟机镜像(x86架构),或者22.03版本openEuler-arm64虚拟机镜像(ARM架构)。
3.2.2.3 解压qcow2镜像
使用xz -d解压为openEuler-22.03-LTS-
xz -d openEuler-22.03-LTS-x86_64.qcow2.xz
3.2.2.4 挂载qcow2镜像并拷贝文件
- 使用
modprobe nbd maxpart=<任意数字>
来加载nbd模块, qemu-nbd -c /dev/nbd0 <虚拟机镜像的路径>
- 创建任意文件夹
/random_dir
, - 执行挂载
mount /dev/nbd0p2 /random_dir
- 拷贝文件
mkdir /another_rootfs
cp -r /random_dir/* /another_rootfs/
此时,虚拟机镜像便已经挂载到当前文件夹中了;
3.2.2.5 qcow2卸载
在准备好rootfs后,需要卸载qcow2文件,此时,需要执行如下指令:
umount /random_dir
qemu-nbd -d /dev/nbd0
3.3 在host侧拉起qtfs_server
创建容器管理面所需要的文件夹,然后插入qtfs_server.ko,并拉起engine进程。
可以用如下脚本来执行此操作,如果执行错误,可能需要dos2unix来将此脚本的格式转换(如下所有脚本皆同理)。注意,在最后两行,需要将qtsf_server.ko和engine的路径填写为存放这两个组件的路径
#!/bin/bash
mkdir /var/lib/libvirt
insmod <ko路径>/qtfs_server.ko qtfs_server_ip=0.0.0.0 qtfs_log_level=INFO #此处需要自行修改ko的路径
<engine路径>/engine 4096 16 #此处需要自行修改engine的路径
3.4 UDSPROXYD服务部署
3.4.1 简介
udsproxyd是一个跨主机的unix domain socket代理服务,需要分别部署在host和dpu上,在host和dpu上的udsproxyd组件是对等的关系,可以实现分布在host与dpu上的2个进程之间的uds通信,通信进程是无感的,也就是说如果这两个进程在同一主机内通过uds正常通信的功能,拉远到host和dpu之间也可以,不需要做代码适配,只需要作为client的一端加一个环境变量LD_PRELOAD=libudsproxy.so
。
3.4.2 部署方式
首先,在dpu-utilities工程内编译udsproxyd:
cd qtfs/ipc
make && make install
当前最新版本下,qtfs server侧的engine服务已经整合了udsproxyd的能力,所以server侧若部署了qtfs后不需要再额外启动udsproxyd。client侧则单独拉起udsproxyd服务:
nohup /usr/bin/udsproxyd <thread num> <addr> <port> <peer addr> <peer port> 2>&1 &
参数解释:
thread num: 线程数量,目前只支持单线程,填1.
addr: 本机使用的ip
port:本机占用的port
peer addr: udsproxyd对端的ip
peer port: 对端port
示例:
nohup /usr/bin/udsproxyd 1 192.168.10.10 12121 192.168.10.11 12121 2>&1 &
如果未拉起qtfs的engine服务,想单独测试udsproxyd,则在server端也对等拉起udsproxyd即可:nohup /usr/bin/udsproxyd 1 192.168.10.11 12121 192.168.10.10 12121 2>&1 &
然后将libudsproxy.so拷贝到libvirt的chroot目录下的/usr/lib64中以提供给libvirtd服务使用,这一步在后面介绍。
3.5 挂载host上依赖目录至DPU
3.5.1 安装软件包
3.5.2.1 在根目录的安装
- 在/another_rootfs中安装libvirt-client
yum install libvirt-client
3.5.2.2 another_rootfs环境配置
- 在
/another_rootfs
中,安装libvirtd;
cd /another_rootfs
tar -xf <path_to>/libvirtd-6.9.0.tar.xz #此处需要改动libvirtd-6.9.0.tar.xz的路径
cd libvirtd-6.9.0
patch -p1 < 0001-libvirt_6.9.0_1201_offload.patch #此处需要改动path_to_patch的路径为libvirt的patch路径,并按顺序一个一个打入patch
patch -p1 < 0003-fix-get-affinity.patch #此处需要改动path_to_patch的路径为libvirt的patch路径,并按顺序一个一个打入patch
patch -p1 < 0004-qmp-port-manage.patch #此处需要改动path_to_patch的路径为libvirt的patch路径,并按顺序一个一个打入patch
chroot /another_rootfs
yum groupinstall "Development tools" -y
yum install -y vim meson qemu qemu-img strace edk2-aarch64 tar
yum install -y rpcgen python3-docutils glib2-devel gnutls-devel libxml2-devel libpciaccess-devel libtirpc-devel yajl-devel systemd-devel dmidecode glusterfs-api numactl
cd /libvirtd-6.9.0
CFLAGS='-Wno-error=format -Wno-error=int-conversion -Wno-error=implicit-function-declaration -Wno-error=nested-externs -Wno-error=declaration-after-statement -Wno-error=unused-result -Wno-error=missing-prototypes -Wno-error=int-conversion -Wno-error=unused-parameter -Wno-error=unused-variable -Wno-error=pointer-sign -Wno-error=discarded-qualifiers -Wno-error=unused-function' meson build --prefix=/usr -Ddriver_remote=enabled -Ddriver_network=enabled -Ddriver_qemu=enabled -Dtests=disabled -Ddocs=enabled -Ddriver_libxl=disabled -Ddriver_esx=disabled -Dsecdriver_selinux=disabled -Dselinux=disabled
ninja -C build install
exit
- 并需要将rexec拷贝到
/another_rootfs/usr/bin
下面,并对其添加可执行权限
cp rexec /another_rootfs/usr/bin
chmod +x /another_rootfs/usr/bin/rexec
- 在
/another_rootfs
中,通过如下脚本创建脚本/usr/bin/qemu-kvm, /usr/libexec/qemu-kvm,此脚本需要将<host的ip>和<rexec server端口>改为host的ip和host上rexec_server的端口号
chroot /another_rootfs
touch /usr/bin/qemu-kvm
touch /usr/libexec/qemu-kvm
cat > /usr/bin/qemu-kvm <<EOF
#!/bin/bash
host=<host的ip>
port=<rexec server端口>
CMD_NET_ADDR=tcp://\$host:\$port exec /usr/bin/rexec /usr/bin/qemu-kvm \$*
EOF
cat > /usr/libexec/qemu-kvm <<EOF
#!/bin/bash
host=<host的ip>
port=<rexec server端口>
CMD_NET_ADDR=tcp://\$host:\$port exec /usr/bin/rexec /usr/bin/qemu-kvm \$*
EOF
chmod +x /usr/libexec/qemu-kvm
chmod +x /usr/bin/qemu-kvm
exit
3.5.2.3 目录挂载
在DPU上执行如下的脚本,将libvirtd所需要的host目录挂载到DPU。
并且,需要确保在以下脚本 prepare.sh 中被挂载的远程目录在host和DPU都存在。
#!/bin/bash
insmod <qtfs.ko路径>/qtfs.ko qtfs_server_ip=<server ip> qtfs_log_level=INFO #此处需要改动qtfs.ko的路径,并改动<server ip>
systemctl stop libvirtd
mkdir -p /var/run/rexec/pids
cat >/var/run/rexec/qmpport << EOF
<qmp端口号>
EOF
cat > /var/run/rexec/hostaddr <<EOF
<服务端ip>
EOF
cat > /var/run/rexec/rexecport << EOF
<rexec端口号>
EOF
rm -f `find /var/run/libvirt/ -name "*.pid"`
rm -f /var/run/libvirtd.pid
if [ ! -d "/another_rootfs/local_proc" ]; then
mkdir -p /another_rootfs/local_proc
fi
if [ ! -d "/another_rootfs/local" ]; then
mkdir -p /another_rootfs/local
fi
mount -t proc proc /another_rootfs/local_proc/
mount -t proc proc /another_rootfs/local/proc
mount -t sysfs sysfs /another_rootfs/local/sys
mount --bind /var/run/ /another_rootfs/var/run/
mount --bind /var/lib/ /another_rootfs/var/lib/
mount --bind /var/cache/ /another_rootfs/var/cache
mount --bind /etc /another_rootfs/etc
mkdir -p /another_rootfs/home/VMs/
mount -t qtfs /home/VMs/ /another_rootfs/home/VMs/
mount -t qtfs /var/lib/libvirt /another_rootfs/var/lib/libvirt
mount -t devtmpfs devtmpfs /another_rootfs/dev/
mount -t hugetlbfs hugetlbfs /another_rootfs/dev/hugepages/
mount -t mqueue mqueue /another_rootfs/dev/mqueue/
mount -t tmpfs tmpfs /another_rootfs/dev/shm
mount -t sysfs sysfs /another_rootfs/sys
mkdir -p /another_rootfs/sys/fs/cgroup
mount -t tmpfs tmpfs /another_rootfs/sys/fs/cgroup
list="perf_event freezer files net_cls,net_prio hugetlb pids rdma cpu,cpuacct memory devices blkio cpuset"
for i in $list
do
echo $i
mkdir -p /another_rootfs/sys/fs/cgroup/$i
mount -t cgroup cgroup -o rw,nosuid,nodev,noexec,relatime,$i /another_rootfs/sys/fs/cgroup/$i
done
## common system dir
mount -t qtfs -o proc /proc /another_rootfs/proc
echo "proc"
mount -t qtfs /sys /another_rootfs/sys
echo "cgroup"
mount -t qtfs /dev/pts /another_rootfs/dev/pts
mount -t qtfs /dev/vfio /another_rootfs/dev/vfio
3.6 拉起libvirtd
在DPU上,打开一个窗口,并且chroot到/another_rootfs
chroot /another_rootfs
用如下的命令先拉起virtlogd,后拉起virtlogd
#!/bin/bash
virtlogd -d
libvirtd -d
因为我们已经将/var/run/
和/another_rootfs/var/run/
绑定在了一起,所以,我们可以在正常的rootfs下,通过virsh来访问libvirtd,从而管理容器。
4 环境恢复
如果需要卸载相关的目录,需要执行如下指令
#!/bin/bash
umount /root/p2/dev/hugepages
umount /root/p2/etc
umount /root/p2/home/VMs
umount /root/p2/local_proc
umount /root/p2/var/lib/libvirt
umount /root/p2/var/lib
umount /root/p2/*
umount /root/p2/dev/pts
umount /root/p2/dev/mqueue
umount /root/p2/dev/shm
umount /root/p2/dev/vfio
umount /root/p2/dev
rmmod qtfs
umount /root/p2/sys/fs/cgroup/*
umount /root/p2/sys/fs/cgroup
umount /root/p2/sys