附录 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,以便支持逻辑卷管理 - 卸载所有文件系统,然后关闭镜像
手动创建一个完整的磁盘镜像
建议使用 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.2. 自动创建一个完整的磁盘镜像
以下 Python 脚本使用 guestfish 库来自动编写镜像创建脚本。该脚本根据 logical_volumes (Python 格式的词典)中的信息创建一系列卷。该词典中的键表示待创建的卷,它的值表示卷的大小。
#!/usr/bin/env python
import guestfs
import os
logical_volumes = {
'root': 4 * 1024,
'swap': 250,
'home': 500,
'tmp': 250,
'var': 5 * 1024
}
# 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', 12 * 1024 * 1024 * 1024)
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', ])
volume_names = []
for volume,size in logical_volumes.iteritems():
g.lvcreate(volume, 'vg', size)
volume_names.append(volume)
volume_names.append('boot')
g.part_set_bootable('/dev/sdb', 1, True)
g.mkmountpoint('/old')
g.mount('/dev/sda', '/old')
for volume_name in volume_names:
# add filesystems to volumes
print('Adding filesystems to %s' % volume_name)
if volume_name == 'boot':
g.mkfs('ext4', '/dev/sdb1')
elif volume_name == 'swap':
g.mkswap_opts('/dev/vg/%s' % volume_name)
else:
g.mkfs('ext4', '/dev/vg/%s' % volume_name)
# Mount the volume and copy files
if volume_name not in ['tmp', 'swap']:
print('Mounting %s' % volume_name)
g.mkmountpoint('/%s' % volume_name)
if volume_name == 'boot':
g.mount('/dev/sdb1', '/%s' % volume_name)
else:
g.mount('/dev/vg/%s' % volume_name, '/%s' % volume_name)
print('Copying files to %s' % volume_name)
if volume_name == 'root':
results = [filename for filename in g.ls('/old/') if filename not in volume_names]
results.append ('root')
for result in results:
print('- Copying %s to root' % result)
g.cp_a('/old/%s' % result, '/root/')
else:
results = g.ls('/old/%s/' % volume_name)
if results == []:
print("- No files found")
else:
for result in results:
print('- Copying %s to %s' % (result, volume_name))
g.cp_a('/old/%s/%s' % (volume_name, result), '/%s/' % volume_name)
g.umount('/%s' % volume_name)
g.umount('/old')
g.mount('/dev/vg/root', '/')
for volume_name in volume_names:
if volume_name not in ['root','swap']:
g.mkdir('/%s' % volume_name)
if volume_name == 'boot':
g.mount('/dev/sdb1', '/%s' % volume_name)
else:
g.mount('/dev/vg/%s' % volume_name, '/%s' % volume_name)
# create /etc/fstab file
print('Generating fstab content:')
fstab_content = []
fstab_content.append('UUID=%s /boot ext4 defaults 0 2' % g.vfs_uuid('/dev/sdb1'))
fstab_content.append('UUID=%s / ext4 defaults 0 1' % g.vfs_uuid('/dev/vg/root'))
fstab_content.append('UUID=%s none swap sw 0 0' % g.vfs_uuid('/dev/vg/swap'))
for volume_name in volume_names:
if volume_name not in ['boot', 'root', 'swap']:
fstab_content.append('UUID=%s /%s ext4 defaults 0 2' % (g.vfs_uuid('/dev/vg/%s' % volume_name), volume_name))
g.write('/etc/fstab', '\n'.join(fstab_content))
print '\n'.join(fstab_content)
# Generate bootloader
print('Installing bootloader')
g.sh('grub2-install --target=i386-pc /dev/sdb')
g.sh('grub2-mkconfig -o /boot/grub2/grub.cfg')
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))
for volume_name in volume_names:
if volume_name not in ['swap', 'root']:
g.umount("/%s" % volume_name)
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.qcow $ cp ~/images/overcloud-full-partitioned.qcow2 ~/images/overcloud-full.qcow
现在,您可以同时上传完整的磁盘镜像和其他镜像。
C.3. 在完整的磁盘镜像上对卷进行加密
您也可以使用 guestfish 在完整的磁盘镜像上对卷进行加密。此操作需要使用 luks-format 子命令来擦除现有的卷并创建一个加密卷。
以下 Python 脚本可用于打开之前创建的 overcloud-full-partitioned.qcow2 镜像,移除当前的 home 卷(为空),并使用加密的 home 卷代替:
#!/usr/bin/env python
import binascii
import guestfs
g = guestfs.GuestFS(python_return_dict=True)
g.add_drive_opts("/home/stack/images/overcloud-full-partitioned.qcow2", format="qcow2", readonly=0)
g.launch()
random_content = binascii.b2a_hex(os.urandom(1024))
g.luks_format('/dev/vg/home', random_content, 0)
g.luks_open('/dev/vg/home', random_content, 'cryptedhome')
g.vgscan()
g.vg_activate_all(True)
g.mkfs('ext4', '/dev/mapper/cryptedhome')
g.mount('/dev/vg/root','/')
volumes = lvs()
volumes.remove('/dev/vg/home')
volumes.remove('/dev/vg/root')
volumes.remove('/dev/vg/swap')
fstab_content = []
fstab_content.append('UUID=%s /boot ext4 defaults 0 2' % g.vfs_uuid('/dev/sda1'))
fstab_content.append('UUID=%s / ext4 defaults 0 1' % g.vfs_uuid('/dev/vg/root'))
fstab_content.append('UUID=%s none swap sw 0 0' % g.vfs_uuid('/dev/vg/swap'))
fstab_content.append('/dev/mapper/cryptedhome /home ext4 defaults 0 1')
for volume in volumes:
volume_name = volume.replace('/dev/vg/', '')
fstab_content.append('UUID=%s /%s ext4 defaults 0 2' % (g.vfs_uuid(volume), volume_name))
g.write('/etc/fstab', '\n'.join(fstab_content))
print '\n'.join(fstab_content)
g.write('/root/home_keyfile', random_content)
g.chmod(0400, '/root/home_keyfile')
mapper = """
home UUID={home_id} /root/home_keyfile
""".format(home_id=g.vfs_uuid('/dev/mapper/cryptedhome'))
g.write('/etc/crypttab', mapper)
g.luks_close('/dev/mapper/cryptedhome')
g.selinux_relabel('/etc/selinux/targeted/contexts/files/file_contexts', '/', force=True)
g.shutdown()
g.close()此脚本也可以:
-
创建密钥 (
random_content) -
使用新加密卷重新生成
/etc/fstab文件 -
在
/root/home_keyfile中保存加密密钥 -
生成一个
crypttab文件,以便使用/root/home_keyfile) 自动解密卷
以此脚本为例,创建加密卷,这是完整的磁盘镜像创建流程的组成部分。
C.4. 上传完整的磁盘镜像
要上传完整的磁盘镜像,请使用镜像上传命令的 --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.