第6章 仮想化環境のゲストとしてのLinux

LPIC-1 102.6 相当

クラウド上のサーバーの多くは、物理マシンではなく仮想マシンとして動いています。この章では、仮想化の基本的な考え方と、Linuxを仮想化環境の「ゲスト」として動かす際に固有の注意点を扱います。

仮想マシンとコンテナの違い

仮想マシン(VM)は、ハイパーバイザと呼ばれるソフトウェアが、CPU・メモリ・ディスクといったハードウェアを仮想的に区切り、その上でゲストOSをまるごと1つ動かす方式です。ゲストOSはカーネルから独立しているため、ホストと異なる種類のOSを動かすこともできます。一方、コンテナは、ホストOSのカーネルを共有したまま、プロセスやファイルシステムの見え方だけを分離する、より軽量な仮想化の方式です。

仮想マシンには、ハードウェアの動作をすべてソフトウェアでエミュレートする完全仮想化と、ゲストOS側にも仮想化を意識した専用ドライバを組み込むことで効率を高める準仮想化という区分があります。近年のCPUには、仮想化支援機能(IntelのVT-x、AMDのAMD-V)が搭載されており、これをハイパーバイザが利用することで、完全仮想化でも実用的な速度を実現しています。

ハイパーバイザ自体にも種類があります。ハードウェア上に直接インストールされ、OSを介さずに動作するType 1(ベアメタル型)はKVMやXenが代表例で、サーバー用途で広く使われています。一方、既存のOS(Windows・macOS・Linuxなど)の上にアプリケーションとしてインストールするType 2(ホスト型)はVirtualBoxやVMware Workstationが代表例で、開発者が手元のパソコンで検証用に使うことが多い方式です。Type 1はOSを経由しない分オーバーヘッドが小さく、サーバー用途に適しています。

仮想化ゲストに固有の注意点

仮想マシンは、テンプレート(既存のディスクイメージ)から複製して作られることがよくあります。この複製方式には、便利さの裏でいくつかの落とし穴があります。

注意: テンプレートから複製したゲストは、SSHのホスト鍵(/etc/ssh/ssh_host_*)や/etc/machine-id、ネットワークインターフェースのMACアドレスやディスクのUUIDまで、元のテンプレートと同じ値を引き継いでしまうことがあります。これらが複数のゲストで重複すると、SSH接続時の警告、systemdやD-Bus関連の不具合、ネットワーク上でのMACアドレス衝突など、原因の分かりにくいトラブルにつながります。

SSHホスト鍵が重複している場合は、いったん既存の鍵を削除してから、ssh-keygen -Aで新しいホスト鍵一式を再生成します。このrmは複製後の正規の再構築手順の一部ですが、削除対象のパス(/etc/ssh/ssh_host_*)を必ず確認してから実行してください。直後にssh-keygen -Aで再生成することを前提とした操作です。

$ rm -f /etc/ssh/ssh_host_*
$ ssh-keygen -A          # 未生成のホスト鍵をまとめて再生成する

machine-idは、そのマシンを一意に識別するための値で、/etc/machine-idと/var/lib/dbus/machine-idに保存されています。これが複製元と重複していると、systemdのジャーナルやD-Busの通信に不具合が出ることがあるため、複製後はsystemd-machine-id-setupのようなコマンドで再生成するのが基本です。

cloud-init による初期設定の自動化

cloud-initは、クラウド環境でゲストが初めて起動するタイミングに合わせて、ホスト名の設定・ユーザー作成・SSH公開鍵の登録・パッケージのインストールなどを自動で行う仕組みです。多くのパブリッククラウドの仮想マシンイメージに、あらかじめ組み込まれています。

設定は#cloud-configから始まるYAML形式のuser-dataとして渡します。

#cloud-config(user-dataの例)
users:
  - name: opsuser
    ssh_authorized_keys:
      - ssh-ed25519 AAAA... user@example.com
    sudo: ALL=(ALL) NOPASSWD:ALL
packages:
  - nginx
  - git
write_files:
  - path: /etc/motd
    content: |
      Managed by cloud-init.
runcmd:
  - systemctl enable --now nginx

usersでユーザーと公開鍵を登録し、packagesで必要なパッケージを導入し、write_filesで任意のファイルを配置し、runcmdで最後に実行したい任意のコマンドを指定する、という組み合わせがよく使われます。これにより、テンプレートイメージ自体は共通のものを使いながら、起動時のパラメータだけでサーバーごとの個別設定を行えます。

準仮想化ドライバとハイパーバイザの検出

仮想マシンのディスクやネットワークインターフェースは、しばしばvirtioという準仮想化ドライバの規格を通じて、ゲストに提供されます。virtio-net(ネットワーク)、virtio-blk・virtio-scsi(ディスク)などがあり、実デバイスをエミュレートするより効率よく動作します。ゲスト側で認識されているデバイスはlspciで、読み込まれているカーネルモジュールはlsmodで確認できます。

自分が仮想化環境の中にいるかどうかは、systemd-detect-virtで手軽に確認できます。

$ systemd-detect-virt
kvm
$ dmidecode -s system-manufacturer   # ハードウェア情報からハイパーバイザ製品名が読み取れることもある

そのほか、/sys/hypervisor/ディレクトリの有無や中身も、仮想化されているかどうかの手がかりになります。

コンテナの位置づけ

コンテナは、Linuxカーネルの名前空間(namespace)機能でプロセス・ネットワーク・ファイルシステムなどの見え方を分離し、cgroups機能でCPUやメモリの使用量を制限することで実現されています。カーネルそのものは共有するため、仮想マシンに比べて起動が速く、リソースの消費も小さいという特徴があります。DockerやPodmanはアプリケーション単位のコンテナ実行によく使われ、LXCはOS全体に近い、より仮想マシンに近い感覚で使えるコンテナ技術として位置づけられます。

確認クイズ

Q1. ハードウェアをソフトウェアで完全にエミュレートせず、ゲストOS側にも専用のドライバを組み込むことで効率を高める仮想化方式はどれですか?

解説: 準仮想化は、ゲストOS側が仮想化環境向けの専用ドライバ(virtioなど)を使うことで、完全仮想化よりも効率よく動作する方式です。

Q2. 仮想マシンをテンプレートから複製した際に、トラブルの原因になりやすいものはどれですか?

解説: テンプレート複製では、SSHホスト鍵やmachine-id、MACアドレスなどが元のマシンと同じ値のまま複製されてしまうことがあり、これが原因不明のトラブルにつながります。

Q3. クラウド環境でゲストの初回起動時に、ユーザー作成やパッケージ導入を自動化する仕組みはどれですか?

解説: cloud-initは、#cloud-config形式のuser-dataを使って、初回起動時のユーザー作成・SSH鍵登録・パッケージ導入などを自動化する仕組みです。

Q4. 現在の環境が仮想化されているかどうかを手軽に確認できるコマンドはどれですか?

解説: systemd-detect-virt は、実行中の環境がKVMやVMwareなどの仮想化環境かどうかを判定して表示するコマンドです。