CUBE SUGAR CONTAINER

技術系のこと書きます。

vagrant-hosts プラグインを使ってホスト名を名前解決する

Vagrant では一つの Vagrantfile を使って複数の仮想マシンを管理することもできる。 そういったとき、それぞれの仮想マシンでお互いのホスト名が解決できると扱いやすくなる。 それを実現するには、自前でプロビジョニングを設定する以外にも vagrant-hosts というプラグインを使うと楽ができる。 今回は、これまでやっていた自前のプロビジョニングと、それを自動化してくれる vagrant-hosts プラグインについて紹介する。

使った環境は次の通り。

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.13.4
BuildVersion:   17E202
$ vagrant --version    
Vagrant 2.0.4
$ vboxmanage | head -n 1
Oracle VM VirtualBox Command Line Management Interface Version 5.2.10

一つの Vagrantfile で複数の仮想マシンを扱う上での問題点

複数のマシンで構成されるシステムの検証で Vagrant を使うときは、次のように一つの Vagrantfile で複数の仮想マシンを取り扱う。 この Vagrantfile を使えば vm1 と vm2 という名前の二つの仮想マシンが Vagrant で管理できる。 ポイントとしては Private Network の設定を入れるところで、こうするとお互いにその IP アドレスを使って通信できるようになる。

$ cat << 'EOF' > Vagrantfile 
# -*- mode: ruby -*-
# vi: set ft=ruby :

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|

  config.vm.define :vm1 do |vm1|
    vm1.vm.box = "bento/ubuntu-16.04"
    vm1.vm.network "private_network", ip: "192.168.33.10"
  end

  config.vm.define :vm2 do |vm2|
    vm2.vm.box = "bento/ubuntu-16.04"
    vm2.vm.network "private_network", ip: "192.168.33.11"
  end

end
EOF

それでは上記の Vagrantfile を使って実際に仮想マシンを起動してみよう。

$ vagrant up

起動し終わったら、試しに vm1 から vm2 に向けて ping を打ってみる。 宛先は Private Network で vm2 に割り振られた IP アドレスの 192.168.33.11 を使う。

$ vagrant ssh vm1 -c "ping -c 3 192.168.33.11"
PING 192.168.33.11 (192.168.33.11) 56(84) bytes of data.
64 bytes from 192.168.33.11: icmp_seq=1 ttl=64 time=0.678 ms
64 bytes from 192.168.33.11: icmp_seq=2 ttl=64 time=0.664 ms
64 bytes from 192.168.33.11: icmp_seq=3 ttl=64 time=0.566 ms

--- 192.168.33.11 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 0.566/0.636/0.678/0.049 ms
Connection to 127.0.0.1 closed.

ちゃんと届いた。

ただし、この状態では vm1 から vm2 に対してホスト名を使った通信はできない。 これは vm1 の DNS リゾルバに vm2 に関する情報が登録されていないため。

$ vagrant ssh vm1 -c "ping -c 3 vm2"
ping: unknown host vm2
Connection to 127.0.0.1 closed.

まあ、これでも問題はないんだけどやっぱりホスト名を使って通信をしたいよねと思う。

従来の解決方法 (自前でのプロビジョニング)

続いては、これまでにやっていた自前でのプロビジョニングについて。 先ほどからの変更点は Shell を使ったプロビジョニングを登録しているところ。

$ cat << 'EOF' > Vagrantfile 
# -*- mode: ruby -*-
# vi: set ft=ruby :

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|

  config.vm.define :vm1 do |vm1|
    vm1.vm.box = "bento/ubuntu-16.04"
    vm1.vm.network "private_network", ip: "192.168.33.10"
    vm1.vm.provision "shell" do |s|
      s.path = "setup.sh"
    end
  end

  config.vm.define :vm2 do |vm2|
    vm2.vm.box = "bento/ubuntu-16.04"
    vm2.vm.network "private_network", ip: "192.168.33.11"
    vm2.vm.provision "shell" do |s|
      s.path = "setup.sh"
    end
  end

end
EOF

続いて上記で参照している setup.sh を用意する。 内容としては /etc/hosts に vm1 と vm2 の情報を書き込むものになっている。

$ cat << 'EOF_' > setup.sh 
#!/bin/sh

set -x

: "Add hostname to /etc/hosts" && {
  grep vm1 /etc/hosts >/dev/null
  if [ $? -ne 0 ]; then
      cat << 'EOF' | sudo tee -a /etc/hosts
192.168.33.10 vm1
192.168.33.11 vm2
EOF
  fi
}
EOF_

/etc/hosts に IP アドレスとホスト名を登録することで DNS リゾルバが名前を解決できるようになる。

実際にプロビジョニングを実行してみよう。

$ vagrant provision

プロビジョニングが終わったら vm1 から vm2 に対して ping を打ってみる。

$ vagrant ssh vm1 -c "ping -c 3 vm2"
PING vm2 (192.168.33.11) 56(84) bytes of data.
64 bytes from vm2 (192.168.33.11): icmp_seq=1 ttl=64 time=0.772 ms
64 bytes from vm2 (192.168.33.11): icmp_seq=2 ttl=64 time=0.618 ms
64 bytes from vm2 (192.168.33.11): icmp_seq=3 ttl=64 time=0.587 ms

--- vm2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 0.587/0.659/0.772/0.080 ms
Connection to 127.0.0.1 closed.

ちゃんと名前解決できて疎通がとれている。

ただ、これだと環境に合わせて setup.sh を作らないといけないしめんどくさい。

vagrant-hosts プラグインを使った場合

vagrant-hosts プラグインを使うと前述した問題点を解消できる。

まずはプラグインをインストールしよう。

$ vagrant plugin install vagrant-hosts

vagrant-hosts プラグインに対応させた Vagrantfile を次に示す。 ポイントは vagrant-hosts を使ったプロビジョニングが登録されているところ。

$ cat << 'EOF' > Vagrantfile 
# -*- mode: ruby -*-
# vi: set ft=ruby :

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|

  config.vm.define :vm1 do |vm1|
    vm1.vm.box = "bento/ubuntu-16.04"
    vm1.vm.network "private_network", ip: "192.168.33.10"
    vm1.vm.provision :hosts, :sync_hosts => true
  end

  config.vm.define :vm2 do |vm2|
    vm2.vm.box = "bento/ubuntu-16.04"
    vm2.vm.network "private_network", ip: "192.168.33.11"
    vm2.vm.provision :hosts, :sync_hosts => true
  end

end
EOF

先ほど作った仮想マシンには既に /etc/hosts にホスト名が登録されてしまっているので、一旦作り直そう。

$ vagrant destroy -f && vagrant up

名前解決はキャッシュが効くので、上手く動いていたと思ったらキャッシュでした、というオチが起こりやすい。 ちなみに解決に失敗した情報もキャッシュされることがある。(ネガティブキャッシュという)

仮想マシンを作り直したら、先ほどと同じように vm1 から vm2 に ping を打ってみよう。

$ vagrant ssh vm1 -c "ping -c 3 vm2"
PING vm2 (192.168.33.11) 56(84) bytes of data.
64 bytes from vm2 (192.168.33.11): icmp_seq=1 ttl=64 time=0.655 ms
64 bytes from vm2 (192.168.33.11): icmp_seq=2 ttl=64 time=0.781 ms
64 bytes from vm2 (192.168.33.11): icmp_seq=3 ttl=64 time=0.658 ms

--- vm2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2005ms
rtt min/avg/max/mdev = 0.655/0.698/0.781/0.058 ms
Connection to 127.0.0.1 closed.

ちゃんと名前解決できた。

自前の仮想マシン以外の名前を登録する

先ほどの例では Vagrantfile に含まれる仮想マシンだけに限った名前解決をした。 とはいえ、それ以外にも IP アドレスに名前をつけたいといった場面はあるはず。 続いてはそれを vagrant-hosts プラグインで扱ってみることにしよう。

早速、サンプルとなる Vagrantfile を以下に示す。 この中では Google Public DNS (Primary) の IP アドレスである 8.8.8.8 を google-dns という名前で解決できるようにしている。

$ cat << 'EOF' > Vagrantfile 
# -*- mode: ruby -*-
# vi: set ft=ruby :

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|

  config.vm.define :vm1 do |vm1|
    vm1.vm.box = "bento/ubuntu-16.04"
    vm1.vm.network "private_network", ip: "192.168.33.10"
    vm1.vm.provision :hosts do |provisioner|
      provisioner.autoconfigure = true
      provisioner.sync_hosts = true
      provisioner.add_host '8.8.8.8', ['google-dns']
    end
  end

  config.vm.define :vm2 do |vm2|
    vm2.vm.box = "bento/ubuntu-16.04"
    vm2.vm.network "private_network", ip: "192.168.33.11"
    vm2.vm.provision :hosts, :sync_hosts => true
    vm2.vm.provision :hosts do |provisioner|
      provisioner.autoconfigure = true
      provisioner.sync_hosts = true
      provisioner.add_host '8.8.8.8', ['google-dns']
    end
  end

end
EOF

追加したのは新しく解決するホスト名なので仮想マシンは使いまわすことにしよう。 vagrant-hosts プラグインを使ったプロビジョニングを実行する。

$ vagrant provision

まずは Google Public DNS (Primary) サーバに ping を打ってみよう。

$ vagrant ssh vm1 -c "ping -c 3 google-dns"
PING google-dns (8.8.8.8) 56(84) bytes of data.
64 bytes from google-dns (8.8.8.8): icmp_seq=1 ttl=63 time=4.83 ms
64 bytes from google-dns (8.8.8.8): icmp_seq=2 ttl=63 time=5.52 ms
64 bytes from google-dns (8.8.8.8): icmp_seq=3 ttl=63 time=8.03 ms

--- google-dns ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 4.836/6.132/8.034/1.376 ms
Connection to 127.0.0.1 closed.

ちゃんと解決できた。

仮想マシンについても再度確認しておく。

$ vagrant ssh vm1 -c "ping -c 3 vm2" 
PING vm2 (192.168.33.11) 56(84) bytes of data.
64 bytes from vm2 (192.168.33.11): icmp_seq=1 ttl=64 time=0.322 ms
64 bytes from vm2 (192.168.33.11): icmp_seq=2 ttl=64 time=0.750 ms
64 bytes from vm2 (192.168.33.11): icmp_seq=3 ttl=64 time=0.710 ms

--- vm2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2001ms
rtt min/avg/max/mdev = 0.322/0.594/0.750/0.193 ms
Connection to 127.0.0.1 closed.

ちゃんと解決できているね。ばっちり。

めでたしめでたし。