Help us understand the problem. What is going on with this article?

Vagrantで複数環境を立ち上げる際、IPアドレスの割当やポートフォワードを全て自動化する

以前の記事で Vagrant+Docker(Provider)を使って複数の環境を立ち上げた が、管理するVagrantfileが増えて行くにつれ、各環境のIPアドレスや設定したポートが分からなくなり、新しい環境を立ち上げる際にポートやIPアドレスがブッキングすることが出てきた。

この問題を解消するべく、どうにかVagrantの機能を使って、IPアドレスの管理とポートフォワードを自動化する方法を試したのでメモ。

環境

  • Mac OSX Yosemite 10.10.2
  • Vagrant 1.7.2
  • VirtualBox 4.3.26

やりたいこと

  • Vagrantfile毎にIPアドレスを自動で割り振りたい
  • ポートフォワードも自動で設定したい
  • /etc/hosts にホスト名は自動で設定したい
  • NFSを使って共有ディレクトリをマウントしたい

Vagrantfileの設定

IPアドレス割り当ての自動化

VMのネットワーク設定をプライベートネットワークとし、type: "dhcp" とすることでDHCPによるIPアドレスの割り当てができる。

デフォルトのネットワークアドレスが微妙だったので、ソースコードを読んでみたところ DHCPのネットワークアドレスや、IPアドレスの割当範囲も設定できる ようだったので、こちらも設定。

node.vm.network :private_network, id: "default-network", type: "dhcp", ip: "192.168.34.0"

192.168.34.xxx のネットワークで自動割り当てがされるように。

もし割当範囲を変えたい場合、dhcp_lower: "192.168.34.10", dchp_upper: "192.168.34.50" といった具合に設定を追加すればよい。

ポートフォワードの自動化

コレはとても簡単。
ポートフォワードの設定に auto_correct: true を設定するだけ。

config.vm.network :forwarded_port, guest: 22, host: 2222, id: "ssh", auto_correct: true

これでSSH用のポートがぶつかったら、Vagrantがよしなにしてくれる

/etc/hosts ファイルの自動更新

これは以前も使っていた vagrant-hostupdater プラグインで対応出来ると思ったら、このプラグインはDHCPでの割り当てには対応していないということが判明。

急遽代替プラグインを探していたら、vagrant-hostmanger というプラグインを発見。

このプラグインはIPリゾルバにクロージャを設定することができ、VMから割り当てられたIPアドレスを無理矢理引っ張ってくることが出来るという優れもの。

以下のような記述を加えることで、無事 /etc/hosts も自動更新ができるように。

config.hostmanager.enabled = true
config.hostmanager.manage_host = true
config.hostmanager.ip_resolver = proc do |vm, resolving_vm|
  ip_address = ''
  if hostname = (vm.ssh_info && vm.ssh_info[:host])
    vm.communicate.execute("/sbin/ifconfig eth1 | grep 'inet addr' | tail -n 1 | egrep -o '[0-9\.]+' | head -n 1 2>&1") do |type, contents|
      ip_address = contents.split("\n").first
    end
  end
  ip_address
end

上記のIPアドレスを取得している箇所 vm.communicate.execute("/sbin/ifconfig eth1 | grep 'inet addr' | tail -n 1 | egrep -o '[0-9\.]+' | head -n 1 2>&1") は、ゲストマシンの種類や環境によって変わると思うので、参考までに。

vm.communicate というメソッドが便利で、システムコールで vagrant ssh -c ... で実行しようとしてた部分を割とシンプルに記載できた。

NFSでのディレクトリ共有

これは現状のVagrantだと複数環境でNFSマウントを利用しようとすると、最後に起動した環境しか正しくディレクトリがマウントされないという状態に陥るため、Vagrant側から/etc/exportsを変更することをやめ、手動で/etc/exportsを設定するように している。

というわけで現時点では手動管理とし、/etc/exports にDHCPのネットワークを追記。

/etc/exports
/srv -network 192.168.34.0 -mask 255.255.255.0 -alldirs -mapall=501:20

VagrantfileにもSynced Folderを設定

config.vm.synced_folder "/srv", "/var/www/html/", type: "nfs", nfs_export: false, mount_options: ["nolock", "vers=3", "udp"]

問題なく /srv 以下のディレクトリがNFSでマウントできることを確認。

できあがったVagrantfile

こんな感じになった。

Vagrantfile
Vagrant.configure(2) do |config|
  config.hostmanager.enabled = true
  config.hostmanager.manage_host = true

  config.vm.define "development.local" do |node|
    node.vm.box = "dduportal/boot2docker"

    node.vm.network :private_network, id: "default-network", type: "dhcp", ip: "192.168.34.1", dhcp_lower: "192.168.34.3", dhcp_upper: "192.168.34.254"
    node.vm.network :forwarded_port, guest: 22, host: 2222, id: 'ssh', auto_correct: true

    node.hostmanager.ip_resolver = proc do |vm, resolving_vm|
      ip_address = ''
      if hostname = (vm.ssh_info && vm.ssh_info[:host])
        vm.communicate.execute("/sbin/ifconfig eth1 | grep 'inet addr' | tail -n 1 | egrep -o '[0-9\.]+' | head -n 1 2>&1") do |type, contents|
          ip_address = contents.split("\n").first
        end
      end
      ip_address
    end

    node.hostmanager.aliases = ["development.local"]
    node.ssh.insert_key = false

    node.vm.synced_folder "/srv", "/var/www/html/", type: "nfs", nfs_export: false, mount_options: ["nolock", "vers=3", "udp"]
  end
end

問題点

Macなどホストマシンを再起動した場合に1つの問題が発生。

全てのVMのIPアドレスが、DHCPにより 起動順に再度割り当てられてしまう ため、IPアドレスに対するホスト名の記述が被ってしまう。

## vagrant-hostmanager-start id: {id}
192.168.34.3    test1.local
## vagrant-hostmanager-end

## vagrant-hostmanager-start id: {id}
192.168.34.3    test2.local
## vagrant-hostmanager-end

これは vagrant-hostmanager が仮想マシンの updestroy の時にのみ /ect/hosts ファイルを書き換える動作を行うため、再起動後に各マシンを vagrant up しないと、最新の情報に書き換えてくれないからだ。

しかしこの状態だと、例えば開発環境(プロキシVM用のVagrantfile)が10個あったとすると、再起動後に全ての環境をいちいち vagrant up しないと、どこかでホストのIPアドレスがぶつかる可能性があり、とても面倒。

結局のところ、これを解決できるプラグインを発見することはできなかったこと、vagrant-hostmanager は既に開発がストップしており、作者がPullRequestにも反応していない状況から自分でやるしかないと判断し、プラグインを改造してみた。

改造版vagrant-hostmanager

下記のリポジトリに公開した。
改造版vagrant-hostmanager

注意
ダウンロードしただけでは利用できないので、自前でGemとしてBuildして、ローカルからインストールする必要があるので注意。

改造内容

改造版では、以下のオプションをサポートした。

vagrant hostmanager --cleanhost

--cleanhost オプションを付けて実行することで、vagrant-hostmanager が登録したホスト設定を一括してクリアするように。

こうすることでマシンの再起動後に上記コマンドを1回実行すれば、その後は必要なマシンだけを vagrant up できる。/etc/hosts に設定されたIPアドレスが被っているかを気にする必要は無くなる。

また、本家では vagrant reload の際には /etc/hosts の書き換えをしてくれなかったので、vagrant reload の際にも書き換えをするようにフックを追加してみた。

一応、機を見てPullRequestしてみようと思うけれど、作者が放置気味なので、改造しつつ自前でビルドして使っていくのが一番良いのかもしれない。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした