Edited at

ディスク間データ移行で学ぶ LVM/ブートローダ/ファイルシステム

この記事はセキュリティキャンプアドベントカレンダー16日目(12/16)の記事になります。

私はセキュリティキャンプ全国大会2017の参加者で、全国大会修了後は、今年の全国大会や、ミニキャンプのチューターを務めさせていただきました。現在進行形でSecHack365にて、Exgdbというgdbの拡張プラグインを開発しています。

また、セキュリティキャンプ繋がりと言えば、セキュリティキャンプの講師も多く在籍されている、サイボウズ・ラボという企業での長期アルバイト/インターンも始めました。

そこではなかなか独学では踏めないような地雷をたくさん踏ませていただいておりまして、お陰様でレイヤー低めのワクワクするような知識をたくさん学ばせていただいている次第で、非常に大変最高というお気持ちです。

ここでは、「ディスク間のデータ移行をしなければならない」という、その踏ませていただいた地雷のうちの一つを解決していきながら、LVMやブートローダ、ファイルシステムなどを軽く解説していこうと思います。


目的

ディスク間のデータ移行を行う。

また、それによって、LVM/ブートローダ(grub)/ファイルシステムを学ぶ。


環境

Ubuntu16


単語集


MBR

Master Boot Recordの略。

ディスクの一番先頭のセクタのこと。パーティション情報などが書かれている。

PCが起動した時に一番最初に読み込まれる。

ここにブートローダの一部が置かれている。


ブートローダ

コンピュータの電源投入後最初に実行され、システムソフトウェアを主記憶に展開し、そこに制御を移すプログラムのこと。今回はgrubを使用。

grubは、/boot以下にあるブート用バイナリを読み込んで実行する。

grubと/bootの紐づけは後述。


PV・VG・LV

それぞれPhysical Volume、Volume Group、Logical Volumeの略である。

PVとは、LVMのレイヤーにおける一番下の単位。

そのいくつかのPVを一まとめにしたものがVGで、仮想的なディスクに相当する。

VGを仮想的なディスクとするならば、LVは仮想的なパーティションと考えることができる。

(参考URL)


データを移行してみる


前提

sdaとsdbという二つのディスクがあるとする。

sdaはsda1,sda2という二つのパーティションに分けられており、sda1は/bootにマウントされている。sda2はubuntu-vgというVGに所属し、ubuntu-vgにはrootとswapという2つのLVがある。rootは/にマウントされている。

ここで、sda上にあるデータを全てsdbに移行してみよう。

$ lsblk

NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:32 0 64G 0 disk
sda 8:0 0 64G 0 disk
├─sda2 8:2 0 18.4G 0 part
│ └─ubuntu--vg-root 253:0 0 15.8G 0 lvm /
│ └─ubuntu--vg-swap_1 253:2 0 10G 0 lvm [SWAP]
└─sda1 8:1 0 243M 0 part /boot


sdaと同じ形のsdbを作成

最終目的

$ lsblk

NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:32 0 64G 0 disk
├─sdb2 8:34 0 58.7G 0 part
│ ├─new--vg-root 253:1 0 48G 0 lvm /mnt/new_root
│ └─new--vg-swap_1 253:2 0 10G 0 lvm [SWAP]
└─sdb1 8:33 0 954M 0 part /mnt/new_root/boot
sda 8:0 0 64G 0 disk
├─sda2 8:2 0 18.4G 0 part
│ └─ubuntu--vg-root 253:0 0 15.8G 0 lvm /
│ └─ubuntu--vg-swap_1 253:2 0 10G 0 lvm [SWAP]
└─sda1 8:1 0 243M 0 part /boot

この形をこれから作っていく。

パーティショニング

$ sudo fdisk /dev/sdb

Command (m for help): n
Select (default p): p
Partition number (1-4, default 1): 1
First sector (2048-134217727, default 2048):
Last sector, +sectors or +size{K,M,G,T,P} (2048-134217727, default 134217727): +1GB
Command (m for help): n
Select (default p): p
Partition number (2-4, default 2): 2
First sector (1955840-134217727, default 1955840):
Last sector, +sectors or +size{K,M,G,T,P} (1955840-134217727, default 134217727): +63GB
Command (m for help): w

確認

$ sudo lsblk

NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:32 0 64G 0 disk
├─sdb2 8:34 0 58.7G 0 part
└─sdb1 8:33 0 954M 0 part
sda 8:0 0 64G 0 disk
├─sda2 8:2 0 18.4G 0 part
│ ├─ubuntu--vg-root 253:0 0 15.8G 0 lvm /
│ └─ubuntu--swap_1 253:0 0 15.8G 0 lvm [SWAP]
└─sda1 8:1 0 243M 0 part /boot

PV作成

現在のPVを確認

$ sudo pvs

PV VG Fmt Attr PSize PFree
/dev/sda2 ubuntu-vg lvm2 a-- 31.76g 16.00g

PVを作成

$ sudo pvcreate /dev/sdb2

Physical volume "/dev/sdb2" successfully created

ここで、sdb1はPV化してはいけない。

PVというのは飽くまでLVMの中だけでの概念であり、/bootはLVM化しないからだ。

確認

$ sudo pvs

PV VG Fmt Attr PSize PFree
/dev/sda2 ubuntu-vg lvm2 a-- 31.76g 16.00g
/dev/sdb2 lvm2 --- 58.67g 58.67g

VG作成

現在のVGを確認

$ sudo vgs

VG #PV #LV #SN Attr VSize VFree
ubuntu-vg 1 1 0 wz--n- 31.76g 16.00g

作成

$ sudo vgcreate new-vg /dev/sdb2

確認

$ sudo vgs

VG #PV #LV #SN Attr VSize VFree
new-vg 1 0 0 wz--n- 58.67g 58.67g
ubuntu-vg 1 1 0 wz--n- 31.76g 16.00g

LV作成

現在のLVを確認

$ sudo lvs

LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
root ubuntu-vg -wi-ao---- 15.76g
swap_1 ubuntu-vg -wi-a----- 10.00g

作成

$ sudo lvcreate -n root -L 48g new-vg

Logical volume "root" created.
$ sudo lvcreate -n swap_1 -L 10g new-vg
Logical volume "swap_1" created.

確認

$ sudo lvs

LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
root new-vg -wi-a----- 48.00g
swap_1 new-vg -wi-a----- 10.00g
root ubuntu-vg -wi-ao---- 15.76g
swap_1 ubuntu-vg -wi-a----- 10.00g

$ sudo lsblk

NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:32 0 64G 0 disk
├─sdb2 8:34 0 58.7G 0 part
│ ├─new--vg-root 253:1 0 48G 0 lvm
│ └─new--vg-swap_1 253:2 0 10G 0 lvm
└─sdb1 8:33 0 954M 0 part
sda 8:0 0 64G 0 disk
├─sda2 8:2 0 18.4G 0 part
│ ├─ubuntu--vg-root 253:0 0 15.8G 0 lvm /
│ └─ubuntu--vg-swap_1 253:2 0 10G 0 lvm
└─sda1 8:1 0 243M 0 part /boot

ファイルシステム作成

$ sudo mkfs.ext4 /dev/new-vg/root

mke2fs 1.42.13 (17-May-2015)
Creating filesystem with 12582912 4k blocks and 3145728 inodes
Filesystem UUID: bb0a8ead-dfca-485c-a70a-9c5fd74756da
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
4096000, 7962624, 11239424

Allocating group tables: done
Writing inode tables: done
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done

$ sudo mkfs.ext4 /dev/sdb1

mke2fs 1.42.13 (17-May-2015)
Creating filesystem with 244224 4k blocks and 61056 inodes
Filesystem UUID: 91afa329-9858-4afc-af0d-c13eb8241206
Superblock backups stored on blocks:
32768, 98304, 163840, 229376

Allocating group tables: done
Writing inode tables: done
Creating journal (4096 blocks): done
Writing superblocks and filesystem accounting information: done

マウント

$ sudo mkdir -p /mnt/new_root/boot

$ sudo mount /dev/new-vg/root /mnt/new_root/
$ sudo mount /dev/sdb1 /mnt/new_root/boot
$ sudo rm -rf /mnt/new_root/lost+found /mnt/new_root/boot/lost+found

確認

$ sudo lsblk

NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
├─sdb2 8:34 0 58.7G 0 part
│ ├─new--vg-root 253:1 0 48G 0 lvm /mnt/new_root
│ └─new--vg-swap_1 253:2 0 10G 0 lvm
└─sdb1 8:33 0 954M 0 part /mnt/new_root/boot

SWAP作成

SWAP作成

$ sudo mkswap /dev/new-vg/swap_1

Setting up swapspace version 1, size = 10 GiB (10737414144 bytes)
no label, UUID=5db10089-7be3-4789-9d03-76fb9f79e9a2

SWAP有効化

$ sudo swapon /dev/mapper/new--vg-swap_1

確認

$ lsblk

NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:32 0 64G 0 disk
├─sdb2 8:34 0 58.7G 0 part
│ ├─new--vg-root 253:1 0 48G 0 lvm /mnt/new_root
│ └─new--vg-swap_1 253:2 0 10G 0 lvm [SWAP]
└─sdb1 8:33 0 954M 0 part /mnt/new_root/boot
sda 8:0 0 64G 0 disk
├─sda2 8:2 0 18.4G 0 part
│ └─ubuntu--vg-root 253:0 0 15.8G 0 lvm /
│ └─ubuntu--vg-swap_1 253:2 0 10G 0 lvm [SWAP]
└─sda1 8:1 0 243M 0 part /boot

これで同じ形になった。


データコピー

ubuntu--vg-rootのデータを全てnew--vg-rootにコピーする。

まずは、現在のファイルシステムを確認する。

$ sudo df -ha

Filesystem Size Used Avail Use% Mounted on
sysfs 0 0 0 - /sys
proc 0 0 0 - /proc
udev 7.9G 0 7.9G 0% /dev
devpts 0 0 0 - /dev/pts
tmpfs 1.6G 8.7M 1.6G 1% /run
/dev/mapper/ubuntu--vg-root 16G 14G 881M 95% /
securityfs 0 0 0 - /sys/kernel/security
tmpfs 7.9G 0 7.9G 0% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
tmpfs 7.9G 0 7.9G 0% /sys/fs/cgroup
cgroup 0 0 0 - /sys/fs/cgroup/systemd
pstore 0 0 0 - /sys/fs/pstore
cgroup 0 0 0 - /sys/fs/cgroup/blkio
cgroup 0 0 0 - /sys/fs/cgroup/pids
cgroup 0 0 0 - /sys/fs/cgroup/hugetlb
cgroup 0 0 0 - /sys/fs/cgroup/freezer
cgroup 0 0 0 - /sys/fs/cgroup/net_cls,net_prio
cgroup 0 0 0 - /sys/fs/cgroup/cpu,cpuacct
cgroup 0 0 0 - /sys/fs/cgroup/cpuset
cgroup 0 0 0 - /sys/fs/cgroup/devices
cgroup 0 0 0 - /sys/fs/cgroup/rdma
cgroup 0 0 0 - /sys/fs/cgroup/perf_event
cgroup 0 0 0 - /sys/fs/cgroup/memory
systemd-1 - - - - /proc/sys/fs/binfmt_misc
mqueue 0 0 0 - /dev/mqueue
debugfs 0 0 0 - /sys/kernel/debug
hugetlbfs 0 0 0 - /dev/hugepages
configfs 0 0 0 - /sys/kernel/config
fusectl 0 0 0 - /sys/fs/fuse/connections
/dev/sda1 236M 147M 77M 66% /boot
binfmt_misc 0 0 0 - /proc/sys/fs/binfmt_misc
cgmfs 100K 0 100K 0% /run/cgmanager/fs
/dev/mapper/new--vg-root 48G 52M 45G 1% /mnt/new_root
/dev/sdb1 923M 1.2M 859M 1% /mnt/new_root/boot
tmpfs 1.6G 0 1.6G 0% /run/user/1002

ここから、

・/sys

・/proc

・/dev

・/run

・/mnt

はコピーしてはいけないディレクトリであることがわかる。

それらを除いた/以下のディレクトリ一覧は以下になる。

$ for f in /!(sys|proc|dev|run|mnt) ;do echo $f ;done

/bin
/etc
/home
/initrd.img
/initrd.img.old
/lib
/lib32
/lib64
/libx32
/lost+found
/media
/metadiff-manager-debug.log
/opt
/root
/sbin
/srv
/tmp
/usr
/var
/vmlinuz
/vmlinuz.old

これらをコピーする。

$ for f in /!(sys|proc|dev|run|mnt) ;do sudo rsync -a $f /mnt/new_root/ ;done

確認

$ ls /mnt/new_root/

bin boot etc home initrd.img initrd.img.old lib lib32 lib64 libx32 lost+found media metadiff-manager-debug.log opt root sbin srv tmp usr var vmlinuz vmlinuz.old


コピーしなかったディレクトリの新規作成, バインドマウント

コピーしなかった各種ディレクトリは、ディレクトリ作成&permission設定をした後、バインドマウントする。

ディレクトリ作成

$ sudo mkdir /mnt/new_root/{sys,proc,dev,run,mnt}

permission確認

$ stat /{sys,proc,dev,run,mnt} -c '%a'

555
555
755
755
755

permission設定

$ for f in /{sys,proc,dev,run,mnt} ;do sudo chmod $(stat $f -c '%a') /mnt/new_root$f ;done

確認

$ ls /mnt/new_root/

bin boot dev etc home initrd.img initrd.img.old lib lib32 lib64 libx32 lost+found media metadiff-manager-debug.log mnt opt proc root run sbin srv sys tmp usr var vmlinuz vmlinuz.old
$ stat /mnt/new_root/{sys,proc,dev,run,mnt} -c '%a'
555
555
755
755
755

バインドマウントする。

バインドマウントとは、一つのブロックデバイスを複数のパスに同時にマウントすること。

$ for f in /{sys,proc,dev,run,mnt} ;do sudo mount --bind $f /mnt/new_root$f ;done


ブートローダをMBRに登録

chrootする。

chrootについてはこちらを参考:https://qiita.com/miyagaw61/items/2ec5b5703ccd525e7ced

$ sudo chroot /mnt/new_root/

#

chroot環境でのブロックデバイスの情報を確認する。

# lsblk

NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 16G 0 disk
sr0 11:0 1 1024M 0 rom
fd0 2:0 1 4K 0 disk
sdb 8:32 0 64G 0 disk
├─sdb2 8:34 0 58.7G 0 part
│ ├─new--vg-root 253:1 0 48G 0 lvm /
│ └─new--vg-swap_1 253:2 0 10G 0 lvm [SWAP]
└─sdb1 8:33 0 954M 0 part /boot
sda 8:0 0 64G 0 disk
├─sda2 8:2 0 18.4G 0 part
│ ├─ubuntu--vg-root 253:0 0 15.8G 0 lvm /mnt
│ └─ubuntu--vg-swap_1 253:2 0 10G 0 lvm
└─sda1 8:1 0 243M 0 part

MBRにブートローダ(grub)をインストールする。

MBRがあるのはディスクの先頭なので、引数にはディスクを与える。

今回はsdbにgrubをインストールしたいので、引数は/dev/sdbになる。

# grub-install /dev/sdb

Installing for i386-pc platform.
Installation finished. No error reported.

次に、update-grubする。

# update-grub

Generating grub configuration file ...
Found linux image: /boot/vmlinuz-4.15.0-24-generic
Found initrd image: /boot/initrd.img-4.15.0-24-generic
Found linux image: /boot/vmlinuz-4.13.0-45-generic
Found initrd image: /boot/initrd.img-4.13.0-45-generic
Found linux image: /boot/vmlinuz-4.4.0-130-generic
Found initrd image: /boot/initrd.img-4.4.0-130-generic
Found memtest86+ image: /boot/memtest86+.elf
Found memtest86+ image: /boot/memtest86+.bin
done

これでsdbのMBRにブートローダ(grub)が入った。

chroot環境から抜ける。

# exit

exit
$


fstabの編集

/bootにマウントしているブロックデバイスのUUIDを確認

$ sudo blkid /dev/sda1

/dev/sda1: UUID="91afa329-9858-4afc-af0d-c13eb8241206" TYPE="ext4" PARTUUID="9c35cd3f-01"

現在のfstabを確認

$ cat /etc/fstab

# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point> <type> <options> <dump> <pass>

/dev/mapper/ubuntu--vg-root / ext4 errors=remount-ro 0 1
# /boot was on /dev/sda1 during installation
UUID=1a8a2a9c-eb35-40a9-b813-46bdeed89fc2 /boot ext2 defaults 0 2
/dev/mapper/ubuntu--vg-swap_1 none swap sw 0 0
/dev/fd0 /media/floppy0 auto rw,user,noauto,exec,utf8 0 0

編集

$ sudo vim /etc/fstab

$ cat /etc/fstab
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point> <type> <options> <dump> <pass>

#/dev/mapper/ubuntu--vg-root / ext4 errors=remount-ro 0 1
/dev/mapper/new--vg-root / ext4 errors=remount-ro 0 1
# /boot was on /dev/sda1 during installation
#UUID=1a8a2a9c-eb35-40a9-b813-46bdeed89fc2 /boot ext2 defaults 0 2
UUID=91afa329-9858-4afc-af0d-c13eb8241206 /boot ext4 defaults 0 2
#/dev/mapper/ubuntu--vg-swap_1 none swap sw 0 0
/dev/mapper/new--vg-swap_1 none swap sw 0 0
/dev/fd0 /media/floppy0 auto rw,user,noauto,exec,utf8 0 0

fstabを反映

$ sudo mount -a

ここでエラーが出たら適宜直す。

そしてシャットダウン。

$ sudo shutdown -h now


ディスクを入れ替え、起動時に読ませるMBRを変える

単純に順番を入れ替えるだけで良い。

ESXiなどであれば、仮想デバイスノードの順番を入れ替える。

そして、現在のsdbのディスクを一番上にもってくると、sdaとして認識される。


起動

データの移行が成功していることが確認できる。


最後に

以上で、「ディスク間データ移行」は完了になります。

初めて聞く言葉、初めて打つコマンドがたくさんあって、当時はとても苦労しました。

この周辺の知識を学ぼうとしている方や同じような地雷を踏んだ方の力になれたら幸いです。

時間が無くて今回は書けませんでしたが、カーネルモジュールとLinuxカーネルを読んでブロックデバイスを学ぶ編やカーネルデバッグでバグを見つけよう編の記事もいつか書きたいと思っています。

近々、CybozuInsideの方でも私の記事が発表されると思いますので、良ければそちらも読んでいただけると泣いて喜びます。

さて、次のアドベントカレンダーは12/17のlmt_swallow氏による " :-) " です。お楽しみに!