In this blog post I will outline the steps you can take to create a local testing setup to initialize images with Cloud-init.
In this blog post you will learn something about:
To follow along I assume you are on Linux and you are comfortable with installing software from your package manager. If you don’t have the commands I use, I assume you are able to install them yourself.
What is Cloud-init? Clout-init is software that can be used to initialize images on first boot. The typical use case is as follows: you have an image that you need to deploy somewhere, and you need to apply some configurations: add users, install your public ssh key, install software, etc. Very useful stuff as almost everyone needs to do this. You only need to do it once by hand to realize its very tedious, error prone, and not replicable.
You can checkout the Cloud-init documentation here.
It can take some time to install an image on a virtual machine in the cloud. Whenever you are trying to create a Cloud-init config its too tedious to test this configuration directly into the cloud, it takes too long, and its much harder to debug when something goes wrong.
I needed to deploy images to the cloud, and wanted to make sure my Cloud-init config did what it was supposed to. In blog post I will outline the steps I took.
To obtain a basic Cloud-init setup I followed the qemu tutorial on their website. It boils down to these steps:
Create a test folder:
mkdir cloud-init-test-folder cd cloud-init-test-folder
Download an image (with Cloud-init installed):
Create a yaml file called
user-data containing the following:
#cloud-config password: password chpasswd: expire: False
This configuration changes the password of the default user (in this case the username is
ubuntu) to password.
Host your configurations using a simple http server:
python3 -m http.server --directory .
Start the vm with
qemu-system-x86_64 (qemu stands for quick emulator):
qemu-system-x86_64 \ -net nic \ -net user \ -machine accel=kvm:tcg \ -cpu host \ -m 512 \ # This gives you VM 512MiB of memory -nographic \ # Run in the terminal -hda jammy-server-cloudimg-amd64.img \ -smbios type=1,serial=ds='nocloud-net;s=http://10.0.2.2:8000/'
Log in with username:
ubuntu and password:
password to verify whether it worked. Press Control + a followed by x (
C-a x) to kill the vm.
The basic setup is not enough when you want to test properly i.e. you most likely want to install some software. I tried installing software using the above setup and immediately (not surprisingly) ran out of disk space.
That’s when I realized (of course) you cannot install additional software on an
img that is only the size of its contents.
This is where the qcow2 disk format comes in.
qcow2 format is am image format that is resizable, and only takes up the amount it needs.
Super useful for this particular use case.
What I did was the following:
You can do that with these commands:
cp jammy-server-cloudimg-amd64.img fresh.img && qemu-img convert -O qcow2 fresh.img fresh.qcow2 && qemu-img resize fresh.qcow2 +10G
Each time you want to tryout a new Cloud-init configuration. You can run these commands again to obtain a fresh image.
I change the
user-data to something a little more interesting (check the documentation for other, way more secure, example configurations).
This configuration configures a Jupyterhub server on first boot:
#cloud-config # add admin user with password # This is purely for demonstration # Please configure something secure yourself users: - default - name: admin shell: /bin/bash sudo: ['ALL=(ALL) NOPASSWD:ALL'] lock_passwd: false plain_text_passwd: password # update and install packages package_update: true package_upgrade: true # turn on ssh with password authentication ssh_pwauth: true # install jupyterhub and add an admin user runcmd: - "curl -L https://tljh.jupyter.org/bootstrap.py | python3 - --admin admin:password"
Again, host your configurations with:
python3 -m http.server --directory .
qemu-system-x86_64 \ -net nic \ -net user,hostfwd=tcp::10022-:22 \ # Configure ssh port to be 10022 -machine accel=kvm:tcg \ -cpu host \ -m 2048 \ # Increase the memory to 2GB -nographic \ -hda fresh.qcow2 \ -smbios type=1,serial=ds='nocloud-net;s=http://10.0.2.2:8000/'
Now you can ssh into your VM with password:
ssh admin@localhost -p 10022
Cool stuff! I ended up trying a whole lot of different configurations, so I was really happy that I took the time to figure out this setup.
Some Commands I found useful:
cloud-init schema --config-file user-data
cloud-init status --wait. This is especially useful when deploying in the actual cloud. Ssh into your VM and run that command and see whether Cloud-init is finished or still running.