この記事はセキュリティキャンプアドベントカレンダー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は仮想的なパーティションと考えることができる。
データを移行してみる
前提
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氏による " :-) " です。お楽しみに!