ESXi上のVMをboxファイルにする方法

ESXi+Vagrantで仮想サーバーを作成する場合、通常であれば、VMイメージ（以下、boxファイル）はVagrant Cloud[^1]で取得すれば事足ります。
しかし、既存のboxファイルでは、既に作られているパーティションやvagrant sshでログインする為のユーザーを変更できません。
今回の記事は、そのような問題を解決するために、ESXi上のVMから目的のboxファイルを作成した時の内容をもとにしています。

# 環境
VMware ESXi 6.7.0
Vagrant 2.2.6

```:Vagrantサーバーのファイル構成
|-- box-build (box作成用のディレクトリ)
|   |-- .vagrant/machines/test-vm/vmware_esxi/id
|   `-- Vagrantfile
`-- box-run (boxの動作確認用のディレクトリ)
    `-- Vagrantfile
```

# 作成方法
### boxファイルにするVMの作成
1. ESXi上にboxにするためのVMを作成してください。
今回は、VM名をtest-vmとします。

2. ユーザーを作成します。
このユーザーは、vagrant sshやプロビジョンに用いられます。
ユーザー名はどのようなものでも構いませんが、`vagrant`という名前でない場合は、VMを起動するためのVagrantfileに特別に設定をする必要があります。
今回はVagrant Cloudに置かれているboxファイルにならい、ユーザー名、パスワードは共に`vagrant`とします。
次のコマンドでユーザーを追加します。
`$ adduser vagrant`

3. vagrantがパスワードなしでsudoを行えるようにします。
`$ sudo visudo`
以下をファイル内に追記します。
`vagrant ALL=NOPASSWD: ALL`
※パスワードありのままだと、プロビジョン時に要求されるパスワードを返すことができないので、プロビジョンが終了しません。

4. vagrantユーザーのホームディレクトリにvagrantサーバーから接続するための公開鍵を置きます。
こちらの[公開鍵](https://github.com/hashicorp/vagrant/blob/master/keys/vagrant.pub)の内容を/home/vagrant/.ssh/authorized_keysに記述してください。
この公開鍵が使われるのは、初回ssh接続の時になります。
それ以降は、初回アクセス時に自動で挿入されたVM固有のsshキーが用いられます。

5. 公開鍵のパーミッションを変更します。
`$ chmod 700 /home/test/.ssh/`
`$ chmod 600 /home/test/.ssh/authorized_keys`

6. VMのマシンIDを削除します。
次のコマンドで/etc/machine-idを削除します。
`$ rm /etc/machine-id`
このファイルを削除しないと、同じboxファイルから複数のVMを作成した際にプロビジョンがそれぞれのVMに行われる不整合がおきます。
なお、machine-idファイルは、VM起動時に自動で追加されます。

7. その他のカスタマイズは自由です。

8. VMを停止させます。
次のコマンドを実行します。
`$ shutdown now`

### VMからboxファイルを作成
1. VMのマシンIDを取得します。
ESXiサーバーへログインし、box化したいVMのマシンIDを取得します。
サーバー上で次のコマンドを実行します。
`$ vim-cmd vmsvc/getallvms`
※このコマンドで表示されたテーブルのVMidがマシンID、NameがVM名になります。

2. Vagrantサーバーでbox作成用のVagrantfileを作成します。
Vagrantfileを記事上部の「Vagrantサーバーのファイル構成」となるようにbox-buildディレクトリに置きます。
また、Vagrantfileの内容は次の通りです。

```ruby:box作成用のVagrantfile
# -*- mode: ruby -*-
Vagrant.configure("2") do |config|

  config.vm.define  "test-vm" do |node|
    node.vm.hostname = "test-vm"
  end

  config.vm.provider :vmware_esxi do |esxi|
    esxi.guest_name = "test-vm"
    esxi.esxi_hostname = "ESXiのIPアドレスorドメイン名"
    esxi.esxi_username = "ルート権限のあるESXiのユーザー"
    esxi.esxi_password = 'prompt:'
  end
end
```

3．マシンIDを記述します。
　取得したマシンIDを以下のファイルに記述します。
　`box-build/.vagrant/machines/{VM名}/vmware_esxi/id`
　今回の内容では、パスは次のようになります。
　`box-build/.vagrant/machines/test-vm/vmware_esxi/id`

4．VMをbox化します。
　box-buildディレクトリ上で以下のコマンドを実行します。
　`$ vagrant package`
　※オプションでboxファイル名を指定しない場合、package.boxというboxファイルがbox-buildディレクトリ上に作成されます。
　記事内では、boxファイル名をpackage.boxとして扱います。

# VMの起動
1. boxファイルをvagrantへ適用します。
次のコマンドでboxファイルをvagrantへ適用します。
box名は、test-boxとします。
`$ vagrant box add package.box --name test-box`
boxファイルが適用されているかは、次のコマンドで確認できます。
`$ vagrant box list`
ここでtest-boxが表示されていれば、成功です。

2. VM起動用のVagrantfileを作成します。
box-runディレクトリにVagrantfileを以下の内容を作成します。

```ruby:box起動用のVagrantfile
# -*- mode: ruby -*-
Vagrant.configure("2") do |config|
  config.vm.box = "test-box"

  config.vm.define "新しいVM名" do |node|
    node.vm.hostname = "新しいVM名"
  end

  config.vm.provider :vmware_esxi do |esxi|
    esxi.esxi_hostname = "ESXiのIPアドレスorドメイン名"
    esxi.esxi_username = "root"
    esxi.esxi_password = "esxiサーバーのパスワード"
    esxi.guest_memsize = 16384
    esxi.guest_numvcpus = 1
  end
end
```

3．VMを起動します。
　box-runディレクトリ上で以下のコマンドを実行します。
　`$ vagrant up`
　少し待つとVMが作成され、起動します。

#まとめ
ネット上にboxファイルを作成する方法を記している記事は多くヒットしますが、プロバイダーにESXiを用いている方法は少なかったので、記事にしました。
box化やVMの起動には、かなり時間がかかる場合があります。（20分くらいかかったこともありました。）お使いの際は、他の作業の合間にすることをお勧めします。

# 参考文献
1. 「sudo のパスワードを入力なしで使うには」,
https://qiita.com/RyodoTanaka/items/e9b15d579d17651650b7 ,
2020年2月24日アクセス


[^1]: [Vagrant Cloud](https://app.vagrantup.com/boxes/search)は、boxファイルのリポジトリです。