Linux + KVM で仮想化ホストを構築してみた(bridge / cloud-init編)
KVM/libvirt の基本的な仮想化ホストを構築した後、Kubernetes ノードとして利用するための仮想マシン環境を整備します。本記事では、Debian のネットワーク管理を ifupdown から systemd-networkd に移行し、Linux ブリッジによる VM ネットワークを構成します。さらに cloud-init とクラウドイメージを利用して、仮想マシンを高速にデプロイする方法を紹介します。
基本編では、KVM/libvirtで仮想化ホストを構築しました。今回は、その仮想化ホストの上に、複数の仮想マシンを作成しKubernetesのノードとして設定していきます。
ただし、Kubernetesを実際にインストールする前に、いくつかの準備及び最適化を実施します。
ホスト:ifupdownをsystemd-networkdに置き換える
初めて Debian を使う人は、最新版の Debian 13(2025年8月リリース)の標準 CLI インストールで、ネットワーク管理ツールとして ifupdown がいまだに使われていることに驚くかもしれません。
ifupdown の起源は 1990 年代後半にさかのぼります。/etc/network/interfaces に設定を書き、ifup / ifdown でインターフェースを制御するという非常にシンプルな仕組みで、長年 Linux の基本的なネットワーク設定方法として使われてきました。
Debian は、派手な流行に飛びつくディストリビューションではありません。Debian Project の
Debian Social Contract にも表れているように、安定性と透明性を重視する文化があります。そのため、ifupdown は「古いから置き換える」という理由だけでは廃止されていません。
しかし クラウドネイティブの文脈では事情が少し変わります。例えば Kubernetes のノードでは、コンテナの起動・停止に伴い多数の仮想インターフェースやルーティングが動的に作成されます。このような環境では、イベント駆動でネットワークを管理する systemd-networkd の方が相性が良いと言われています。
今回は、仮想化ホスト上に仮想マシンを作成し、それらを Kubernetes のノードとして利用する構成を想定します。仮想化ホスト自体は ifupdown のままでも問題ありませんが、ゲスト OS との構成を統一するため、本記事では systemd-networkd への移行を行います。
現行ifupdownの設定の確認
まずはcat /etc/network/interfacesで現在の設定を確認します。今回は、DHCPサーバーが存在するものの、サーバーであるため固定IPにしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
systemd-networkd用設定ファイルの作成
systemd-networkdは、/etc/systemd/networkディレクトリを使用して構成を管理します。このディレクトリに、3種類のファイルを含むことができます。
| ファイルの種類 | レイヤー | 用途 |
|---|---|---|
| .linkファイル | L1 - L2下層 | 物理NICの属性、MACアドレス、MTUなど |
| .netdevファイル | L2上層 | L2仮想デバイス(bridge / vlan / bondなど) |
| .networkファイル | L3 | IPアドレス、DHCLクライアントとしての動きなど |
このように、TCP/IPの階層を意識した役割分担になっています。
今回は、MACアドレスの指定もbondなどの利用もないため、.networkファイルのみ作成していきます。
1 | |
他のsystemdの設定と同様に、ファイル名に数字をつけることで優先順位を調整できます。中身はこんな感じです。
1 2 3 4 5 6 7 | |
切り替えの実施
設定に問題がないことを確認したら、一旦ifupdownの設定を退避させます。ifupdownは、設定ファイルが存在しない場合サービスが動いているものの、何も設定変更しようとしないため、制御権が自然とsystemd-networkdに移ります。
1 | |
systemd-networkdを有効化し、再起動します。
1 2 | |
systemd-resolvedについて
ここで、「systemd-networkdを有効化したのに、一緒にsystemd-resolvedも有効化しないのはおかしくない?」というご指摘があるかもしれません。
確かに私自身も、その中途半端な感じを否めませんが、ここでは一旦systemd-resolvedなしで進めます。
再起動後、動作確認します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | |
systemd-networkdが正常に動作していることを確認してから、ifupdownのサービスを停止します。
1 | |
再度、ネットワークに問題ないことを確認してから、ifupdownパッケージを完全に削除します。
1 2 | |
この手順を踏まえることで、リモート(SSHなど)からでも切り替えが可能です。
ブリッジネットワークの構成
基本編では、デフォルトのNATネットワーク上で仮想マシンを構築しました。
これでもKubernetesのテスト環境として問題ありませんが、もし複数の仮想化ホストが存在し、その上にある仮想マシンを、物理ホストを跨いでKubernetesクラスタを構成する場合、フラットな通信が必要なため、NAT環境では正常に動作しません。
そのため、ブリッジネットワークを構成し、仮想マシンをブリッジネットワークに移動します。
Linuxブリッジの作成
ここでは、systemd-networkdの場合のブリッジの作成方法を記載します。ifupdownの場合は手順が異なり、レガシーパッケージbridge-utilsのインストールも必要です。
前のステップで/etc/systemd/network/10-enp0s25.networkを作成しましたが、以下のように変更および新規作成します。
1 2 3 4 5 6 | |
1 2 3 4 | |
1 2 3 4 5 6 7 | |
ポイントは
- enp0s25のL3設定では、IPアドレスの設定を削除し、代わりに
br0というブリッジに参加する旨のBridge=br0を記述 - L2上層部機能を定義する
20-br0.netdevで、ブリッジを定義 br0というインターフェースに対して、L3のIP設定を付与
設定内容を確認後、systemd-networkdを再起動します。通常、ネットワーク断は発生しませんが、設定ミス等があると切れることがあるので、リモートではなく目の前で作業したほうが良いかもしれません。
1 | |
動作確認
networkctlコマンドで、br0とenp0s25の詳細状況を確認します。
1 2 3 4 5 6 7 8 9 10 11 12 | |
1 2 3 4 5 6 7 8 9 10 | |
仮想マシンのbr0への移動
停止状態のvm1のネットワークI/Fを確認します。
1 2 3 4 | |
変更します。
1 2 | |
変更後の確認をします。
1 2 3 4 | |
IPアドレスの確認
ただし、ここの問題は、ゲストOSのIPアドレスがわからないのです。例えば今までdefaultのNATに存在するVMに対して、IPアドレスを確認すると
1 2 3 4 | |
このように、IPアドレスが表示されますが、br0に切り替えたVMに対して同じコマンドを実行すると
1 2 3 | |
このように、何も表示されません。ただし、実際にシリアルコンソールで接続して、IPアドレスを確認すると、問題なくIPアドレスがDHCPよりアサインされていることがわかります。
1 2 3 | |
では、IPアドレスを持っているのに、なぜホストがそれを検出できないのか?次のセクションでこれを解決していきます。
IPアドレスとQEUMエージェント
domiflistの情報源
virsh domifaddr vm1を実行すると、IPアドレスが表示される仕組みについて、virshは2つの情報源からゲストOSのIPアドレスを取得できます。
- 1つは、libvirt内蔵のDHCPサーバーのリース情報からです。例えばdefaultのNATネットワークの場合、DHCPサーバーが
192.168.122.0/24のネットワークからIPアドレスをリースしているため、当然ながらどのVMがどのIPアドレスを持っているかを知っています。 - もう1つは、ゲストOSにインストールされているQEUMエージェントです。これはVMwareでいう
vmtoolsに該当するパッケージです。ブリッジネットワークなど、libvirt管理外のネットワークに接続しているゲストOSのIPアドレスを取得するには、このエージェントが必要です。
また、デフォルトではリースから情報を取ってきていますので、エージェントを情報源として使用するには、明示的に指定する必要があります。
今回は、VMをbr0に切り替えたため、当然ながらリース情報からIPアドレスを取得できません。また、QEUMエージェントも未インストールのため、情報を取得できないはずです。試してみましょう。
1 2 3 4 5 6 7 | |
QEMUエージェントのインストール
そこでゲストOSに接続し、QEMUエージェントをインストールします。
1 | |
インストール後、再度ホスト側で確認します。
1 2 3 4 5 6 7 8 | |
このように、正常に表示されるようになります。
クラウドイメージの利用とcloud-init
これまでは、ISOイメージから仮想マシンのゲストOSをインストールし、ブリッジネットワークへの公開、QEMUエージェントのインストールまで実施してきました。
しかし、複数ノードのKubernetes環境を構築するには、たくさんの仮想マシンが必要で、1台ずつISOからインストールして、設定していくのは非常に煩雑です。
これを解決するには、2000年代頃ですと、「テンプレートとなる仮想マシンを作成しておいて、あとはテンプレートからクローンするだけで良い」という発想が王道ではないかと思います。
もちろん、KVM/libvirtはクローンや、VMwareでいう「Linked Clone」に対応しています(Linked Cloneに相当する概念はoverlay qcow2です)。しかし、このやり方は結構レガシーで、クローンした後にそれぞれの仮想マシンにログインして、個別設定(IPアドレスやホスト名などなど)を行う必要があります。
よりモダンな方法は、cloud-initです。cloud-initはVMの初回起動時に、自動で初期設定を行う仕組みで、2000年代の言葉で説明すると「WindowsのRunOnceやSysprep」、「Linuxの古典的な/etc/rc.local」に近いものです。
cloud-initを利用するにはcloud-initパッケージをインストールしておく必要がありますが、そこで各ベンダーがリリースしている「クラウドイメージ」があって、実体はOSインストール済み・cloud-initパッケージインストール済みのディスクイメージです。
クラウドイメージを使用することで、OSインストール不要、cloud-initによる迅速なデプロイが実現できます。
準備作業
cloud-initは主にAWSやGoogle Cloudなどのクラウド環境で使われる仕組みなので、今回のようなクラウド以外の環境(NoCloud)で使用するには、パッケージのインストールが必要です。
1 | |
このパッケージは、cloud-localdsやgrowpartなどのクラウドイメージ関連ツールを提供しています。
次に、クラウドイメージをダウンロードします。今回は、ゲストOSとしてUbuntuを利用する予定なので、Ubuntuのクラウドイメージをダウンロードします。
1 | |
クラウドイメージ
Ubuntu 25.10 (Questing Quokka)のdailyビルドを使用します。2026年3月14日現在の最新は、[20260310]のものです。
最後に、ゲストOSにログインするためのキーペアを作成します。パスワードを使用したログインも可能ですが、セキュリティの観点でキーペアを使用します。
1 2 3 4 | |
パブリックキーをメモしておきます。
パブリックキーについて
パブリックキーのフォーマットは、<type> <base64-key> [comment]です。
typeとkeyそのものは必須ですが、コメントはオプションです。
seedの作成
seedとは、cloud-init実行時の設定情報を保存する仮想ディスクです(.isoや.imgなど)。このseedは、主に3つの部分から構成されています。
| 構成要素 | 必須 | 内容 |
|---|---|---|
| user-data | 必須 | cloud-init のメイン設定。ユーザー作成、SSH鍵、パッケージインストール、コマンド実行、hostname など VM 初期化の設定を書く。 |
| meta-data | オプション | インスタンス識別情報を与える。VM を clone する場合に instance-id を変えるためにも使う。 |
| network-config | オプション | ネットワーク設定を渡す。DHCP / static IP / gateway / DNS などを cloud-init 経由で OS のネットワーク設定(netplan / networkd など)に反映させる。 |
今回は、以下の内容でこの3つのファイルを作成します。どれもYAML形式ですが、user-dataとmeta-dataはcloud-initの書式、network-configは対象サーバーがUbuntuなのでNetplan形式で記述します。
user-data
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
user-dataについて
このファイルの書き方は、公式サイトをご参照ください。
meta-data
1 2 | |
network-config
1 2 3 4 5 6 7 8 9 10 11 | |
network-configについて
Netplanの設定ファイルでは、通常だと「enp1s0」のようなインタフェース名が必要ですが、match: driver: virtio_netによって、インターフェース名が分からなくても仮想NICがマッチされ、設定されます。
仮想NICが1つしかない場合、正しく動作することが保証されます。
上記の3つのテキストファイルを使用して、seedを作成します。
1 | |
仮想マシンの作成
クラウドイメージとseedをコピーします。
1 2 | |
仮想マシンを作成します。既存ディスクイメージをそのまま使用するので、必ず--importオプションをつけます。
1 2 3 4 5 6 7 8 9 10 11 | |
作成後、起動されます。しばらくすると、IPアドレスが確認できるようになります。
1 2 3 4 5 6 7 8 | |
これで、問題なくSSHでログインできるようになります。