부록 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로 바꾼 후에 이미지 업로드 명령을 실행해야 합니다.