工作过程中需要使用kvm来进行测试环境搭建。因为需要用到多台kvm虚机,所以写了个快速创建的脚本。

KVM 基本命令

KVM的命令非常繁杂,但是一般工作过程中只需要用到几个基本的命令即可。可以根据需要选择交互式命令运行或者非交互式。

  1. 查看当前有哪些虚机在运行

      virsh list
  2. 查看当前有哪些虚机(包括没有运行的)

      virsh list --all
  3. 查看虚机的title

      virsh list --title
  4. 启动虚机,假设虚机名称是 vm-name

      virsh start vm-name
  5. 停止虚机,假设虚机名称是 vm-name

      virsh destroy vm-name
  6. 清除虚机(从管理列表中删除,不删除时间虚机数据),假设虚机名称是 vm-name

      virsh undefine vm-name
  7. 查看虚机配置信息,假设虚机名称是 vm-name

      virsh dumpxml --domain vm-name
  8. 编辑虚机配置信息,假设虚机名称是 vm-name

      virsh edit --domain vm-ming
  9. 创建一个虚机,假设虚机配置文件名称是 vm-name.xml

      virsh define vm-name.xml
  10. 查看虚机网络

      virsh net-list
  11. 查看特定网络配置,假设网络名称为 manage

      virsh net-dumpxml --network manage
  12. 停止网络,假设网络名称为 manage

      virsh net-destroy --network manage
  13. 启动网络,假设网络名称为 manage

      virsh net-start --network manage

KVM 磁盘创建

空磁盘创建,假设在 /var/vm/vm1/ 目录中,创建一个大小为 100G 的磁盘文件 sys.qcow2

  qemu-img create -f qcow2 /var/vm/vm1/sys.qcow2 100G

以现有磁盘作为基础镜像

  • backimg: 基础镜像磁盘文件

  • vm_disk_path: 新创建的磁盘位置

  • vm_disk_size: 新创建的磁盘大小,单位由后面的字母 G 决定。

  qemu-img create -f qcow2 -o backing_file=${backimg} ${vm_disk_path} ${vm_disk_size}G

KVM 虚机配置文件编写

下面配置中用 %..% 表示需要调整的地方。

  • vm_id: 虚机唯一id,不可重复

  • vm_name: 虚机名称

  • vm_title: 虚机标题

  • vm_mem: 虚机内存大小

  • vm_cpu: 虚机cpu数量

  • vm_disk_path: 虚机磁盘位置

  • vm_mac_wan: 虚机wan口mac地址

  • vm_mac_mgr: 虚机管理口mac地址

  • vm_mac_fwd0: 虚机0号转发口mac地址

  • vm_mac_fwd1: 虚机1号转发口mac地址

默认情况下,虚机中的接口在宿主机中的名称都是 vnetX 形式的, X 为数字编号,这样很难识别该接口属于哪个虚机。经过尝试发现可以自行指定接口名称。

  <interface type='bridge'>
    <source bridge='br-vms'/>
    <mac address='%vm_mac_mgr%'/>
    <model type='virtio'/>
    <target dev='%vm_name%-mgr'/>
  </interface>

需要注意的是:

  1. 接口名称不能以 =vnet*=开头,否则该名称无效,KVM会进行自动编号。

  2. 接口名称不能太长,否则KVM会忽略该名称。

  3. 接口名称中分隔符 - 不能超过3个。

  <domain type='kvm' id='%vm_id%'>
    <name>%vm_name%</name>
    <title>%vm_title%</title>
    <memory unit='GiB'>%vm_mem%</memory>
    <currentMemory unit='GiB'>%vm_mem%</currentMemory>
    <vcpu placement='static'>%vm_cpu%</vcpu>
    <resource>
      <partition>/machine</partition>
    </resource>
    <os>
      <type arch='x86_64' machine='pc-i440fx-rhel7.0.0'>hvm</type>
      <boot dev='hd'/>
    </os>
    <features>
      <acpi/>
      <apic/>
    </features>
    <cpu mode='custom' match='exact' check='full'>
      <model fallback='forbid'>Haswell-IBRS</model>
      <feature policy='disable' name='hle'/>
      <feature policy='disable' name='rtm'/>
      <feature policy='require' name='hypervisor'/>
      <feature policy='require' name='xsaveopt'/>
    </cpu>
    <clock offset='utc'>
      <timer name='rtc' tickpolicy='catchup'/>
      <timer name='pit' tickpolicy='delay'/>
      <timer name='hpet' present='no'/>
    </clock>
    <on_poweroff>destroy</on_poweroff>
    <on_reboot>restart</on_reboot>
    <on_crash>destroy</on_crash>
    <pm>
      <suspend-to-mem enabled='no'/>
      <suspend-to-disk enabled='no'/>
    </pm>
    <devices>
      <emulator>/usr/libexec/qemu-kvm</emulator>
      <disk type='file' device='disk'>
        <driver name='qemu' type='qcow2'/>
        <source file='%vm_disk_path%'/>
        <backingStore/>
        <target dev='vda' bus='virtio'/>
      </disk>
      <interface type='bridge'>
        <source bridge='virbr0'/>
        <mac address='%vm_mac_wan%'/>
        <model type='virtio'/>
        <target dev='%vm_name%-wan'/>
      </interface>
      <interface type='bridge'>
        <source bridge='br-vms'/>
        <mac address='%vm_mac_mgr%'/>
        <model type='virtio'/>
        <target dev='%vm_name%-mgr'/>
      </interface>
      <interface type='bridge'>
        <source bridge='br-fwd'/>
        <mac address='%vm_mac_fwd0%'/>
        <model type='virtio'/>
        <target dev='%vm_name%-fwd0'/>
      </interface>
      <interface type='bridge'>
        <source bridge='br-fwd'/>
        <mac address='%vm_mac_fwd1%'/>
        <model type='virtio'/>
        <target dev='%vm_name%-fwd1'/>
      </interface>
      <graphics type='vnc' port='5900' autoport='yes' listen='0.0.0.0'>
        <listen type='address' address='0.0.0.0'/>
      </graphics>
      <video>
        <model type='cirrus' vram='16384' heads='1' primary='yes'/>
        <alias name='video0'/>
      </video>
      <memballoon model='virtio'>
        <alias name='balloon0'/>
      </memballoon>
    </devices>
  </domain>

KVM 网络配置文件编写

  <network>
    <name>br-fwd</name>
    <bridge name='br-fwd' stp='on' delay='0'/>

    <mac address='fe:c0:a8:de:01:00'/>
    <ip address='192.168.222.1' netmask='255.255.255.0'>
      <dhcp>
        <range start='192.168.222.2' end='192.168.222.254'/>
      </dhcp>
    </ip>
  </network>

KVM 虚机创建

手动执行步骤

  1. 创建虚机目录 mkdir vm-name

  2. 创建虚机磁盘 qemu-img create -f qcow2 -o backing_file=${backimg} ${vm_disk_path} ${vm_disk_size}G

  3. 编辑虚机配置文件 vm-name.xml

  4. 创建虚机 virsh define vm-name.xml

  5. 启动虚机 virsh start vm-name

脚本实现

  #!/bin/zsh
  #
  # custom const
  #
  vm_id=$(date "+%s")
  vm_root=/home/vms/
  vm_ip_fix_prefix=192.168.111.
  vm_ip_mgr_prefix=172.16.115.
  vm_disk_size=40

  backimg=/home/vms/base/backimg/common.qcow2
  vm_cfg_tmpl=/home/vms/base/cfg/tmpl.xml
  vm_net_cfg=/home/vms/base/cfg/net-tmpl.xml

  # interactivity
  read "vm_name?Enter VM Name: "
  read "vm_cpu?Enter CPU Num: "
  read "vm_mem?Enter Mem Size(GB): "
  read "vm_ip_suffix?Enter IP addr: $vm_ip_mgr_prefix"

  vm_ip_fix=${vm_ip_fix_prefix}${vm_ip_suffix}
  vm_ip_mgr=${vm_ip_mgr_prefix}${vm_ip_suffix}

  hex_ip=$(gethostip -x ${vm_ip_fix})
  hex_ip=$(sed 's/../&:/g' <<< ${hex_ip})
  vm_mac_wan=80:${hex_ip}EE
  vm_mac_mgr=80:${hex_ip}FF
  vm_mac_fwd0=80:${hex_ip}00
  vm_mac_fwd1=80:${hex_ip}01
  vm_path=${vm_root}${vm_name}
  vm_disk_path=${vm_path}/vm-sys.qcow2
  vm_cfg=/home/vms/${vm_name}/${vm_name}.xml

  vm_info=$(cat <<EOF
  > Name: $vm_name
  > CPU: $vm_cpu
  > Mem: $vm_mem GB
  > Disk: $vm_disk_size GB
  > VM Path: $vm_path
  > Disk Path: ${vm_disk_path}
  > Config: ${vm_cfg}
  > Mgr IP: ${vm_ip_mgr}
  > Fix IP: ${vm_ip_fix}
  > Mac Mgr : $vm_mac_mgr
  > Mac Wan : $vm_mac_wan
  > Mac FWD0: $vm_mac_fwd0
  > Mac FWD1: $vm_mac_fwd1
  EOF
  )

  echo
  echo "We are going to create ${vm_name}:"
  echo ${vm_info}
  echo

  read -q  "ans?Are you sure?"
  if [ "$ans" != "y" ]; then
          exit 0
  fi

  if [ -d ${vm_path} ]; then
          echo "${vm_path}" already EXISTS, please REMOVE it first or CHANGE VM name!
          exit 1
  fi

  mkdir -p ${vm_path}
  echo ${vm_info} > ${vm_path}/README

  # Create sys-disk
  qemu-img create -f qcow2 -o backing_file=${backimg} ${vm_disk_path} ${vm_disk_size}G
  #qemu-img create -f qcow2 ${vm_disk_path} ${vm_disk_size}G

  # Prepare define xml
  cp ${vm_cfg_tmpl} $vm_cfg
  sed -i "s/%vm_id%/$vm_id/g" $vm_cfg
  sed -i "s/%vm_name%/$vm_name/g" $vm_cfg
  sed -i "s/%vm_title%/$vm_ip_fix/g" $vm_cfg
  sed -i "s/%vm_cpu%/$vm_cpu/g" $vm_cfg
  sed -i "s/%vm_mem%/$vm_mem/g" $vm_cfg
  sed -i "s+%vm_disk_path%+$vm_disk_path+g" $vm_cfg
  sed -i "s/%vm_mac_mgr%/$vm_mac_mgr/g" $vm_cfg
  sed -i "s/%vm_mac_wan%/$vm_mac_wan/g" $vm_cfg
  sed -i "s/%vm_mac_fwd0%/$vm_mac_fwd0/g" $vm_cfg
  sed -i "s/%vm_mac_fwd1%/$vm_mac_fwd1/g" $vm_cfg

  # Update network
  sed -i "/%static-mac-ip%/ a       <host mac='${vm_mac_mgr}' name='${vm_name}' ip='${vm_ip_fix}'/>" $vm_net_cfg
  virsh net-update manage add ip-dhcp-host \
            "<host mac='${vm_mac_mgr}' \
             name='$vm_name' ip='${vm_ip_fix}' />" \
             --live --config

  virsh define ${vm_cfg}
  virsh start ${vm_name}