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

この記事は[セキュリティキャンプアドベントカレンダー](https://adventar.org/calendars/3191)16日目(12/16)の記事になります。

私はセキュリティキャンプ全国大会2017の参加者で、全国大会修了後は、今年の全国大会や、ミニキャンプのチューターを務めさせていただきました。現在進行形でSecHack365にて、[Exgdb](https://github.com/miyase256/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)](https://users.miraclelinux.com/technet/document/linux/training/2_2_3.html)

## データを移行してみる

### 前提

sdaとsdbという二つのディスクがあるとする。
sdaはsda1,sda2という二つのパーティションに分けられており、sda1は/bootにマウントされている。sda2はubuntu-vgというVGに所属し、ubuntu-vgにはrootとswapという2つのLVがある。rootは/にマウントされている。
ここで、sda上にあるデータを全てsdbに移行してみよう。

```Bash
$ 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を作成

**最終目的**

```Bash
$ 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
```

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

**パーティショニング**

```Bash
$ 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
```

確認

```Bash
$ 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を確認

```Bash
$ sudo pvs
  PV         VG          Fmt  Attr PSize  PFree
  /dev/sda2  ubuntu-vg   lvm2 a--  31.76g 16.00g
```

PVを作成

```Bash
$ 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
```

作成

```Bash
$ 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を確認

```Bash
$ 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
```

作成

```Bash
$ 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.
```

確認

```Bash
$ 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
```

```Bash
$ 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
```

**ファイルシステム作成**

```Bash
$ 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
```

```Bash
$ 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
```

**マウント**

```Bash
$ 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
```

確認

```Bash
$ 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作成

```Bash
$ 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有効化

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

確認

```Bash
$ 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にコピーする。
まずは、現在のファイルシステムを確認する。

```Bash
$ 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
はコピーしてはいけないディレクトリであることがわかる。

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

```Bash
$ 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
```

これらをコピーする。

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

確認

```Bash
$ 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設定をした後、バインドマウントする。

ディレクトリ作成

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

permission確認

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

permission設定

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

確認

```Bash
$ 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
```

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

```Bash
$ 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氏による " :-) " です。お楽しみに！