virt-install による仮想マシンの自動インストール

KVM を利用するとスクリプトを使って仮想マシンを全自動でインストールすることができます。KVM のインストール手順は前回に説明しました。今回は virt-install、guestfish というコマンドを利用して CentOS 7.1 の仮想マシンを全自動でインストールしてみます。Fedora 22 で動作確認をしました。

guestfish のインストール

guestfish は KVM ホストに事前にインストールしておく必要があります。Fedora 22 ではつぎのコマンドで guestfish をインストールできます。

# dnf install '*guestf*' -y

virt-install コマンド

virt-install コマンドを使用して、仮想マシンのインストールをコマンドラインから実行することができます。kickstart ファイルを組み合わせることで、手作業による操作が不要になり、全自動で仮想マシンのインストールが可能になります。

virt-install コマンドの使い方に関しては、つぎに示すオプションが重要なポイントです。

(1)「--initrd-inject=<file>」
インストーラが仮想マシンを起動する前に、指定したローカルのファイルをインストーラの initrd.img の中に埋め込みます。今回の例では kickstart ファイルを指定しています。ここで「ローカル」とは KVM ホスト上のファイルのことを意味します。

(2)「--extra-args=”ks=file:/<kickstart file>”」
仮想マシンの起動時に、initrd 内の kickstart ファイルを使用して Anaconda による自動インストールを開始させます。

(3)「--noautoconsole」
仮想マシンのインストール時にコンソールを表示しないオプションです。OSのインストールが完了した後に、仮想マシンをシャットダウンするには、このオプションが必要です。

guestfish コマンド

guestfish を使用すると、仮想マシンを起動することなく仮想マシン内のファイル編集やコマンド実行が可能になります。guestfish を使用して仮想マシン上の任意のコマンドを実行するには、「command」コマンドを使用します。ただし、筆者が確認したところ、「firewall-cmd」など一部のコマンドがうまく実行できませんでした。このため、Firewall の設定は firewalld の設定ファイルを直接書き換えています。

今回の例は、guestfish を使用しなくても、kickstart の「%post」セクションを使用して実現できます。しかし、guestfish には copy-in、copy-out、upload、download といったコマンドが用意されており、これを使ってローカルとゲスト間のファイル転送が簡単に実現できます。これと同じことを kickstart で実現するのは面倒です。

仮想マシンの自動インストールスクリプト

以下に示すスクリプトは CentOS 7.1 の仮想マシンを指定した IP アドレスでインストールし、Web サーバーを起動して、Firewall の設定をする例です。シェルの実行が完了した後は、ブラウザで http://192.168.122.111 にアクセスして Apache のテスト画面を見ることができます。また仮想マシンのコンソールにアクセスするには、root ユーザーから「virsh console centos7-web」コマンドを実行します。CTRL + ] をタイプすると仮想マシンのコンソールから抜け出ることができます。

スクリプトを実行する前に、事前に CentOS 7.1 のインストールメディア「CentOS-7-x86_64-DVD-1503-01.iso」を「/var/lib/libvirt/images」ディレクトリ以下にコピーしておいてください。

以下のサンプルはここからダウンロードできます。

#!/bin/sh

echo "*** installation job starting ***"

# ----------------------------------------------------------
# Parameters
# ----------------------------------------------------------

# Guest Name (Domain Name)
DOM=centos7-web
# Guest Image File
IMG=/var/lib/libvirt/images/${DOM}.qcow2
# Guest Memory Size (MB)
RAM=1024
# Guest Image File Size (GB)
SIZE=10
# Install Media (ISO)
DVD=/var/lib/libvirt/images/CentOS-7-x86_64-DVD-1503-01.iso
# Kickstart file
KSF=${DOM}-ks.cfg

# root password
PASSWORD=password
# Hostname
HOSTNAME=centos7-web.example.com
# IP Address
IP=192.168.122.111
# Netmask
NETMASK=255.255.255.0
# Gateway
GATEWAY=192.168.122.1
# DNS server
NAMESERVER=8.8.8.8,8.8.4.4
# NTP server
NTPSERVERS=0.centos.pool.ntp.org,1.centos.pool.ntp.org,2.centos.pool.ntp.org,3.centos.pool.ntp.org

# ----------------------------------------------------------
# Initial Check
# ----------------------------------------------------------

# Make sure only root can run our script
if [ "$(id -u)" != "0" ]; then
    echo "This script must be run as root" 1>&2
    exit 1
fi

# ----------------------------------------------------------
# remove & clear old vm if existing
# ----------------------------------------------------------

# stop domain forcefully
virsh destroy ${DOM} >/dev/null 2>&1
# undefine domain
virsh undefine ${DOM} --remove-all-storage >/dev/null 2>&1
# remove domain image file
rm -f ${IMG} >/dev/null 2>&1

# ----------------------------------------------------------
# create kickstart file
# ----------------------------------------------------------

echo "*** kickstart file creating ***"

cat << _EOF_ > ${KSF}
# Install OS instead of upgrade
install
# shutdown after installation
shutdown
# System authorization information
auth --enableshadow --passalgo=sha512
# Use CDROM installation media
cdrom
# graphical or text install
text
# Run the Setup Agent on first boot
firstboot --disable
# use /dev/vda for install destination
ignoredisk --only-use=vda
# SELinux configuration
selinux --enforcing
# Keyboard layouts
keyboard --vckeymap=us --xlayouts='us'
# System language
lang en_US.UTF-8
# Network information
network \
--device=eth0 \
--bootproto=static \
--ip=${IP} \
--netmask=${NETMASK} \
--gateway=${GATEWAY} \
--nameserver=${NAMESERVER} \
--noipv6 \
--activate
network --hostname=${HOSTNAME}
# Firewall configuration
firewall --enabled --ssh --http
# Root password
rootpw --plaintext ${PASSWORD}
# System services
services --enabled="chronyd"
# System timezone
timezone Asia/Tokyo --isUtc --ntpservers=${NTPSERVERS}
# System bootloader configuration
bootloader --location=mbr --boot-drive=vda --append=" crashkernel=auto" 
# Partition clearing information
clearpart --none --initlabel 
# Disk partitioning information
part /boot --fstype="xfs" --ondisk=vda --size=500
part pv.20 --fstype="lvmpv" --ondisk=vda --size=8192 --grow
volgroup centos --pesize=4096 pv.20
logvol swap --fstype="swap" --size=921 --name=swap --vgname=centos
logvol / --fstype="xfs" --grow --maxsize=51200 --size=1024 --name=root --vgname=centos

%packages
@core
@base
@web-server
kexec-tools

bind-utils
binutils
chrony
lsof
ltrace
man-pages
net-tools
pciutils
psmisc
sos
strace
sysstat
tcpdump
telnet
usbutils
wget

%end
_EOF_

echo "*** kickstart file created ***"

# ----------------------------------------------------------
# guest install using virt-install command
# ----------------------------------------------------------

echo "*** virt-install starting ***"

virt-install \
--name=${DOM} \
--ram=${RAM} \
--vcpus=1 \
--os-type=linux \
--os-variant=rhel7.0 \
--file=${IMG} \
--file-size=${SIZE} \
--location=${DVD} \
--network=bridge:virbr0 \
--nographics \
--initrd-inject=${KSF} \
--extra-args="ks=file:/${KSF} console=tty0 console=ttyS0,115200n8" \
--noautoconsole

# wait until guest installation is completed
finished="0";
while [ "${finished}" = "0" ]; do
  sleep 5
  domstate=`virsh domstate ${DOM}`
  if [ "${domstate}" = "shut off" ]; then
    finished=1;
  fi
done
sleep 5

echo "*** virt-install finished ***"

# ----------------------------------------------------------
# customize guest using guestfish
# ----------------------------------------------------------

echo "*** guestfish starting ***"

# swapiness settings
F1=/etc/sysctl.d/swappiness.conf
# ipv6 settings
F2=/etc/sysctl.d/disable_ipv6.conf
# firewall settings file (original)
F3=/etc/firewalld/zones/public.xml
# firewall settings (new) ... ssh, http, https enabled
F3_NEW=$(mktemp)
cat << _EOF_ > ${F3_NEW}
<?xml version="1.0" encoding="utf-8"?>
<zone>
  <short>Public</short>
  <description>For use in public areas. You do not trust the other computers on networks to not harm your computer. Only selected incoming connections are accepted.</description>
  <service name="ssh"/>
  <service name="http"/>
  <service name="https"/>
</zone>
_EOF_

# run guestfish
guestfish -d ${DOM} -i << _EOF_
  # change grub timeout setting
  command "sed -i -e 's/GRUB_TIMEOUT=5/GRUB_TIMEOUT=0/g' /etc/default/grub"
  command "grub2-mkconfig -o /boot/grub2/grub.cfg"

  # suppress swapping
  write        ${F1} "vm.swappiness = 0\n"

  # disable ipv6
  write        ${F2} "net.ipv6.conf.all.disable_ipv6 = 1\n"
  write-append ${F2} "net.ipv6.conf.default.disable_ipv6 = 1\n"

  # enable httpd
  command "systemctl enable httpd"

  # backup oroginal firewall setting
  cp-a ${F3} ${F3}.old
  # upload new firewall settings
  upload ${F3_NEW} ${F3}
  # enable firewalld
  command "systemctl enable firewalld"
_EOF_

echo "*** guestfish finished ***"

# ----------------------------------------------------------
# Start guest
# ----------------------------------------------------------

virsh start ${DOM}

echo "*** all job finished ***"

# ----------------------------------------------------------
# EOJ
# ----------------------------------------------------------

2016.10.2 追記

最新のスクリプトは github に公開しています

最新のスクリプトは以下の github に公開しています

https://github.com/uvirt/kvm-sample