Vagrantで初期にディスク拡張を自動化するプロビジョニングコードを作ってみた(制約あり)

## 経緯

VagrantのゲストOSは初期でディスクが10GBしかないが、用途によってはそれ以上ディスクが欲しくなる事もある。
共有ディスクを割り当てるなどで代替できる場合もあるが、ホストがWindowsでゲスト側でsymlinkを使いたいなどの
対処も必要なときは割り当てるディスクを拡張する必要があるが、結構手順がめんどい。
そこで容量大きいゲストOSを何度も作って壊しやすいように、ディスク拡張からFSへの適用までをある程度
自動化するプロビジョニングコードを作ってみた。

## tl;dr

- partedでパーティション追加、LVM用にパーティションを割当、ファイルシステム上で拡張を認識、を自動化しただけ
- 途中の再起動をプロビジョニング内でやる方法がわからなかったので、一度手動で再プロビジョニングが必要


## 必要なもの

- VirtualBox
    - 他の仮想環境では不明
- Vagrant
    - 検証時の環境は2.1.5
- vagrant-disksize プラグイン
- CentOSのvagrantイメージのボックス
    - ubuntuとかでは適宜apt-getなどの読み替えが必要。
    - ルートボリュームがLVM管理になっているイメージが必要。


## Vagrantfileの修正分

`vagrant init` でできた初期のファイルと比較して修正は2点。
Vagrantfile全体は [Gist][gist-vf] にアップしているものを参照。

- `disksize.size` を設定
    - ここでは例として50GBを割り当てている。
    - ここの割当と次のプロビジョニング内の数値を同じにすること
- provisionのスクリプトを追加

```diff:diff-Vagrantfile
$ diff Vagrantfile Vagrantfile.67
58a59,61
>   # for disk size
>   config.disksize.size = '50GB'
>
66,69c69,85
<   # config.vm.provision "shell", inline: <<-SHELL
<   #   apt-get update
<   #   apt-get install -y apache2
<   # SHELL
---
>   config.vm.provision "shell", inline: <<-SHELL
>   which parted || {
>     yum install -y parted
>     START=$(parted -l /dev/sda | grep lvm | head -1 | awk '{ print $3 }' || exit 1)
>     parted -s -a optimal /dev/sda -- mkpart primary ext4 $START 50G
>     reboot
>     echo '[NOTICE] after several seconds(for reboot), you should run "vagrant
provision"'
>     exit 0
>   }
>   pvdisplay | grep sda3 > /dev/null || {
>     pvcreate /dev/sda3
>     vgextend VolGroup /dev/sda3
>     lvextend -l +100%FREE /dev/VolGroup/lv_root
>     resize2fs /dev/VolGroup/lv_root
>     yum update curl nss -y
>   }
>   SHELL
```

#### Gist追記

- CentOS7.2用のVagrantfile追加、6.7と違う点は以下の通り。
    - ボリュームグループ名
    - 論理ボリューム名
    - リサイズコマンド(resize2fs => xfs_growfs)
- CentOS6.7用のVagrantfileにcurl/nssのupdateを追記、このトピックの本筋ではないが毎回SSLエラー引っかかるので。

## 使い方

途中の処理で再起動を要求されるため、プロビジョニングを2種類用意する必要があった。
名称指定で分けることもできるが `vagrant up` の際にプロビジョニングの個別指定方法がよくわからなかったので、
**一度 `vagrant up` してから時間を置いて `vagrant provision` させる** ことで対応するようにした。

```bash
PS> vagrant up
~~~~ 途中省略 ~~~~
    default: Warning: WARNING: the kernel failed to re-read the partition table on /dev/sda (Device or resource busy). As a result, it may not reflect all of your changes until after reboot.
    default: [NOTICE] after several seconds(for reboot), you should run "vagrant provision"

~~~~ rebootコマンドで再起動するぐらいの時間待ってから ~~~~

PS> vagrant provision
~~~~ 途中省略 ~~~~
    default: Performing an on-line resize of /dev/VolGroup/lv_root to 11821056 (4k) blocks.
    default: The filesystem on /dev/VolGroup/lv_root is now 11821056 blocks long.
```



## プロビジョニングスクリプトの中身

処理が2つあるのでそれぞれコメントつけて解説。
個別コマンドの機能についてはここでは細かくは説明しません。
いずれのコードブロックも、初回起動時に **一度だけ** 動くように調整する必要がある。

### パーティション作成までの処理

初回の `vagrant up` で実行されるブロック。

```bash
# partedがない場合に処理
which parted || {
  # partedをインストール
  yum install -y parted
  # ルートボリュームは lvmのフラグがついている、ボリュームのセクタ終了位置を取得(多分10.5Gと出る)
  START=$(parted -l /dev/sda | grep lvm | head -1 | awk '{ print $3 }' || exit 1)
  # 前のボリュームの終わり位置からの指定で新規パーティションを作成
  parted -s -a optimal /dev/sda -- mkpart primary ext4 $START 50G
  # 再起動が要求されるので一度再起動
  echo '[NOTICE] after several seconds(for reboot), you should run "vagrant provision"'
  reboot
  exit 0
}
```

- [CentOS6.7 のボックス][es-cent67] から作成したゲストで調整しているので、partedがない場合に動くようにしている。
    - CentOS7.2 のボックスなどは parted がインストールされているので `ls /dev/sda3` などの条件でよいかも。
- パーティションを作る際の開始位置を前のパーティション情報から取得する必要がある。
    - ` 2      525MB   10.5GB  9960MB  primary               lvm` のような行を解析している。
    - 前の終了位置から1ずらした方がよさそうだが、G単位だと同じ値でも大丈夫そう？
- sda デバイス上にパーティションを作成
    - 他のサイトだとmkpartの前にmklabelやっているがここではスキップしている。
        - mklabelやろうとすると稼働中デバイスなので非インタラクティブだとエラーになる。
        - mklabelやらなくても動いてはいる(影響確認はしていない)
    - 前のコマンドで取得した開始位置から Vagrantfile で指定したディスクサイズ値までを指定
- 再起動して終了
    - 後続処理は再起動後に処理されるものなので一度ここで明示的に打ち切っている。

### ファイルシステムへ割当まで

少し待ってからの `vagrant provision` で実行されるブロック。

```bash
# sda3 がLVM割当されていない場合のみ処理
pvdisplay | grep sda3 > /dev/null || {
  # LVM用に初期化
  pvcreate /dev/sda3
  # グループで使用できるよう割当
  vgextend VolGroup /dev/sda3
  # 論理ボリュームに追加分をすべて割当
  lvextend -l +100%FREE /dev/VolGroup/lv_root
  # 論理パーティションでリサイズを認識して拡張させる
  resize2fs /dev/VolGroup/lv_root
}
```

- 新規に作成されるパーティションは `/dev/sda3`、これがLVM下にない場合のみ動くようにしている。
    - 元デバイスが `/dev/sdb` だったりするイメージでは要調整
- 追加パーティションをLVM管理に登録、全領域をルートパーティションに割り当てる。
    - この辺は他のサイトで調べた内容をそのまま並べただけ。
    - イメージによってグループ名(`VolGroup`)と論理パーティション名(`/dev/VolGroup/lv_root`)は調整が必要かも。


## 改善点

書いてる途中で思いついたので一応記載。

### LVM対応を別スクリプト化して、再起動後に起動時実行させる案

- crontab の `@reboot` でも rc.local スクリプトの登録でもどちらでも動かせそう。
    - 正常稼働後は実行予約自体削除すれば一度きり処理にできる。
    - 再起動後に自動的に動くので明示的な `vagrant provision` が不要になる。
- ホスト側のワークスペースはゲストからも `/vagrant` で見れるのでスクリプト配置は容易。
- Vagrantfileの1ファイルで完結しないので少し手間だが、本来プロビジョニングコード自体分けた方がスッキリする。
    - 自分では1ファイルの方が使い回しが楽なので適用はしない予定


## 最後に

ここはこうした方がよいとかあれば適宜コメントください。
特に、スクリプトモードでmklabelやる方法とか、プロビジョニングの途中でゲストの reload 挟む作法とかあれば是非( ´艸｀)


----

## 参考

- [【 parted 】コマンド――GPT対応のパーティションを作成、削除する](http://www.atmarkit.co.jp/ait/articles/1803/08/news017.html)
    - partedの説明
- [【Qiita記事】スクリプト内でpartedを使ってパーティション作成](https://qiita.com/yaa/items/e57fbdb60a8fc68ea37d)
    - parted の非インタラクティブモードの説明、他の参考サイトへのリンクもあり。
- [LVM の論理ボリュームを拡張する](http://iwamocchan11.hatenadiary.jp/entry/2015/11/04/010752)
    - LVM関連のコマンドの使い方がいろいろと。




[es-cent67]:https://github.com/CommanderK5/packer-centos-template/releases/download/0.6.7/vagrant-centos-6.7.box
[gist-vf]:https://gist.github.com/meian/59ca74290a8eab9da8eb23c7817ff6cc