从容器镜像构建虚拟机镜像——d2vm工具介绍

发布时间 2023-04-18 11:19:17作者: frankming

从容器镜像构建虚拟机镜像——d2vm工具介绍

简介

容器镜像是容器技术的核心组成部分之一,得益于基于UnionFS的多层次容器镜像, 开发者在构建镜像时能够做到对已有镜像层的高度复用,而在使用镜像时也能够较大程度上节约磁盘空间和网络带宽,从而为容器技术的广泛流行奠定了基础。

虚拟机镜像指包含可启动操作系统的虚拟磁盘文件,虚拟机镜像是虚拟机创建流程的前提条件,用户在创建虚拟机时需要事先准备一个虚拟机镜像,随后使用该镜像创建一个虚拟机实例。目前为止,虚拟机镜像格式已经发展得十分成熟,除了原始的raw镜像格式外,众多镜像格式在此基础上添加了一些高级特性,如快照、扩容、加密、后备镜像等等。

不过,对于如何制作虚拟机镜像这方面,仍没有一个能够像容器镜像构建那样简单方便且好用的工具,一部分工具将构建过程嵌入到代码中,可读性较差,且扩展性受限,而一些新的工具采用了声明式的方式,虽说提升了可读性,但镜像复用、构建速度等方面仍有待提升。那么,有没有一种办法将容器镜像转化成虚拟机镜像,从而完整地享受到容器镜像构建的优势?在网上搜索,已经有一些热心的开源开发者贡献了自己的方案,本文主角:d2vm 就是其中之一。

使用

首先,按照文档中提供的方法安装d2vm,这里采用容器化部署方式:

docker pull linkacloud/d2vm:latest
alias d2vm="docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock --privileged -v \$PWD:/d2vm -w /d2vm linkacloud/d2vm:latest"

将容器镜像centos:7.9.2009转换为虚拟机镜像centos7.9.qcow2

d2vm convert centos:7.9.2009 -o centos7.9.qcow2 -p 000000
Using local image centos:7.9.2009
Inspecting image centos:7.9.2009
Docker image based on CentOS Linux 7 (Core)
Building kernel enabled image
Creating vm image
Creating raw image
Mounting raw image
Creating raw image file system
Copying rootfs to raw image
Setting up rootfs
Installing linux kernel
Unmounting raw image
Writing MBR
Converting to qcow2

随后,对已生成的虚拟机镜像进行调试:

# 如果是centos,需要执行ln -s /usr/libexec/qemu-kvm /usr/bin/qemu-system-x86_64
d2vm run qemu centos7.9.qcow2
...
[  OK  ] Reached target Network.
[  OK  ] Reached target Network is Online.
[  OK  ] Started Update UTMP about System Runlevel Changes.
[    4.623933] tsc: Refined TSC clocksource calibration: 2199.996 MHz
[    4.855933] IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready

CentOS Linux 7 (Core)
Kernel 3.10.0-1160.88.1.el7.x86_64 on an x86_64

localhost login:

可以看到,该镜像成功启动。

原理

众所周知,容器常常类比为轻量级的虚拟机,因为复用了宿主机的操作系统内核,不需要运行额外的内核,其省去了内核运行的开销,且也省去了内核的启动过程。那么容器镜像转虚拟机镜像的重点则自然也在于补全内核和启动过程这两部分。

基础的Linux发行版容器镜像出于节省磁盘空间的目的,往往会在镜像制作的最后删除/boot、/lib/modules等目录,或者制作时压根不安装kernel相关的软件包,转虚拟机镜像的第一步就是安装这类必备的软件包,包括kernel、systemd、network等等。

其次需要补全启动过程,容器镜像缺失了一部分系统启动所需要的文件,这部分文件需要补全。在Linux系统的启动过程中,bios会事先读取磁盘的分区表,从中获取引导程序并转交控制权,引导程序从配置文件中获取内核和initrd的位置并加载,initrd使用完后会挂载根分区并执行初始进程。在定义分区表并划分分区后,容器镜像至少需要补全如下文件:

  1. 引导程序;
  2. 引导配置文件,包括内核的位置与启动参数;
  3. /etc/fstab文件中需要包括根目录的挂载信息;
  4. 视情况初始化/etc/resolv.conf、/etc/hostname、/etc/hosts等文件。

如下是d2vm工具将容器镜像转化为虚拟机镜像的流程:

graph TB A(开始)-->B[执行d2vm convert命令] B-->D[检测镜像发行版] D-->E["准备Dockerfile,安装kernel"] E-->F["创建空白镜像,并生成分区"] F-->G["为分区创建文件系统,并挂载"] G-->H[将容器镜像文件导出到挂载目录] H-->J[配置根文件系统] J-->I[安装引导程序] I-->K[写入mbr至磁盘头部] K-->L[卸载镜像挂载点] L-->M[转换镜像格式] M-->Z(结束)

小结

该工具目前仍处于初期阶段,虽说已经能够成功跑通容器镜像转虚拟机镜像的流程,验证了方案的可行性,但也有一些问题有待解决。如下列举了部分已知问题:

  • 仅支持docker,没有适配其他容器镜像构建工具如buildah等
  • 仅支持mbr分区表格式,不支持efi,也不支持grub
  • 无法自定义分区划分
  • 镜像构建过程固化在代码中,无法自定义,导致内网中会因网络问题无法安装软件包而构建失败
  • 不支持openEuler等发行版
  • 部分代码存在bug,比如容器镜像地址不允许指定端口

总的来说,d2vm作为一款容器镜像转虚拟机镜像工具,已经能够顺利地实现转换的流程,而且处理流程浅显易懂,而在执行过程中所需要的问题,也可随着项目的发展而逐渐修复掉,因此值得先作为一个玩具尝试一下。

另外,有另外一款容器镜像转虚拟机镜像的项目:[elemental-toolkit](rancher/elemental-toolkit: The toolkit to build, ship and maintain cloud-init driven Linux derivatives based on container images (github.com)),该项目更加庞大,也更复杂,之后有时间也可以看看。

参考文档

Tool support for image creation — Virtual Machine Image Guide documentation (openstack.org)

linka-cloud/d2vm: Build Virtual Machine Image from Dockerfile or Docker image (github.com)

From Docker Container to Bootable Linux Disk Image (iximiuz.com)