用 Diskimage-Builder 制作镜像

如何快速地制作自定义镜像。

制作镜像的困难

不管是普通用户还是运维人员,在 OpenStack 环境搭建完成后,第一个要做的事情就是上传镜像。

OpenStack 上的虚机最常用的镜像格式是 qcow2。早期的时候,要制作这样的镜像需要从 iso 镜像手动创建虚机,然后安装完系统后把磁盘保存为镜像。整个过程非常繁琐。

现在常用的 Linux 系统如 Ubuntu、CentOS 都提供了现成的 qcow2 格式的最小安装镜像文件,可以直接下载使用。现在的难题变成了如何制作一个安装了自定义软件的定制镜像。

对于普通用户,可以在 OpenStack 系统上可以先安装最小环境的虚机,然后安装相关软件后,再通过给虚机做快照,保存为镜像的方式生成新的镜像。

这样做出来的镜像当然是可以的,但是过程繁琐,难以自动化(理论上是可以的),耗时耗力,镜像的质量难以保证。

Diskimage-builder 是什么

Diskimage-builder, 下面简称为 dib,是一个为云环境自动构建自定义操作系统镜像的工具。支持多个主流的发行版本,支持多种格式,包含裸金属文件系统镜像和 ram-disk 镜像。

dib 工具仍然需要一个主流 Linux 的镜像文件作为基础,但是它不用创建虚机,而是使用 chroot 技术,把相关目录挂载到本地系统中,完成软件的安装。

熟悉 docker 和 Linux 容器的应该能很容易理解。

除此之外,dib 还针对常见的场景,提供了现成的脚本,称为 element,通过组合这些 elements 和提供必要的配置,用户可以完成大部分的镜像制作需求。

支持的发行版

以下 Linux 发行版本可以作为基础镜像:

  • Centos 6, 7
  • Debian 8 (“jessie”)
  • Fedora 28, 29, 30
  • RHEL 6, 7
  • Ubuntu 14.04 (“trusty”)
  • Gentoo
  • openSUSE Leap 42.3, 15.0, 15.1 and Tumbleweed

安装

该工具是 Python 编写的,所以使用 pip 安装即可:

pip install diskimage-builder

操作系统的包管理也可以安装,比如 yum install diskimage-builder,不过可能版本会比较旧一些。

大部分的镜像格式需要用到 qemu-img 工具,得额外安装一下:

yum install qemu-img

安装完成后就可以使用:

# disk-image-create  --help
Usage: disk-image-create [OPTION]... [ELEMENT]...

其中的 [ELEMENT] 是要传入的主要参数,至少需要指定 1 个。所以下面重点介绍一下什么是 ELEMENT.

什么是 ELEMENT

一个 ELEMENT 就是一小段代码,可以在制作镜像的时候完成一个小任务。

任务可以是安装某软件包,修改配置文件等操作。dib 自带了很多 elements,构建基础镜像可以直接使用。

这些 elements 之间有依赖关系,有的任务比较底层,并不需要直接指定。通常用户需要指定几个最重要的。

有的 element 可以指定相应的参数,一般是通过 环境变量 传递,也可以通过某个特定文件,具体看文档说明。下面我也会给几个最常见的需求场景。

只要掌握了相关规则,用户也可以编写自己的 elements。

快速开始

一个虚机镜像至少需要指定 2 个 elements,例如:

# disk-image-create  centos7 vm

其中 centos7 是指定操作系统发行版,这个是必需的。其它还有哪些操作系统支持,可以在文档里查询。

vm 就是指镜像是供虚拟机用的。如果要制作可供 Ironic 使用的裸金属镜像,则需要指定 baremetal

默认生成的镜像文件名是 image.qcow2,用户可以通过 -o 选项指定一个文件名,也就是替换 image,通过 -t 选项指定不同的类型。

如果是 baremetal 则会生成 3 个文件,除了磁盘镜像文件,还有 .vmlinuz 后缀的内核镜像和 .initrd 文件。

基础操作系统初次创建时,需要从网上下载一个基础的镜像文件,并缓存起来。缓存目录 ~/.cache/image-create/

HOW TO

系统的介绍 elements 的原理和用法比较困难,大多数人也无需掌握,所以下面我把几个最常见的场景列一下,日常直接用就可以了。

指定本地镜像文件

有 2 个方法,要么指定一个下载较快的地方,通过 DIB_CLOUD_IMAGES 环境变量:

export DIB_CLOUD_IMAGES=http://cloud.centos.org/centos/7/images

注意centos7 的文档中对这个变量的示例有点错误,这个只设置镜像所在的文件夹的 URL。

如果要指定本地镜像文件,使用 DIB_LOCAL_IMAGE

export DIB_LOCAL_IMAGE=/root/CentOS-7-x86_64-GenericCloud-1809.qcow2

注意,这里只有 RHEL 系的操作系统支持,保存 CentOS 和 Fedora,Ubuntu 不支持这个配置。

如果不清楚文件名的可以执行的时候留意打印的日志信息。

离线构建

每次构建的时候下载下来的镜像文件会缓存下来,但是仍然会去网上检测最新版本,如果版本有更新,还是会触发下载,使用 --offline 选项,可以避免这一行为。

配置 EPEL

CentOS 系统默认不带 epel 源,需要指定 epel:

disk-image-create centos7 vm epel

同时可以指定镜像源,例如:

export DIB_EPEL_MIRROR=http://mirrors.aliyun.com/epel

安装 pip 和虚拟环境

用到 python 自然要标配 pip 和虚拟环境了:

disk-image-create centos7 vm epel pip_and_virtualenv

指定安装方式,并且配置镜像源:

export DIB_INSTALLTYPE_pip_and_virtualenv=package
export DIB_PYPI_MIRROR_URL=http://mirrors.aliyun.com/pypi/simple/

安装 cloud-init-datasources

云上的虚机只有安装了 cloud-init 才能更好地工作,像 centos 和 ubuntu 这些操作系统的基础镜像里已经包含了,默认不做设置也是可以的。

但是 cloud-init 支持的数据源比较多,如果不指定的情况下,它默认会逐个尝试,会拖慢系统启动,甚至可能出现意想不到的错误,所以最好明确指定:

export DIB_CLOUD_INIT_DATASOURCES='ConfigDrive, OpenStack'
disk-image-create centos7 vm cloud-init-datasources ...

创建开发用户

默认构建出来的镜像没有密码,只能在创建虚机的时候注入密钥才能连接。可以创建一个带密码的用户,方便日常使用:

export DIB_DEV_USER_USERNAME=test
export DIB_DEV_USER_PASSWORD=testpass
export DIB_DEV_USER_PWDLESS_SUDO=YES

disk-image-create centos7 vm devuser ...

安装软件包

构建镜像的时候会默认启动名为 package-installs 的元素来负责软件包的安装。我们按照它的要求准备一份软件包清单即可。

这个文件的名字必须是 package-installs.yaml,内容示例:

libxml2:
grub2:
  phase: pre-install.d
networkmanager:
  uninstall: True
os-collect-config:
  installtype: source
linux-image-amd64:
  arch: amd64
dmidecode:
  not-arch: ppc64, ppc64le
lshw:
  arch: ppc64, ppc64le
python-dev:
  dib_python_version: 2
python3-dev:
  dib_python_version: 3
package-a:
  when: DIB_USE_PACKAGE_A = 1
package-b:
  when: DIB_USE_PACKAGE_A != 1

顶格写的就是包名,下面是一些选项,如果不指定的话直接不写就可以了,就像第 1 条的 libxml2:

这个文件要求是在 element 所在的目录下,也就是说,包含这个文件的文件夹是一个自定义元素,为了让工具能认识这个元素,必须把这个文件夹所在的目录加入到 ELEMENTS_PATH 中。

例如,文件完整路径是 /tmp/myelements/mypackage/package-installs.yaml

# 导入环境变量
export ELEMENTS_PATH=/tmp/myelements

# 指定自定义的这个 element
disk-image-create centos7 vm epel mypackage

ELEMENTS_PATH 这个环境变量和系统的 PATH 环境变量类似,是可以指定多个路径的。

指定 YUM 源

在上面软件包安装时,有可能系统自带的源里面没有,或者网速较慢,可以先准备好一个 repo 文件,例如:

cat /var/images/docker-aliyun.repo
[docker-ce-stable]
name=Docker CE Stable - $basearch
baseurl=https://mirrors.aliyun.com/docker-ce/linux/centos/7/$basearch/stable
enabled=1
gpgcheck=0
gpgkey=https://mirrors.aliyun.com/docker-ce/linux/centos/gpg

然后设置环境变量:

export DIB_YUM_REPO_CONF=/var/images/docker-ce.repo

注意:这里的配置仅影响镜像构建时刻的安装,安装完毕后该 repo 文件会被清理掉。所以比较适合安装本地自己构建的软件包。

裸金属镜像

裸金属镜像使用 baremetal 替换 vm 即可,但是为了确保能本地引导,即不要每次启动都从 PXE 引导,还需要加上 grub2 元素。

disk-image-create baremetal grub2 centos7 ...

注意,裸金属镜像最终有 3 个文件,需要在 Glance 中上传 3 次,对应不同的磁盘类型:

镜像后缀disk-formatcontainer-format说明
.vmlinuzakiakiUUID 对应到 kernel_id
.initrdariariUUID 对应到 ramdisk_id
.qcow2qcow2bare指定 kernel_id 和 ramdisk_id

这样显然比较麻烦,所以一般都是通过脚本一次性搞定:

GLANCE_IMAGE_NAME=${IMAGE_NAME}
GLANCE_IMAGE_KERNEL=${GLANCE_IMAGE_NAME}-kernel
GLANCE_IMAGE_INITRD=${GLANCE_IMAGE_NAME}-initrd

glance image-create --name ${GLANCE_IMAGE_KERNEL} --visibility public \
  --disk-format aki --container-format aki < ${IMAGE_NAME}.vmlinuz

glance image-create --name ${GLANCE_IMAGE_INITRD} --visibility public \
  --disk-format ari --container-format ari < ${IMAGE_NAME}.initrd

GLANCE_KERNEL_UUID=$(glance image-list|grep ${GLANCE_IMAGE_KERNEL}|awk -F "| " '{print $2}')
GLANCE_INITRD_UUID=$(glance image-list|grep ${GLANCE_IMAGE_INITRD}|awk -F "| " '{print $2}')

glance image-create --name my-image --visibility public \
  --disk-format qcow2 --container-format bare --property \
  kernel_id=$GLANCE_KERNEL_UUID --property \
  ramdisk_id=$GLANCE_INITRD_UUID < ${IMAGE_NAME}.qcow2

禁止自动更新

在制作一个指定版本的 CentOS 基础镜像时,最后结果仍是最新版本。这是因为每次都会自动执行更新操作,使用下面的环境变量禁止这一行为:

export DIB_AVOID_PACKAGES_UPDATE=1

如果本文对你有帮助,请 点赞分享关注,谢谢!

Davy
Davy
学习📚 技术👨‍💻 投资📈

一些心得体会,希望能对你有所帮助🚀。