부록 C. 전체 디스크 이미지
기본 오버클라우드 이미지는 플랫 파티션 이미지입니다. 즉, 이미지 자체에 파티셔닝 정보 또는 부트로더가 포함되어 있지 않습니다. director는 부팅할 때 별도의 커널 및 램디스크를 사용하고, 오버클라우드 이미지를 디스크에 쓸 때 기본 파티셔닝 레이아웃을 생성합니다. 하지만 파티셔닝 레이아웃 및 부트로더를 포함한 전체 디스크 이미지를 생성할 수 있습니다.
C.1. 전체 디스크 이미지 생성
overcloud-full.qcow2 플랫 파티션 이미지에서 전체 디스크 이미지를 생성하는 작업에는 다음 단계가 포함됩니다.
-
전체 디스크 이미지를 위해
overcloud-full플랫 파티션을 기본 파티션으로 엽니다. - 새 전체 디스크 이미지를 원하는 크기로 생성합니다. 이 예에서는 10GB 이미지를 사용합니다.
-
전체 디스크 이미지에서 파티션 및 볼륨을 생성합니다. 원하는 전체 디스크 이미지에 필요한 만큼 파티션과 볼륨을 생성합니다. 이 예에서는
boot에 대해 격리된 파티션을 생성하고 파일 시스템의 다른 컨텐츠에 대해 논리적 볼륨을 생성합니다. - 파티션 및 볼륨에 초기 파일 시스템을 생성합니다.
- 플랫 파티션 파일 시스템을 마운트하고 컨텐츠를 전체 디스크 이미지의 올바른 파티션에 복사합니다.
-
fstab컨텐츠를 생성하여 전체 디스크 이미지의/etc/fstab에 저장합니다. - 모든 파일 시스템을 마운트 해제합니다.
-
전체 디스크 이미지에서만 파티션을 마운트합니다.
/에서 마운트된 root 파티션으로 시작하고 해당 디렉터리에 다른 파티션을 마운트합니다. -
쉘 명령을 사용해 부트로더를 설치하여
grub2-install및grub2-mkconfig를 전체 디스크 이미지에서 실행합니다. 이렇게 하면grub2부트로더가 전체 디스크 이미지에 설치됩니다. -
dracut를 업데이트하여 논리 볼륨 관리 지원을 추가합니다. - 모든 파일 시스템을 마운트 해제하고 이미지를 종료합니다.
C.2. 전체 디스크 이미지를 수동으로 생성
이미지 생성에 권장되는 툴은 다음 명령을 사용하여 설치하는 guestfish입니다.
$ sudo yum install -y guestfish
설치되면 guestfish 대화형 쉘을 실행합니다.
$ 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에 대한 가상화 배포 및 관리 가이드 의 " Guestfish 쉘"을 참조하십시오 .
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()
이 스크립트를 언더클라우드에 실행 파일로 저장하고 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에 암호화 키 저장 -
/root/home_keyfile을 사용하여 자동으로 볼륨 암호를 해독할crypttab파일 생성
이 스크립트를 예로 사용하여 전체 디스크 이미지 생성 프로세스의 일부로 암호화된 볼륨을 생성하십시오.
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.