OpenStack系列(十一) 构建OpenStack镜像
1 | 作者:李晓辉 |
在云计算的世界里,虚拟机镜像(VM Image)是构建和部署虚拟化环境的基础。它就像是虚拟机的“模板”,包含了操作系统、预装软件以及配置信息,使得用户能够快速部署和启动新的虚拟机实例。OpenStack作为领先的开源云平台,提供了强大的工具和灵活的机制来创建、管理和分发虚拟机镜像。
本文将深入探讨如何在OpenStack环境中构建虚拟机镜像。我们将从基础概念入手,逐步介绍如何创建自定义镜像,包括选择合适的操作系统、安装必要的软件、进行系统优化以及将镜像上传到OpenStack的镜像服务(Glance)中。此外,我们还会分享一些最佳实践和技巧,帮助你高效地管理和使用虚拟机镜像,以满足不同业务场景的需求。
无论你是OpenStack的初学者,还是希望深入了解镜像管理的资深用户,本文都将为你提供实用的指导和参考。让我们一起走进OpenStack虚拟机镜像的世界,探索如何通过镜像技术提升云环境的部署效率和灵活性。
常见镜像格式
格式 | 描述 |
---|---|
raw | 这是一种最简单的镜像格式,就像把硬盘的内容直接拷贝下来,没有经过任何加工。它的文件扩展名通常有 .img 、.raw 或 .bin 。这种格式的好处是简单直接,但文件可能会比较大。 |
QCOW2 | 这是一种比较智能的镜像格式,由 QEMU 仿真器支持。它可以随着数据的增加而动态扩展空间,就像一个可以自动扩容的气球。这是 Linux KVM 最常用的格式,因为它既灵活又节省空间。 |
ISO | 大家可能都见过这种格式,它通常用于光盘(CD 或 DVD)的内容。如果你曾经刻录过光盘,或者下载过操作系统的安装光盘镜像,那很可能就是 ISO 格式。 |
AKI | 这是 Amazon 的一种内核映像格式,主要用于 Amazon 的云服务。在 Linux KVM 或 Red Hat OpenStack Platform 中,我们通常用不到它。 |
AMI | 同样是 Amazon 的一种系统映像格式,主要用于 Amazon 的云服务。在 Linux KVM 或 Red Hat OpenStack Platform 中,我们也不常用它。 |
ARI | 这是 Amazon 的一种 ramdisk 映像格式,也是为 Amazon 的云服务设计的。在 Linux KVM 或 Red Hat OpenStack Platform 中,我们一般不会用到它。 |
VDI | 这是 VirtualBox 的一种磁盘镜像格式,最初是为了 VirtualBox 虚拟机管理程序设计的。不过,QEMU 仿真器也支持它。 |
VHD | 这是微软 Hyper-V 使用的虚拟硬盘格式,最早由 Windows Virtual PC 使用。虽然 VMware、Xen、VirtualBox 等也支持这种格式,但它并不是特别流行。 |
VMDK | 这是 VMware 最初创建的一种虚拟机磁盘格式,但现在已经被广泛接受,成为了一种通用的开放格式。 |
RAW or QCOW2?
RAW和QCOW2是最常见的格式,以下是一些主要区别:
属性 | RAW | QCOW2 |
---|---|---|
Image 大小 | RAW Image 是源磁盘的精确副本,包括空白空间。如果文件系统或存储后端支持稀疏存储,RAW Image 实际占用的空间通常会比它显示的大小小很多。 | QCOW2 是一种经过优化的虚拟磁盘格式,可以节省空间。不要把它和压缩或者稀疏存储混淆,它的优化是通过文件内部结构实现的。通常,QCOW2 Image 比来自同一源的 RAW Image 小。此外,QCOW2 还支持 zlib 压缩。QCOW2 Image 已经经过优化,所以它可能不需要依赖稀疏文件系统或存储功能。 |
性能 | RAW 格式性能更高,因为它在虚拟机启动时就分配好了磁盘空间,避免了动态分配空间带来的延迟。使用 RAW Image 时,不需要额外的处理步骤。 | QCOW2 格式性能稍低,因为它的特性需要在运行时动态处理。当需要的空间超过当前分配的大小时,扩展空间会带来一定的延迟。 |
加密 | 不支持 | 支持可选加密。可以使用 256 位 AES 加密或 LUKS 加密。 |
快照 | 不支持 | QCOW2 支持快照功能,每个快照都是 Image 在某个时间点的只读副本。快照是存储在 Image 内部的。 |
写入时复制 | 不支持 | 当 QCOW2 Image 发生更改时,更改会写入一个单独的叠加文件,而原始 Image 文件保持不变。在修改之前,需要将要更改的块复制到叠加层。多个叠加文件可以共享同一个基础 Image,这样就可以从同一个基础 Image 启动多个实例。 |
创建qcow2镜像文件
可以看到,虚拟大小为1G,实际大小只有193k
1 | (undercloud) [stack@director ~]$ qemu-img create -f qcow2 test.qcow2 1G |
创建raw镜像文件
可以看到虚拟大小和实际大小都是1G
1 | (undercloud) [stack@director ~]$ qemu-img create -f raw test.raw 1G |
镜像文件的格式转换
1 | (undercloud) [stack@director ~]$ qemu-img convert -f qcow2 test.qcow2 -O raw test-convert.raw |
构建⾃定义镜像
你可以用以下三种方法中的任意一种来制作自定义镜像:
diskimage-builder
这是一个强大的工具,可以帮你快速构建各种操作系统镜像,支持多种自定义配置。Guestfish 或 Virt-customize
这两个工具可以让你对现有的虚拟机镜像进行修改,比如安装软件、配置网络等,非常适合对镜像进行精细调整。Cloud-init
这是一个在虚拟机启动时自动配置系统的工具。你可以通过它来实现自定义的初始化设置,比如设置用户名、安装软件包等。
Diskimage-builder
Diskimage-builder概述
Diskimage-builder在构建镜像的时候,会在 chroot 环境里把 /proc、/sys 和 /dev 给绑定挂载上。它生成的镜像可精简了,只包含实现 OpenStack 功能所需的那些组件。镜像可以很简单,比如文件系统镜像,也可以很复杂,做成完整的磁盘镜像,全看怎么定制。
还有个好玩的,它用元素来决定往镜像里放啥内容,还有要做的修改。镜像至少得有一个基础分发元素,比如 rhel,然后还能用其他元素来修改这个 rhel 基础镜像。它会根据这些元素去调用脚本,把各种东西应用到镜像里。
每个元素都有两个文件,element-deps
和 element-provides
,它们是干啥用的呢?
定义依赖:
element-deps
文件就是一个普通的文本文件,里面写的是这个元素需要依赖的其他元素。就好比你做饭需要食材一样,这个元素也需要其他元素先准备好。Diskimage-builder 在构建镜像的时候,会先看看这个文件,把里面提到的依赖元素先搞定,然后再来构建这个元素。比如说,如果一个元素依赖于ubuntu
元素,那 Diskimage-builder 就会先搞定ubuntu
,再继续往下做。定义提供的功能:
element-provides
文件也是一个文本文件,不过它写的是这个元素能提供啥功能或者服务。这就相当于你已经准备好了某些东西,别人就不需要再重复准备了。在构建镜像的时候,Diskimage-builder 会检查这个文件,如果某个功能已经有人提供过了,那它就不会再重复构建,这样就能省事儿,避免重复劳动。
安装diskimage-builder
1 | [root@director ~]# yum install diskimage-builder |
列出包含的元素
1 | [root@director ~]# ls /usr/share/diskimage-builder/elements |
基础元素
/usr/share/diskimage-builder/elements/base
是 Diskimage-builder 里的一个超重要的地方!它里面装的是基础元素(base element)的定义和脚本。
基础元素就是构建镜像的“地基”,里面有一些最基本的配置和软件包,啥镜像都得从这儿开始搭起来。就好比盖房子,先得有个地基,这个基础元素就是镜像的地基。有了它,你才能往上加各种自定义的东西,做出自己想要的镜像。
Diskimage-builder 阶段⼦⽬录说明
说到 Diskimage-builder 构建镜像的过程,其实是有好几个阶段的,挺有意思的!每个阶段的脚本都放在 element
目录下的子目录里。这些子目录可能一开始没有,所以如果需要的话,就自己动手创建一下。
这些脚本的名字前面都有两位数字,运行的时候会按照数字的顺序来执行。有个小规矩是这样的:数据文件一般都放在 element
目录里,但只有那些可执行的脚本才会被放在阶段子目录里。要是某个脚本没有执行权限,那它就不会被运行。
这些阶段子目录的处理顺序是固定的,就像做饭的步骤一样,得按照顺序来,不然可能会乱套。
root.d:
作用:包含在
chroot
环境中执行的脚本。这些脚本在构建过程中会在目标文件系统的根目录中运行,通常用于安装和配置基本软件包和设置,所以你有什么自定义的项目需要添加,可以放这里。执行顺序:相对较早执行,以便确保基础环境的配置。
extra-data.d:
作用:包含下载和处理额外数据的脚本。这些数据可以是镜像中需要包含的特定文件或配置。
执行顺序:通常在安装和配置的中期执行,以确保额外的数据在需要时可用。
pre-install.d:
作用:在安装任何软件包之前运行的脚本。这些脚本通常用于准备系统环境,例如更新包列表或设置环境变量。
执行顺序:在
install.d
之前。
install.d:
作用:执行安装操作的脚本,用于安装必要的软件包和工具。
执行顺序:在
pre-install.d
之后,post-install.d
之前。
post-install.d:
作用:在软件包安装完成后运行的脚本,用于配置已安装的软件或执行其他需要在安装后进行的操作。
执行顺序:在
install.d
之后
block-device.d:
作用:包含用于配置块设备(如磁盘分区、文件系统等)的脚本。通常用于准备磁盘分区和文件系统结构。
执行顺序:根据需要,通常在配置阶段或安装阶段之后。
finalize.d:
作用:执行最终定制操作的脚本,用于做出最后的调整和优化,例如设置引导加载程序。
执行顺序:在所有配置和安装操作完成后执行。
cleanup.d:
作用:在所有操作完成后运行的脚本,用于清理构建过程中产生的临时文件和数据,以减小镜像大小并优化性能。
执行顺序:最后执行,以确保所有的临时文件和数据被清除。
基本的Diskimage-builder 变量
变量 | 描述 |
---|---|
DIB_LOCAL_IMAGE | 要从中构建的基础映像 |
DIB_YUM_REPO_CONF | 在映像构建期间要复制到chroot环境中的客户端 Yum 存储库配置文件 |
ELEMENTS_PATH | 元素的工作副本的路径 |
Diskimage-builder 选项
1 | [root@director ~]# disk-image-create vm rhel -n -p python-django-compressor -a amd64 -o web.img 2>&1 | tee diskimage-build.log |
vm
:这个参数是告诉 Diskimage-builder,我们要构建的是一个虚拟机的镜像。它会用一些默认的设置,让镜像更适合虚拟机运行。rhel
:这个参数是说,我们要构建的是基于 Red Hat Enterprise Linux 的镜像,也就是用 RHEL 的系统。-n
:这个参数有点意思,它告诉 Diskimage-builder 不要自动包含基础元素。有时候我们不想安装 cloud-init 或者更新软件包,这个参数就很有用了。-p
:这个参数是用来指定要安装的软件包。在这个例子中,我们要安装python-django-compressor
包。-a
:这个参数是设置镜像的架构,这里我们用的是amd64
,也就是 64 位的架构。-o
:这个参数是设置输出的镜像文件名,这里我们把它叫做web.img
。
最后,2>&1 | tee diskimage-build.log
是把构建过程中的输出信息保存到一个日志文件里,方便我们回头查看。
构建案例
我们基于osp-small.qcow2来构建新的镜像
先下载一下这个镜像
1 | su - |
准备基础元素目录和定制目录
1 | cp -a /usr/share/diskimage-builder/elements/ /root/ |
定制在rhel中安装并配置httpd服务
1 | cd elements/rhel/post-install.d |
授予执行权限
1 | cd |
根据以上信息,定制构建变量
1 | export DIB_LOCAL_IMAGE=/root/osp-small.qcow2 |
开始构建镜像
这个构建可能需要较长时间,请耐心等待
1 | disk-image-create vm rhel -t qcow2 -a amd64 -p httpd -o httpd.qcow2 |
输出
1 | 2024-11-11 06:36:12.495 | Converting image using qemu-img convert |
Guestfish和 Virt-customize
Guestfish 和 Virt-customize 都使⽤ libguestfs API 来执⾏其功能。Libguestfs 需要可以处理各种不同格式的后端,默认情况下使⽤ libvirt
Guestfish
下例使⽤ -i 选项来⾃动挂载分区,使⽤ -a 选项来添加磁盘镜像,并且使⽤ –network 选项来启⽤⽹络访问
1 | [root@workstation ~]# yum install libguestfs-tools -y |
我们发现,报告没有libvirt,因为我们是虚拟机,没有是正常的,导出这个direct的变量重新运行即可
1 | [root@workstation ~]# export LIBGUESTFS_BACKEND=direct |
不要忘了最后更新selinux标签
1 | ><fs> command 'yum install httpd -y' |
尝试创建一个实例看看是否成功
Virt-customize
下例中使⽤ virt-customize 命令及 -a 选项来添加磁盘,安装⼀个软件包,设置 root 密码,再还原 SELinux 上下⽂
1 | [root@workstation ~]# wget -O virt-test.qcow2 http://materials/osp-small.qcow2 |
Cloud-init
Cloud-init概述
说到镜像管理,确实是个让人头疼的问题,镜像太多太杂很容易让人晕头转向。为了避免这种情况,其实可以只维护几个通用的基础镜像,然后在真正部署的时候再根据具体需求进行定制。这样一来,既节省了存储空间,又方便管理。
说到定制,就不得不提到 Cloud-init 了。这家伙可厉害了,它是一组 Python 脚本和工具,专门用来在虚拟机或云实例启动的时候做早期初始化工作。它能干的事情可不少,比如把 SSH 密钥塞进去,这样你就能安全地登录;还能配置网络设置,让机器能正常上网;甚至还能设置主机名,让每台机器都有自己的名字。总之,它能帮你把实例的初始配置搞定得妥妥的。
Cloud-init 要完成这些任务,需要一些“指令”,这些指令就是所谓的“用户数据”。你可以把它想象成给 Cloud-init 的“任务清单”,告诉它要做什么,怎么做。有了用户数据,Cloud-init 就能按照你的要求去配置实例了。这样一来,不管是什么基础镜像,都能通过 Cloud-init 变成你需要的样子,是不是很方便呀!
Cloud-init 服务
Cloud-init 有四个主要的服务,每个服务都有自己的任务和执行时机,它们一起把实例的初始化工作安排得明明白白。我给你一个个说说:
cloud-init-local.service
任务:这个服务是第一个出场的,它会在网络还没准备好,但根分区已经可以读写的时候运行。它的主要工作是把网络配置信息传递给实例,相当于给实例准备好“上网指南”。
执行命令:
cloud-init init --local
cloud-init.service
任务:这是第二个服务,它得等网络配置好了之后才能开始工作。这个阶段主要是处理用户数据,按照配置文件里的
cloud_init_modules
模块列表,一步步把用户要求的配置都搞定。执行命令:
cloud-init init
cloud-config.service
任务:这个服务在第二个服务之后运行,主要任务是执行配置文件里
cloud_config_modules
下列出的模块。简单来说,就是把用户指定的配置细节都落实到位。执行命令:
cloud-init modules --mode=config
cloud-final.service
任务:这是最后一个服务,它要等前面的服务都完成,甚至等
rc-local.service
也执行完了才会启动。它的任务是执行配置文件里cloud_final_modules
下的模块,做最后的收尾工作,比如清理日志、设置开机启动项之类的。执行命令:
cloud-init modules --mode=final
这四个服务分工明确,一个接一个地把实例初始化的活儿干完,感觉就像接力赛一样,特别有条理!
如果有兴趣,可以看看模块的内容:
1 | /usr/lib/python3.6/site-packages/cloudinit/config/ |