KVMでsystemd-networkdを用いてブリッジ構築をする方法

これはKMC Advent Calendar 2022 - Adventar の9日目の記事です。

昨日はcrashRTさんの「After Effects おすすめチュートリアル」でした。

はじめに

こんにちはこんにちは。KMC2回生のsegreです。昨日まで第45代の副会長をしていました。今年からは副代表としてもう1年仕事をします(何をするのか全くわかっていませんが...)。最近は趣味の釣りもオフシーズンに入り、サーバーの環境構築をしたりLinuCの勉強をしたりとLinuxに浸かった生活をしています。

さて、この記事ではLinuxKVM仮想マシンをsystemd-networkd環境でブリッジを構築し外部と接続する方法についてお話します。

KVMについて

KVM (Kernel-based Virtual Machine) とはLinuxに組み込まれたオープンソースの仮想化ソフトウェアと一般的に説明されます。KVMLinuxカーネルをハイパーバイザーに変換させ、仮想ネットワークカード、グラフィックアダプター、CPU、メモリー、ディスクなどを備えています。

ただし、KVMは仮想化ソフトウェアではなくカーネルモジュールとしてLinuxに組み込まれているものであり、QEMUによるエミュレーションを支援するという説明がより正確なものとなります。この部分の説明は本記事の趣旨とは外れるため詳しくは

QEMU、KVM、libvirtの基礎知識まとめ - えんでぃの技術ブログ などを参照してください。

KVM仮想マシンを作成

今回はこちらのコマンドでUbuntu Server 22.04.1 仮想マシンtest_vmを作成しました。

# virt-install \ 
        --connect=qemu:///system \
        -n test_vm \
        -r 2048 \
        --disk path=/var/lib/libvirt/images/test_vm/root.img,size=20 \
        --check path_in_use=off \
        --vcpus=2 \
        --os-variant=ubuntu22.04 \
        --network network=default  \
        --location /home/segre/Documents/iso/ubuntu-22.04.1-live-server-amd64.iso,kernel=casper/vmlinuz,initrd=casper/initrd \
        --console pty,target_type=serial\
        --nographics \
        --extra-args 'console=ttyS0 115200n8 serial' 

ここでnetwork network=defaultと指定していることに注意して下さい。

networkにdefaultを指定するとどうなるか

network network=defaultと指定すると、仮想マシンが外部ネットワークと通信する際にiptablesのNAT(IPマスカレード)機能が用いられます。

IPマスカレード

IPマスカレード機能を用いると、仮想ブリッジvirbr0を作成し、virbr0に到達したパケットは、送信元IPアドレスを物理NICIPアドレスに変換して外部ネットワークに送信します。

ここでNIC、eth、vnet、brについて説明します。

  • NIC : ネットワークインターフェースカードの略でネットワーク通信に必要なカード型の機器であり、イーサネットを挿すためのコネクタなどが付いています。
  • eth : イーサネットの略でネットワークインターフェースを指します。IPアドレスが割り当てられ、NIC内でのネットワーク通信を担っています。仮想ネットワークインターフェースの場合はveth0/veth1などと表されることが多いです。
  • vnet : タップデバイスを表しています。KVMにおいてホストマシンが仮想マシンに接続するために用いられるデバイスです。
  • br : 仮想ブリッジを表しています。virbrは仮想環境でIP変換を行うためのスイッチ、brは仮想ブリッジを接続するためのスイッチです(後述)。

図からも分かる通り、仮想マシンから外部へ通信することはできますが、外部から仮想マシンに通信することはできません。そのため、外部から通信したい場合はbr0を作成し、仮想ブリッジを構築する必要があります。続いての章ではその方法を説明します。

物理NICを仮想スイッチに接続する

物理NICを仮想スイッチに接続する仮想ブリッジを作成することにより外部と通信することができるようになります。ここではsystemd-networkdによって仮想ブリッジbr0を作成します。なおLinuxでは他にNetworkManagerによって管理する方法もあり、NetworkManagerの場合は設定方法が違うことに注意してください。

仮想ブリッジを作成する

ネットワーク設定の確認をする

まずはネットワークがどのような構成になっているか確認します。 $ brctl show コマンドによりブリッジとそのブリッジに取り付けられたインターフェースを表示することができます。

$ brctl show
bridge name     bridge id               STP enabled     interfaces
virbr0          XXXX.YYYYYYYYYYYY       yes             vnet3

systemd-networkdによって管理されている場合はvirbr0という仮想ブリッジが表示されていると思います。ここではvnet3というタップデバイスが接続されていることがわかります。インターフェースの詳細を確認するには$ ip link show master virbr0から確認することができます。

続いて仮想マシンのvethがどのタップデバイスに接続されているか確認するためには$ virsh dumpxml VM名を実行して仮想マシンの設定xmlファイルのinterfaceタグを確認します。今回はVMを1つしか作成していないのでtest_vmのveth0がvnet3に接続されているかがわかりますが、VMが複数ある場合はこのコマンドによって確認する必要があります。test_vmxmlを見てみると

<interface type='bridge'>
      <mac address='XX:XX:XX:XX:XX:XX'/>
      <source bridge='virbr0'/>
      <target dev='vnet0'/>
      <model type='virtio'/>
      <alias name='vnet3'/>
</interface>

となっていることからvnet3に接続されていることがわかります。

ブリッジを定義する

続いてnetworkファイルを編集していきます。なお、brctlコマンドを用いてブリッジを構築することもできますが、brctlによって作られたブリッジは再起動すると消えてしまうため、今回はnetworkファイルを編集する方法を紹介します。 systemd-networkdではネットワークファイルは/etc/systemd/network/以下に保存されています。

$ ls /etc/systemd/network -l
total 8
-rw-r--r-- 1 root root 223 Aug 23 13:39 eth0.network

eth0の設定ファイルがあることからIPマスカレードが用いられていることがわかります。このファイルの中身を見てみると

$ cat eth0.network
[Match]
Name=eth0

[Network]
Address=XXX.YYY.ZZZ.AA/24
Gateway=XXX.YYY.ZZZ.1

となっています。

ファイル内容を説明します。[Match]セクションにはどのインターフェースを用いているか記述します。Name=とするとデバイス名のリストにマッチします。

[Network]セクションにはDHCHの設定等のネットワークの設定を記述します。Adress=とするとIPアドレスNICに割り当てることができます。また、Gateway=とするとデフォルトゲートウェイを設定できます。

なお、Adress=Gateway=はそれぞれ[Adress]セクションと[Gateway]セクションに入れるのが適切ですが、[Address] が Address キーだけを含み [Route] セクションが Gateway キーだけを含む場合、略式表記として [Network] セクションに Address=Gateway=を記述することができます。

ここからはブリッジbr0を定義していきます。新しいブリッジを定義するためにはnetworkファイルとnetdevファイルを作成する必要があります。ここではbr0.networkbr0.netdevを作成します。

br0.netdevファイルは

$ cat br0.netdev
[NetDev]
Name=br0
Kind=bridge

とするとブリッジbr0を定義できます。

続いてbr0.networkはこれまでeth0.networkに記述されていた内容を書きます。そのため

$ cat br0.network
[Match]
Name=eth0

[Network]
Address=XXX.YYY.ZZZ.AA/24
Gateway=XXX.YYY.ZZZ.1

とするのが適切です。

eth0.networkはブリッジbr0に接続しているため

$ cat eth0.network
[Match]
Name=eth0

[Network]
Bridge=br0

とします。 以上を記述して# sysmctl restart sysytemd-networkdとすると仮想ブリッジを作成することができます。

タップデバイスとブリッジを接続する

最後に先程定義したbr0とタップデバイスvnet0を接続すると通信することができます。VMの設定を変更するためにはxmlファイルを編集します。

# virsh edit test_vmとするとxmlファイルを編集できます。# virsh shutdown test_vmVMをshutdownしてから、ファイル中のinterfaceセクションのに変更します。

<interface type='bridge'>
      <mac address='XX:YY:XX:YY:XX:YY'/>
      <source bridge='br0'/>
      <model type='virtio'/>
 </interface>

以上でブリッジを構築することができます。brctlを確認してみてください。

$ brctl show
bridge name     bridge id               STP enabled     interfaces
br0             XXXX.YYYYYYY       no              eth0
                                                                       vnet0
virbr0         XXXX.YYYYYYY       yes

# virsh start test_vmすると、br0がeth0とvnet0をつなぎ、外部から通信できるようになっていると思います。

まとめ

今回はsystemd-networkd環境でKVMで新しくブリッジを作成し外部から通信する方法を紹介しました。

まとめると

  1. NetworkManager/Systemd-Networkd/Netplanかを確認 brctl showip link show master virbr0virsh xmldump [VM] などからネットワーク図作成 systemd-networkd

  2. /etc/systemd/network/ 以下にブリッジ定義 → networkd restart

  3. VM shutdown → # virsh edit [VM] から ... 編集 → VM start

となります。

今後はitamaeを用いたサーバー管理等を勉強していきたいです。 ありとうございました。

明日はtenさんの「『実装意図』を語るために」です。

参考にさせていただいた記事

KVM とは - Linux 仮想化の仕組み | Red Hat

・ QEMU、KVM、libvirtの基礎知識まとめ - えんでぃの技術ブログ

systemd-networkd - ArchWiki