virt-install + Preseed で Ubuntu を自動インストールする

もう OS のインストールは疲れたよぱとらっす...。

開発機で KVM を使用しているが、毎回仮想マシンの OS をインストールするのが億劫だ。テンプレートとなる仮想マシンを作って virt-clone するという手もあるが、それだと次のような難点がある。

  • IP アドレスやホスト名、MAC アドレスなどの設定を修正しなければならない
  • ディスクサイズを変更できない

そこで Preseed を用いて Ubuntu の自動インストールを試してみる。

Preseed とは

Debian 系 OS のインストール作業を自動化する仕組み。インストール時に求められる入力項目をあらかじめ設定ファイルにまとめておき、それをカーネルパラメータとして食わせる。Red Hat 系の Kickstart のようなもの。

OS インストール時に Preseed を読み込ませるためには、PXE サーバを構築するなどの手間がかかる。しかし幸いなことに、virt-install なら --extra-args オプションでカーネルパラメータを渡すことができる。

前提

以下のような環境を前提とする。

  • ゲスト OS は Ubuntu Server 12.04.3 amd64
  • ゲストはホストの NIC にブリッジ接続する
  • サブネットマスクや GW などの情報はホストから自動で取得する
  • 仮想ディスクのフォーマットは qcow2 を使う

手順

まずインストールディスクの iso イメージをホストにダウンロードしておく。

$ sudo mkdir -p /var/lib/libvirt/isos
$ sudo wget -O /var/lib/libvirt/isos/ \
    wget http://www.ftp.ne.jp/Linux/packages/ubuntu/releases-cd/12.04.3/ubuntu-12.04.3-server-amd64.iso

Preseed ファイル (preseed.cfg) を作成する。書き方は Ubuntu のドキュメントなどを参考にする。

d-i debian-installer/locale string en_US
d-i debian-installer/language string en
d-i debian-installer/country string US
d-i debian-installer/locale string en_US.UTF-8

d-i console-setup/ask_detect boolean false
d-i console-setup/layoutcode string us

d-i netcfg/choose_interface select eth0
d-i netcfg/dhcp_timeout string 30
d-i netcfg/get_hostname string localhost
d-i netcfg/get_domain string localdomain
d-i netcfg/wireless_wep string

d-i mirror/country string manual
d-i mirror/http/hostname string jp.archive.ubuntu.com
d-i mirror/http/directory string /ubuntu
d-i mirror/http/proxy string 

d-i clock-setup/utc boolean true
d-i time/zone string Asia/Tokyo
d-i clock-setup/ntp boolean true

d-i partman-auto/method string lvm
d-i partman-lvm/device_remove_lvm boolean true
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true
d-i partman-auto-lvm/guided_size string max
d-i partman-auto/choose_recipe select atomic
d-i partman-partitioning/confirm_write_new_label boolean true
d-i partman/choose_partition select Finish partitioning and write changes to disk
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true

d-i passwd/user-fullname string ubuntu
d-i passwd/username string ubuntu
d-i passwd/user-password password ubuntu
d-i passwd/user-password-again password ubuntu
d-i user-setup/allow-password-weak boolean true
d-i user-setup/encrypt-home boolean false

tasksel tasksel/first multiselect
d-i pkgsel/include string openssh-server build-essential
d-i pkgsel/upgrade select none
d-i pkgsel/update-policy select none
d-i grub-installer/only_debian boolean true
d-i grub-installer/with_other_os boolean true

d-i finish-install/reboot_in_progress note

なおこのサンプルは次のような前提で作っている。

  • パーティショニングは LVM を使う
  • 言語は en_US、キーボードは US を使う

必要な情報を virt-install に渡すためのスクリプト (virt-install.sh) を作成する。

#!/bin/bash

#-----------------------------
# settings
#-----------------------------
NAME=(仮想マシンのホスト名)
ADDRESS=(仮想マシンに割り当てる IP アドレス)
VCPUS=2
RAM=1024 # (M)
DISK=80 # (G)
PRESEED=/path/to/preseed.cfg
#-----------------------------

DIR=/var/lib/libvirt/images
IMG=${DIR}/${NAME}.qcow2
ISO=/var/lib/libvirt/isos/ubuntu-12.04.3-server-amd64.iso

PRESEED_BASENAME=$(basename ${PRESEED})

NETMASK=$(grep -v '^\s*#' /etc/network/interfaces | grep 'netmask' /etc/network/interfaces | awk '{print $2}')
DNS=$(grep -v '^\s*#' /etc/network/interfaces | grep 'dns-nameservers' /etc/network/interfaces | awk '{print $2}')
DOMAIN=$(grep -v '^\s*#' /etc/network/interfaces | grep 'dns-search' /etc/network/interfaces | awk '{print $2}')
GATEWAY=$(grep -v '^\s*#' /etc/network/interfaces | grep 'gateway' /etc/network/interfaces | awk '{print $2}')
VNC_PORT=$(expr $(echo ${ADDRESS} | awk -F. '{print $4}') + 5900)

[ -f ${IMG} ] && mv -v ${IMG} ${IMG}.$(date '+%Y%m%d%H%M%S')
qemu-img create -f qcow2 ${IMG} ${DISK}G

virsh destroy ${NAME}
virsh undefine ${NAME}

virt-install --connect qemu:///system \
  --initrd-inject=${PRESEED} \
  --extra-args="
preseed/file=/${PRESEED_BASENAME}
auto=true
priority=critical
locale=en_US.UTF-8
console-setup/charmap=UTF-8
console-setup/layoutcode=us
console-setup/ask_detect=false
hostname=${NAME}
domain=${DOMAIN}
netcfg/get_ipaddress=${ADDRESS}
netcfg/get_netmask=${NETMASK}
netcfg/get_gateway=${GATEWAY}
netcfg/get_nameservers=${DNS}
netcfg/disable_dhcp=true
netcfg/disable_autoconfig=true 
DEBCONF_DEBUG=5
" \
  --name=${NAME} \
  --vcpus=${VCPUS} \
  --ram=${RAM} \
  --disk="path=${IMG},format=qcow2,bus=virtio,cache=none" \
  --os-type=linux \
  --os-variant=virtio26 \
  --network="bridge=br0" \
  --accelerate \
  --location=${ISO} \
  --graphics="vnc,port=${VNC_PORT},listen=0.0.0.0,keymap=ja" \
  --force

そしてこのスクリプトを実行する。

$ chmod +x virt-install.sh
$ sudo ./virt-install.sh

VNC

(ホストの IP アドレス):(仮想マシンの IP アドレスの第4オクテット + 5900)

にアクセスすると、インストール状況が確認できる。

無事インストールが完了すると、自動的に shut off 状態になるので、

$ virsh list --all

仮想マシンが作られているか確認し、

$ virsh list (マシン名)

で起動すれば良い。

まとめ

以上の手順で、KVM のゲストとして Ubuntu を自動インストールできた。ただし、virt-clone と比較して次のような課題が残る。

  • インストールに相当な時間 (数十分) を要する
  • まっさらの状態しか作れない
  • インターネット接続がないとインストールできない

これらを考慮すると、素直に virt-clone した仮想ディスクをホストでループバックマウントして中身を書き換える方が楽な気がする。もしくは OpenStack などの仕組みを使うか。