マウントポイント/var, /optを別ストレージにお引越し(GPT)

# はじめに

今回、私達が管理しているオンプレミスサーバの容量が逼迫している問題が発生していました。
問題となっていたサーバはCentOS 7がプリインストールされており、これをそのまま運用していました。
このサーバのストレージ構成は

- /dev/sda (システム用 128 GB)
- /dev/sdb (データ保存用 6 TB)

となっています。プリインストールでの設定では

- /dev/sda1 (50 GB)にマウントポイント`/`
- /dev/sdb1 (6 TB) にマウントポイント`/home`

となっていて、DBなどが入っている`/var`や`/opt`が徐々に太っていき`/dev/sda1`を逼迫させ、サーバエラーが起こるようになってしまいました。
また`/home`はWebページのソースや個人のデータが入っているのですが、これは高々1 TBも使われておらず、ここが急激に増えるということはなさそうです。

そこで、`/var`, `/opt`を`/dev/sda` から`/dev/sdb`へお引越ししようということになりました。

その際に

1. `/dev/sdb1`を3 TBに縮め
1. パーティションを切って`/dev/sdb2`を1 TB, `/dev/sdb3`を2 TB分作り
1. `/var`,`/opt`のデータを移し
1. マウントポイント`/var`を`/dev/sdb3`, `/opt`を`/dev/sdb2`に設定する

という手順で移行を行います。また、大容量ストレージを扱うために`/dev/sdb`はGPT形式でフォーマットされています。MBR形式と若干手順が異なるかもしれません。具体的には

- `gdisk`ではなく`fdisk`を使う
- GPTの場合、パーティションを切ったあと手でファイルシステム(フォーマット)をしなければならない

などがあります。

※ 本稿の通りにやってデータが失われてしまっても筆者は責任を負いません。必ず完全バックアップを取ってから行ってください。また、テスト環境でテストしてから行うことを推奨します。

## SELinuxについて

マウントポイントを変更する作業を行うのですが、その際にSELinuxのラベリングを正しく設定しないといけないようです。
私はSELinuxに明るくなく、`disable`した環境で行っておりますことをご了承ください。

# パーティションを縮める

最初に`/dev/sdb1`のパーティションを縮めます。

まずは`/dev/sdb1`をアンマウントします。

```shell-session
# umount /dev/sdb1
```

次にファイルシステムのリサイズをします。

```shell-session
# e2fsck -f /dev/sdb1
# resize2fs /dev/sdb1 3000G

resize2fs 1.42.9 (28-Dec-2013)

Resizing the filesystem on /dev/sdb1 to 786432000 (4k) blocks.
```

`resize2fs`はファイルシステムのサイズを変更するコマンドです。このコマンドではパーティションの操作は行いません。
これでパーティション内に散らばったデータをデフラグし、スーパーブロックの情報を変更します。これが結構時間かかります。
(参考: https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/6/html/storage_administration_guide/ext4grow)

ここで、出力されたリサイズ後のブロックサイズから、何バイト分の容量があるかを計算しておきます。
この例では1 block = 4 kB = 4096 Bですので、容量は

```
786432000 * 4096 = 3,221,225,472,000 B
```

となります。

次に、パーティションを縮めます。必ず`/dev/sdb`がマウントされていないか確認し、`parted`の対話モードを使ってパーティションのリサイズをします。



```shell-session

# parted /dev/sdb
(parted) unit b  # 出力単位をByteにします
(parted) p # 現在の情報を出力
Model: ASR8405 RAID1-data (scsi)
Disk /dev/sdb: 5996837601280B
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:
Number  Start     End             Size            File system  Name  Flags
 1      1048576B  5996836552703B  5996835504128B  ext4
```

ここで、`/dev/sdb1`のスタートが1048576 Bであるので、先程出したサイズ(Byte)にスタートのオフセットを足して、Endの位置を計算します。

```
1,048,576 + 3,221,225,472,000 = 3,221,226,520,576‬ B
```

`parted`の対話コンソールの続きです。

```shell-session

(parted) resizepart
Partition number? 1

End?  [5996836552192B]? 3221226520576‬B

Warning: Shrinking a partition can cause data loss, are you sure you want to continue?
Yes/No? Yes # 前にファイルシステムのリサイズをしているので大丈夫

(parted) p

# 略

Disk Flags:
Number  Start     End             Size            File system  Name  Flags
 1      1048576B  3221226521087B  3221225472512B  ext4

(parted) q # サイズが正しく変更できたのを確認できたので、partedを終了
```

これで`/dev/sdb1`が正しく縮まったことが確認できました。

# 新たにパーティションを切る

次は`/dev/sdb2`を作ります。この作業には`gdisk`を使うことにします。`parted`でも新しいパーティションを作れるのですが、Alignmentがうまく出来ませんでした。`gdisk`はシームレスにAlignmentもやってくれるので、パーティション切りのときはこちらを使うことにしました。逆に、パーティションのリサイズは`parted`でやるのが手っ取り早いです。

ここでは`/dev/sdb2`を`opt`として1 TB, `/dev/sdb3`を`/var`として2 TBあげることとします。

`gdisk`の対話コマンドはHelpを参照してください。ここで使うコマンドはそれぞれ

- `n`: 新規パーティションを作成
- `c`: パーティション名の変更
- `p`: パーティションテーブルのプリント
- `w`: パーティションの書き込み

で、`w`を実行するまでは変更は書き込まれません。

```shell-session
# gdisk /dev/sdb

GPT fdisk (gdisk) version 0.8.10
 
Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.


Command (? for help): n
Partition number (2-128, default 2): 2
First sector (34-11712573406, default = 6291460096) or {+-}size{KMGTP}:
Last sector (6291460096-11712573406, default = 11712573406) or {+-}size{KMGTP}: +1T
Current type is 'Linux filesystem'
Hex code or GUID (L to show codes, Enter = 8300):
Changed type of partition to 'Linux filesystem'

Command (? for help): n
Partition number (3-128, default 3): 3
First sector (34-11712573406, default = 8438943744) or {+-}size{KMGTP}:
Last sector (8438943744-11712573406, default = 11712573406) or {+-}size{KMGTP}:
Current type is 'Linux filesystem'
Hex code or GUID (L to show codes, Enter = 8300):
Changed type of partition to 'Linux filesystem'
 
Command (? for help): c
Partition number (1-3): 2
Enter name: opt
 
Command (? for help): c
Partition number (1-3): 3
Enter name: var
 
Command (? for help): p
Disk /dev/sdb: 11712573440 sectors, 5.5 TiB
Logical sector size: 512 bytes
Disk identifier (GUID): BE239631-679B-474D-B43A-305F9146AE5D
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 11712573406
Partitions will be aligned on 2048-sector boundaries
Total free space is 4061 sectors (2.0 MiB)
 
Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048      6291458048   2.9 TiB     0700
   2      6291460096      8438943743   1024.0 GiB  8300  opt
   3      8438943744     11712573406   1.5 TiB     8300  var
 
Command (? for help): w
 
Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!
 
Do you want to proceed? (Y/N): Y
OK; writing new GUID partition table (GPT) to /dev/sdb.
Warning: The kernel is still using the old partition table.
The new table will be used at the next reboot.
The operation has completed successfully.
```

パーティションが切られましたので再起動し、デバイスを認識させます。

# 新たなパーティションのフォーマットをする

次に、新たに切ったパーティションをそれぞれ`ext4`でフォーマットします。`ext4`でフォーマットするのには`mkfs.ext4`コマンドを使用します。

```shell-session
# mkfs.ext4 /dev/sdb2
mke2fs 1.42.9 (28-Dec-2013)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
67108864 inodes, 268435456 blocks
13421772 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=2415919104
8192 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks:
        32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
        4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968,
        102400000, 214990848
 
Allocating group tables: done
Writing inode tables: done
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done

 
# mkfs.ext4 /dev/sdb3
mke2fs 1.42.9 (28-Dec-2013)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
102301696 inodes, 409203707 blocks
20460185 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=2558525440
12488 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks:
        32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
        4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968,
        102400000, 214990848
 
Allocating group tables: done
Writing inode tables: done
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done
```

これでフォーマットも完了です。

# `/var`, `/opt`を新たに切ったパーティションに移す

## データのコピー

まず、既存の(`/dev/sda1`にある) `/var`,`/opt`にアクセスしているserviceがあると移行作業が行えないので、single user modeに入ります。

```
# init 1
```

今回の作業ではサーバを直で操作ではなく、ログ取りの関係上ssh経由で行っていたため、この作業のみ本体を直接触って`network`,`sshd`を起動させました。

```
# systemctl start network.service
# systemctl start sshd.service
```

次に、`/dev/sdb2`,`/dev/sdb3`を仮マウントして、現在の`/opt`,`/var`の中身をそれぞれコピーします。

```shell-session
# cd /mnt
# mkdir opt_new var_new
# mount /dev/sdb2 opt_new/
# mount /dev/sdb3 var_new/
# rsync -au /opt/* opt_new/
# rsync -au /var/* var_new/
```

これにて`/dev/sda1`にあった`/opt`が`/dev/sdb2`に、 `/dev/sda1`にあった`/var`が`/dev/sdb3`にコピーされました。

アンマウントし、念の為`fsck`させます。

```
# umount /dev/sdb2
# umount /dev/sdb3

# e2fsck -f /dev/sdb2
e2fsck 1.42.9 (28-Dec-2013)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
/dev/sdb2: 312692/67108864 files (0.2% non-contiguous), 9517164/268435456 blocks

# e2fsck -f /dev/sdb3
e2fsck 1.42.9 (28-Dec-2013)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
/dev/sdb3: 231758/102301696 files (0.2% non-contiguous), 11135325/409203707 blocks
```

OKです。

## マウント先のディレクトリを作成

次に、今の`/opt`, `/var`をそれぞれ`/opt.org`, `/var.org`に変更し、マウント先のディレクトリ`/opt`,`/var`を作ります。

```shell-session
# cd /
# mv opt opt.org
# mv var var.org
# mkdir opt var
```

## `/etc/fstab`の編集

次に`/etc/fstab`を編集します。その前に、デバイスのUUIDの確認をします。簡単のため、UUIDは適当に分かりやすく変えていますので、適宜読み替えてください。

```
# ls -l /dev/disk/by-uuid/
total 0
lrwxrwxrwx 1 root root 10 Jul  2 14:51 33333333-3333-3333-3333-333333333333 -> ../../sdb3
lrwxrwxrwx 1 root root 10 Jul  2 14:51 22222222-2222-2222-2222-222222222222 -> ../../sdb2
lrwxrwxrwx 1 root root 10 Jul  2 14:23 11111111-1111-1111-1111-111111111111 -> ../../sdb1
```

確認が出来ましたら`/etc/fstab`を編集して、`/dev/sdb2`をマウントポイント`/opt`に、`/dev/sdb3`をマウントポイント`/var`に指定します。

```shell-session:/etc/fstab(関係あるところのみ抜粋)
UUID=11111111-1111-1111-1111-111111111111 /home                   ext4     defaults        1 2
UUID=22222222-2222-2222-2222-222222222222 /opt                    ext4     defaults        1 2
UUID=33333333-3333-3333-3333-333333333333 /var                    ext4     defaults        1 2
```

これで再起動をかけます。

## 確認

起動したあとに`df`で確認です(関係あるところのみ抜粋)。

```
# df -Th
Filesystem                             Type      Size  Used Avail Use% Mounted on
/dev/sdb1                              ext4      2.9T  676G  2.1T  25% /home
/dev/sdb2                              ext4     1008G   21G  937G   3% /opt
/dev/sdb3                              ext4      1.6T   18G  1.5T   2% /var
```

正常にマウントされております。

また、`/opt`,`/var`を使うサービス類も正しく動作出来ていることが確認できました。

# 最後に

この時代、クラウドでストレージも可変に組めるのであまりこういった危険な作業をすることも少なくなっているとは思いますが、やる機会がありましたらご参考ください。くれぐれもバックアップを忘れずに取って、ご安全に。