附录 C. 完整的磁盘镜像
主要的 overcloud 镜像是一个平面分区镜像。这表示镜像本身不包含分区信息或引导加载程序。director 在引导时使用单独的内核和 ramdisk,在将 overcloud 镜像写入磁盘时创建一个基本的分区布局。但是,您可以创建包含分区布局和引导加载程序的完整磁盘镜像。
C.1. 创建完整的磁盘镜像
从 overcloud-full.qcow2 平面分区镜像创建完整的磁盘镜像包含以下步骤:
-
打开
overcloud-full平面分区,这是完整磁盘镜像的基础。 - 按照所需的尺寸大小,创建完整的新磁盘镜像。本例使用 10 GB 镜像。
-
在完整的磁盘镜像上创建分区和卷。根据完整磁盘镜像所需的数量创建分区和卷。本例为
boot创建了一个隔离分区,为文件系统中的其他内容创建了逻辑卷。 - 在分区和卷上创建初始文件系统。
- 安装平面分区文件系统,将内容复制到完整磁盘镜像的适当分区上。
-
生成
fstab内容,并将其保存到完整磁盘镜像的/etc/fstab中。 - 卸载所有文件系统。
-
仅在完整磁盘镜像上挂载分区。先将根分区挂载到
/中,然后将其他分区挂载到相应的目录中。 -
使用 shell 命令安装引导加载程序,以便在完整磁盘镜像上执行
grub2-install和grub2-mkconfig。此操作会将grub2引导加载程序安装到完整磁盘镜像中。 -
更新
dracut,以便支持逻辑卷管理 - 卸载所有文件系统,然后关闭镜像
C.2. 手动创建一个完整的磁盘镜像
建议使用 guestfish 工具创建镜像,运行以下命令可安装此工具:
$ sudo yum install -y guestfish
安装完成后,运行 guestfish 交互式 shell:
$ guestfish
Welcome to guestfish, the guest filesystem shell for
editing virtual machine filesystems and disk images.
Type: 'help' for help on commands
'man' to read the manual
'quit' to quit the shell
><fs>
有关使用 guestfish 的更多信息,请参阅 Red Hat Enterprise Linux 7 虚拟部署和管理指南中的 "The Guestfish Shell"。
C.3. 自动创建一个完整的磁盘镜像
以下的 Python 脚本使用 guestfish 程序库自动生成完整磁盘镜像。
#!/usr/bin/env python
import guestfs
import os
# remove old generated drive
try:
os.unlink("/home/stack/images/overcloud-full-partitioned.qcow2")
except:
pass
g = guestfs.GuestFS(python_return_dict=True)
# import old and new images
print("Creating new repartitioned image")
g.add_drive_opts("/home/stack/images/overcloud-full.qcow2", format="qcow2", readonly=1)
g.disk_create("/home/stack/images/overcloud-full-partitioned.qcow2", "qcow2", 10.2 * 1024 * 1024 * 1024) #10.2G
g.add_drive_opts("/home/stack/images/overcloud-full-partitioned.qcow2", format="qcow2", readonly=0)
g.launch()
# create the partitions for new image
print("Creating the initial partitions")
g.part_init("/dev/sdb", "mbr")
g.part_add("/dev/sdb", "primary", 2048, 616448)
g.part_add("/dev/sdb", "primary", 616449, -1)
g.pvcreate("/dev/sdb2")
g.vgcreate("vg", ['/dev/sdb2', ])
g.lvcreate("var", "vg", 5 * 1024)
g.lvcreate("tmp", "vg", 500)
g.lvcreate("swap", "vg", 250)
g.lvcreate("home", "vg", 100)
g.lvcreate("root", "vg", 4 * 1024)
g.part_set_bootable("/dev/sdb", 1, True)
# add filesystems to volumes
print("Adding filesystems")
ids = {}
keys = [ 'var', 'tmp', 'swap', 'home', 'root' ]
volumes = ['/dev/vg/var', '/dev/vg/tmp', '/dev/vg/swap', '/dev/vg/home', '/dev/vg/root']
swap_volume = volumes[2]
count = 0
for volume in volumes:
if count!=2:
g.mkfs('ext4', volume)
ids[keys[count]] = g.vfs_uuid(volume)
count +=1
# create filesystem on boot and swap
g.mkfs('ext4', '/dev/sdb1')
g.mkswap_opts(volumes[2])
ids['swap'] = g.vfs_uuid(volumes[2])
# mount drives and copy content
print("Start copying content")
g.mkmountpoint('/old')
g.mkmountpoint('/root')
g.mkmountpoint('/boot')
g.mkmountpoint('/home')
g.mkmountpoint('/var')
g.mount('/dev/sda', '/old')
g.mount('/dev/sdb1', '/boot')
g.mount(volumes[4], '/root')
g.mount(volumes[3], '/home')
g.mount(volumes[0], '/var')
# copy content to root
results = g.ls('/old/')
for result in results:
if result not in ('boot', 'home', 'tmp', 'var'):
print("Copying %s to root" % result)
g.cp_a('/old/%s' % result, '/root/')
# copy extra content
folders_to_copy = ['boot', 'home', 'var']
for folder in folders_to_copy:
results = g.ls('/old/%s/' % folder)
for result in results:
print("Copying %s to %s" % (result, folder))
g.cp_a('/old/%s/%s' % (folder, result),
'/%s/' % folder)
# create /etc/fstab file
print("Generating fstab content")
fstab_content = """
UUID={boot_id} /boot ext4 defaults 0 2
UUID={root_id} / ext4 defaults 0 1
UUID={swap_id} none swap sw 0 0
UUID={tmp_id} /tmp ext4 defaults 0 2
UUID={home_id} /home ext4 defaults 0 2
UUID={var_id} /var ext4 defaults 0 2
""".format(
boot_id=g.vfs_uuid('/dev/sdb1'),
root_id=ids['root'],
swap_id=ids['swap'],
tmp_id=ids['tmp'],
home_id=ids['home'],
var_id=ids['var'])
g.write('/root/etc/fstab', fstab_content)
# unmount filesystems
g.umount('/root')
g.umount('/boot')
g.umount('/old')
g.umount('/var')
# mount in the right directories to install bootloader
print("Installing bootloader")
g.mount(volumes[4], '/')
g.mkdir('/boot')
g.mkdir('/var')
g.mount('/dev/sdb1', '/boot')
g.mount(volumes[0], '/var')
# do a selinux relabel
g.selinux_relabel('/etc/selinux/targeted/contexts/files/file_contexts', '/', force=True)
g.selinux_relabel('/etc/selinux/targeted/contexts/files/file_contexts', '/var', force=True)
g.sh('grub2-install --target=i386-pc /dev/sdb')
g.sh('grub2-mkconfig -o /boot/grub2/grub.cfg')
# create dracut.conf file
dracut_content = """
add_dracutmodules+="lvm crypt"
"""
g.write('/etc/dracut.conf', dracut_content)
# update initramfs to include lvm and crypt
kernels = g.ls('/lib/modules')
for kernel in kernels:
print("Updating dracut to include modules in kernel %s" % kernel)
g.sh('dracut -f /boot/initramfs-%s.img %s --force' % (kernel, kernel))
g.umount('/boot')
g.umount('/var')
g.umount('/')
# close images
print("Finishing image")
g.shutdown()
g.close()
将此脚本作为可执行文件保存到 undercloud,然后以 stack 用户的身份运行该文件。
$ ./whole-disk-image.py
此操作会从平面分区镜像自动创建完整的磁盘镜像。完整的磁盘镜像创建完成之后,替换旧的 overcloud-full.qcow2 镜像:
$ mv ~/images/overcloud-full.qcow2 ~/images/overcloud-full-old.qcow2 $ cp ~/images/overcloud-full-partitioned.qcow2 ~/images/overcloud-full.qcow2
现在,您可以同时上传完整的磁盘镜像和其他镜像。
C.4. 在完整的磁盘镜像上对卷进行加密
您也可以使用 guestfish 在完整的磁盘镜像上对卷进行加密。此操作需要使用 luks-format 子命令来擦除现有的卷并创建一个加密卷。
以下 Python 脚本是 第 C.3 节 “自动创建一个完整的磁盘镜像” 中脚本的修改版。新脚本中加密了 home 卷:
#!/usr/bin/env python
import binascii
import guestfs
import os
# remove old generated drive
try:
os.unlink("/tmp/overcloud-full-partitioned.qcow2")
except:
pass
g = guestfs.GuestFS(python_return_dict=True)
# import old and new images
print("Creating new repartitioned image")
g.add_drive_opts("/tmp/overcloud-full.qcow2", format="qcow2", readonly=1)
g.disk_create("/tmp/overcloud-full-partitioned.qcow2", "qcow2", 10 * 1024 * 1024 * 1024) #10G
g.add_drive_opts("/tmp/overcloud-full-partitioned.qcow2", format="qcow2", readonly=0)
g.launch()
# create the partitions for new image
print("Creating the initial partitions")
g.part_init("/dev/sdb", "mbr")
g.part_add("/dev/sdb", "primary", 2048, 616448)
g.part_add("/dev/sdb", "primary", 616449, -1)
g.pvcreate("/dev/sdb2")
g.vgcreate("vg", ['/dev/sdb2', ])
g.lvcreate("var", "vg", 4400)
g.lvcreate("tmp", "vg", 500)
g.lvcreate("swap", "vg", 250)
g.lvcreate("home", "vg", 100)
g.lvcreate("root", "vg", 4000)
g.part_set_bootable("/dev/sdb", 1, True)
# encrypt home partition and write keys
print("Encrypting volume")
random_content = binascii.b2a_hex(os.urandom(1024))
g.luks_format('/dev/vg/home', random_content, 0)
# open the encrypted volume
volumes = ['/dev/vg/var', '/dev/vg/tmp', '/dev/vg/swap', '/dev/mapper/cryptedhome', '/dev/vg/root']
g.luks_open('/dev/vg/home', random_content, 'cryptedhome')
g.vgscan()
g.vg_activate_all(True)
# add filesystems to volumes
print("Adding filesystems")
ids = {}
keys = [ 'var', 'tmp', 'swap', 'home', 'root' ]
swap_volume = volumes[2]
count = 0
for volume in volumes:
if count!=2:
g.mkfs('ext4', volume)
if keys[count] == 'home':
ids['home'] = g.vfs_uuid('/dev/vg/home')
else:
ids[keys[count]] = g.vfs_uuid(volume)
count +=1
# create filesystem on boot and swap
g.mkfs('ext4', '/dev/sdb1')
g.mkswap_opts(volumes[2])
ids['swap'] = g.vfs_uuid(volumes[2])
# mount drives and copy content
print("Start copying content")
g.mkmountpoint('/old')
g.mkmountpoint('/root')
g.mkmountpoint('/boot')
g.mkmountpoint('/home')
g.mkmountpoint('/var')
g.mount('/dev/sda', '/old')
g.mount('/dev/sdb1', '/boot')
g.mount(volumes[4], '/root')
g.mount(volumes[3], '/home')
g.mount(volumes[0], '/var')
# copy content to root
results = g.ls('/old/')
for result in results:
if result not in ('boot', 'home', 'tmp', 'var'):
print("Copying %s to root" % result)
g.cp_a('/old/%s' % result, '/root/')
# copy extra content
folders_to_copy = ['boot', 'home', 'var']
for folder in folders_to_copy:
results = g.ls('/old/%s/' % folder)
for result in results:
print("Copying %s to %s" % (result, folder))
g.cp_a('/old/%s/%s' % (folder, result),
'/%s/' % folder)
# write keyfile for encrypted volume
g.write('/root/root/home_keyfile', random_content)
g.chmod(0400, '/root/root/home_keyfile')
# generate mapper for encrypted home
mapper = """
home UUID={home_id} /root/home_keyfile
""".format(home_id=ids['home'])
g.write('/root/etc/crypttab', mapper)
# create /etc/fstab file
print("Generating fstab content")
fstab_content = """
UUID={boot_id} /boot ext4 defaults 1 2
UUID={root_id} / ext4 defaults 1 1
UUID={swap_id} none swap sw 0 0
UUID={tmp_id} /tmp ext4 defaults 1 2
UUID={var_id} /var ext4 defaults 1 2
/dev/mapper/home /home ext4 defaults 1 2
""".format(
boot_id=g.vfs_uuid('/dev/sdb1'),
root_id=ids['root'],
swap_id=ids['swap'],
tmp_id=ids['tmp'],
home_id=ids['home'],
var_id=ids['var'])
g.write('/root/etc/fstab', fstab_content)
# umount filesystems
g.umount('/root')
g.umount('/boot')
g.umount('/old')
g.umount('/var')
g.umount('/home')
# close encrypted volume
g.luks_close('/dev/mapper/cryptedhome')
# mount in the right directories to install bootloader
print("Installing bootloader")
g.mount(volumes[4], '/')
g.mkdir('/boot')
g.mkdir('/var')
g.mount('/dev/sdb1', '/boot')
g.mount(volumes[0], '/var')
# add rd.auto=1 on grub parameters
g.sh('sed -i "s/.*GRUB_CMDLINE_LINUX.*/GRUB_CMDLINE_LINUX=\\"console=tty0 crashkernel=auto rd.auto=1\\"/" /etc/default/grub')
g.sh('grub2-install --target=i386-pc /dev/sdb')
g.sh('grub2-mkconfig -o /boot/grub2/grub.cfg')
# create dracut.conf file
dracut_content = """
add_dracutmodules+="lvm crypt"
"""
g.write('/etc/dracut.conf', dracut_content)
# update initramfs to include lvm and crypt
kernels = g.ls('/lib/modules')
for kernel in kernels:
print("Updating dracut to include modules in kernel %s" % kernel)
g.sh('dracut -f /boot/initramfs-%s.img %s --force' % (kernel, kernel))
# do a selinux relabel
g.selinux_relabel('/etc/selinux/targeted/contexts/files/file_contexts', '/', force=True)
g.selinux_relabel('/etc/selinux/targeted/contexts/files/file_contexts', '/var', force=True)
g.umount('/boot')
g.umount('/var')
g.umount('/')
# close images
print("Finishing image")
g.shutdown()
g.close()此脚本也可以:
-
创建密钥 (
random_content) -
在
/root/home_keyfile中保存加密密钥 -
生成一个
crypttab文件,以便使用/root/home_keyfile) 自动解密卷
以此脚本为例,创建加密卷,这是完整的磁盘镜像创建流程的组成部分。
C.5. 上传完整的磁盘镜像
要上传完整的磁盘镜像,请使用镜像上传命令的 --whole-disk-image 选项。例如:
$ openstack overcloud image upload --whole-disk --image-path /home/stack/images
此命令从 /home/stack/images 上传镜像,但将 overcloud-full.qcow2 文件视为完整的磁盘镜像。这表示,在运行磁盘上传命令之前,您必须先将所需的完整磁盘镜像重命名为 overcloud-full.qcow2。

Where did the comment section go?
Red Hat's documentation publication system recently went through an upgrade to enable speedier, more mobile-friendly content. We decided to re-evaluate our commenting platform to ensure that it meets your expectations and serves as an optimal feedback mechanism. During this redesign, we invite your input on providing feedback on Red Hat documentation via the discussion platform.