えんでぃの技術ブログ

えんでぃの技術ブログ

ネットワークエンジニアの視点で、IT系のお役立ち情報を提供する技術ブログです。

KVMの基本操作集

linux-kvm-logo
引用元:linux-kvm.org

前の記事

本記事では、KVMの初期設定が完了していることを前提としています。
初期設定手順に興味のある方は、以下の記事もご参照ください。

endy-tech.hatenablog.jp

virt-manager、Cockpitのセットアップについては、以下の記事をご参照ください。

endy-tech.hatenablog.jp

endy-tech.hatenablog.jp

お伝えしたいこと

KVMの基本操作をケース別に紹介します。

基本はCLIの手順紹介がメインです。
virt-managerとCockpitのスクリーンショットも載せていますが、今後画面のデザインが変わっても原則更新しません。

GUIの手順については、雰囲気を掴むためのおまけと思っていただければ幸いです。

VMの作成

virt-installで行う場合

過去記事を参照してください。

virt-managerで行う場合

過去記事を参照してください。

Cockpitで行う場合

過去記事を参照してください。

VMの削除

virshで行う場合

過去記事を参照してください。

virt-managerで行う場合

VMを右クリックして "Delete" します。

virt-manager_vm_deletion1

virt-manager_vm_deletion2

Cockpitで行う場合

仮想マシン一覧の画面で … をクリックして "Delete" します。

cockpit_vm_deletion1

cockpit_vm_deletion

VMの起動

virshで行う場合

VMの起動にはvirsh startを使います。
コマンドがわかりやすいので、普段GUIを使っていてもvirsh startだけはCLIといった使い方ができるかなと思います。

virsh start vm_name

以下に具体例を示します。

# VMの一覧を表示
virsh list --all
#  -    fedora33                             shut off

virsh start fedora33
# Domain 'fedora33' started

virt-managerで行う場合

トップ画面で対象のVMを右クリック → Runで起動します。

virt-manager_start_vm

Cockpitで行う場合

仮想マシン一覧の画面で対象のVMを探し、"Run"をクリックします。

cockpit_start_vm

VMの停止

virshで行う場合

VMの停止にはvirsh shutdownを使います。
このコマンドはshutdownシーケンスがちゃんと走るので、VMに優しいです。

VMがハングしている場合や、OSインストール画面などOS起動前の状態など、shutdownを受け付けないケースもありえます。
そんなときは、virsh destroyを使います。 destroyというとVMを破壊しそうですが、実際にはVMの強制終了を意味します。
ニュアンスとしては「VMプロセスをdestroy (kill) する」のであって、VMXML定義ファイルには触れません。

virsh shutdown fedora33
virsh destroy fedora33

virt-managerで行う場合

トップ画面で対象のVMを右クリック → Shut Down → Shut Down でシャットダウンします。
右クリック → Shut Down → Forced Off で強制終了します。

virt-manager_stop_vm

Cockpitで行う場合

仮想マシン一覧の画面で対象のVMを探し、Shut downでVMをシャットダウンします。
… をクリックして "Force shut down" でVMを強制終了します。

cockpit_stop_vm

VMのクローン

virt-cloneで行う場合

virt-cloneコマンドでクローンできます。

前提として、まずはVM名を調べておきます。
virsh listはデフォルトで起動しているVMしか表示しませんが、--allをつけることで全てのVMを表示できます。

virsh list --all
#  Id   Name         State
# ---------------------------
#  -    fedora33     shut off

fedora33というVMをクローンします。
新しいVMの名前は、f33_newとします。

virt-clone --original fedora33 --name f33_new --auto-clone 
# Allocating 'f33_new.qcow2'     |  20 GB  00:00:05

# Clone 'f33_new' created successfully.

virt-cloneの主なオプションは以下のとおりです。
--originalでコピー元のVM名、--nameでコピー先のVM名、--fileでコピー先のVMイメージファイルのフルパスを指定するのが基本です。
個人的には、上記のように--name--auto-cloneを併用し、イメージファイル名を--nameで指定した文字列から自動生成させるのが便利に感じました。

オプション 意味
-o,
--original
コピー元のVM名を指定
-n,
--name
コピー先のVM名を指定
-f,
--file
コピー先のVMが利用するディスクイメージファイルのフルパスを指定
--auto-clone --name--fileの片方または両方を自動的に決定する。
--nameを自動生成するときは、fedora33-cloneのように-cloneをつける。
更にクローンすると、fedora33-clone-1, fedora33-clone-2, ...と連番をつける。
--fileを自動補完した場合、defaultプール配下にVM名.qcow2が生成した(※)。
--nameのみ明示的に指定した場合は、イメージファイル名の自動補完には--nameの文字列が使用された(※)

(※) man virt-cloneにはこの仕様は書いていません。私の環境で実機検証した結果、このようになりました

virt-clonevirt-installパッケージに同梱されています。
dnf provides virt-cloneや、dnf repoquery -l virt-installなどで確認できます。

virt-managerで行う場合

VM名を右クリックして "Clone" します。

virt-manager_clone1

virt-manager_clone2

Cockpitで行う場合

仮想マシン一覧の画面で … をクリックして "Clone" します。

cockpit_clone1

cockpit_clone2

CDドライブにISOファイルをセットする

virshで行う場合

まずはVM名を指定して、紐付いているデバイス名を確認します。
VM名を調べたい場合は、virsh list --allを先に実行してください。

以下のコマンド出力から、sdaにCDドライブが存在することがわかりました。

virsh domblklist fedora33 --details
#  Type   Device   Target   Source
# -------------------------------------------------------------------
#  file   disk     vda      /home/shared/libvirt/disks/fedora33.qcow2
#  file   cdrom    sda      -
#  file   cdrom    sdb      -

virsh change-mediaコマンドにより、CDROMにISOファイルをセットします。

virsh change-media fedora33 sda /home/shared/isos/fedora/Fedora-Server-dvd-x86_64-33-1.2.iso

# Successfully updated media.

もう一度確認コマンドを実行し、ISOファイルがセットされたことを確認します。

virsh domblklist fedora33 --details
#  Type   Device   Target   Source
# -----------------------------------------------------------------------------------------
#  file   disk     vda      /home/shared/libvirt/disks/fedora33.qcow2
#  file   cdrom    sda      /home/shared/isos/fedora/Fedora-Server-dvd-x86_64-33-1.2.iso
#  file   cdrom    sdb      -

virsh change-mediaの構文は以下のとおりです。

virsh change-media domain-name path [--eject|--insert|--update] [source]

キーワードの意味は下表のとおりです。

キーワード 説明
domain-name domain、つまりVMの名前
path 捜査対象のCDドライブのデバイス名。
sda, vda, hda などの文字列が入る
--eject CDドライブの中身を空にする。
sourceの指定は不要
--insert CDドライブにISOファイルをセットする。
sourceの指定は必須
--update sourceを指定した場合、--ejectしてから--insertする。
sourceを指定しなかった場合、--ejectのみ実行する。
--eject, --insert, --update をいずれも指定しなかった場合、デフォルトで--updateが選択される
source ISOファイルを指定する。
--ejectを指定した場合は、sourceの入力は不要。
--insertを指定した場合はsourceの入力は必須。
--updateを指定した場合は、sourceを入力するか否かで挙動が変わる

virt-managerで行う場合

VMの詳細画面で、"SATA CDROM"からISOファイルのパスを指定します。

virt-manager_insert_cd

Cockpitで行う場合

仮想マシン一覧の画面で、捜査対象の仮想マシン名をクリックして詳細画面を開きます。
中段の "Disks" セクションの "Add disk" をクリックし、以下のように選択します。

設定項目 詳細
Source Custom path
Custom path (ISOファイルのパス)
Device CD/DVD dusk

cockpit_add_disk1

cockpit_add_disk2

起動順序の設定

virshで行う場合

HDD、CDドライブなど複数のディスクを持ったVMを起動する際の起動順序の制御方法を紹介します。

起動順序を制御するためのvirshサブコマンドは、2021年時点では存在しないようです。
従って、virsh edit VM_NAMEXMLファイルを直接編集します。

編集は以下のように行います。

  1. <os>セクション配下の<boot>セクションを削除する
  2. <disk>セクション配下に<boot order>セクションを追加する

例えば、編集前のファイルが以下の状態だったとします。

(中略)
  <os>
    <type arch='x86_64' machine='pc-q35-5.1'>hvm</type>
    <boot dev='hd'/>
  </os>
(中略)
  <devices>
    <emulator>/usr/bin/qemu-system-x86_64</emulator>
    <disk type='file' device='disk'>
      <driver name='qemu' type='qcow2'/>
      <source file='/home/shared/libvirt/disks/fedora33.qcow2'/>
      <backingStore/>
      <target dev='vda' bus='virtio'/>
      <address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
    </disk>
    <disk type='file' device='cdrom'>
      <driver name='qemu' type='raw'/>
      <target dev='sda' bus='sata'/>
      <readonly/>
      <address type='drive' controller='0' bus='0' target='0' unit='0'/>
    </disk>
(中略)

編集後のファイルは、以下のようになります。
<os>配下の<boot>を削除し、<disk>配下に<boot order>を追加しました。
<boot order>には'1'以上の値を指定し、値が小さいほど優先順位が高くなります。
今回の場合は、device='disk'が第一優先で、device='cdrom'が第二優先になっています。
<boot order>が付いていないデバイスは、そもそも起動デバイスの候補から外れます。

(中略)
  <os>
    <type arch='x86_64' machine='pc-q35-5.1'>hvm</type>
  </os>
(中略)
  <devices>
    <emulator>/usr/bin/qemu-system-x86_64</emulator>
    <disk type='file' device='disk'>
      <driver name='qemu' type='qcow2'/>
      <source file='/home/shared/libvirt/disks/fedora33.qcow2'/>
      <backingStore/>
      <target dev='vda' bus='virtio'/>
      <address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
      <boot order='1'/>
    </disk>
    <disk type='file' device='cdrom'>
      <driver name='qemu' type='raw'/>
      <target dev='sda' bus='sata'/>
      <readonly/>
      <address type='drive' controller='0' bus='0' target='0' unit='0'/>
      <boot order='2'/>
    </disk>

手順としては以上です。

最後に、libvirt公式サイトのリンクを貼ります。

libvirt.org

公式に書いてあるように、<boot order>が使えるかどうかはハイパーバイザー次第のようです。
幸いにも、QEMU<boot order>に対応していました。

<boot order>より以前にある指定方法として<boot>もありますが、こちらは理由がなければ基本使いません。
Disk、CDといった粒度で起動の優先順位を指定することはできますが、Diskが2つ以上存在した時、どちらのDiskを使うかの挙動が複雑です。
可能な限り、DiskやCDが同種類で複数存在する場合でも優先順位を指定できる<boot order>を使うようにしましょう。
<boot>に関する情報は以下のリンクにあります。

libvirt.org

virt-managerで行う場合

GUIを使えば、virshよりも簡単な操作で設定できます。

以下のVMの設定画面で、起動デバイスに設定したいデバイスにチェックを入れ、▲▼で優先順位を指定するだけです。
※以下の画像は▲▼が黒くて見えづらいので若干合成してあります

virt-manager_boot_order

Cockpitで行う場合

仮想マシンの詳細画面を開き、"Boot order" の "edit" をクリックして編集するだけです。
こちらも簡単ですね。

cockpit_boot_order1

cockpit_boot_order2

仮想ネットワークの作成

概要

仮想ネットワークにはいくつかのタイプがあります。
私が使ったことがあるのは、以下の2種類です。
多くの場合はNATのみで事足りますが、VM間通信用のネットワークセグメントを複数作成したい場合はNoneのネットワークも1つ追加すると便利です。

  • NAT: 外部通信とVM間通信が可能
  • None: VM間通信のみ可能

また、仮想ネットワーク内でDHCPを使うことができます。
DHCPにより、作成したVMに自動的にIPアドレスデフォルトゲートウェイの設定を配布できます。
ネットワークサイズが/24だとすると、DHCPアドレスを.101〜.254のようにしておくと、.2〜.100を静的に割り当てることもできるので便利です。
.1はゲートウェイのアドレスに使われるので、この部分は残しておきます。

さて、以降で具体的な手順に入ります。

virshで行う場合

CLIの手順はlibvirt wikiを参考にしました。

net-createというコマンドがありますが、xmlファイルを指定することでしか作成できません。
従って、基本的には既存のXMLファイルを雛形にして編集することになると思います。

まず、virsh net-list --allで仮想ネットワーク名を確認します。
--allをつけることで、起動していない仮想ネットワークも表示します。

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

新規の仮想ネットワークを作る場合は、/usr/share/libvirt/networks/default.xmlを雛形にすると便利です。
以下のようにXMLファイルをコピーします。

cp /usr/share/libvirt/networks/default.xml tmp.xml
cat tmp.xml
# <network>
#   <name>default</name>
#   <bridge name="virbr0"/>
#   <forward/>
#   <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>

既に作成済みの仮想ネットワークを雛形にする場合は、virsh net-dumpxmlで既存の仮想ネットワークのXMLファイルをtmp.xmlとして保存しておきます。

virsh net-dumpxml default | tee tmp.xml
# <network connections='1'>
#   <name>default</name>
#   <uuid>b130cf72-a5eb-4414-9fd8-7d1182f2b50b</uuid>
#   <forward mode='nat'>
#     <nat>
#       <port start='1024' end='65535'/>
#     </nat>
#   </forward>
#   <bridge name='virbr0' stp='on' delay='0'/>
#   <mac address='52:54:00:96:e6:67'/>
#   <ip address='192.168.122.1' netmask='255.255.255.0'>
#     <dhcp>
#       <range start='192.168.122.101' end='192.168.122.254'/>
#     </dhcp>
#   </ip>
# </network>

<name><domain nameを仮想ネットワーク名に変更します。
(※) domain nameの行は、場合によっては存在しないこともあります

<uuid><bridge name<mac addressの行を削除します。
これらの値はlibvirtによって重複しない値を自動生成させます。

書き換え後の例を以下に示します。
ちなみにlibvirt wikiによると、<forward/>の部分は<forward mode='nat'/>がデフォルト値となります。
NATモードではなく、None (Isolated) モードにしたい場合は、<forward/>行を削除してください。

cat tmp.xml
# <network>
#   <name>tmp</name>
#   <forward/>
#   <ip address="192.168.200.1" netmask="255.255.255.0">
#     <dhcp>
#       <range start="192.168.200.101" end="192.168.200.254"/>
#     </dhcp>
#   </ip>
# </network>

以下のコマンドにより、仮想ネットワークを作成します。
net-defineコマンドの場合は、XMLファイルとして構成を保持します。
net-definenet-createに置き換えることで、undefined状態 (メモリ上にしか存在せず、destroyすると直ちに消える) の仮想ネットワークを作成することもできます。

virsh net-define tmp.xml
# Network tmp defined from tmp.xml

作成したtmpの情報を表示します。
net-listの出力より、tmpの定義直後はまだ起動しておらず、OSブート時に自動起動もしないことがわかります。
net-dumpxmlの出力より、UUIDやMACアドレスの値、NAT設定などが自動生成していることがわかります。

virsh net-list --all
#  Name        State      Autostart   Persistent
# ------------------------------------------------
#  default     active     yes         yes
#  internal1   active     yes         yes
#  tmp         inactive   no          yes

virsh net-info tmp
# Name:           tmp
# UUID:           3bfd3e32-9531-4e67-a2b0-963df9f4cc85
# Active:         yes
# Persistent:     yes
# Autostart:      no
# Bridge:         virbr2

virsh net-dumpxml tmp
# <network>
#   <name>tmp</name>
#   <uuid>85a1cccf-dc54-4164-8e72-a6f020b4883c</uuid>
#   <forward mode='nat'/>
#   <bridge name='virbr2' stp='on' delay='0'/>
#   <mac address='52:54:00:55:21:d6'/>
#   <ip address='192.168.200.1' netmask='255.255.255.0'>
#     <dhcp>
#       <range start='192.168.200.101' end='192.168.200.254'/>
#     </dhcp>
#   </ip>
# </network>

以下のコマンドによって仮想ネットワークを起動し、通信可能な状態にします。
更に、自動起動も有効化します。

virsh net-start tmp
# Network tmp started

virsh net-autostart tmp 
# Network tmp marked as autostarted

再び確認コマンドで表示します。
State=active, Autostart=yesとなりました。

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

仮想ネットワークを削除する際は、destroy (強制終了。startの反対) とundefine (設定削除。defineの反対) を行います。

virsh net-destroy tmp
# Network tmp destroyed

virsh net-undefine tmp
# Network tmp has been undefined

削除後、tmpが表示されなくなることを確認します。

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

virt-managerで行う場合

トップ画面において、コネクション、またはコネクション配下のVMを選択した状態で、上部メニューからEdit > Connection Detailsを開きます。
または、下図赤枠部分のコネクション名の部分を右クリックして、Detailsを選択します。

virt-manager_connection_details

Virtual Networksタブを開き、左下の+ボタンから仮想ネットワークを追加します。
※以下の画像は+-が黒くて見えづらいので若干合成してあります

virt-manager_add_network1

virt-manager_add_network2

Cockpitで行う場合

仮想マシン一覧から、右上の"Networks"を選択します。

cockpit_add_network1

右上の"Create virtual network"を選択します。

cockpit_add_network2

必要事項を入力して、Createします。
Cockpit 249では、Forward modeがNAT、Open、None (isolated mode) の3種類しか選べませんでした。
将来的にアップデートされるとは思いますが、もし機能的に困る場合はvirshかvirt-managerを使うようにしてください。

私の検証用途では凝ったことはしないので、私としてはそれほど困りませんでした。

cockpit_add_network3

ストレージプールの作成

ストレージプールという言葉自体の解説は、KVMの初期設定、及びvirsh, virt-installによるVM作成 - pool作成の事前準備を参照してください。

virshで行う場合

以下のコマンドでPoolを定義します。
Pool名、Poolと紐付けるディレクトリパスは適宜変更してください。

virsh pool-define-as kickstart dir --target /home/shared/libvirt/kickstarts

virsh pool-define-asのSyntaxは以下のとおりです。

パラメータ 意味
kickstart
(第一引数)
Pool名
dir
(第二引数)
Pool Type名。
他にもfsdiskなど多数あるが、本ブログで紹介したのはdirのみ (参考)
--target .. dir Typeの場合は指定必須。
Poolと紐付けるディレクトリパスを指定する

作成したPoolを確認します。
作成直後のPoolは停止しているので、--allオプションをつけることで停止中のPoolも含めて表示します。

virsh pool-list --all
#  Name        State      Autostart
# ---------------------------------
#  default     active     yes
#  kickstart   inactive   no

Poolを今すぐ起動します。
また、Autostartを有効化することで、OS起動時にPoolも自動起動するように設定します。

virsh pool-start kickstart
# Pool kickstart started

virsh pool-autostart kickstart
# Pool kickstart marked as autostarted

Poolの状態を再度確認します。
Poolが起動状態になり、Autostartも有効化されました。

virsh pool-list
#  Name        State    Autostart
# ---------------------------------
#  default     active   yes
#  kickstart   active   yes

Poolの停止はvirsh pool-destroy kickstart、Poolの定義削除はvirsh pool-undefine kickstartコマンドにて行います。

virt-managerで行う場合

トップ画面において、コネクション、またはコネクション配下のVMを選択した状態で、上部メニューからEdit > Connection Detailsを開きます。
または、下図赤枠部分のコネクション名の部分を右クリックして、Detailsを選択します。

virt-manager_connection_details

Storageタブを開き、左下の+マークを選択します。

virt-manager_add_pool1

必要なパラメータを入力し、Finishを選択します。

virt-manager_add_pool2

Cockpitで行う場合

仮想マシン一覧の画面で、左上にあるStorage poolsのリンクをクリックし、Pool一覧の画面を開きます。

cockpit_add_pool1

画面右上のCreate storage poolをクリックし、Pool作成画面を開きます。

cockpit_add_pool2

必要なパラメータを指定してPoolを作成します。
以下は/home/shared/libvirt/kickstarts/ディレクトリと紐づく、kickstartという名前のPoolを作成する例です。

cockpit_add_pool3

Activateボタンをクリックし、作成したPoolを起動します。

cockpit_add_pool4

仮想ディスクイメージファイルの拡張

ディスクファイル (qcow2) のサイズ変更は、2021/11時点ではCLIでしかできないようです。
したがって、virshの手順のみ紹介します。

virshで行う場合

Linuxのディスク拡張手順 #qcow2ファイルの拡張を参照してください。

(参考) DHCPで割り当てているIPアドレスの一覧表示

virshで行う場合

virsh net-dhcp-leases <network-name>DHCPリースしているIPアドレスを一覧表示できます。
VMIPアドレスDHCPで設定しているとき、VMSSHする前にIPアドレスを調べる際に便利です。

ネットワークの一覧はvirsh net-listで確認できますが、多くの場合はdefaultを使っているのではないかと思います。

virsh net-dhcp-leases default
#  Expiry Time          MAC address        Protocol  IP address          Hostname   Client ID or DUID
# ------------------------------------------------------------------------------------------------------
#  2022-02-27 13:41:03  52:54:00:6c:54:c8  ipv4      192.168.122.127/24  stream9-1  01:52:54:00:6c:54:c8

よく使うコマンドなので、ワンライナーをaliasとして登録するのも便利かもしれません。
私の環境では、以下の内容を~/.bashrcに登録しています。

alias ips=$'virsh net-dhcp-leases default | sed -ne \'3,$p\' | grep -v ^$ | awk \'gsub(/\/[0-9]+/, "", $0) { print $6,$5 }\''

実行すると以下のようになります。
短い入力でコピペしやすいシンプルな出力が得られます。

ips
# stream9-1 192.168.122.127

上記はANSI C Quoting ($' ')を使っています。
通常のシングルクォーテーション (' ')と比較して、シングルクォーテーション自体を \'エスケープできるのが便利なので使っています。

bash以外の環境ではANSI C Quotingが使えないかもしれないので、ダブルクォーテーションを使ったパターンも併記しておきます。
ダブルクォーテーションを使う場合は$"\、そしてバックティックもエスケープ対象になるので、少々見づらくなります。
(参考: Double Quotes)

#alias ips="virsh net-dhcp-leases default | sed -ne '3,\$p' | grep -v ^\$ | awk 'gsub(/\/[0-9]+/, \"\", \$0) { print \$6,\$5 }'"

virt-managerで行う場合

virt-managerには、DHCPリースを一覧表示する機能は無いようです。

仮想マシンの詳細設定画面でNICの項目から読み取ることはできます。
しかし、仮想マシン1台分の情報しか確認できません。

virt-manager_nic

Cockpitで行う場合

Cockpitには、DHCPリースを一覧表示する機能は無いようです。

仮想マシンの詳細設定画面でNetwork Interfacesの項目から読み取ることはできます。
しかし、仮想マシン1台分の情報しか確認できません。

cockpit_network_interfaces

まとめ

KVM周りの各種基本操作についてご紹介しました。

慣れないうちはGUIで、慣れてきたらVMの起動/停止など簡単な部分からCLIに移行し、徐々に作業効率を上げていきましょう。