(April 2018)
We want to create an isolated virtual test environment, where it’s easy to spin-up virtual machines.
Install any necessary packages for basic KVM virtualization, including kvm
, qemu-kvm
, and libvirt-clients
on Debian, or libvirt-daemon-kvm
, qemu-kvm
, and libvirt-client
on RHEL.
To run the VM as a non-root user, this may be necessary:
# usermod -a -G kvm paulgorman
A Linux bridge device acts like a virtual network switch.
Even if we already have a bridge for routine use (i.e., for use with KVM or Docker), create a new bridge for the isolated test lab.
It may be useful to install the bridge-utils
package.
# ip link add lab-br0 type bridge
Possibly needed:
# chmod 4755 /usr/lib/qemu/qemu-bridge-helper
In order to conserve disk space, and make instance create fast and easy, we’ll use QEMU’s ability to make new sparse disk images based on existing qcow2 images.
Install the qemu-tools
package on Debian or qemu-img
on RHEL.
Download a qcow2 base image to use for instances.
It’s important to use a particular version for the base image, since replacing the base image with the “latest” version would corrupt images derived from the previous base.
$ wget https://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-1802.qcow2.xz
$ tar xf CentOS-7-x86_64-GenericCloud-1802.qcow2.xz
# mv CentOS-7-x86_64-GenericCloud-1802.qcow2 /var/lib/libvirt/images/
Because all our instances start from the same vendor-supplied base image, we use cloud-init to customize them for our needs.
Minimally, we install a key so we can SSH into the instances.
If we care about setting unique hostnames, we’ll generate a new meta-data
file for each instance and recreate the cidata.vfat
image;
otherwise, reuse the image for every instance.
$ mkdir -p ~/libvirt/cidata
$ cat > ~/libvirt/cidata/meta-data <<EOF
instance-id: iid-012345
local-hostname: myvm-CentOS-7-x86_64
EOF
$ cat > ~/libvirt/cidata/user-data <<EOF
#cloud-config
timezone: America/Detroit
locale: en_US.UTF-8
users:
- name: paul
sudo: ['ALL=(ALL) NOPASSWD:ALL']
groups: sudo
shell: /bin/bash
ssh-authorized-keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCqia4ScZ+v6Xuvv4HrTf8R4wIHAilul7LIlR5LTKzHvaCXrTAVkIqUh24cJMEXCOKyS7kOnAkbNzKj/NPQNfw42JQzmGF3I6VT8HTqi2NfTQ4lqe+vaMiCZU9mW8q33J3SJzLD/HciA2SqqSr3QMI9urDLn3ovpyKqYjxnSo1f9i+LInmXGgFfmuWbGAXpWJynUJxNB0WzZa3NpCmexlXw1f27tR1+fSJUDDP/HfwimmZms7Q8hA03dohI5OJmbI2YAX+OrHfO4Bya1jeQ5vLKoEX7jywLXHhuJORJ+6fUZBGL6sMQGfWKbYThdpU8MDmNSlOdHl9incoS8b9Q8trJ
EOF
$ virt-make-fs --type=vfat --label=cidata ~/libvirt/cidata ~/libvirt/cidata.vfat
We can reuse this cidata.vfat
image for ever instance we create (unless we care about setting different hostnames or usernames on initial boot).
I have run into problems getting the Debian OpenStack qcow2 image to do nocloud cloud-init against a vfat image.
For debian, I use the cloud-localds
utility (from the cloud-image-utils
package) instead (which, I think, uses genisoimage
):
$ cloud-localds -v ~/libvirt/seed.img ~/libvirt/cidata/user-data.yaml ~/libvirt/cidata/meta-data.yaml
#!/bin/sh
set -euf
d=$(date +%Y%m%d%H%M%S)
vm=lab-"$d"-CentOS-7
echo "Set VM name to $vm"
cat > ~/libvirt/cidata/meta-data <<EOF
instance-id: iid-$d
local-hostname: $vm
EOF
virt-make-fs --type=vfat --label=cidata ~/libvirt/cidata ~/libvirt/cidata.vfat
qemu-img create -f qcow2 -b /var/lib/libvirt/images/CentOS-7-x86_64-GenericCloud-1802.qcow2 ~/libvirt/"$vm".qcow2 10G
virt-install --import \
--name="$vm" \
--ram=1024 \
--vcpus=1 \
--network bridge=lab-br0 \
--os-variant rhel7.4 \
--graphics none \
--disk ~/libvirt/"$vm".qcow2 \
--disk ~/libvirt/cidata.vfat
virsh detach-disk --persistent "$vm" vdb
Press Ctrl-]
to disconnect from the instance console.
Delete an instance:
#!/bin/sh
set -euf
virsh destroy $1
virsh undefine $1
rm ~/libvirt/"$1".qcow2
Create a Debian instance:
#!/bin/sh
set -euf
d=$(date +%Y%m%d%H%m%S)
vm=asterisk-"$d"-Debian-9
echo "VM name set to $vm"
cat > ~/libvirt/cidata/meta-data <<EOF
instance-id: iid-$d
local-hostname: $vm
EOF
cat > ~/libvirt/cidata/user-data <<EOF
#cloud-config
users:
- name: paul
groups: sudo
shell: /bin/bash
sudo: ['all=(all) nopasswd:all']
ssh-authorized-keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCuGq7I5AhEs9EcZ0sbHpo0fSYZczjrfZcFBsH0ue/S7wcmvTBNWc2t9vy8SpT+HwRngeLYSgBkWcOxwrWvQlXY9e4E9m2/CAQu57X2gI5NhiiCLHloqQ4c+kJmgr4fHbAPtgqHIpT7Bzd5CmKn0IFyzlDr9VpyDtCNeRk44Ery80yfMkoKVOzlIHKct5TeTfnZPjtgKoLJkgYg6+G9x6QWOmfL1l5Bj7WKZcC57jQsf+gxFnwZrfdSaCCg3h4F+F0KlkYvGjE3c5CJY8je41sMVKkoC9ujfZNdT0n3rNb9KTlwv+nPICFIK8gn3hwjNcKWyKs0qw5CZHDZiZNpXegIWhJdIAE0X1txDtXYJ7yM/sO3U3pa8DR0AoyY/3TgML8Y/fpqk4nWDi/s/7dDRlbNaD6pi5xYe+qeYJUhw77B39jKWHn8o+1IPzuulqNF3A0xC5+lXoTaXWIiNVv+sZB6nZkjrWuW6NcMFJdFQ2G6t4K/+9TcOUygh95/Ekx7iGN9uw1G4Xv5eRHSb7bJbi7Q2z5ZTh0i2XOYqp5aHtcm7Ok9VzzvScr8TRgp4hafh5IWoABWNTedod0a9spaYEZo9lpQk7UekY37tucqj81N9wramCbYecCLjUhMSpr9/SLDgHAzId1XoaZ99PId4g1FLOms7u11U3bTTW4BGbP4tw==
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDWc7JmBmB3hZuyU48PXsJhWR4BKM0ILyj4esC47r8xKBzd2ZfFQtKaM0ZLPbEcYEYcpWHB7BlIohRhtAZl7gblyGxd9M3e4tfHXea8j1b0OsdImNw5IlUEET3MsdlcUY8OmFud6y8bMoAfdep4mQf5CcEcod5uJyoNOCO1LHR4coAH+KEWhV2pzQHm+AI6Kwcmj4BflAHg4b09sDzPlrLgT//nKtqkOtJW7T5FqzbrmCksEF6hW/wq9Ov4Mmc0UIclsAHXIrL4UW9o8JEwPmxY8t3jgDgXGii7shMxlCvjcmFcEh2Xr+dVsD1GhSw3UBckKzv8W00z2nle8M9F+EkCzvQwviXVaiH2qCIlOs4v+p+WC0ERpBOtTqFtiF1quKDATU2mH+/5i3mm+jphXqaz5s6k9gf0Bmgx2La5cuIQXPDtWfMsqZKe7R3dlJwfL6qk5KrmcsUj2KZStlEZeFlCHTA1OHG7aY0dkeI3LpgNJkKKSfOgqEnznvXgqmQadMIWMoH7FyqCsPQNKbkUGAae0lzCMCXoB0mIf+MzIdm/F3Heaa0UqNzspjhZGaIDf/1vFZ4MCJtxU7K6CStDn6AJTlpRxD5XjFb7rdXOWwq6Bu0V/xS+Yuk615gud75l6vvhNw1j4mPOG+mcu07h4GI/y4G05YaeVhcVod8Y4uRBzQ==
EOF
cloud-localds -v ~/libvirt/seed.iso ~/libvirt/cidata/user-data ~/libvirt/cidata/meta-data
qemu-img create -f qcow2 -b /var/lib/libvirt/images/debian-9.4.3-20180416-openstack-amd64.qcow2 ~/libvirt/"$vm".qcow2 10g
virt-install --import \
--name="$vm" \
--ram=1024 \
--vcpus=1 \
--cpu host \
--network bridge=br0 \
--os-variant debian9 \
--disk ~/libvirt/"$vm".qcow2 \
--graphics spice \
--disk ~/libvirt/seed.iso
virsh detach-disk --persistent "$vm" vdb
Debian doesn’t seem to like the graphics none
for the install.
Although, after starting the spice window, we can do virsh console asterisk-20180417220424-Debian-9
.
Sometimes cloud-init slows down boot. There’s little reason to run it after install. Disable cloud-init like:
# touch /etc/cloud/cloud-init.disabled
We use Ansible to do post-install configureation of the new instance.
$ ansible-playbook ~/ansible/lab-centos7.yaml --inventory 10.0.0.147,
…with the playbook like:
- hosts: all
vars_prompt:
- name: "hosts"
prompt: "Which hosts do we set up (hit ENTER for all)?"
private: no
become: true
tasks:
- name: Install minimal custom .bashrc
copy:
src: /home/paulgorman/repo/dotfiles/minimal-bashrc
dest: /home/paulgorman/.bashrc
- name: Install minimal custom .vimrc
copy:
src: /home/paulgorman/repo/dotfiles/minimal-vimrc
dest: /home/paulgorman/.vimrc
- name: Install minimal custom .tmux.conf
copy:
src: /home/paulgorman/repo/dotfiles/minimal-tmux.conf
dest: /home/paulgorman/.tmux.conf
- name: Get the latest updates
yum:
name: '*'
state: latest
- name: Add EPOL repo
yum:
name: epel-release
state: latest
- name: Install tmux
yum:
name: tmux
state: latest
- name: Install wget
yum:
name: wget
state: latest
- name: Install curl
yum:
name: curl
state: latest
- name: Install vim
yum:
name: vim-enhanced
state: latest
- name: Install git
yum:
name: git
state: latest