Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
46
Help us understand the problem. What is going on with this article?

More than 1 year has passed since last update.

@tmiki

【Vagrant】ちゃんと理解する仮想ネットワーク #1 ~基礎編~【ネットワーク】

はじめに

昨今のアプリケーション開発では、開発環境に仮想マシン(ハイパーバイザ型にせよコンテナ型にせよ)を構築することが殆どだと思います。
弊社でもここ数年、開発環境として当たり前のように、Vagrant+VirtualBoxを利用したりDockerを利用することが増えています。
そんな中で、開発環境の構築、特にネットワーク周りで躓いている人が少なくないので、仮想環境ネットワークの仕組みを整理しようと思います。

Prerequisite

  • 仮想環境に関する基礎的な用語を知っている(ゲストOS/ホストOS、など)
  • IPアドレスにおけるサブネットマスク、ネットワーク部/ホスト部を理解している
  • Vagrantの基本的な役割・位置づけを知っている(Vagrant自体は仮想環境は提供しない、など)
  • VagrantとVirtualBoxで簡単な構成の仮想マシンを操作することができる(Getting Startedに該当する操作は一通りやったことがある)

検証した環境

Vagran+VirtualBoxの環境です。ホストOSはWindows。至ってよくある構成かと思います。

なお、検証に利用したVagrantfileは下記で公開しています。
https://github.com/tmiki/server-config/blob/master/vagrant_example_for_networking/Vagrantfile

ホストOS

OS: Windows7 Professional
ハイパーバイザ: Oracle VirtualBox 5.1.14 r 112924
Vagrant 2.1.1

ゲストOS

OS: "Amazon Linux AMI 2017.03" (https://app.vagrantup.com/mvbcoding/boxes/awslinux)

仮想ネットワーク構成

NAT Adapter / NAT network(※正式名称不明)

Vagrantfileで追加の設定をせずとも、デフォルトで構成されるネットワークです。
一般的にNAT networkとも呼ばれているようですが正式名称は不明です。ドキュメントをざっと見た限りですが、明示的な固有名詞が見当たりません。本稿では便宜上、「NAT Adapter」と呼ぶことにします。

VM内からみると、NICに「10.0.2.15/24」のIPアドレスが付与されているものとなります。ifconfigコマンドでこれを確認することができます。

ifconfig
[vagrant@vm1private ~]$ ifconfig
eth0      Link encap:Ethernet  HWaddr 08:00:27:AF:85:70
          inet addr:10.0.2.15  Bcast:10.0.2.255  Mask:255.255.255.0
          inet6 addr: fe80::a00:27ff:feaf:8570/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:74361 errors:0 dropped:0 overruns:0 frame:0
          TX packets:24315 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:77175045 (73.5 MiB)  TX bytes:1408725 (1.3 MiB)

# ※以降省略

VirtualBoxの設定上、「アダプター 1: 準仮想化ネットワーク (NAT)」となっているものが該当します。
2018-11-05_00h25_28_NAT.png

図で表すと下記のようになります。
VagrantNetworking1_NATAdapter.png

「アダプター 1: 準仮想化ネットワーク (NAT)」に関するVirtualBoxのドキュメントは下記にあります。

Chapter 6. Virtual networking -> 6.3. Network Address Translation (NAT)

少し長いですが、NAT Adapterに関する説明を引用します。

Network Address Translation (NAT) is the simplest way of accessing an external network from a virtual machine. Usually, it does not require any configuration on the host network and guest system. For this reason, it is the default networking mode in VirtualBox.

A virtual machine with NAT enabled acts much like a real computer that connects to the Internet through a router. The "router", in this case, is the VirtualBox networking engine, which maps traffic from and to the virtual machine transparently. In VirtualBox this router is placed between each virtual machine and the host. This separation maximizes security since by default virtual machines cannot talk to each other.

The disadvantage of NAT mode is that, much like a private network behind a router, the virtual machine is invisible and unreachable from the outside internet; you cannot run a server this way unless you set up port forwarding (described below).

The network frames sent out by the guest operating system are received by VirtualBox's NAT engine, which extracts the TCP/IP data and resends it using the host operating system. To an application on the host, or to another computer on the same network as the host, it looks like the data was sent by the VirtualBox application on the host, using an IP address belonging to the host. VirtualBox listens for replies to the packages sent, and repacks and resends them to the guest machine on its private network.

The virtual machine receives its network address and configuration on the private network from a DHCP server integrated into VirtualBox. The IP address thus assigned to the virtual machine is usually on a completely different network than the host. As more than one card of a virtual machine can be set up to use NAT, the first card is connected to the private network 10.0.2.0, the second card to the network 10.0.3.0 and so on. If you need to change the guest-assigned IP range for some reason, please refer to Section 9.11, “Fine-tuning the VirtualBox NAT engine”.

ポイントを整理すると下記のようになるでしょうか。

  • 所謂、NATルータと同様の働きをし、VMとホストOS間に位置している。
  • デフォルトでは他のVMとの通信、外部からの通信は許可されない、port forwarding設定をしない限りは。
  • ゲストOSから送信された通信データは、VirtualBox's NAT engineでいったん受け取って展開し、これをホストOS側の機能を利用して送信する(これも所謂NATルータと同じ働き)
    • ホストOS宛、若しくは外部のホスト宛ての通信の際には、ホストOSに所属するIPアドレスを利用する。
    • VirtualBoxは上記に対する戻りのパケットがあるかどうかをチェックし、あれば適切にゲストOSに転送する。
  • Private Network内でDHCPサーバが稼働し、ゲストのVMにはホスト側のネットワークと異なるIPアドレスを付与する。
  • 1つのVMに複数のNAT Adapterを設定することができる。
    • 1つ目のNICには10.0.2.0のネットワーク(サイズは24bit)が設定される。
    • 2つ目のNICには10.0.3.0のネットワーク(サイズは24bit)が設定される。

port_forwardingについては後述します。

また言うまでもないことですが、10.0.2.0/24のネットワークセグメントが他に存在する場合、そのままでは通信することができません。
NAT network側のIPアドレスレンジを変更するか、/25などのより小さいCIDRで明示的にルーティングテーブルを追加する必要があるでしょう。(※未検証)

private_network

基本的な設定・構成

一般的にVMで使うネットワークは「private_network」になるかと思います。公式ドキュメントは下記になります。

Private Networks - Networking - Vagrant by HashiCorp

以下のようなVagrantfileを作ってvagrant upを実行すると、当該VM内でNIC(eth?)が付与され、192.168.255.11のIPアドレスが付与されます。

Vagrantfile
Vagrant.configure("2") do |config|
  config.vm.box = "mvbcoding/awslinux"
  config.vm.box_url = "https://app.vagrantup.com/mvbcoding/boxes/awslinux"

  config.vm.provider "virtualbox" do |v|
    v.memory = 512
    v.cpus = 1
  end

  # This VM uses "Private Networks" described on the document below.
  # https://www.vagrantup.com/docs/networking/private_network.html
  config.vm.define :"vm1private" do |c1|
    c1.vm.hostname = "vm1private"
    c1.vm.network "private_network",ip: "192.168.255.11"
  end

  config.vm.provision :shell, :path => "bootstrap.sh"

end

当該VM内でifconfigを実行すると、恐らく下記のような結果が得られるかと思います。「private_network」として指定したIPアドレスがeth1に付与されていることが確認できます。ここまでは良くある話なので、特に疑問は無いでしょう。
VM上でWebアプリを実行させ、Windows側でWebブラウザを立ち上げてhttp://192.168.255.11:8080/ にアクセスしたり、MySQLクライアントから192.168.255.11にアクセスしたり、といった操作をするかと思います。

ifconfig
[vagrant@vm1private ~]$ ifconfig
eth0      Link encap:Ethernet  HWaddr 08:00:27:AF:85:70
          inet addr:10.0.2.15  Bcast:10.0.2.255  Mask:255.255.255.0
          inet6 addr: fe80::a00:27ff:feaf:8570/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:74361 errors:0 dropped:0 overruns:0 frame:0
          TX packets:24315 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:77175045 (73.5 MiB)  TX bytes:1408725 (1.3 MiB)

eth1      Link encap:Ethernet  HWaddr 08:00:27:21:DF:9F
          inet addr:192.168.255.11  Bcast:192.168.255.255  Mask:255.255.255.0
          inet6 addr: fe80::a00:27ff:fe21:df9f/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:22471 errors:0 dropped:0 overruns:0 frame:0
          TX packets:26 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:6843527 (6.5 MiB)  TX bytes:1740 (1.6 KiB)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:355 errors:0 dropped:0 overruns:0 frame:0
          TX packets:355 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1
          RX bytes:31204 (30.4 KiB)  TX bytes:31204 (30.4 KiB)

それでは、上記VMのeth1が接続するネットワークはどのような構成になっているでしょうか。図示すると下記のようになります。(当たり前ですが、IPアドレスは例です)
なお、上記のVagrantfileで作成したVMは、下図の一番右のGuest VMが該当します。

VagrantNetworking3_private_network.png

まず、各private_networkセグメントごとに、ホストOS(つまりWindows側)もNICが作られます。VirtualBoxであれば、ホストOSであるWindows側に、「VirtualBox Host-Only Network(| #[0-9]+)」といった名称でNICが作られます。
またこれに対して、当該セグメントの先頭のアドレス(e.g. 192.168.255.0/24であれば、192.168.255.1)が割り当てられます。
これは実際に、Windowsのネットワークアダプターの設定から確認することができます。(私のPCの場合は「VirtualBox Host-Only Network #3」でした)
2018-11-05_02h22_34.png

VirtualBoxのGUIからも、構成情報を確認してみましょう。「アダプター2:」として、「VirtualBox Host-Only Network #3」が指定されていることが分かります。
2018-11-05_00h25_28_mark.png

Vagrantはvagrant upするときに、下記のようにネットワーク設定を構成していきます。

  1. ホストOSの既存のネットワークアダプターのIPアドレスレンジを確認し、これから作るVMのIPアドレスにマッチするものはあるか確認する。
  2. マッチするものがあれば、VM作成時にこれを利用する。
  3. マッチするものが無ければ、新たなネットワークアダプターを作る。

vagrant upしたときに、初回は必ず下記の確認ポップアップが出るかと思いますが、これはWindows OS上にネットワークアダプターを新たに作成するからです。同一のVMが対象であれば、2回目以降はこのポップアップが出ないのは経験済みでしょう。
2018-11-05_00h07_18.png

なお、private_networkはその名称の通り、原則としてホストOS内に閉じたネットワーク環境となります。通常、以下の組み合わせでのみ、通信が可能なネットワークとなります。

  • Windows上のアプリケーションから、private_network内のゲストOSと通信する
  • 同一private_network内のゲストOS同士で通信する

下記組み合わせは、通常の構成では通信できません。

  • 別のprivate_network間(e.g. 192.168.56.0/24⇔192.168.255.0/24間)でのゲストOS同士で通信する
  • private_network内のゲストOSから、ホストOS外部のPC/サーバ(さきほどの図で言うと、物理的なLANである10.1.1.0/24に接続されているPC/サーバ)との通信

正確にいうとホストOS、及びホストOS外のネットワークの構成を変更することで、上記のような通信を実現することは可能です。
しかしながら、これは本稿の範囲を大きく外れてしまうため割愛します。別の機会に書くかもしれません。
# L3レイヤまでの仕組みをちゃんと理解すれば、どう実現するかは自明なので特に書く必要はないかもしれません。

private_network構成まとめ

private_network構成のポイントは下記のようにまとめられます。

  • 仮想ネットワークセグメント(i.e. 192.168.56.0/24や192.168.255.0/24など)ごとに、Windows上にネットワークアダプター(VirtualBox Host-Only Network(|#[0-9]+))が作られる
  • Vagrantfileに指定されたIPアドレスが、既存のネットワークアダプターのIPアドレスレンジにマッチしない場合は、新たにネットワークアダプターを作成する
  • ゲストOS間で、別のネットワークセグメントに所属するIPアドレスを利用した通信はできない(やろうと思えばできる。)
  • private_networkに指定されたIPアドレスからは、ホストOS外のPC/サーバとは直接通信できない(やろうと思えばできる。)

forwarded_port

通常、他のPCからゲストOSにアクセスさせる場合には、「forwarded_port」を用います。ゲストOSの特定ポートをホストOSの特定ポートに結びつけて公開する、ということができます。
ちなみに細かいことですが、Listenするポート番号はゲスト/ホスト間で一致する必要はありません。それぞれ任意のポートを指定できます。

図で表すと下記のようになるかと思います。
VagrantNetworking2_forwarded_port.png

外部のPC/サーバからのアクセスの際には、先ほどまで見てきたNAT Adapter(VirtualBoxのGUI上、「アダプター 1: 準仮想化ネットワーク (NAT)」として付与されているもの)経由で通信が行われます。通常これは、ゲストOSがLinuxであればeth0として設定され、10.0.2.15/24が付与されます。

Forwarded Ports - Networking - Vagrant by HashiCorp

では実際に試してみましょう。
先ほどのVagrantfileに「forwarded_port」の設定を追加し、vagrant reloadを実行します。
下記設定の場合、ホストOS(つまりWindows側)の18000/TCPがゲストOSの8000/TCPに結び付けられます。

Vagrantfile(port_forwarded)
Vagrant.configure("2") do |config|
  config.vm.box = "mvbcoding/awslinux"
  config.vm.box_url = "https://app.vagrantup.com/mvbcoding/boxes/awslinux"

  config.vm.provider "virtualbox" do |v|
    v.memory = 512
    v.cpus = 1
  end

  # This VM uses "Private Networks" described on the document below.
  # https://www.vagrantup.com/docs/networking/private_network.html
  config.vm.define :"vm1private" do |c1|
    c1.vm.hostname = "vm1private"
    c1.vm.network "private_network",ip: "192.168.255.11"
    c1.vm.network "forwarded_port", guest:8000, host:18000
  end

  config.vm.provision :shell, :path => "bootstrap.sh"

end

vagrant reloadを実行したら、VirtualBoxのGUIで設定を確認してみましょう。対象のVMを選択して「設定」→「ネットワーク」と進んでいき、「アダプター 1」の「高度」設定を展開します。ここに「ポートフォワーディング」というボタンがあるのでこれを押します。
「forwarded_port」で指定した設定が追加されているのが確認できると思います。
2018-11-05_04h03_23.png

動作確認のため、ゲストOSでSimpleHTTPServerを立ち上げます。

ゲストOS側作業
### 適当なhtmlファイルを作る
$ mkdir htdocs
$ cd ./htdocs
$ vi test.html  # 適当に中身を作る

### SimpleHTTPServerを実行する
$ python -m SimpleHTTPServer
Serving HTTP on 0.0.0.0 port 8000 ...

上記ゲストOSを動かしているホストOSのネットワークアダプターには、現在10.1.1.3/24が設定されています。
別のホストからcurlを実行してみましょう。
なお通常、外部からのアクセスはWindows Firewallで遮断されます。一時的にWindows Firewallを無効化するか、適切な許可設定を追加しておく必要があります。

別のPCから
### 他のPCから上記ゲストOSに対してcurlを実行する
$ curl http://10.1.1.3:18000/
※実行結果省略
$ curl http://10.1.1.3:18000/test.html
※実行結果省略

SimpleHTTPServer側のログを見てみましょう。下記のように出力されているはずです。
接続元のIPアドレスが「10.0.2.2」になっていることが分かります。これはNAT AdapterでNATされているからです。

ゲストOS側作業
[vagrant@vm1private htdocs]$ python -m SimpleHTTPServer
Serving HTTP on 0.0.0.0 port 8000 ...
※途中省略
10.0.2.2 - - [04/Nov/2018 18:55:37] "GET / HTTP/1.1" 200 -
10.0.2.2 - - [04/Nov/2018 18:55:42] "GET /test.html HTTP/1.1" 200 -

forwarded_port構成まとめ

forwarded_port構成のポイントは下記のようにまとめられます。

  • ホストOS外の他のPC/サーバからゲストOSにアクセスさせるためには、通常はforwarded_portを使う。
  • forwarded_portはNAT Adapterに対して設定が追加される。
  • ゲストOSのポートを外部に公開する際には、Windows Firewallをきちんと設定する。

public_network

基本的な設定・構成

Vagrantはpublic_networkという構成が利用できます。これは一般的には、仮想ブリッジ/仮想スイッチと呼ばれる方式で接続するものです。

ブリッジ/スイッチとは、所謂スイッチングハブです。Ethernetレベル(L2)のフレームを中継・転送する機器です。
詳細は、下記サイトが参考になるでしょう。

Ethernet LANとは
3 Minutes Networking

そのため、公式ドキュメントには下記のように書いてあります。将来的に、public_networkは:bridgedに置き換えられるとのことです。

Public Networks - Networking - Vagrant by HashiCorp

Confused? We kind of are, too. It is likely that public networks will be replaced by :bridged in a future release, since that is in general what should be done with public networks, and providers that do not support bridging generally do not have any other features that map to public networks either.

具体的にどのような構成になるか、図で表すと次のようになります。
まずホストOSのNIC(ネットワークアダプター)に、仮想ブリッジ用のドライバをインストールし、仮想ブリッジとして動作させます。ゲストOSのNICは、あたかも物理的なLAN(この場合は10.1.1.0/24)に接続されているかのように通信することができます。

VagrantNetworking4_public_network.png

Vagrantfileは下記のようになります。デフォルトではDHCPクライアントが動作するよう設定されます(ホストOSが)。
そのため、一般的なネットワーク構成(=DHCPサーバが稼働しているネットワーク)であれば、特に他の設定は不要です。

ただしこの構成は1点注意事項があります。会社/組織のセキュリティポリシー等により、LANへ接続できるホストに制限がある場合、接続許可を申請するなどの対応が必要になるでしょう。
また、勝手なIPアドレスを付与することは恐らく出来ないでしょう。

Vagrantfile
Vagrant.configure("2") do |config|
  config.vm.box = "mvbcoding/awslinux"
  config.vm.box_url = "https://app.vagrantup.com/mvbcoding/boxes/awslinux"

  config.vm.provider "virtualbox" do |v|
    v.memory = 512
    v.cpus = 1
  end

  # This VM uses "Public Networks" described on the document below.
  # https://www.vagrantup.com/docs/networking/public_network.html
  config.vm.define :"vm2public" do |c2|
    c2.vm.hostname = "vm2public"
    c2.vm.network "public_network"
  end

  config.vm.provision :shell, :path => "bootstrap.sh"

end

上記VM内でifconfigを実行すると下記のように出力されます。eth1に対し、ホストOSと同一のネットワークセグメントのIPアドレスが付与されていることが確認できます。

[vagrant@vm2public ~]$ ifconfig
eth0      Link encap:Ethernet  HWaddr 08:00:27:AF:85:70
          inet addr:10.0.2.15  Bcast:10.0.2.255  Mask:255.255.255.0
          inet6 addr: fe80::a00:27ff:feaf:8570/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:2139 errors:0 dropped:0 overruns:0 frame:0
          TX packets:3683 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:148508 (145.0 KiB)  TX bytes:212696 (207.7 KiB)

eth1      Link encap:Ethernet  HWaddr 08:00:27:DB:35:B6
          inet addr:10.1.1.40  Bcast:10.1.1.255  Mask:255.255.255.0
          inet6 addr: fe80::a00:27ff:fedb:35b6/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:7146 errors:0 dropped:0 overruns:0 frame:0
          TX packets:102 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:1821617 (1.7 MiB)  TX bytes:9281 (9.0 KiB)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:1320 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1320 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1
          RX bytes:116124 (113.4 KiB)  TX bytes:116124 (113.4 KiB)

VirtualBoxのGUIから当該VMの構成を確認してみます。「アダプター 2:」として、ブリッジアダプターが指定されていることが分かります。これは、物理的なNICである「Realtek PCIe GBE Family Controller」に仮想ブリッジが設定されており、これを経由して物理LANに接続されている、ということになります。
2018-11-05_00h26_22_mark.png

Windowsのネットワークアダプター設定から、当該NICにブリッジ用のドライバが追加されていることを確認することができます。通常、物理的なEthernetデバイスとして「ローカル エリア接続」というネットワークアダプター(私のPCの場合は「INT_IF」という名称になっていますが)が作成されますが、このプロパティから確認することができます。
2018-11-05_04h38_57.png

public_network構成まとめ

public_network構成のポイントは下記のようにまとめられるでしょう。

  • public_networkは、ホストOSの物理的なNICに仮想ブリッジを作る
  • ゲストOSは上記仮想ブリッジ経由で、物理的なLANに接続される
  • 会社/組織のネットワーク内でpublic_networkを利用する場合、一般的には接続許可/申請等が必要になるので注意する

おわりに

VMWare/VirtualBox、そしてVagrant等のツールの発展により、手軽に仮想環境を利用できるようになって早幾年。しかし、ちゃんと使いこなすためにはその仕組みを理解しておく必要があります。理解があいまいなままだと、いざトラブルの時にお手上げになってしまいます。

本稿、一般的なVMに関する仮想ネットワークを扱ってきましたが、次回はDockerのネットワークについて書きたいと思います。これもひろく使われている割には、ネットワークに関する理解不足のためトラブルに見舞われる、という例が少なくないように思います。

参考文献

3分間ネットワーキング

ネットワークの初学者向けの資料といえばこれです。まずは第1回から第36回まで読んでみてください。
計算上は、3分×36回=108分で読めます。

ネットワークエンジニアとして

これもネットワーク初学者向けの解説がまとまっています。

そのほか、参考になりそうな資料は下記記事にまとめています。
【ネットワーク】AWS VPCに関する諸々を理解するために【VPC】 - Qiita

46
Help us understand the problem. What is going on with this article?
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
46
Help us understand the problem. What is going on with this article?