cloud-init, cloud image e Cloud Hypervisor
Três nomes que aparecem juntos e fazem coisas diferentes. Confundi-los é a causa mais comum de uma VM que arranca e não faz nada — ou que não arranca de todo.
Os três em uma frase
| Peça | O que é | Quando aparece |
|---|---|---|
| cloud image (cloud-img) | Um disco .qcow2 publicado por uma distro, já instalado e
pronto a arrancar. Não tem utilizador, nem password, nem chave SSH. |
O FROM de um VMfile, e o disco base de
vm create. |
| cloud-init | O programa que corre DENTRO dessa imagem no primeiro arranque e a personaliza: hostname, utilizadores, chaves, rede, comandos. | É o que torna a cloud image utilizável. Sem ele, ficas com um disco genérico onde não consegues entrar. |
| Cloud Hypervisor | Um VMM — o programa do HOST que executa a VM. Alternativa ao QEMU/libvirt, feito para microVMs. | --backend cloud-hypervisor, e obrigatório para
type: microvm. |
Analogia com containers, que ajuda mais do que qualquer definição:
a cloud image é a imagem, o cloud-init é o
ENTRYPOINT da primeira execução, e o
Cloud Hypervisor é o runtime — o equivalente ao
runc.
1 · Cloud image — o disco
Cada distro publica um qcow2 pré-instalado, pequeno (algumas centenas de MB) e com o cloud-init já lá dentro. O motor sabe descarregar três famílias e verifica sempre o checksum — nunca aceita um download sem o confrontar.
# Num VMfile
FROM ubuntu:24.04 # cloud-images.ubuntu.com
FROM debian:bookworm # cloud.debian.org
FROM rocky:9 # dl.rockylinux.org
# Ou directamente, sem VMfile nenhum
delonix vm create dev --url-img https://.../imagem.qcow2
Os três publicam checksums de maneiras diferentes,
e isso está tratado: Ubuntu usa SHA256SUMS no formato GNU, Debian
publica só SHA512SUMS (não há SHA256 nenhum), e
Rocky usa um .CHECKSUM por ficheiro no formato BSD
SHA256 (ficheiro) = hash. Com --url-img, o motor
procura um <url>.sha256 ao lado; se não existir,
diz que está a confiar só no TLS em vez de calar.
O disco vem pequeno de propósito — tipicamente 2 GB. Cresce-o
antes de instalares seja o que for, com SIZE 20G no VMfile: crescer
depois de um RUN ter enchido o disco é tarde.
2 · cloud-init — a personalização do primeiro arranque
Uma cloud image acabada de descarregar não tem conta nenhuma. O cloud-init
procura um datasource no arranque, lê de lá o user-data, e
aplica-o. O Delonix usa o datasource NoCloud: gera um ISO por
instância e liga-o à VM.
# O motor gera o seed sozinho quando lhe dás qualquer um destes
delonix vm create dev --hostname dev-01 --ssh-key ~/.ssh/id_ed25519.pub
delonix vm create dev --user-data ./user-data.yaml
delonix vm create dev --seed ./o-meu-seed.iso # ISO já feito por ti
Um user-data mínimo e completo:
#cloud-config
hostname: dev-01
users:
- name: delonix
groups: [sudo]
shell: /bin/bash
sudo: ['ALL=(ALL) NOPASSWD:ALL']
ssh_authorized_keys:
- ssh-ed25519 AAAA... o-teu-email@exemplo
package_update: true
packages: [nginx]
runcmd:
- [systemctl, enable, --now, nginx]
final_message: "pronto em $UPTIME segundos"
Duas camadas, e a distinção importa. O
CLOUDINIT de um VMfile é assado NA IMAGEM (fica em
/etc/cloud/cloud.cfg.d) — é o comportamento por omissão de
todas as VMs feitas a partir dela. O --user-data do
vm create é POR INSTÂNCIA e assenta por cima. Uma é a receita da
imagem, a outra é a configuração daquela VM.
Armadilha real, já paga: um kind: VirtualMachine
sem seed nenhum fazia o cloud-init saltar a fase de rede, e a VM ficava sem IP e
sem rota (Network is unreachable lá dentro). Por isso o motor gera
sempre um seed mínimo, mesmo quando não pedes nada — não é
opcional.
E o cloud-init só corre uma vez: guarda um marcador em
/var/lib/cloud. Um vm restart não o volta a executar —
para reaplicar, ou limpas esse estado dentro da VM ou crias outra.
3 · Cloud Hypervisor — o VMM
Quem executa a VM. O Delonix tem dois backends por trás do mesmo trait, e a escolha não é cosmética:
| Cloud Hypervisor | libvirt/QEMU | |
|---|---|---|
| Arranque | Muito rápido (dezenas de ms) — feito para microVMs | Segundos; máquina completa emulada |
| Dispositivos | Só virtio, mínimo | Tudo (TPM, vídeo, USB, …) |
| Rede | tap na SDN do holder — alcança os
containers por IP directo |
virbr0, no netns do HOST — outra L2 |
| Isolamento por namespace | Sim | Não — --namespace é recusado com erro, não
aceite-e-ignorado |
| Snapshots | Não implementado (fail-closed, com erro que aponta para o libvirt) | vm snapshot/restore: memória + disco |
| Arranque de cloud image | Precisa de firmware
(hypervisor-fw/EDK2) ou de kernel+initrd directos |
Arranca a cloud image tal como está |
delonix vm create micro --backend cloud-hypervisor --firmware /usr/share/hypervisor-fw
delonix vm create pesada --backend libvirt # default quando CH não está instalado
Qual usar, e quando
| Se queres… | Usa |
|---|---|
| Uma VM de trabalho, com snapshots e consola | --backend libvirt (é o que a golden do Kubernetes exige — não arranca em CH) |
| Arranque em milissegundos, isolamento por namespace, ou alcançar containers por IP | --backend cloud-hypervisor + firmware |
| Personalizar UMA VM | cloud-init por instância: --hostname/--ssh-key/--user-data |
| Personalizar TODAS as VMs de um modelo | Um VMfile com CLOUDINIT, e image vm build |
| Um disco à tua medida, publicável | vm init --vmfile → image vm build → vm push |
Onde isto falha, e o que ver
| Sintoma | Quase sempre é |
|---|---|
| A VM arranca e não entras por SSH | Sem seed de cloud-init — não há utilizador nenhum. Passa
--ssh-key. |
IP <none> para sempre |
libvirt caiu em modo user (SLIRP), cujo IP é invisível. Junta-te
ao grupo libvirt — o motor avisa-o no create. |
| A VM não arranca em Cloud Hypervisor | Falta o firmware. CH não faz boot BIOS: precisa de
--firmware ou de --kernel+--initrd. |
O disco enche a meio do image vm build |
SIZE em falta, ou depois de um RUN. É
propriedade da stage, e corre antes de tudo. |
Mudaste o user-data e nada muda |
O cloud-init já correu naquela VM. Cria outra, ou limpa
/var/lib/cloud lá dentro. |
Referências
- Documentação do cloud-init — e em especial os exemplos de user-data e o datasource NoCloud, que é o que o Delonix usa.
- Cloud images do Ubuntu · do Debian · do Rocky
- Cloud Hypervisor e o
rust-hypervisor-firmware
(o
--firmwareque uma cloud image precisa para arrancar em CH). - Formato do domínio libvirt —
útil se usares os escape-hatches
libvirtXml/libvirtXmlOverlaydokind: VirtualMachine.
cloud-init, cloud image and Cloud Hypervisor
Three names that show up together and do different things. Mixing them up is the most common reason a VM boots and does nothing — or doesn't boot at all.
The three, in one sentence each
| Piece | What it is | Where it shows up |
|---|---|---|
| cloud image (cloud-img) | A .qcow2 disk published by a distro, already installed and
ready to boot. No user, no password, no SSH key. |
The FROM of a VMfile, and the base disk for
vm create. |
| cloud-init | The program that runs INSIDE that image on first boot and customizes it: hostname, users, keys, networking, commands. | It's what makes the cloud image usable. Without it you're left with a generic disk you can't log into. |
| Cloud Hypervisor | A VMM — the HOST-side program that runs the VM. An alternative to QEMU/libvirt, built for microVMs. | --backend cloud-hypervisor, and required for
type: microvm. |
A container analogy helps more than any definition:
the cloud image is the image, cloud-init is the
ENTRYPOINT of the first run, and
Cloud Hypervisor is the runtime — the equivalent of
runc.
1 · Cloud image — the disk
Every distro publishes a small (a few hundred MB), pre-installed qcow2 with cloud-init already inside. The engine knows how to download three families and always verifies the checksum — it never accepts a download without checking it against one.
# In a VMfile
FROM ubuntu:24.04 # cloud-images.ubuntu.com
FROM debian:bookworm # cloud.debian.org
FROM rocky:9 # dl.rockylinux.org
# Or directly, with no VMfile at all
delonix vm create dev --url-img https://.../image.qcow2
The three publish checksums differently,
and that's handled: Ubuntu uses SHA256SUMS in GNU format, Debian
publishes only SHA512SUMS (no SHA256 at all), and
Rocky uses a per-file .CHECKSUM in BSD format
(SHA256 (file) = hash). With --url-img, the engine
looks for a <url>.sha256 next to it; if it doesn't exist, it
says so — trusting TLS alone — instead of staying quiet.
The disk comes small on purpose — typically 2 GB. Grow it
before installing anything, with SIZE 20G in the VMfile: growing it
after a RUN has already filled the disk is too late.
2 · cloud-init — first-boot customization
A freshly downloaded cloud image has no account at all. cloud-init
looks for a datasource at boot, reads user-data from it, and
applies it. Delonix uses the NoCloud datasource: it generates a
per-instance ISO and attaches it to the VM.
# The engine generates the seed on its own when you give it any of these
delonix vm create dev --hostname dev-01 --ssh-key ~/.ssh/id_ed25519.pub
delonix vm create dev --user-data ./user-data.yaml
delonix vm create dev --seed ./my-seed.iso # ISO you already built yourself
A minimal, complete user-data:
#cloud-config
hostname: dev-01
users:
- name: delonix
groups: [sudo]
shell: /bin/bash
sudo: ['ALL=(ALL) NOPASSWD:ALL']
ssh_authorized_keys:
- ssh-ed25519 AAAA... your-email@example
package_update: true
packages: [nginx]
runcmd:
- [systemctl, enable, --now, nginx]
final_message: "ready in $UPTIME seconds"
Two layers, and the distinction matters. A
VMfile's CLOUDINIT is baked INTO THE IMAGE (it lands in
/etc/cloud/cloud.cfg.d) — it's the default behavior of
every VM made from it. vm create's --user-data
is PER INSTANCE and sits on top. One is the image's recipe, the other is that
particular VM's configuration.
Real trap, already paid for: a kind: VirtualMachine
with no seed at all made cloud-init skip the networking phase, leaving the VM
with no IP and no route (Network is unreachable inside). That's why the
engine always generates a minimal seed, even when you ask for
nothing — it isn't optional.
And cloud-init only runs once: it keeps a marker in
/var/lib/cloud. A vm restart doesn't re-run it —
to reapply it, either clear that state inside the VM or create another one.
3 · Cloud Hypervisor — the VMM
The thing that runs the VM. Delonix has two backends behind the same trait, and the choice isn't cosmetic:
| Cloud Hypervisor | libvirt/QEMU | |
|---|---|---|
| Boot | Very fast (tens of ms) — built for microVMs | Seconds; a full emulated machine |
| Devices | virtio only, minimal | Everything (TPM, video, USB, …) |
| Networking | tap on the holder's SDN — reaches
containers by direct IP |
virbr0, in the HOST's netns — a different L2 |
| Per-namespace isolation | Yes | No — --namespace is refused with an error,
never accepted-and-ignored |
| Snapshots | Not implemented (fail-closed, with an error pointing at libvirt) | vm snapshot/restore: memory + disk |
| Booting a cloud image | Needs firmware
(hypervisor-fw/EDK2) or a direct kernel+initrd |
Boots the cloud image as-is |
delonix vm create micro --backend cloud-hypervisor --firmware /usr/share/hypervisor-fw
delonix vm create heavy --backend libvirt # default when CH isn't installed
Which to use, and when
| If you want… | Use |
|---|---|
| A workhorse VM, with snapshots and a console | --backend libvirt (it's what the Kubernetes golden image needs — it won't boot in CH) |
| Millisecond boot, per-namespace isolation, or to reach containers by IP | --backend cloud-hypervisor + firmware |
| To customize ONE VM | Per-instance cloud-init: --hostname/--ssh-key/--user-data |
| To customize EVERY VM from one template | A VMfile with CLOUDINIT, and image vm build |
| Your own publishable disk | vm init --vmfile → image vm build → vm push |
Where this breaks, and what to check
| Symptom | Almost always |
|---|---|
| The VM boots and you can't SSH in | No cloud-init seed — there's no user at all. Pass
--ssh-key. |
IP <none> forever |
libvirt fell back to user mode (SLIRP), whose IP is invisible. Join
the libvirt group — the engine warns about it at create. |
| The VM won't boot under Cloud Hypervisor | Missing firmware. CH doesn't do BIOS boot: it needs
--firmware or --kernel+--initrd. |
The disk fills up mid-image vm build |
Missing SIZE, or set after a RUN. It's a
stage property, and runs before everything else. |
You changed user-data and nothing changes |
cloud-init already ran on that VM. Create another one, or clear
/var/lib/cloud inside it. |
References
- cloud-init documentation — especially the user-data examples and the NoCloud datasource, which is what Delonix uses.
- Ubuntu cloud images · Debian's · Rocky's
- Cloud Hypervisor and
rust-hypervisor-firmware
(the
--firmwarea cloud image needs to boot under CH). - libvirt domain format —
useful if you use the
kind: VirtualMachineescape hatcheslibvirtXml/libvirtXmlOverlay.