Cloud-Init - Getting Started

If you read my blog carefully, you may know that I really like Ansible. You can manage and configure machines and APIs very easily with Ansible automation. But what if the machine is offline or needs a configuration to boot up? This is where Cloud-Init can help.

Cloud-Init - Getting Started
Photo by aziz ayad / Unsplash

If you read my blog carefully, you may know that I really like Ansible. You can manage and configure machines and APIs very easily with Ansible automation. But what if the machine is offline or needs a configuration to boot up? This is where Cloud-Init can help. Configurations are written in YAML, which fits well in my tool chain.

Cloud-Init

Cloud-Init is a quite simple, but powerful way to configure machines. Let's assume, you want to set up a machine, that requires some settings pre-configured or packages installed. You can think of "create a user" or "install updates", but also "configure network" or "put a file there".

Other than Kickstart, Cloud-Init works for almost all major GNU/Linux distributions, as long as you are using a special cloud-image. But what is special about these images? And how does this work?

How does Cloud-Init work?

Under the hood, the whole Cloud-Init magic seems trivial.

  1. a new cloud-image machine starts and therefore the cloud-init.service is started, too
  2. cloud-init checks, validates and parses an existing cloud-init configuration
  3. cloud-init applies (via API, Commands and config changes) the cloud-init configuration

That's already it. If there is no Cloud-Init configuration, nothing will be done. Optionally, a reboot is triggered.

The above-mentioned "apply" stage works in modules, which are somewhat nicely documented in the official documentation.

Where to use Cloud-Init?

Ok, so where can one use Cloud-Init? Looking at the name, it should work in the cloud. Chances are high, that your hosting/cloud provider already supports it. You can expect very sane support in at least:

  • OpenStack
  • Apache CloudStack
  • Amazone Web Services (AWS)
  • Microsoft Azure
  • Exoscale
  • KVM/libvirt
  • and many more

Most of them can also be consumed as a data source for additional features.

Using Cloud-Init with Fedora

Enough of the theory, let's make something work. I suggest starting with Fedora VM on a KVM/libvirt hypervisor/host. Anyway, the example can also be used with Ubuntu, CentOS or AlmaLinux as long as the libvirt/virt-install version is recent enough.

Hint
The guide is tested on Fedora 36 with virt-install 4.0.0.

Requirements

To follow the guide, you need to have a Fedora 36 KVM host, similar to the home server setup as described in "Fedora - Home Server (Virtualization)" article. In addition, you will need "virt-install" in Version 3.0.0 or newer.

If you have a plain Fedora Server or Workstation, you can install all necessary tooling with a single command:

# Install needed tooling
$ sudo dnf install \
  libvirt-daemon-config-network \
  libvirt-daemon-kvm \
  qemu-kvm \
  virt-install

# Reboot afterwards
$ reboot

Now, we can already check if everything works.

# Check user VMs
$ virsh list
 Id   Name   State
--------------------

# Check root VMs
$ sudo virsh list
 Id   Name   State
--------------------

The next thing we need is a Cloud Image. I am using the Fedora 36 Cloud Base Image for now. But, please feel free to test your configuration with another Cloud Image like Ubuntu.

# Download image
$ wget https://download.fedoraproject.org/pub/fedora/linux/releases/36/Cloud/x86_64/images/Fedora-Cloud-Base-36-1.5.x86_64.qcow2

Now, we can start to play with Cloud-Init deployments.

SImple Cloud-Init Configuration

Writing a Cloud-Init configuration can be done with basically every editor. Let's start with something really simple. Just create a new file, named "user-data" and fill in the below.

#cloud-config

system_info:
  default_user:
    name: "admin"
    plain_text_passwd: "password"
    lock_passwd: false
    groups: ["wheel"]
user-data

This will configure the VM with a user and the given password.

Starting a VM

Now, that we are having the image and the configuration, we can use the virt-install command to create a machine and apply the Cloud-Init configuration.

# Copy the image / Create the disk
$ cp Fedora-Cloud-Base-36-1.5.x86_64.qcow2 vm01.qcow2

# Create the VM
$ virt-install \
  --name vm01 \
  --memory 1024 \
  --vcpus 2 \
  --graphics none \
  --os-variant detect=on \
  --import \
  --disk vm01.qcow2,size=20 \
  --graphics none \
  --console pty,target_type=virtio \
  --serial pty \
  --cloud-init user-data=user-data

After firing the above command, you will find yourself in a new console (inside the VM). Waiting for some seconds and you can monitor the initial bootup. At the end, you can log in to your VM with the above provided user and password.

Starting install...
Creating domain...                                                                                                                                                                                                      |    0 B  00:00:00     
Running text console command: virsh --connect qemu:///session console vm01
Connected to domain 'vm01'
Escape character is ^] (Ctrl + ])

...SNIP...

[  OK  ] Finished cloud-final.servi… Execute cloud user/final scripts.
[  OK  ] Reached target cloud-init.target - Cloud-init target.

Fedora Linux 36 (Cloud Edition)
Kernel 5.17.5-300.fc36.x86_64 on an x86_64 (ttyS0)

fedora login: 

Feel free to play in the VM as you like. If you are done, you can quit the console with the mentioned Escape sequence ( CTRL + ALT Gr + 9 on a German keyboard).

After quitting the VM, it will be running in the background. If you like, you can stop/start or remove the machine with the below commands.

# List VMs
$ virsh list
 Id   Name   State
----------------------
 1    vm01   running

# Stop VM
$ virsh destroy vm01
Domain 'vm01' destroyed

# Start VM
$ virsh start vm01
Domain 'vm01' started

# Remove VM (after stopping)
$ virsh undefine vm01
Domain 'vm01' has been undefined

# Connect to the console of a VM
$ virsh console vm01

The virsh command is very powerful, and we may revisit at some point. For now, this should be sufficient.

Another Cloud-Init Configuration

For a more advanced example, we can also install a couple of packages, and apply updates.

#cloud-config

preserve_hostname: false
fqdn: "vm01"

system_info:
  default_user:
    name: "admin"
    plain_text_passwd: "password"
    lock_passwd: false
    groups: ["wheel"]

packages:
  - "wget"
  - "bash-completion"

package_update: true
package_reboot_if_required: true

write_files:
  - path: "/root/hello.txt"
    owner: "root:root"
    permissions: '0644'
    content: "Hello World!"
user-data

Inside the VM:

# Connect to the VM
$ virsh console VMNAME

Connected to domain 'vm01'
Escape character is ^] (Ctrl + ])

vm01 login: admin
Password:

[admin@vm01 ~]$ sudo cat /root/hello.txt
Hello World!

Feel free to enhance your configuration even more with the available Cloud-Init modules.

Alternatives

There are a couple of alternatives for different purposes, which I want to link here.

Kickstart was already addressed in another article and is mostly for RHEL family systems and bare metal or "from scratch" installations.

Preseed and Autoyast are similar to Kickstart, but meant for Debian/Ubuntu and SUSE Linux.

There is also Ignition, which is intended for cloud deployments and origins in CoreOS. It is used for Fedora CoreOS.

If you want to dig deeper, you can have a look at the official documentation and some nice articles about the same topic.

cloud-init Documentation — cloud-init 22.2 documentation
Module Reference — cloud-init 22.2 documentation
libvirt: virsh
libvirt, virtualization, virtualization API
Setting up a VM on Fedora Server using Cloud Images and virt-install version 3 - Fedora Magazine
Many servers use one or more virtual machines (VMs), e.g. to isolate a public service in the best possible way and to protect the host server from compromise. This article explores the possibilities of deploying Fedora Cloud Base images as a VM in an autonomous Fedora 33 Server Edition using version…
Installation of a libvirt VM over a serial console
Installing a libvirt VM over a serial console

Conclusion

That's it for today. Feel free to play with Cloud-Init and reach out to me, if I forgot something. I would also love to get your feedback if you use Cloud-Init and if there are any other mechanisms, that should be added to the article.