Create a Cloud Image With Kvm
Create a VM with GenericCloud Image
Installing software
yum install -y cloud-utils cloud-localds genisoimage
Create the Disk with NoCloud data on it
- touch cloud-config
cat cloud-config.cfg
#cloud-config
users:
- default
password: passw0rd
chpasswd: { expire: False }
ssh_pwauth: True
network: {config: disabled}
cloud-localds cloud-config.iso cloud-config.cfg
Download a Cloud Image
# get a centos stream generic cloud image
wget https://cloud.centos.org/centos/8-stream/x86_64/images/CentOS-Stream-GenericCloud-8-20210210.0.x86_64.qcow2
## Resize the image to 52G from original image of 2G
qemu-img resize CentOS-Stream-GenericCloud-8-20210210.0.x86_64.qcow2 +50G
qemu-img info CentOS-Stream-GenericCloud-8-20210210.0.x86_64.qcow2
Create a VM
virt-install \
--name centos-stream \
--boot hd,menu=on \
--ram 4096 \
--vcpu 2 \
--disk path=cloud-config.iso.device=cdrom \
--disk path=CentOS-Stream-GenericCloud-8-20210210.0.x86_64.qcow2,device=disk \
--graphics vnc \
--os-type linux \
--os-variant centos-stream8 \
--network network:default
Referance
KVM: Testing cloud-init locally using KVM for a CentOS cloud image
The ability to quickly stand up a guest OS with cloud-init is most often associated with deployment of virtual machines in an IaaS like EC2 or Azure.
But cloud-init is not just for remote cloud providers, and using cloud-init for local images that can be quickly deployed in KVM works great for local development and testing.
This article will step through testing a guest CentOS 7 cloud image on KVM, from an Ubuntu Host.
If you are instead looking for similar instructions for RHEL, see my other article.
Prerequisites
As a prerequisite for this article, you must install KVM and libvirt as described here.
Also install additional packages needed to manage cloud-images:
sudo apt-get install -y cloud-image-utils libosinfo-bin
CentOS Cloud Image
We will use the CentOS generic cloud image called “CentOS-7-x86_64-GenericCloud.qcow2“. Download this 942Mb file to your “~/Downloads” directory.
Create a snapshot so that we can branch from this disk image without affecting the parent. We will also use this opportunity to increase the root filesystem from 8G to 10G.
# original image is 8G, create snapshot and make it 10G
qemu-img create -b ~/Downloads/CentOS-7-x86_64-GenericCloud.qcow2 -f qcow2 snapshot-centos7-cloudimg.qcow2 10G
# show snapshot info
qemu-img info snapshot-centos7-cloudimg.qcow2
Create ssh keypair
In order to use ssh public/private key login later, we need to generate a keypair. cloud-init will embed the public side of the key into the running OS.
ssh-keygen -t rsa -b 4096 -f id_rsa -C ctest1 -N "" -q
This creates files named “id_rsa” and “id_rsa.pub”.
Create cloud-init configuration
Create a file named “cloud_init.cfg” with the below content.
#cloud-config
hostname: ctest1
fqdn: ctest1.example.com
manage_etc_hosts: true
users:
- name: centos
sudo: ALL=(ALL) NOPASSWD:ALL
groups: adm,sys
home: /home/centos
shell: /bin/bash
lock_passwd: false
ssh-authorized-keys:
- <sshPUBKEY>
# only cert auth via ssh (console access can still login)
ssh_pwauth: false
disable_root: false
timezone: 'Asia/Shanghai'
chpasswd:
list: |
root:linux
centos:newpass123
expire: False
packages:
- qemu-guest-agent
- bind-utils
- vim-enhanced
packages:
- vim
- wget
- httpd
# manually set BOOTPROTO for static IP
# older cloud-config binary has bug?
runcmd:
- [ sh, -c, 'sed -i s/BOOTPROTO=dhcp/BOOTPROTO=static/ /etc/sysconfig/network-scripts/ifcfg-eth0' ]
- [ sh, -c, 'ifdown eth0 && sleep 1 && ifup eth0 && sleep 1 && ip a' ]
# written to /var/log/cloud-init.log, /var/log/messages
final_message: "The system is finally up, after $UPTIME seconds"
Then replace the “<sshPUBKEY>” placeholder in the file above, with the content of “id_rsa.pub”.
Create network configuration
Create a file named “network_config_static.cfg” to define the networking parameters. We will use a simple static configuration on the default KVM bridge subnet.
yum_repos:
epel-163:
baseurl: http://mirrors.163.com/centos/$releasever/os/$basearch/
name: Centos-7
enabled: true
apt:
primary:
- arches: [default]
uri: "http://mirrors.aliyun.com/ubuntu/"
search:
- "http://mirrors.aliyun.com/ubuntu/"
resolv_conf:
nameservers: ['218.104.1xx.1xx', '8.8.8.8']
searchdomains:
- localdomain
domain: localdomain
options:
rotate: true
timeout: 1
version: 2
ethernets:
eth0:
dhcp4: false
# default libvirt network
addresses: [ 192.168.122.152/24 ]
gateway4: 192.168.122.1
nameservers:
addresses: [ 192.168.122.1,8.8.8.8 ]
search: [ example.com ]
Insert metadata into seed image
Now we generate a seed disk that has the cloud-config metadata.
# insert network and cloud config into seed image
cloud-localds -v --network-config=network_config_static.cfg ctest1-seed.qcow2 cloud_init.cfg
# show seed disk just generated
$ qemu-img info ctest1-seed.qcow2
image: ctest1-seed.qcow2
file format: raw
virtual size: 368K (376832 bytes)
disk size: 368K
Start VM
Now we use virtlib to create the guest VM with the cloud image and seed disk that has the cloud-init metadata.
# get supported list of os-variant
osinfo-query os | grep centos
# create guest VM
virt-install --name ctest1 \
--virt-type kvm --memory 2048 --vcpus 2 \
--boot hd,menu=on \
--disk path=ctest1-seed.qcow2,device=cdrom \
--disk path=snapshot-centos7-cloudimg.qcow2,device=disk \
--graphics vnc \
--os-type Linux --os-variant centos7.0 \
--network network:default
After the machine has booted up and you have given cloud-init a couple of minutes to configure networking, you should be able to login to this guest OS from either virt-viewer or ssh.
ssh [email protected] -i id_rsa
# final cloud-init status
cat /run/cloud-init/result.json
# cloud logs
sudo less /var/log/cloud-init.log
# cloud logs output
sudo grep cloud-init /var/log/messages
Disable cloud-init system
Once setup with the proper hostname, network config, packages, etc., you can either leave cloud-config enabled to enforce these settings, or you can disable cloud-init so that you can manage them yourself (or another tool).
From within OS:
# flag that signals that cloud-init should not run
sudo touch /etc/cloud/cloud-init.disabled
# optional, remove cloud-init completely
sudo yum remove cloud-init
# shutdown VM so CDROM seed can be ejected
sudo shutdown -h now
From host OS using virsh to control libvirt eject.
# get name of target path
targetDrive=$(virsh domblklist ctest1 | grep ctest1-seed.qcow2 | awk {' print $1 '})
# force ejection of CD
virsh change-media ctest1 --path $targetDrive --eject --force
cloud-init will no longer be invoked when the guest VM is powered back on.
More advanced example
For a more advanced example see my local-kvm-cloudimage/centos7 repository. This has full shell scripts and support for additional data disks formatted as xfs.
And here is the equivalent RHEL8.1 example.