KVMのゲストマシンから外部へ通信できるようにする

Posted by rhoboro on 2016-08-13

前回の続きです。
前回はネットワーク周りの設定を何もせずにKVM仮想マシンを作成しました。 この状態だと、ゲストOSはネットワーク的に孤立した状態となっていて外部と通信できません。
そこで今回はこのマシンからホストOSを通して外部に通信できるようにしていきます。

現在の設定内容の確認

まずはホストマシン、ゲストマシンそれぞれ現在どのようになっているかを確認しておきましょう。
ほぼろの環境では下記のようになっていました。

$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 04:01:4c:ce:86:01 brd ff:ff:ff:ff:ff:ff
    inet 128.199.93.213/18 brd 128.199.127.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet 10.15.0.5/16 brd 10.15.255.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::601:4cff:fece:8601/64 scope link 
       valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
    link/ether 04:01:4c:ce:86:02 brd ff:ff:ff:ff:ff:ff
4: virbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP 
    link/ether 52:54:00:ab:1b:1d brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0
       valid_lft forever preferred_lft forever
5: virbr0-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast master virbr0 state DOWN qlen 500
    link/ether 52:54:00:ab:1b:1d brd ff:ff:ff:ff:ff:ff
6: vnet0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master virbr0 state UNKNOWN qlen 500
    link/ether fe:54:00:f7:83:82 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::fc54:ff:fef7:8382/64 scope link 
       valid_lft forever preferred_lft forever

1.はローカルループバックですね。きっとここを見てる方では見たことのないひとはいないはず。
2.はグローバルIPが割り当てられていて、外の世界とつながっているNICです。(VPSなのでこれも仮想NICのはずです)
3.は...何用でしょうか。わかる人いたら教えてくださいw
4.〜6.に関してはKVMを入れたことによるものなので、まずは下記コマンドの出力から見ていきましょう。

$ brctl show
bridge name     bridge id               STP enabled     interfaces
virbr0          8000.525400ab1b1d       yes             virbr0-nic
                                                        vnet0

これを見るとわかる通り、4.のvirbr0は仮想ブリッジで、libvirtdが自動で作成したものです。 この仮想ブリッジにvirbr0-nicというインターフェースとvnet0というインターフェースが接続されています。
vnet0がKVMゲストマシンのNICと繋がっているtapデバイスと呼ばれるものです。

つぎにゲストマシンの確認です。
まだネットワークの設定をしていないので、シリアルコンソール接続します。

$ sudo virsh console centos7
[sudo] password for rhoboro: 
Connected to domain centos7
Escape character is ^]

CentOS Linux 7 (Core)
Kernel 3.10.0-327.el7.x86_64 on an x86_64

[root@localhost ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 52:54:00:f7:83:82 brd ff:ff:ff:ff:ff:ff

どこにもつながっていないeth0がありますね。

一応ホストマシン側でも仮想マシン定義ファイルの内容を確認。

$ sudo virsh edit centos7
    ~省略~
    <interface type='network'>
      <mac address='52:54:00:f7:83:82'/>
      <source network='default'/>
      <model type='virtio'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
    </interface>
    ~省略~

今回試すネットワーク構成

仮想マシンを外部と通信できるようにするには基本的に下記の2つの方法があるようです。

  1. ホストマシンのiptablesのIPマスカレード機能を使い、物理NICに来たパケットを仮想マシンのNICが接続された仮想ブリッジに転送する(NAT)
    • libvirtインストール時に自動作成される仮想ブリッジvirbr0はこのためにある。
    • 外からゲストマシンにはアクセスできない。ホスト<=>ゲスト間の通信は可能。
  2. 仮想マシンのNICが接続された仮想ブリッジを作成し、仮想ブリッジと物理NICを直接接続する
    • ホストマシンと同じネットワークに属することになるので、外からもゲストマシンにアクセス可能。

今回は1.の方法を試します。 本当は2.の方法を試したかったのですが、いま利用しているDigitalOceanのDropletには静的なグローバルIPが1つだけ割り当てられている状況なため、 おそらく2.の方法はIPアドレスを割り振ってもらえないはず。 やっぱリアルマシンのLinuxが欲しいな...
(コレ、いま書きながら気付きました。実際に試す前に気づけただけでもよしとしましょうw)

実際に試してみる

さきほど記載した通り、libvirtdはデフォルトで仮想ブリッジvirbr0を自動で作成しています。 今回はこの仮想ブリッジを使い、

KVMゲストマシンのNIC => vnet0 => virbr0 => ホストマシンのNIC => 外の世界

という流れで外部へ通信ができるようにします。

何はともあれ、実際に試してみるのが早いと思いますので、何も考えずにまずは下記のコマンドを実行してみてさい。

$ sudo virsh attach-interface centos7 network default

たったこの1行だけでKVMゲストマシンから外部へ通信ができるようになるはずです。
気になる方はゲストマシンにログインしてpingでも飛ばしてみましょう。 先ほどまでと違い、相手さえいれば届くはずです。

ざっくり解説

まずは、defaultとは何か、を見ていきましょう。

# virsh 
Welcome to virsh, the virtualization interactive terminal.

Type:  'help' for help with commands
       'quit' to quit

virsh # net-list
 Name                 State      Autostart     Persistent
----------------------------------------------------------
 default              active     yes           yes

virsh # net-dumpxml default
<network connections='2'>
  <name>default</name>
  <uuid>49067ae7-e979-4a08-b702-f95d1287febb</uuid>
  <forward mode='nat'>
    <nat>
      <port start='1024' end='65535'/>
    </nat>
  </forward>
  <bridge name='virbr0' stp='on' delay='0'/>
  <mac address='52:54:00:ab:1b:1d'/>
  <ip address='192.168.122.1' netmask='255.255.255.0'>
    <dhcp>
      <range start='192.168.122.2' end='192.168.122.254'/>
    </dhcp>
  </ip>
</network>

慣れるまではこれを見てもわからないことだらけだと思いますが、defaultとはKVMが作成した仮想ネットワークの名前です。
具体的には、natで実NICにフォワードされている、192.168.122.0/24のネットワークで、仮想ブリッジvirbr0が接続されています。 また、このネットワークはdhcpによりIPアドレスを割り当てていきます。(dnsmasqというDNSサーバーが動いています)

先ほどのコマンドsudo virsh attach-interface centos7 network defaultはこのネットワークと接続するための仮想インターフェースをゲストマシンにアタッチするためのコマンドでした。
実際、ゲストマシンでip aコマンドを試してみるとensXというインターフェースが追加されており、192.168.122.XのIPアドレスが割り当てられていることがわかると思います。

さて、これで外部と通信できるようになりましたが、せっかくなので次回はもう少しKVM、libvirtでのネットワーク周りについて見ていこうと思います。 ということで、先ほど追加したインターフェースはip aで調べたMACアドレスをコピーして、

$ sudo virsh detach-interface centos7 --mac XX:XX:XX:XX:XX:XX

で削除しておきましょう(笑

まとめ

ネットワークは私自身もまだ勉強中なため難しいところが多く感じてしまいます。
ただ、一度理解してしまうと最初に考えていたものほど難しくなく、「なんだ、こんなことだったのか」と納得できる部分も多いです。
実際、この記事ではあまり触れられていないのですが、この記事を書くことでブリッジに関する理解は確実に深まりました。
次回は定義ファイルのXMLを編集しながらネットワーク周りをもう少し弄ろうと思います(笑

参考にさせていただいたサイト

tags: KVM, DigitalOcean