用 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-format | container-format | 说明 |
---|---|---|---|
.vmlinuz | aki | aki | UUID 对应到 kernel_id |
.initrd | ari | ari | UUID 对应到 ramdisk_id |
.qcow2 | qcow2 | bare | 指定 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
如果本文对你有帮助,请 点赞、分享、关注,谢谢!