Linuxで使いやすい仮想環境をさくっと使う (QEMU/KVM, Systemd nspawn)
Live With Linux::software
- TOP
- Articles
- Live With Linux
- Linuxで使いやすい仮想環境をさくっと使う (QEMU/KVM, Systemd nspawn)
まえがき
この記事は「systemd-nspawnでManjaro LinuxからArch Linuxする」のoverrideでもある。
はじめに
Manjaro Linuxユーザーでもいくつかの理由により別環境を必要とすることもある。
典型的にはサーバー系ソフトウェアを試すときだ。もちろん、多彩なパッケージにAURまで備えるManjaro Linuxであればきっとサーバーソフトウェアを導入することもできるはずだが、設定を試したりするにも、あるいはその変更があくまでリハーサルであり最終的にdiscardするためにも、普段使っている環境から独立・隔離された場所で試したいと思うこともあるはずだ。 また、テストなどにおいて設定などが干渉しないようにしたいこともあるだろう。
一般的にこのようなケースで活躍するのが仮想化だ。 ここでは環境を用意するのが容易で、優れた点も多いQEMU/KVMと、Systemd-nspawnの話をしようと思う。
QEMU/KVM
概要
QEMUは極めて多彩なハードウェアをエミュレーションする強力な仮想マシンだ。 その柔軟性は驚くべきものだが、その反面性能はあまり高くない。特にVMWareやVirtualBoxと比べると、レガシー環境では優れているが、性能面ではだいぶ劣ってしまう。
それを補うのがKVMだ。KVMはLinuxのカーネルモジュールであり、Linuxカーネルをハイパーバイザーとして動作させるものである。 これを使うことでQEMUをハイパーバイザー型仮想環境として利用することができる。
そのポイントは、コマンドラインから操作するものの、非常に簡便で、なおかつ「KVMを使うか否か」の差がオプションの有無に留まることだ。
インストール
Manjaro
Linuxの場合はqemu
パッケージでQEMUがインストールされる。
他のアーキテクチャ(32bit
Intel(IA32)を含む)を必要とする場合はqemu-arch-extra
が必要だ。
さらに、KVMを使う場合はlibvert
もインストールする。
QEMUのGUIフロントエンドも存在はするものの(例えばvirt-manager
)、最低限コマンドが打てるなら煩雑になるだけである可能性が高い。
ディスクを作る
QEMUのネイティブなディスク形式はQCow2である。
$ qemu-img create -f foo-linux.qcow2 30G
これで30GBのfoo-linux.qcow2
ディスクが出来上がる。
実際にはディスクに使用に従って容量が増加するので、いきなり30GBのファイルが出来上がるわけではない。
ISOイメージから起動する
仮にUbuntuのISOイメージをブートしてみよう。
$ qemu-system-x86_64 -m 8G -cdrom ubuntu-ja-19.10-desktop-amd64.iso
ここでは-m
オプションでメモリ8GBで起動している。メモリ4GBでは厳しかった。
ディスクを接続した状態で起動する
-drive
オプションでディスクイメージを指定する。
$ qemu-system-x86_64 -m 8G -cdrom ubuntu-ja-19.10-desktop-amd64.iso -drive file=ubuntu.qcow2
通常はこれで良いのだが、複数ディスクがあり、なおかつブータブルになっている場合にどのディスクからブートするかという問題が生じてしまうかもしれない。このような場合は、全て-drive
オプションで指定する必要がある。
$ qemu-system-x86_64 -m 8G -drive file=ubuntu-ja-19.10-desktop-amd64.iso,index=0,media=cdrom -drive file=ubuntu.qcow2,index=1,media=qcow2
インストール後は単純にディスクを指定して起動すれば良い。
KVMを使う
CPUが仮想化支援を持っており、カーネルがKVMを有効にしており、KVMがモジュールである場合ロードされており、libvert
が導入されている場合、単純に-enable-kvm
オプションによってKVMが有効になる。
$ qemu-system-x86_64 -enable-kvm -m 8G -cdrom ubuntu-ja-19.10-desktop-amd64.iso
レガシーOSを起動する
単にi386エミュレータで起動すれば良いのだが、より厳しい場合は細かな指定が必要かもしれない。
qemu-system-i386 -m 128M -drive file=$HOME/rh7.qcow2,index=2,format=qcow2 -drive file=RH7-1.iso,index=0,media=cdrom -drive file=RH7-2.iso,index=1,media=cdrom -vga cirrus -machine pc-i440fx-2.8 -no-acpi -no-hpet
主にはOSからハードウェアが認識できるように古いチップセット、古いビデオカードとして見せている。 その分性能は落ちる。
だが、私が試した限り、Red Hat Linux 7はこれでもX Window Systemがスピンしてしまうし、Vine Linux 2はAnacondaが「ディスクにスペースがない」と認識してしまう。
ネットワーク
デフォルトではゲストOS側から見るとブリッジ接続された状態になっており、IPアドレスを持つインターフェイスが見える。 この状態で外部に通信するとちゃんと通信は届くし、ホストに対して通信することもできる。
ホストから通信したい場合、もっとも手っ取り早いのはデフォルトで起動し、ゲスト側からssh -R
をかけてホストのポートからゲストのポートへ転送することである。
QEMUのネットワーク設定はなかなか複雑なので、簡単に処理するならこの方法が勧めやすい。
また、例えばホストのTCP/5555
をゲストのTCP/22
に転送するには-device e1000,netdev=net0 -netdev user,id=net0,hostfwd=tcp::5555-:22
のようなオプションを書くことで実現することができる。
Systemd nspawn
概要
systemd-nspawn
はSystemdに備わっているコンテナ仮想化機能である。
Linuxの名前空間分離機能であるcgroupsを利用したものであり、LXCなどと同じタイプのものであるが、Systemdがある環境ならばコマンド一発のお手軽さである。
仕組みは簡単だ。コマンドの引数としたディレクトリをルートディレクトリとするファイルツリー構造上で名前空間を分離し(より適切な言い方なら、その分離した名前空間のうちファイル空間のルートディレクトリを指定したディレクトリにし)、その名前空間上でdefault.target
Systemdユニットを起動する。
Systemdのプログラムそのものもコンテナ側のものを使用する。default.target
を起動するのはコンテナのSystemdなので、Systemd
nspawnはコンテナを作ったらSystemdを起動するというところまでが仕事だ。
このため、ホスト環境、コンテナ環境ともにSystemdが存在する必要がある。 一方、カーネルはホスト環境で実行中のカーネルだから、コンテナ環境にLinuxカーネルが存在することは必須ではない。 言い換えると、コンテナ環境が実行中のカーネルとかけ離れたカーネルに基づいてビルドされた環境である場合(ABI的な理由で)一部はうまく動作しないかもしれない。
もっとも、この説明は話を簡略化しすぎている。
systemd-nspawn
にはもっと色々と使いみちがあるし、別にコンテナ側にSystemdがなくても使うことは可能だからだ。
basestrap
Arch LinuxにおいてはArch
Linuxの基本的な環境を構築するpacstrap
というコマンドがある。
これはインストールにも使用するものだが、簡単に言えばchroot環境上にpacmanでパッケージをインストールするものである。
# pacstrap -i -c ~/Container base base-devel
Manjaro Linuxでもpacstrap
を使っていたが、Arch
Linuxとは独立のツールであることを強調するためか、コマンド名はbasestrap
に改められた。
パッケージもpacstrap
パッケージからmanjaro-tools
パッケージグループに含まれるようになった。
# basestrap -i -c ~/Container base base-devel
起動と終了
コンテナ環境を起動するには、systemd-nspawn
を使用する。
# systemd-nspawn -b -D ~/Container
終了するにはコンテナ環境上でSystemdを用いてシャットダウンする。
# systemd poweroff
ネットワーク
デフォルトでは1systemd-nspawn
はネットワーク名前空間を分離せず、そのままホストのインターフェイスを共有することになる。
サーバーのテストをしたい場合など、それで都合が悪い場合は、systemd-nspawn
に-n
オプションを追加すると、ネットワークの名前空間が分離され、ホストとコンテナは仮想的なイーサネットケーブルで直接接続された状態になる
(1:1だが、PPPではなくEthernetである)。
自動的にIPアドレスが設定されたりはしないので、まずはip link
を使ってデバイスを確認する。
# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: host0@if5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 72:61:3d:4a:44:1b brd ff:ff:ff:ff:ff:ff link-netnsid 0
iproute2を使ってhost0
ネットワークデバイスにアドレスをセットし、upする。
# ip addr add 192.168.65.2/24 dev host0
# ip link set up host0
同様に、ホスト側のve-*
ネットワークデバイスもセットすれば、1対1で通信できるようになる。
なお、Archwikiには、コンテナはmymachines
としてホスト名で名前解決できるとあるが、コンテナ側でhostnamectl
を使って設定してもうまくいかなかった。